mirror of
https://github.com/open-goal/jak-project.git
synced 2025-02-17 12:40:04 +00:00
[Compiler] Bitfield Types (#146)
* add the ability to define and read bitfield types * new set * add bitfield setting * add static bitfields
This commit is contained in:
parent
4138e5d852
commit
21fbdce7aa
@ -502,6 +502,15 @@ bool StructureType::operator==(const Type& other) const {
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
bool BitFieldType::operator==(const Type& other) const {
|
||||
if (typeid(*this) != typeid(other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* p_other = dynamic_cast<const BitFieldType*>(&other);
|
||||
return other.is_equal(*this) && m_fields == p_other->m_fields;
|
||||
}
|
||||
|
||||
int StructureType::get_size_in_memory() const {
|
||||
return m_size_in_mem;
|
||||
}
|
||||
@ -568,4 +577,44 @@ std::string BasicType::print() const {
|
||||
|
||||
int BasicType::get_offset() const {
|
||||
return BASIC_OFFSET;
|
||||
}
|
||||
|
||||
/////////////////
|
||||
// Bitfield
|
||||
/////////////////
|
||||
|
||||
BitField::BitField(TypeSpec type, std::string name, int offset, int size)
|
||||
: m_type(std::move(type)), m_name(std::move(name)), m_offset(offset), m_size(size) {}
|
||||
|
||||
bool BitField::operator==(const BitField& other) const {
|
||||
return m_type == other.m_type && m_name == other.m_name && m_offset == other.m_offset &&
|
||||
other.m_size == m_size;
|
||||
}
|
||||
|
||||
BitFieldType::BitFieldType(std::string parent, std::string name, int size, bool sign_extend)
|
||||
: ValueType(std::move(parent), std::move(name), false, size, sign_extend, RegKind::GPR_64) {}
|
||||
|
||||
bool BitFieldType::lookup_field(const std::string& name, BitField* out) const {
|
||||
for (auto& field : m_fields) {
|
||||
if (field.name() == name) {
|
||||
*out = field;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string BitField::print() const {
|
||||
return fmt::format("[{} {}] sz {} off {}", name(), type().print(), size(), offset());
|
||||
}
|
||||
|
||||
std::string BitFieldType::print() const {
|
||||
std::string result;
|
||||
result += fmt::format("Parent type: {}\nFields:\n", get_parent());
|
||||
for (auto& field : m_fields) {
|
||||
result += fmt::format(" {}\n", field.print());
|
||||
}
|
||||
result += fmt::format("Mem size: {}, load size: {}, signed {}, align {}\n", get_size_in_memory(),
|
||||
get_load_size(), get_load_signed(), get_in_memory_alignment());
|
||||
return result;
|
||||
}
|
@ -174,12 +174,12 @@ class Field {
|
||||
void set_inline();
|
||||
std::string print() const;
|
||||
const TypeSpec& type() const { return m_type; }
|
||||
|
||||
bool is_inline() const { return m_inline; }
|
||||
|
||||
bool is_array() const { return m_array; }
|
||||
|
||||
bool is_dynamic() const { return m_dynamic; }
|
||||
const std::string& name() const { return m_name; }
|
||||
int offset() const { return m_offset; }
|
||||
bool operator==(const Field& other) const;
|
||||
|
||||
int alignment() const {
|
||||
assert(m_alignment != -1);
|
||||
@ -191,17 +191,11 @@ class Field {
|
||||
return m_array_size;
|
||||
}
|
||||
|
||||
const std::string& name() const { return m_name; }
|
||||
|
||||
int offset() const { return m_offset; }
|
||||
|
||||
bool operator==(const Field& other) const;
|
||||
|
||||
private:
|
||||
friend class TypeSystem;
|
||||
void set_alignment(int alignment) { m_alignment = alignment; }
|
||||
|
||||
void set_offset(int offset) { m_offset = offset; }
|
||||
|
||||
std::string m_name;
|
||||
TypeSpec m_type;
|
||||
int m_offset = -1;
|
||||
@ -260,8 +254,34 @@ class BasicType : public StructureType {
|
||||
~BasicType() = default;
|
||||
};
|
||||
|
||||
class BitField {};
|
||||
class BitField {
|
||||
public:
|
||||
BitField() = default;
|
||||
BitField(TypeSpec type, std::string name, int offset, int size);
|
||||
const std::string name() const { return m_name; }
|
||||
int offset() const { return m_offset; }
|
||||
int size() const { return m_size; }
|
||||
const TypeSpec& type() const { return m_type; }
|
||||
bool operator==(const BitField& other) const;
|
||||
std::string print() const;
|
||||
|
||||
class BitFieldType : ValueType {};
|
||||
private:
|
||||
TypeSpec m_type;
|
||||
std::string m_name;
|
||||
int m_offset = -1; // in bits
|
||||
int m_size = -1; // in bits.
|
||||
};
|
||||
|
||||
class BitFieldType : public ValueType {
|
||||
public:
|
||||
BitFieldType(std::string parent, std::string name, int size, bool sign_extend);
|
||||
bool lookup_field(const std::string& name, BitField* out) const;
|
||||
std::string print() const override;
|
||||
bool operator==(const Type& other) const override;
|
||||
|
||||
private:
|
||||
friend class TypeSystem;
|
||||
std::vector<BitField> m_fields;
|
||||
};
|
||||
|
||||
#endif // JAK_TYPE_H
|
||||
|
@ -1307,4 +1307,65 @@ ReverseDerefInfo TypeSystem::get_reverse_deref_info(const ReverseDerefInputInfo&
|
||||
ReverseDerefInfo result;
|
||||
result.success = reverse_deref(input, &result.deref_path, &result.addr_of, &result.result_type);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Is the given type a bitfield type?
|
||||
*/
|
||||
bool TypeSystem::is_bitfield_type(const std::string& type_name) const {
|
||||
return dynamic_cast<BitFieldType*>(lookup_type(type_name));
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get information about a field within a bitfield type.
|
||||
*/
|
||||
BitfieldLookupInfo TypeSystem::lookup_bitfield_info(const std::string& type_name,
|
||||
const std::string& field_name) const {
|
||||
auto type = get_type_of_type<BitFieldType>(type_name);
|
||||
BitField f;
|
||||
if (!type->lookup_field(field_name, &f)) {
|
||||
fmt::print("[TypeSystem] Type {} has no bitfield named {}\n", type_name, field_name);
|
||||
throw std::runtime_error("lookup_bitfield failed");
|
||||
}
|
||||
|
||||
BitfieldLookupInfo result;
|
||||
result.result_type = f.type();
|
||||
result.offset = f.offset();
|
||||
result.sign_extend = lookup_type(result.result_type)->get_load_signed();
|
||||
result.size = f.size();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Add a new field to a bitfield type.
|
||||
* Set the field size to -1 if you want to just use the size of the type and not clip it.
|
||||
*/
|
||||
void TypeSystem::add_field_to_bitfield(BitFieldType* type,
|
||||
const std::string& field_name,
|
||||
const TypeSpec& field_type,
|
||||
int offset,
|
||||
int field_size) {
|
||||
// in bits
|
||||
auto load_size = lookup_type(field_type)->get_load_size() * 8;
|
||||
if (field_size == -1) {
|
||||
field_size = load_size;
|
||||
}
|
||||
|
||||
if (field_size > load_size) {
|
||||
fmt::print(
|
||||
"[TypeSystem] Type {}'s bitfield {}'s set size is {}, which is larger than the actual "
|
||||
"type: {}\n",
|
||||
type->get_name(), field_name, field_size, load_size);
|
||||
throw std::runtime_error("Failed to add bitfield to type");
|
||||
}
|
||||
|
||||
if (field_size + offset > type->get_load_size() * 8) {
|
||||
fmt::print(
|
||||
"[TypeSystem] Type {}'s bitfield {} will run off the end of the type (ends at {} bits, "
|
||||
"type is {} bits)\n",
|
||||
type->get_name(), field_name, field_size + offset, type->get_load_size() * 8);
|
||||
throw std::runtime_error("Failed to add bitfield to type");
|
||||
}
|
||||
BitField field(field_type, field_name, offset, field_size);
|
||||
type->m_fields.push_back(field);
|
||||
}
|
@ -24,6 +24,13 @@ struct FieldLookupInfo {
|
||||
int array_size = -1;
|
||||
};
|
||||
|
||||
struct BitfieldLookupInfo {
|
||||
TypeSpec result_type;
|
||||
int offset = -1;
|
||||
int size = -1;
|
||||
bool sign_extend = false;
|
||||
};
|
||||
|
||||
struct DerefInfo {
|
||||
bool can_deref = false;
|
||||
bool mem_deref = false;
|
||||
@ -102,6 +109,8 @@ class TypeSystem {
|
||||
|
||||
FieldLookupInfo lookup_field_info(const std::string& type_name,
|
||||
const std::string& field_name) const;
|
||||
BitfieldLookupInfo lookup_bitfield_info(const std::string& type_name,
|
||||
const std::string& field_name) const;
|
||||
void assert_field_offset(const std::string& type_name, const std::string& field_name, int offset);
|
||||
int add_field_to_type(StructureType* type,
|
||||
const std::string& field_name,
|
||||
@ -122,6 +131,13 @@ class TypeSystem {
|
||||
std::vector<std::string> get_path_up_tree(const std::string& type);
|
||||
int get_next_method_id(Type* type);
|
||||
|
||||
bool is_bitfield_type(const std::string& type_name) const;
|
||||
void add_field_to_bitfield(BitFieldType* type,
|
||||
const std::string& field_name,
|
||||
const TypeSpec& field_type,
|
||||
int offset,
|
||||
int field_size);
|
||||
|
||||
/*!
|
||||
* Get a type by name and cast to a child class of Type*. Must succeed.
|
||||
*/
|
||||
|
@ -136,7 +136,44 @@ void add_field(StructureType* structure, TypeSystem* ts, const goos::Object& def
|
||||
}
|
||||
}
|
||||
|
||||
void declare_method(StructureType* type, TypeSystem* type_system, const goos::Object& def) {
|
||||
void add_bitfield(BitFieldType* bitfield_type, TypeSystem* ts, const goos::Object& def) {
|
||||
auto rest = &def;
|
||||
|
||||
auto name = symbol_string(car(rest));
|
||||
rest = cdr(rest);
|
||||
|
||||
auto type = parse_typespec(ts, car(rest));
|
||||
rest = cdr(rest);
|
||||
|
||||
int offset_override = -1;
|
||||
int size_override = -1;
|
||||
|
||||
if (!rest->is_empty_list()) {
|
||||
while (!rest->is_empty_list()) {
|
||||
auto opt_name = symbol_string(car(rest));
|
||||
rest = cdr(rest);
|
||||
|
||||
if (opt_name == ":offset") {
|
||||
offset_override = get_int(car(rest));
|
||||
rest = cdr(rest);
|
||||
} else if (opt_name == ":size") {
|
||||
size_override = get_int(car(rest));
|
||||
rest = cdr(rest);
|
||||
} else {
|
||||
throw std::runtime_error("Invalid option in field specification: " + opt_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (offset_override == -1) {
|
||||
throw std::runtime_error("Bitfield type must manually specify offsets always");
|
||||
}
|
||||
|
||||
// it's fine if the size is -1, that means it'll just use the type's size.
|
||||
ts->add_field_to_bitfield(bitfield_type, name, type, offset_override, size_override);
|
||||
}
|
||||
|
||||
void declare_method(Type* type, TypeSystem* type_system, const goos::Object& def) {
|
||||
for_each_in_list(def, [&](const goos::Object& _obj) {
|
||||
auto obj = &_obj;
|
||||
// (name args return-type [id])
|
||||
@ -281,6 +318,97 @@ StructureDefResult parse_structure_def(StructureType* type,
|
||||
return result;
|
||||
}
|
||||
|
||||
struct BitFieldTypeDefResult {
|
||||
TypeFlags flags;
|
||||
bool generate_runtime_type = true;
|
||||
};
|
||||
|
||||
BitFieldTypeDefResult parse_bitfield_type_def(BitFieldType* type,
|
||||
TypeSystem* ts,
|
||||
const goos::Object& fields,
|
||||
const goos::Object& options) {
|
||||
BitFieldTypeDefResult result;
|
||||
for_each_in_list(fields, [&](const goos::Object& o) { add_bitfield(type, ts, o); });
|
||||
TypeFlags flags;
|
||||
flags.heap_base = 0;
|
||||
flags.size = type->get_size_in_memory();
|
||||
flags.pad = 0;
|
||||
|
||||
auto* rest = &options;
|
||||
int size_assert = -1;
|
||||
int method_count_assert = -1;
|
||||
uint64_t flag_assert = 0;
|
||||
bool flag_assert_set = false;
|
||||
while (!rest->is_empty_list()) {
|
||||
if (car(rest).is_pair()) {
|
||||
auto opt_list = &car(rest);
|
||||
auto& first = car(opt_list);
|
||||
opt_list = cdr(opt_list);
|
||||
|
||||
if (symbol_string(first) == ":methods") {
|
||||
declare_method(type, ts, *opt_list);
|
||||
} else {
|
||||
throw std::runtime_error("Invalid option list in field specification: " +
|
||||
car(rest).print());
|
||||
}
|
||||
|
||||
rest = cdr(rest);
|
||||
} else {
|
||||
auto opt_name = symbol_string(car(rest));
|
||||
rest = cdr(rest);
|
||||
|
||||
if (opt_name == ":size-assert") {
|
||||
size_assert = get_int(car(rest));
|
||||
if (size_assert == -1) {
|
||||
throw std::runtime_error("Cannot use -1 as size-assert");
|
||||
}
|
||||
rest = cdr(rest);
|
||||
} else if (opt_name == ":method-count-assert") {
|
||||
method_count_assert = get_int(car(rest));
|
||||
if (method_count_assert == -1) {
|
||||
throw std::runtime_error("Cannot use -1 as method_count_assert");
|
||||
}
|
||||
rest = cdr(rest);
|
||||
} else if (opt_name == ":flag-assert") {
|
||||
flag_assert = get_int(car(rest));
|
||||
flag_assert_set = true;
|
||||
rest = cdr(rest);
|
||||
} else if (opt_name == ":no-runtime-type") {
|
||||
result.generate_runtime_type = false;
|
||||
} else if (opt_name == ":heap-base") {
|
||||
u16 hb = get_int(car(rest));
|
||||
rest = cdr(rest);
|
||||
flags.heap_base = hb;
|
||||
} else {
|
||||
throw std::runtime_error("Invalid option in field specification: " + opt_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (size_assert != -1 && flags.size != u16(size_assert)) {
|
||||
throw std::runtime_error("Type " + type->get_name() + " came out to size " +
|
||||
std::to_string(int(flags.size)) + " but size-assert was set to " +
|
||||
std::to_string(size_assert));
|
||||
}
|
||||
|
||||
flags.methods = ts->get_next_method_id(type);
|
||||
|
||||
if (method_count_assert != -1 && flags.methods != u16(method_count_assert)) {
|
||||
throw std::runtime_error(
|
||||
"Type " + type->get_name() + " has " + std::to_string(int(flags.methods)) +
|
||||
" methods, but method-count-assert was set to " + std::to_string(method_count_assert));
|
||||
}
|
||||
|
||||
if (flag_assert_set && (flags.flag != flag_assert)) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Type {} has flag 0x{:x} but flag-assert was set to 0x{:x}", type->get_name(),
|
||||
flags.flag, flag_assert));
|
||||
}
|
||||
|
||||
result.flags = flags;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TypeSpec parse_typespec(TypeSystem* type_system, const goos::Object& src) {
|
||||
@ -350,7 +478,14 @@ DeftypeResult parse_deftype(const goos::Object& deftype, TypeSystem* ts) {
|
||||
}
|
||||
ts->add_type(name, std::move(new_type));
|
||||
} else if (is_type("integer", parent_type, ts)) {
|
||||
throw std::runtime_error("Creating a child type of integer is not supported yet.");
|
||||
auto pto = ts->lookup_type(parent_type);
|
||||
assert(pto);
|
||||
auto new_type = std::make_unique<BitFieldType>(
|
||||
parent_type_name, name, pto->get_size_in_memory(), pto->get_load_signed());
|
||||
auto sr = parse_bitfield_type_def(new_type.get(), ts, field_list_obj, options_obj);
|
||||
result.flags = sr.flags;
|
||||
result.create_runtime_type = sr.generate_runtime_type;
|
||||
ts->add_type(name, std::move(new_type));
|
||||
} else {
|
||||
throw std::runtime_error("Creating a child type from " + parent_type.print() +
|
||||
" is not allowed or not supported yet.");
|
||||
|
@ -51,4 +51,10 @@
|
||||
## V0.2
|
||||
- Breaking change: return type of a function using `return-from #f` to return a value from the entire function is now the lowest common ancestor of all possible return values.
|
||||
- Fixed bug where `return-from` could reach outside of an inlined function.
|
||||
- Fixed bug where `return-from` might not behave correctly when returning from inside a let inside an inlined function.
|
||||
- Fixed bug where `return-from` might not behave correctly when returning from inside a let inside an inlined function.
|
||||
- Added `fmin` and `fmax` floating point min and max. These work on multiple arguments and use the `minss`/`maxss` instructions for the best performance.
|
||||
- Added `imul64` instruction for doing a real 64-bit multiplication. This must be used when porting code that looks at the `hi` register after an EE `mult`.
|
||||
- Added `shl`, `shr`, and `sar` shifts which take a constant integer. These cannot be used with a variable shift amount.
|
||||
- Added bitfield types to the type system
|
||||
- Added the ability to cast integers to bitfield types
|
||||
- Fixed a bug where casting between integer types with `the` that did not involve emitting code would permanently change the type of the variable.
|
@ -49,6 +49,8 @@ class Compiler {
|
||||
void init_logger();
|
||||
void init_settings();
|
||||
bool try_getting_macro_from_goos(const goos::Object& macro_name, goos::Object* dest);
|
||||
void set_bitfield(const goos::Object& form, BitFieldVal* dst, RegVal* src, Env* env);
|
||||
Val* do_set(const goos::Object& form, Val* dst, RegVal* src, Env* env);
|
||||
Val* compile_goos_macro(const goos::Object& o,
|
||||
const goos::Object& macro_obj,
|
||||
const goos::Object& rest,
|
||||
@ -89,6 +91,7 @@ class Compiler {
|
||||
bool is_quoted_sym(const goos::Object& o);
|
||||
bool is_basic(const TypeSpec& ts);
|
||||
bool is_structure(const TypeSpec& ts);
|
||||
bool is_bitfield(const TypeSpec& ts);
|
||||
const goos::Object& pair_car(const goos::Object& o);
|
||||
const goos::Object& pair_cdr(const goos::Object& o);
|
||||
void expect_empty_list(const goos::Object& o);
|
||||
@ -134,6 +137,7 @@ class Compiler {
|
||||
bool is_none(Val* in);
|
||||
|
||||
Val* compile_variable_shift(const RegVal* in, const RegVal* sa, Env* env, IntegerMathKind kind);
|
||||
Val* compile_fixed_shift(const RegVal* in, u8 sa, Env* env, IntegerMathKind kind);
|
||||
|
||||
Val* compile_format_string(const goos::Object& form,
|
||||
Env* env,
|
||||
@ -160,6 +164,10 @@ class Compiler {
|
||||
const TypeSpec& type,
|
||||
const goos::Object& field_defs,
|
||||
Env* env);
|
||||
Val* compile_new_static_bitfield(const goos::Object& form,
|
||||
const TypeSpec& type,
|
||||
const goos::Object& field_defs,
|
||||
Env* env);
|
||||
|
||||
public:
|
||||
// Atoms
|
||||
@ -225,6 +233,9 @@ class Compiler {
|
||||
Val* compile_shlv(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_sarv(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_shrv(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_shl(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_sar(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_shr(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_mod(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_logxor(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_lognot(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
|
@ -392,6 +392,9 @@ void IR_FunctionAddr::do_codegen(emitter::ObjectGenerator* gen,
|
||||
IR_IntegerMath::IR_IntegerMath(IntegerMathKind kind, RegVal* dest, RegVal* arg)
|
||||
: m_kind(kind), m_dest(dest), m_arg(arg) {}
|
||||
|
||||
IR_IntegerMath::IR_IntegerMath(IntegerMathKind kind, RegVal* dest, u8 shift_amount)
|
||||
: m_kind(kind), m_dest(dest), m_shift_amount(shift_amount) {}
|
||||
|
||||
std::string IR_IntegerMath::print() {
|
||||
switch (m_kind) {
|
||||
case IntegerMathKind::ADD_64:
|
||||
@ -412,6 +415,12 @@ std::string IR_IntegerMath::print() {
|
||||
return fmt::format("shlv {}, {}", m_dest->print(), m_arg->print());
|
||||
case IntegerMathKind::SHRV_64:
|
||||
return fmt::format("shrv {}, {}", m_dest->print(), m_arg->print());
|
||||
case IntegerMathKind::SAR_64:
|
||||
return fmt::format("sar {}, {}", m_dest->print(), m_shift_amount);
|
||||
case IntegerMathKind::SHL_64:
|
||||
return fmt::format("shl {}, {}", m_dest->print(), m_shift_amount);
|
||||
case IntegerMathKind::SHR_64:
|
||||
return fmt::format("shr {}, {}", m_dest->print(), m_shift_amount);
|
||||
case IntegerMathKind::AND_64:
|
||||
return fmt::format("and {}, {}", m_dest->print(), m_arg->print());
|
||||
case IntegerMathKind::OR_64:
|
||||
@ -430,7 +439,8 @@ RegAllocInstr IR_IntegerMath::to_rai() {
|
||||
rai.write.push_back(m_dest->ireg());
|
||||
rai.read.push_back(m_dest->ireg());
|
||||
|
||||
if (m_kind != IntegerMathKind::NOT_64) {
|
||||
if (m_kind != IntegerMathKind::NOT_64 && m_kind != IntegerMathKind::SHL_64 &&
|
||||
m_kind != IntegerMathKind::SAR_64 && m_kind != IntegerMathKind::SHR_64) {
|
||||
rai.read.push_back(m_arg->ireg());
|
||||
}
|
||||
|
||||
@ -480,6 +490,15 @@ void IR_IntegerMath::do_codegen(emitter::ObjectGenerator* gen,
|
||||
gen->add_instr(IGen::sar_gpr64_cl(get_reg(m_dest, allocs, irec)), irec);
|
||||
assert(get_reg(m_arg, allocs, irec) == emitter::RCX);
|
||||
break;
|
||||
case IntegerMathKind::SHL_64:
|
||||
gen->add_instr(IGen::shl_gpr64_u8(get_reg(m_dest, allocs, irec), m_shift_amount), irec);
|
||||
break;
|
||||
case IntegerMathKind::SHR_64:
|
||||
gen->add_instr(IGen::shr_gpr64_u8(get_reg(m_dest, allocs, irec), m_shift_amount), irec);
|
||||
break;
|
||||
case IntegerMathKind::SAR_64:
|
||||
gen->add_instr(IGen::sar_gpr64_u8(get_reg(m_dest, allocs, irec), m_shift_amount), irec);
|
||||
break;
|
||||
case IntegerMathKind::IMUL_32: {
|
||||
auto dr = get_reg(m_dest, allocs, irec);
|
||||
gen->add_instr(IGen::imul_gpr32_gpr32(dr, get_reg(m_arg, allocs, irec)), irec);
|
||||
|
@ -211,6 +211,7 @@ enum class IntegerMathKind {
|
||||
class IR_IntegerMath : public IR {
|
||||
public:
|
||||
IR_IntegerMath(IntegerMathKind kind, RegVal* dest, RegVal* arg);
|
||||
IR_IntegerMath(IntegerMathKind kind, RegVal* dest, u8 shift_amount);
|
||||
std::string print() override;
|
||||
RegAllocInstr to_rai() override;
|
||||
void do_codegen(emitter::ObjectGenerator* gen,
|
||||
@ -221,7 +222,8 @@ class IR_IntegerMath : public IR {
|
||||
protected:
|
||||
IntegerMathKind m_kind;
|
||||
RegVal* m_dest;
|
||||
RegVal* m_arg;
|
||||
RegVal* m_arg = nullptr;
|
||||
u8 m_shift_amount = 0;
|
||||
};
|
||||
|
||||
enum class FloatMathKind { DIV_SS, MUL_SS, ADD_SS, SUB_SS, MIN_SS, MAX_SS };
|
||||
|
@ -150,6 +150,10 @@ bool Compiler::is_structure(const TypeSpec& ts) {
|
||||
return m_ts.typecheck(m_ts.make_typespec("structure"), ts, "", false, false);
|
||||
}
|
||||
|
||||
bool Compiler::is_bitfield(const TypeSpec& ts) {
|
||||
return m_ts.is_bitfield_type(ts.base_type());
|
||||
}
|
||||
|
||||
bool Compiler::try_getting_constant_integer(const goos::Object& in, int64_t* out, Env* env) {
|
||||
(void)env;
|
||||
if (in.is_int()) {
|
||||
|
@ -26,7 +26,6 @@ RegVal* Val::to_xmm(Env* fe) {
|
||||
if (rv->ireg().kind == emitter::RegKind::XMM) {
|
||||
return rv;
|
||||
} else {
|
||||
assert(false);
|
||||
auto re = fe->make_xmm(coerce_to_reg_type(m_ts));
|
||||
fe->emit(std::make_unique<IR_RegSet>(re, rv));
|
||||
return re;
|
||||
@ -179,3 +178,42 @@ RegVal* StackVarAddrVal::to_reg(Env* fe) {
|
||||
fe->emit(std::make_unique<IR_GetStackAddr>(re, m_slot));
|
||||
return re;
|
||||
}
|
||||
|
||||
std::string BitFieldVal::print() const {
|
||||
return fmt::format("[bitfield sz {} off {} sx {} of {}]", m_size, m_offset, m_sign_extend,
|
||||
m_parent->print());
|
||||
}
|
||||
|
||||
RegVal* BitFieldVal::to_reg(Env* env) {
|
||||
// first get the parent value
|
||||
auto parent_reg = m_parent->to_gpr(env);
|
||||
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
auto result = fe->make_ireg(coerce_to_reg_type(m_ts), emitter::RegKind::GPR);
|
||||
env->emit(std::make_unique<IR_RegSet>(result, parent_reg));
|
||||
|
||||
int start_bit = m_offset;
|
||||
int end_bit = m_offset + m_size;
|
||||
int epad = 64 - end_bit;
|
||||
assert(epad >= 0);
|
||||
int spad = start_bit;
|
||||
|
||||
// shift left as much as possible to kill upper bits
|
||||
if (epad > 0) {
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::SHL_64, result, epad));
|
||||
}
|
||||
|
||||
int next_shift = epad + spad;
|
||||
assert(next_shift + m_size == 64);
|
||||
assert(next_shift >= 0);
|
||||
|
||||
if (next_shift > 0) {
|
||||
if (m_sign_extend) {
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::SAR_64, result, next_shift));
|
||||
} else {
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::SHR_64, result, next_shift));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
@ -85,6 +85,7 @@ class RegVal : public Val {
|
||||
class SymbolVal : public Val {
|
||||
public:
|
||||
SymbolVal(std::string name, TypeSpec ts) : Val(std::move(ts)), m_name(std::move(name)) {
|
||||
// this is for define, which looks at the SymbolVal and not the SymbolValueVal.
|
||||
mark_as_settable();
|
||||
}
|
||||
const std::string& name() const { return m_name; }
|
||||
@ -98,10 +99,14 @@ class SymbolVal : public Val {
|
||||
class SymbolValueVal : public Val {
|
||||
public:
|
||||
SymbolValueVal(const SymbolVal* sym, TypeSpec ts, bool sext)
|
||||
: Val(std::move(ts)), m_sym(sym), m_sext(sext) {}
|
||||
: Val(std::move(ts)), m_sym(sym), m_sext(sext) {
|
||||
// this is for set, which looks at the Symbol's Value.
|
||||
mark_as_settable();
|
||||
}
|
||||
const std::string& name() const { return m_sym->name(); }
|
||||
std::string print() const override { return "[<" + name() + ">]"; }
|
||||
RegVal* to_reg(Env* fe) override;
|
||||
const SymbolVal* sym() const { return m_sym; }
|
||||
|
||||
protected:
|
||||
const SymbolVal* m_sym = nullptr;
|
||||
@ -240,6 +245,28 @@ class FloatConstantVal : public Val {
|
||||
StaticFloat* m_value = nullptr;
|
||||
};
|
||||
|
||||
// Bitfield
|
||||
class BitFieldVal : public Val {
|
||||
public:
|
||||
BitFieldVal(TypeSpec ts, Val* parent, int offset, int size, bool sign_extend)
|
||||
: Val(std::move(ts)),
|
||||
m_parent(parent),
|
||||
m_offset(offset),
|
||||
m_size(size),
|
||||
m_sign_extend(sign_extend) {
|
||||
m_is_settable = parent->settable();
|
||||
}
|
||||
std::string print() const override;
|
||||
RegVal* to_reg(Env* env) override;
|
||||
int offset() const { return m_offset; }
|
||||
int size() const { return m_size; }
|
||||
bool sext() const { return m_sign_extend; }
|
||||
Val* parent() { return m_parent; }
|
||||
|
||||
protected:
|
||||
Val* m_parent = nullptr;
|
||||
int m_offset = -1;
|
||||
int m_size = -1;
|
||||
bool m_sign_extend = false;
|
||||
};
|
||||
|
||||
#endif // JAK_VAL_H
|
||||
|
@ -106,9 +106,9 @@ static const std::unordered_map<
|
||||
{"shlv", &Compiler::compile_shlv},
|
||||
{"shrv", &Compiler::compile_shrv},
|
||||
{"sarv", &Compiler::compile_sarv},
|
||||
// {"shl", &Compiler::compile_shl},
|
||||
// {"shr", &Compiler::compile_shr},
|
||||
// {"sar", &Compiler::compile_sar},
|
||||
{"shl", &Compiler::compile_shl},
|
||||
{"shr", &Compiler::compile_shr},
|
||||
{"sar", &Compiler::compile_sar},
|
||||
{"mod", &Compiler::compile_mod},
|
||||
{"logior", &Compiler::compile_logior},
|
||||
{"logxor", &Compiler::compile_logxor},
|
||||
|
@ -88,6 +88,99 @@ Val* Compiler::compile_define_extern(const goos::Object& form, const goos::Objec
|
||||
return get_none();
|
||||
}
|
||||
|
||||
void Compiler::set_bitfield(const goos::Object& form, BitFieldVal* dst, RegVal* src, Env* env) {
|
||||
assert(!dst->sext());
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
|
||||
// first, get the value we want to modify:
|
||||
auto original_original = dst->parent()->to_gpr(env);
|
||||
// let's not directly modify original, and instead create a copy then use do_set on parent.
|
||||
// this way we avoid "cheating" the set system, although it should be safe...
|
||||
auto original = fe->make_gpr(original_original->type());
|
||||
env->emit(std::make_unique<IR_RegSet>(original, original_original));
|
||||
|
||||
// we'll need a temp register to hold a mask:
|
||||
auto temp = fe->make_gpr(src->type());
|
||||
// mask value should be 1's everywhere except for the field so we can AND with it
|
||||
u64 mask_val = ~(((1 << dst->size()) - 1) << dst->offset());
|
||||
env->emit(std::make_unique<IR_LoadConstant64>(temp, mask_val));
|
||||
// modify the original!
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::AND_64, original, temp));
|
||||
|
||||
// put the source in temp
|
||||
env->emit(std::make_unique<IR_RegSet>(temp, src));
|
||||
|
||||
// to shift us all the way to the left and clear upper bits
|
||||
int left_shift_amnt = 64 - dst->size();
|
||||
int right_shift_amnt = (64 - dst->size()) - dst->offset();
|
||||
assert(right_shift_amnt >= 0);
|
||||
|
||||
if (left_shift_amnt > 0) {
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::SHL_64, temp, left_shift_amnt));
|
||||
}
|
||||
|
||||
if (right_shift_amnt > 0) {
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::SHR_64, temp, right_shift_amnt));
|
||||
}
|
||||
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::OR_64, original, temp));
|
||||
do_set(form, dst->parent(), original, env);
|
||||
}
|
||||
|
||||
/*!
|
||||
* The internal "set" logic.
|
||||
*/
|
||||
Val* Compiler::do_set(const goos::Object& form, Val* dest, RegVal* source, Env* env) {
|
||||
if (!dest->settable()) {
|
||||
throw_compile_error(form,
|
||||
"Tried to use set! on something that wasn't settable: " + dest->print());
|
||||
}
|
||||
auto as_mem_deref = dynamic_cast<MemoryDerefVal*>(dest);
|
||||
auto as_pair = dynamic_cast<PairEntryVal*>(dest);
|
||||
auto as_reg = dynamic_cast<RegVal*>(dest);
|
||||
auto as_sym_val = dynamic_cast<SymbolValueVal*>(dest);
|
||||
auto as_bitfield = dynamic_cast<BitFieldVal*>(dest);
|
||||
|
||||
if (as_mem_deref) {
|
||||
// setting somewhere in memory
|
||||
auto base = as_mem_deref->base;
|
||||
auto base_as_mco = dynamic_cast<MemoryOffsetConstantVal*>(base);
|
||||
if (base_as_mco) {
|
||||
// if it is a constant offset, we can use a fancy x86-64 addressing mode to simplify
|
||||
auto ti = m_ts.lookup_type(as_mem_deref->type());
|
||||
env->emit(std::make_unique<IR_StoreConstOffset>(
|
||||
source, base_as_mco->offset, base_as_mco->base->to_gpr(env), ti->get_load_size()));
|
||||
return source;
|
||||
} else {
|
||||
// nope, the pointer to dereference is some compliated thing.
|
||||
auto ti = m_ts.lookup_type(as_mem_deref->type());
|
||||
env->emit(
|
||||
std::make_unique<IR_StoreConstOffset>(source, 0, base->to_gpr(env), ti->get_load_size()));
|
||||
return source;
|
||||
}
|
||||
} else if (as_pair) {
|
||||
// this could probably be part of MemoryDerefVal and not a special case here.
|
||||
env->emit(std::make_unique<IR_StoreConstOffset>(source, as_pair->is_car ? -2 : 2,
|
||||
as_pair->base->to_gpr(env), 4));
|
||||
return source;
|
||||
} else if (as_reg) {
|
||||
typecheck(form, as_reg->type(), source->type(), "set! lexical variable");
|
||||
env->emit(std::make_unique<IR_RegSet>(as_reg, source));
|
||||
return source;
|
||||
} else if (as_sym_val) {
|
||||
typecheck(form, as_sym_val->type(), source->type(), "set! global symbol");
|
||||
auto result_in_gpr = source->to_gpr(env);
|
||||
env->emit(std::make_unique<IR_SetSymbolValue>(as_sym_val->sym(), result_in_gpr));
|
||||
return result_in_gpr;
|
||||
} else if (as_bitfield) {
|
||||
set_bitfield(form, as_bitfield, source, env);
|
||||
return get_none();
|
||||
}
|
||||
|
||||
throw_compile_error(form, "Set not implemented for: " + dest->print());
|
||||
return get_none();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set something to something.
|
||||
* Lots of special cases.
|
||||
@ -101,71 +194,6 @@ Val* Compiler::compile_set(const goos::Object& form, const goos::Object& rest, E
|
||||
// and to_reg'd first, then the destination is computed, if the destination requires math to
|
||||
// compute.
|
||||
auto source = compile_error_guard(args.unnamed.at(1), env)->to_reg(env);
|
||||
|
||||
if (destination.is_symbol()) {
|
||||
// destination is just a symbol, so it's either a lexical variable or a global.
|
||||
|
||||
// first, attempt a lexical set:
|
||||
auto lex_place = env->lexical_lookup(destination);
|
||||
if (lex_place) {
|
||||
// typecheck and set!
|
||||
typecheck(form, lex_place->type(), source->type(), "set! lexical variable");
|
||||
env->emit(std::make_unique<IR_RegSet>(lex_place, source));
|
||||
return source;
|
||||
} else {
|
||||
// try to set symbol
|
||||
auto existing = m_symbol_types.find(destination.as_symbol()->name);
|
||||
if (existing == m_symbol_types.end()) {
|
||||
throw_compile_error(
|
||||
form, "could not find something called " + symbol_string(destination) + " to set!");
|
||||
} else {
|
||||
typecheck(form, existing->second, source->type(), "set! global symbol");
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
auto sym_val =
|
||||
fe->alloc_val<SymbolVal>(symbol_string(destination), m_ts.make_typespec("symbol"));
|
||||
auto result_in_gpr = source->to_gpr(env);
|
||||
if (!sym_val->settable()) {
|
||||
throw_compile_error(
|
||||
form, "Tried to use set! on something that wasn't settable: " + sym_val->print());
|
||||
}
|
||||
env->emit(std::make_unique<IR_SetSymbolValue>(sym_val, result_in_gpr));
|
||||
return result_in_gpr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// destination is some complex expression, so compile it and hopefully get something settable.
|
||||
auto dest = compile_error_guard(destination, env);
|
||||
if (!dest->settable()) {
|
||||
throw_compile_error(form,
|
||||
"Tried to use set! on something that wasn't settable: " + dest->print());
|
||||
}
|
||||
auto as_mem_deref = dynamic_cast<MemoryDerefVal*>(dest);
|
||||
auto as_pair = dynamic_cast<PairEntryVal*>(dest);
|
||||
if (as_mem_deref) {
|
||||
// setting somewhere in memory
|
||||
auto base = as_mem_deref->base;
|
||||
auto base_as_mco = dynamic_cast<MemoryOffsetConstantVal*>(base);
|
||||
if (base_as_mco) {
|
||||
// if it is a constant offset, we can use a fancy x86-64 addressing mode to simplify
|
||||
auto ti = m_ts.lookup_type(as_mem_deref->type());
|
||||
env->emit(std::make_unique<IR_StoreConstOffset>(
|
||||
source, base_as_mco->offset, base_as_mco->base->to_gpr(env), ti->get_load_size()));
|
||||
return source;
|
||||
} else {
|
||||
// nope, the pointer to dereference is some compliated thing.
|
||||
auto ti = m_ts.lookup_type(as_mem_deref->type());
|
||||
env->emit(std::make_unique<IR_StoreConstOffset>(source, 0, base->to_gpr(env),
|
||||
ti->get_load_size()));
|
||||
return source;
|
||||
}
|
||||
} else if (as_pair) {
|
||||
// this could probably be part of MemoryDerefVal and not a special case here.
|
||||
env->emit(std::make_unique<IR_StoreConstOffset>(source, as_pair->is_car ? -2 : 2,
|
||||
as_pair->base->to_gpr(env), 4));
|
||||
return source;
|
||||
} else {
|
||||
throw_compile_error(form, "Set not implemented for this yet");
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Unexpected error in Set");
|
||||
auto dest = compile_error_guard(destination, env);
|
||||
return do_set(form, dest, source, env);
|
||||
}
|
@ -150,6 +150,7 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest
|
||||
IRegConstraint constr;
|
||||
constr.instr_idx = 0; // constraint at function start
|
||||
auto ireg = new_func_env->make_ireg(lambda.params.at(i).type, emitter::RegKind::GPR);
|
||||
ireg->mark_as_settable();
|
||||
constr.ireg = ireg->ireg();
|
||||
constr.desired_register = emitter::gRegInfo.get_arg_reg(i);
|
||||
new_func_env->params[lambda.params.at(i).name] = ireg;
|
||||
@ -350,6 +351,7 @@ Val* Compiler::compile_function_or_method_call(const goos::Object& form, Env* en
|
||||
auto type = eval_args.at(i)->type();
|
||||
auto copy = env->make_ireg(type, get_preferred_reg_kind(type));
|
||||
env->emit(std::make_unique<IR_RegSet>(copy, eval_args.at(i)));
|
||||
copy->mark_as_settable();
|
||||
lexical_env->vars[head_as_lambda->lambda.params.at(i).name] = copy;
|
||||
}
|
||||
|
||||
@ -494,6 +496,7 @@ Val* Compiler::compile_real_function_call(const goos::Object& form,
|
||||
std::vector<RegVal*> arg_outs;
|
||||
for (auto& arg : args) {
|
||||
arg_outs.push_back(env->make_ireg(arg->type(), emitter::RegKind::GPR));
|
||||
arg_outs.back()->mark_as_settable();
|
||||
env->emit(std::make_unique<IR_RegSet>(arg_outs.back(), arg));
|
||||
}
|
||||
|
||||
|
@ -442,6 +442,57 @@ Val* Compiler::compile_variable_shift(const RegVal* in,
|
||||
return result;
|
||||
}
|
||||
|
||||
Val* Compiler::compile_shl(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}, {goos::ObjectType::INTEGER}}, {});
|
||||
auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(env);
|
||||
auto sa = args.unnamed.at(1).as_int();
|
||||
if (sa < 0 || sa > 64) {
|
||||
throw_compile_error(form, "Cannot shift by more than 64, or by a negative amount");
|
||||
}
|
||||
return compile_fixed_shift(first, sa, env, IntegerMathKind::SHL_64);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_shr(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}, {goos::ObjectType::INTEGER}}, {});
|
||||
auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(env);
|
||||
auto sa = args.unnamed.at(1).as_int();
|
||||
if (sa < 0 || sa > 64) {
|
||||
throw_compile_error(form, "Cannot shift by more than 64, or by a negative amount");
|
||||
}
|
||||
return compile_fixed_shift(first, sa, env, IntegerMathKind::SHR_64);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_sar(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}, {goos::ObjectType::INTEGER}}, {});
|
||||
auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(env);
|
||||
auto sa = args.unnamed.at(1).as_int();
|
||||
if (sa < 0 || sa > 64) {
|
||||
throw_compile_error(form, "Cannot shift by more than 64, or by a negative amount");
|
||||
}
|
||||
return compile_fixed_shift(first, sa, env, IntegerMathKind::SAR_64);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_fixed_shift(const RegVal* in, u8 sa, Env* env, IntegerMathKind kind) {
|
||||
// type check
|
||||
if (get_math_mode(in->type()) != MathMode::MATH_INT) {
|
||||
throw std::runtime_error("Can't shift a " + in->type().print());
|
||||
}
|
||||
|
||||
if (sa > 64) {
|
||||
throw std::runtime_error("Can't shift by more than 64");
|
||||
}
|
||||
|
||||
// copy to result register
|
||||
auto result = env->make_gpr(in->type());
|
||||
env->emit(std::make_unique<IR_RegSet>(result, in));
|
||||
// do the shift
|
||||
env->emit(std::make_unique<IR_IntegerMath>(kind, result, sa));
|
||||
return result;
|
||||
}
|
||||
|
||||
Val* Compiler::compile_mod(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}, {}}, {});
|
||||
|
@ -34,8 +34,91 @@ bool integer_fits(s64 in, int size, bool is_signed) {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
u32 float_as_u32(float x) {
|
||||
u32 result;
|
||||
memcpy(&result, &x, 4);
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Val* Compiler::compile_new_static_bitfield(const goos::Object& form,
|
||||
const TypeSpec& type,
|
||||
const goos::Object& _field_defs,
|
||||
Env* env) {
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
u64 as_int = 0;
|
||||
|
||||
auto type_info = dynamic_cast<BitFieldType*>(m_ts.lookup_type(type));
|
||||
assert(type_info);
|
||||
assert(type_info->get_load_size() <= 8);
|
||||
|
||||
auto* field_defs = &_field_defs;
|
||||
while (!field_defs->is_empty_list()) {
|
||||
auto field_name_def = symbol_string(pair_car(*field_defs));
|
||||
field_defs = &pair_cdr(*field_defs);
|
||||
|
||||
auto field_value = pair_car(*field_defs);
|
||||
field_defs = &pair_cdr(*field_defs);
|
||||
|
||||
if (field_name_def.at(0) != ':') {
|
||||
throw_compile_error(form,
|
||||
"expected field def name to start with :, instead got " + field_name_def);
|
||||
}
|
||||
|
||||
field_name_def = field_name_def.substr(1);
|
||||
auto field_info = m_ts.lookup_bitfield_info(type_info->get_name(), field_name_def);
|
||||
|
||||
auto field_offset = field_info.offset;
|
||||
auto field_size = field_info.size;
|
||||
assert(field_offset + field_size <= type_info->get_load_size() * 8);
|
||||
|
||||
if (is_integer(field_info.result_type)) {
|
||||
s64 value = 0;
|
||||
if (!try_getting_constant_integer(field_value, &value, env)) {
|
||||
throw_compile_error(form,
|
||||
fmt::format("Field {} is an integer, but the value given couldn't be "
|
||||
"converted to an integer at compile time.",
|
||||
field_name_def));
|
||||
}
|
||||
|
||||
// todo, check the integer fits!
|
||||
u64 unsigned_value = value;
|
||||
u64 or_value = unsigned_value;
|
||||
// shift us all the way left to clear upper bits.
|
||||
or_value <<= (64 - field_size);
|
||||
// and back right.
|
||||
or_value >>= (64 - field_size);
|
||||
if (or_value != unsigned_value) {
|
||||
throw_compile_error(form, fmt::format("Field {}'s value doesn't fit.", field_name_def));
|
||||
}
|
||||
|
||||
as_int |= (or_value << field_offset);
|
||||
} else if (is_float(field_info.result_type)) {
|
||||
if (field_size != 32) {
|
||||
throw_compile_error(form,
|
||||
fmt::format("Tried to put a float into a float bitfield that's not 4 "
|
||||
"bytes. This is probably not what you wanted to do."));
|
||||
}
|
||||
|
||||
float value = 0.f;
|
||||
if (!try_getting_constant_float(field_value, &value, env)) {
|
||||
throw_compile_error(form, fmt::format("Field {} is a float, but the value given couldn't "
|
||||
"be converted to a float at compile time.",
|
||||
field_name_def));
|
||||
}
|
||||
u64 float_value = float_as_u32(value);
|
||||
as_int |= (float_value << field_offset);
|
||||
}
|
||||
|
||||
else {
|
||||
assert(false); // for now
|
||||
}
|
||||
}
|
||||
|
||||
return fe->alloc_val<IntegerConstantVal>(type, as_int);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_new_static_structure_or_basic(const goos::Object& form,
|
||||
const TypeSpec& type,
|
||||
const goos::Object& _field_defs,
|
||||
|
@ -464,7 +464,13 @@ Val* Compiler::compile_deref(const goos::Object& form, const goos::Object& _rest
|
||||
continue;
|
||||
}
|
||||
|
||||
// todo try bitfield
|
||||
auto bitfield_type = dynamic_cast<BitFieldType*>(type_info);
|
||||
if (bitfield_type) {
|
||||
auto bitfield_info = m_ts.lookup_bitfield_info(type_info->get_name(), field_name);
|
||||
result = fe->alloc_val<BitFieldVal>(bitfield_info.result_type, result, bitfield_info.offset,
|
||||
bitfield_info.size, bitfield_info.sign_extend);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto index_value = compile_error_guard(field_obj, env)->to_gpr(env);
|
||||
@ -546,8 +552,13 @@ Val* Compiler::compile_the(const goos::Object& form, const goos::Object& rest, E
|
||||
|
||||
if (m_ts.typecheck(m_ts.make_typespec("integer"), desired_ts, "", false, false)) {
|
||||
auto result = number_to_integer(base, env);
|
||||
result->set_type(desired_ts);
|
||||
return result;
|
||||
if (result != base) {
|
||||
result->set_type(desired_ts);
|
||||
return result;
|
||||
} else {
|
||||
result = get_parent_env_of_type<FunctionEnv>(env)->alloc_val<AliasVal>(desired_ts, base);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ts.typecheck(m_ts.make_typespec("float"), desired_ts, "", false, false)) {
|
||||
@ -651,6 +662,11 @@ Val* Compiler::compile_new(const goos::Object& form, const goos::Object& _rest,
|
||||
if (is_structure(type_of_object)) {
|
||||
return compile_new_static_structure_or_basic(form, type_of_object, *rest, env);
|
||||
}
|
||||
|
||||
if (is_bitfield(type_of_object)) {
|
||||
return compile_new_static_bitfield(form, type_of_object, *rest, env);
|
||||
}
|
||||
|
||||
} else if (allocation == "stack") {
|
||||
auto type_of_object = m_ts.make_typespec(type_as_string);
|
||||
|
||||
|
@ -1528,9 +1528,10 @@ class IGen {
|
||||
/*!
|
||||
* Shift 64-ptr left (logical) by the constant shift amount "sa".
|
||||
*/
|
||||
static Instruction shl_gpr64_u8(uint8_t reg, uint8_t sa) {
|
||||
static Instruction shl_gpr64_u8(Register reg, uint8_t sa) {
|
||||
assert(reg.is_gpr());
|
||||
Instruction instr(0xc1);
|
||||
instr.set_modrm_and_rex(4, reg, 3, true);
|
||||
instr.set_modrm_and_rex(4, reg.hw_id(), 3, true);
|
||||
instr.set(Imm(1, sa));
|
||||
return instr;
|
||||
}
|
||||
@ -1538,9 +1539,10 @@ class IGen {
|
||||
/*!
|
||||
* Shift 64-ptr right (logical) by the constant shift amount "sa".
|
||||
*/
|
||||
static Instruction shr_gpr64_u8(uint8_t reg, uint8_t sa) {
|
||||
static Instruction shr_gpr64_u8(Register reg, uint8_t sa) {
|
||||
assert(reg.is_gpr());
|
||||
Instruction instr(0xc1);
|
||||
instr.set_modrm_and_rex(5, reg, 3, true);
|
||||
instr.set_modrm_and_rex(5, reg.hw_id(), 3, true);
|
||||
instr.set(Imm(1, sa));
|
||||
return instr;
|
||||
}
|
||||
@ -1548,9 +1550,10 @@ class IGen {
|
||||
/*!
|
||||
* Shift 64-ptr right (arithmetic) by the constant shift amount "sa".
|
||||
*/
|
||||
static Instruction sar_gpr64_u8(uint8_t reg, uint8_t sa) {
|
||||
static Instruction sar_gpr64_u8(Register reg, uint8_t sa) {
|
||||
assert(reg.is_gpr());
|
||||
Instruction instr(0xc1);
|
||||
instr.set_modrm_and_rex(7, reg, 3, true);
|
||||
instr.set_modrm_and_rex(7, reg.hw_id(), 3, true);
|
||||
instr.set(Imm(1, sa));
|
||||
return instr;
|
||||
}
|
||||
|
@ -0,0 +1,2 @@
|
||||
(+ (shl 2 3) (shl 1 0) (shl 0 4) (shr 2 3) (shr 10 2) (shl -2 1) (sar -16 2))
|
||||
;; 16 1 0 0 2 -4 -4
|
@ -0,0 +1,21 @@
|
||||
(deftype test-bf-type (int32)
|
||||
((f1 int16 :offset 0)
|
||||
(f2 uint8 :offset 16)
|
||||
(f3 int8 :size 3 :offset 24)
|
||||
(f4 uint8 :size 2 :offset 27)
|
||||
(f5 int8 :size 2 :offset 29)
|
||||
)
|
||||
)
|
||||
|
||||
(deftype test-bf-type2 (int64)
|
||||
((f1 int16 :offset 0)
|
||||
(f2 uint8 :offset 16)
|
||||
(f3 float :offset 24)
|
||||
)
|
||||
)
|
||||
|
||||
(let ((temp (the test-bf-type #xf9f2f344))
|
||||
(temp2 (the test-bf-type2 #x133f456789012345)))
|
||||
(format #t "~A" (< (fabs (- 1.7711 (+ 1.0 (-> temp2 f3)))) 0.002))
|
||||
(format #t "~X~X~X~X~X~%" (-> temp f1) (-> temp f2) (-> temp f3) (-> temp f4) (-> temp f5))
|
||||
)
|
@ -0,0 +1,66 @@
|
||||
(start-test "bitfield-tricky-access")
|
||||
|
||||
(deftype bitfield-test-type-4 (uint32)
|
||||
((a uint8 :offset 3)
|
||||
(b uint16 :offset 19 :size 12)
|
||||
)
|
||||
)
|
||||
|
||||
(deftype bitfield-in-structure-type (structure)
|
||||
((a uint32 :offset 0)
|
||||
(bitfield bitfield-test-type-4 :offset 0)
|
||||
)
|
||||
)
|
||||
|
||||
;; check we can use a bitfield in a symbol
|
||||
(define *global-bitfield* (the bitfield-test-type-4 #x0))
|
||||
(set! (-> *global-bitfield* a) #xfe)
|
||||
(set! (-> *global-bitfield* b) #x12341234)
|
||||
|
||||
(expect-eq #xfe (-> *global-bitfield* a))
|
||||
(expect-eq #x234 (-> *global-bitfield* b))
|
||||
(expect-eq #x11A007F0 (the uint *global-bitfield*))
|
||||
|
||||
|
||||
;; check we can use a bitfield as a field inside a structure
|
||||
(let ((heap-bitfield (new 'global 'bitfield-in-structure-type)))
|
||||
(set! (-> heap-bitfield a) 0)
|
||||
(set! (-> heap-bitfield bitfield a) #xfe)
|
||||
(set! (-> heap-bitfield bitfield b) #x12341234)
|
||||
(expect-eq #xfe (-> heap-bitfield bitfield a))
|
||||
(expect-eq #x234 (-> heap-bitfield bitfield b))
|
||||
(expect-eq #x11A007F0 (the uint (-> heap-bitfield bitfield)))
|
||||
)
|
||||
|
||||
|
||||
(deftype bitfield-test-type-5 (uint32)
|
||||
((a uint8 :offset 0)
|
||||
(b uint16 :offset 19 :size 12)
|
||||
)
|
||||
)
|
||||
|
||||
;; check we can use a bitfield inside of a bitfield
|
||||
(deftype nested-bitfield (uint64)
|
||||
((a uint8 :offset 2 :size 3)
|
||||
(bitfield bitfield-test-type-5 :offset 5)
|
||||
)
|
||||
)
|
||||
|
||||
(let ((thing (the nested-bitfield #x0)))
|
||||
(set! (-> thing a) #xfffff) ;; shoudlbe truncated to #b111
|
||||
(expect-eq 7 (-> thing a))
|
||||
(expect-eq 0 (-> thing bitfield a))
|
||||
(set! (-> thing bitfield a ) #xffff) ;; truncated to #xff
|
||||
(expect-eq #xff (-> thing bitfield a))
|
||||
(expect-eq 7 (-> thing a))
|
||||
)
|
||||
|
||||
(define *global-nested-bitfield* (the nested-bitfield 0))
|
||||
(set! (-> *global-nested-bitfield* a) #xfffff) ;; shoudlbe truncated to #b111
|
||||
(expect-eq 7 (-> *global-nested-bitfield* a))
|
||||
(expect-eq 0 (-> *global-nested-bitfield* bitfield a))
|
||||
(set! (-> *global-nested-bitfield* bitfield a ) #xffff) ;; truncated to #xff
|
||||
(expect-eq #xff (-> *global-nested-bitfield* bitfield a))
|
||||
(expect-eq 7 (-> *global-nested-bitfield* a))
|
||||
|
||||
(finish-test)
|
22
test/goalc/source_templates/with_game/test-set-bitfield.gc
Normal file
22
test/goalc/source_templates/with_game/test-set-bitfield.gc
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
(deftype test-bf-type3 (int64)
|
||||
((f1 uint16 :offset 0)
|
||||
(f2 uint8 :size 7 :offset 16)
|
||||
(f3 float :offset 23)
|
||||
(f4 uint8 :size 1 :offset 55)
|
||||
(f5 uint8 :offset 56)
|
||||
)
|
||||
)
|
||||
|
||||
(let ((temp (the test-bf-type3 #x0)))
|
||||
(set! (-> temp f1) #x12)
|
||||
(set! (-> temp f2) #x13)
|
||||
(set! (-> temp f3) 12.3433)
|
||||
(set! (-> temp f4) #xffffffff) ; will get truncated.
|
||||
(set! (-> temp f1) #x12)
|
||||
(set! (-> temp f2) #x13)
|
||||
(format #t "~A" (eq? 0 (-> temp f5))) ; check it gets truncated
|
||||
(format #t "~f~%" (+ (-> temp f3) (-> temp f2) (-> temp f1) (-> temp f4)))
|
||||
)
|
||||
|
@ -0,0 +1,13 @@
|
||||
(deftype test-bf-type7 (int64)
|
||||
((f1 uint16 :offset 0)
|
||||
(f2 uint8 :size 7 :offset 16)
|
||||
(f3 float :offset 23)
|
||||
(f4 uint8 :size 1 :offset 55)
|
||||
(f5 uint8 :offset 56)
|
||||
)
|
||||
)
|
||||
|
||||
(let ((temp (new 'static 'test-bf-type7 :f1 #x12 :f2 #x13 :f3 12.3433 :f4 #x1)))
|
||||
(format #t "~A" (eq? 0 (-> temp f5))) ; check it gets truncated
|
||||
(format #t "~f~%" (+ (-> temp f3) (-> temp f2) (-> temp f1) (-> temp f4)))
|
||||
)
|
@ -227,10 +227,15 @@ TEST_F(ArithmeticTests, NestedFunctionCall) {
|
||||
runner.run_static_test(env, testCategory, "nested-function.static.gc", {"10\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, ShiftOperations) {
|
||||
TEST_F(ArithmeticTests, VariableShift) {
|
||||
runner.run_static_test(env, testCategory, "shiftvs.static.gc", {"11\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, FixedShift) {
|
||||
// same math as the variable shift test, just using the fixed shift operators.
|
||||
runner.run_static_test(env, testCategory, "shift-fixed.static.gc", {"11\n"});
|
||||
}
|
||||
|
||||
TEST_F(ArithmeticTests, Subtraction) {
|
||||
runner.run_static_test(env, testCategory, "subtract-1.static.gc", {"4\n"});
|
||||
runner.run_static_test(env, testCategory, "subtract-2.static.gc", {"4\n"});
|
||||
|
@ -302,6 +302,24 @@ TEST_F(WithGameTests, GameCount) {
|
||||
get_test_pass_string("game-count", 4));
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, BitFieldAccess) {
|
||||
runner.run_static_test(env, testCategory, "test-bitfield-access.gc",
|
||||
{"#tfffffffffffff344f213ffffffffffffffff\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, SimpleBitField) {
|
||||
runner.run_static_test(env, testCategory, "test-set-bitfield.gc", {"#t50.3432\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, StaticBitField) {
|
||||
runner.run_static_test(env, testCategory, "test-static-bitfield.gc", {"#t50.3432\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, TrickyBitField) {
|
||||
runner.run_static_test(env, testCategory, "test-bitfield-tricky-access.gc",
|
||||
get_test_pass_string("bitfield-tricky-access", 14));
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, Math) {
|
||||
runner.run_static_test(env, testCategory, "test-math.gc", get_test_pass_string("math", 31));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user