[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:
water111 2020-11-29 18:01:30 -05:00 committed by GitHub
parent 4138e5d852
commit 21fbdce7aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 819 additions and 100 deletions

View file

@ -502,6 +502,15 @@ bool StructureType::operator==(const Type& other) const {
// clang-format on // 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 { int StructureType::get_size_in_memory() const {
return m_size_in_mem; return m_size_in_mem;
} }
@ -568,4 +577,44 @@ std::string BasicType::print() const {
int BasicType::get_offset() const { int BasicType::get_offset() const {
return BASIC_OFFSET; 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;
} }

View file

@ -174,12 +174,12 @@ class Field {
void set_inline(); void set_inline();
std::string print() const; std::string print() const;
const TypeSpec& type() const { return m_type; } const TypeSpec& type() const { return m_type; }
bool is_inline() const { return m_inline; } bool is_inline() const { return m_inline; }
bool is_array() const { return m_array; } bool is_array() const { return m_array; }
bool is_dynamic() const { return m_dynamic; } 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 { int alignment() const {
assert(m_alignment != -1); assert(m_alignment != -1);
@ -191,17 +191,11 @@ class Field {
return m_array_size; 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: private:
friend class TypeSystem; friend class TypeSystem;
void set_alignment(int alignment) { m_alignment = alignment; } void set_alignment(int alignment) { m_alignment = alignment; }
void set_offset(int offset) { m_offset = offset; } void set_offset(int offset) { m_offset = offset; }
std::string m_name; std::string m_name;
TypeSpec m_type; TypeSpec m_type;
int m_offset = -1; int m_offset = -1;
@ -260,8 +254,34 @@ class BasicType : public StructureType {
~BasicType() = default; ~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 #endif // JAK_TYPE_H

View file

@ -1307,4 +1307,65 @@ ReverseDerefInfo TypeSystem::get_reverse_deref_info(const ReverseDerefInputInfo&
ReverseDerefInfo result; ReverseDerefInfo result;
result.success = reverse_deref(input, &result.deref_path, &result.addr_of, &result.result_type); result.success = reverse_deref(input, &result.deref_path, &result.addr_of, &result.result_type);
return result; 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);
} }

View file

@ -24,6 +24,13 @@ struct FieldLookupInfo {
int array_size = -1; int array_size = -1;
}; };
struct BitfieldLookupInfo {
TypeSpec result_type;
int offset = -1;
int size = -1;
bool sign_extend = false;
};
struct DerefInfo { struct DerefInfo {
bool can_deref = false; bool can_deref = false;
bool mem_deref = false; bool mem_deref = false;
@ -102,6 +109,8 @@ class TypeSystem {
FieldLookupInfo lookup_field_info(const std::string& type_name, FieldLookupInfo lookup_field_info(const std::string& type_name,
const std::string& field_name) const; 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); void assert_field_offset(const std::string& type_name, const std::string& field_name, int offset);
int add_field_to_type(StructureType* type, int add_field_to_type(StructureType* type,
const std::string& field_name, const std::string& field_name,
@ -122,6 +131,13 @@ class TypeSystem {
std::vector<std::string> get_path_up_tree(const std::string& type); std::vector<std::string> get_path_up_tree(const std::string& type);
int get_next_method_id(Type* 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. * Get a type by name and cast to a child class of Type*. Must succeed.
*/ */

View file

@ -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) { for_each_in_list(def, [&](const goos::Object& _obj) {
auto obj = &_obj; auto obj = &_obj;
// (name args return-type [id]) // (name args return-type [id])
@ -281,6 +318,97 @@ StructureDefResult parse_structure_def(StructureType* type,
return result; 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 } // namespace
TypeSpec parse_typespec(TypeSystem* type_system, const goos::Object& src) { 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)); ts->add_type(name, std::move(new_type));
} else if (is_type("integer", parent_type, ts)) { } 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 { } else {
throw std::runtime_error("Creating a child type from " + parent_type.print() + throw std::runtime_error("Creating a child type from " + parent_type.print() +
" is not allowed or not supported yet."); " is not allowed or not supported yet.");

View file

@ -51,4 +51,10 @@
## V0.2 ## 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. - 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` 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.

View file

@ -49,6 +49,8 @@ class Compiler {
void init_logger(); void init_logger();
void init_settings(); void init_settings();
bool try_getting_macro_from_goos(const goos::Object& macro_name, goos::Object* dest); 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, Val* compile_goos_macro(const goos::Object& o,
const goos::Object& macro_obj, const goos::Object& macro_obj,
const goos::Object& rest, const goos::Object& rest,
@ -89,6 +91,7 @@ class Compiler {
bool is_quoted_sym(const goos::Object& o); bool is_quoted_sym(const goos::Object& o);
bool is_basic(const TypeSpec& ts); bool is_basic(const TypeSpec& ts);
bool is_structure(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_car(const goos::Object& o);
const goos::Object& pair_cdr(const goos::Object& o); const goos::Object& pair_cdr(const goos::Object& o);
void expect_empty_list(const goos::Object& o); void expect_empty_list(const goos::Object& o);
@ -134,6 +137,7 @@ class Compiler {
bool is_none(Val* in); bool is_none(Val* in);
Val* compile_variable_shift(const RegVal* in, const RegVal* sa, Env* env, IntegerMathKind kind); 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, Val* compile_format_string(const goos::Object& form,
Env* env, Env* env,
@ -160,6 +164,10 @@ class Compiler {
const TypeSpec& type, const TypeSpec& type,
const goos::Object& field_defs, const goos::Object& field_defs,
Env* env); Env* env);
Val* compile_new_static_bitfield(const goos::Object& form,
const TypeSpec& type,
const goos::Object& field_defs,
Env* env);
public: public:
// Atoms // Atoms
@ -225,6 +233,9 @@ class Compiler {
Val* compile_shlv(const goos::Object& form, const goos::Object& rest, Env* env); 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_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_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_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_logxor(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_lognot(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_lognot(const goos::Object& form, const goos::Object& rest, Env* env);

View file

@ -392,6 +392,9 @@ void IR_FunctionAddr::do_codegen(emitter::ObjectGenerator* gen,
IR_IntegerMath::IR_IntegerMath(IntegerMathKind kind, RegVal* dest, RegVal* arg) IR_IntegerMath::IR_IntegerMath(IntegerMathKind kind, RegVal* dest, RegVal* arg)
: m_kind(kind), m_dest(dest), m_arg(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() { std::string IR_IntegerMath::print() {
switch (m_kind) { switch (m_kind) {
case IntegerMathKind::ADD_64: case IntegerMathKind::ADD_64:
@ -412,6 +415,12 @@ std::string IR_IntegerMath::print() {
return fmt::format("shlv {}, {}", m_dest->print(), m_arg->print()); return fmt::format("shlv {}, {}", m_dest->print(), m_arg->print());
case IntegerMathKind::SHRV_64: case IntegerMathKind::SHRV_64:
return fmt::format("shrv {}, {}", m_dest->print(), m_arg->print()); 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: case IntegerMathKind::AND_64:
return fmt::format("and {}, {}", m_dest->print(), m_arg->print()); return fmt::format("and {}, {}", m_dest->print(), m_arg->print());
case IntegerMathKind::OR_64: case IntegerMathKind::OR_64:
@ -430,7 +439,8 @@ RegAllocInstr IR_IntegerMath::to_rai() {
rai.write.push_back(m_dest->ireg()); rai.write.push_back(m_dest->ireg());
rai.read.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()); 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); gen->add_instr(IGen::sar_gpr64_cl(get_reg(m_dest, allocs, irec)), irec);
assert(get_reg(m_arg, allocs, irec) == emitter::RCX); assert(get_reg(m_arg, allocs, irec) == emitter::RCX);
break; 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: { case IntegerMathKind::IMUL_32: {
auto dr = get_reg(m_dest, allocs, irec); auto dr = get_reg(m_dest, allocs, irec);
gen->add_instr(IGen::imul_gpr32_gpr32(dr, get_reg(m_arg, allocs, irec)), irec); gen->add_instr(IGen::imul_gpr32_gpr32(dr, get_reg(m_arg, allocs, irec)), irec);

View file

@ -211,6 +211,7 @@ enum class IntegerMathKind {
class IR_IntegerMath : public IR { class IR_IntegerMath : public IR {
public: public:
IR_IntegerMath(IntegerMathKind kind, RegVal* dest, RegVal* arg); IR_IntegerMath(IntegerMathKind kind, RegVal* dest, RegVal* arg);
IR_IntegerMath(IntegerMathKind kind, RegVal* dest, u8 shift_amount);
std::string print() override; std::string print() override;
RegAllocInstr to_rai() override; RegAllocInstr to_rai() override;
void do_codegen(emitter::ObjectGenerator* gen, void do_codegen(emitter::ObjectGenerator* gen,
@ -221,7 +222,8 @@ class IR_IntegerMath : public IR {
protected: protected:
IntegerMathKind m_kind; IntegerMathKind m_kind;
RegVal* m_dest; 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 }; enum class FloatMathKind { DIV_SS, MUL_SS, ADD_SS, SUB_SS, MIN_SS, MAX_SS };

View file

@ -150,6 +150,10 @@ bool Compiler::is_structure(const TypeSpec& ts) {
return m_ts.typecheck(m_ts.make_typespec("structure"), ts, "", false, false); 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) { bool Compiler::try_getting_constant_integer(const goos::Object& in, int64_t* out, Env* env) {
(void)env; (void)env;
if (in.is_int()) { if (in.is_int()) {

View file

@ -26,7 +26,6 @@ RegVal* Val::to_xmm(Env* fe) {
if (rv->ireg().kind == emitter::RegKind::XMM) { if (rv->ireg().kind == emitter::RegKind::XMM) {
return rv; return rv;
} else { } else {
assert(false);
auto re = fe->make_xmm(coerce_to_reg_type(m_ts)); auto re = fe->make_xmm(coerce_to_reg_type(m_ts));
fe->emit(std::make_unique<IR_RegSet>(re, rv)); fe->emit(std::make_unique<IR_RegSet>(re, rv));
return re; return re;
@ -179,3 +178,42 @@ RegVal* StackVarAddrVal::to_reg(Env* fe) {
fe->emit(std::make_unique<IR_GetStackAddr>(re, m_slot)); fe->emit(std::make_unique<IR_GetStackAddr>(re, m_slot));
return re; 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;
}

View file

@ -85,6 +85,7 @@ class RegVal : public Val {
class SymbolVal : public Val { class SymbolVal : public Val {
public: public:
SymbolVal(std::string name, TypeSpec ts) : Val(std::move(ts)), m_name(std::move(name)) { 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(); mark_as_settable();
} }
const std::string& name() const { return m_name; } const std::string& name() const { return m_name; }
@ -98,10 +99,14 @@ class SymbolVal : public Val {
class SymbolValueVal : public Val { class SymbolValueVal : public Val {
public: public:
SymbolValueVal(const SymbolVal* sym, TypeSpec ts, bool sext) 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(); } const std::string& name() const { return m_sym->name(); }
std::string print() const override { return "[<" + name() + ">]"; } std::string print() const override { return "[<" + name() + ">]"; }
RegVal* to_reg(Env* fe) override; RegVal* to_reg(Env* fe) override;
const SymbolVal* sym() const { return m_sym; }
protected: protected:
const SymbolVal* m_sym = nullptr; const SymbolVal* m_sym = nullptr;
@ -240,6 +245,28 @@ class FloatConstantVal : public Val {
StaticFloat* m_value = nullptr; 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 #endif // JAK_VAL_H

View file

@ -106,9 +106,9 @@ static const std::unordered_map<
{"shlv", &Compiler::compile_shlv}, {"shlv", &Compiler::compile_shlv},
{"shrv", &Compiler::compile_shrv}, {"shrv", &Compiler::compile_shrv},
{"sarv", &Compiler::compile_sarv}, {"sarv", &Compiler::compile_sarv},
// {"shl", &Compiler::compile_shl}, {"shl", &Compiler::compile_shl},
// {"shr", &Compiler::compile_shr}, {"shr", &Compiler::compile_shr},
// {"sar", &Compiler::compile_sar}, {"sar", &Compiler::compile_sar},
{"mod", &Compiler::compile_mod}, {"mod", &Compiler::compile_mod},
{"logior", &Compiler::compile_logior}, {"logior", &Compiler::compile_logior},
{"logxor", &Compiler::compile_logxor}, {"logxor", &Compiler::compile_logxor},

View file

@ -88,6 +88,99 @@ Val* Compiler::compile_define_extern(const goos::Object& form, const goos::Objec
return get_none(); 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. * Set something to something.
* Lots of special cases. * 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 // and to_reg'd first, then the destination is computed, if the destination requires math to
// compute. // compute.
auto source = compile_error_guard(args.unnamed.at(1), env)->to_reg(env); auto source = compile_error_guard(args.unnamed.at(1), env)->to_reg(env);
auto dest = compile_error_guard(destination, env);
if (destination.is_symbol()) { return do_set(form, dest, source, env);
// 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");
} }

View file

@ -150,6 +150,7 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest
IRegConstraint constr; IRegConstraint constr;
constr.instr_idx = 0; // constraint at function start constr.instr_idx = 0; // constraint at function start
auto ireg = new_func_env->make_ireg(lambda.params.at(i).type, emitter::RegKind::GPR); auto ireg = new_func_env->make_ireg(lambda.params.at(i).type, emitter::RegKind::GPR);
ireg->mark_as_settable();
constr.ireg = ireg->ireg(); constr.ireg = ireg->ireg();
constr.desired_register = emitter::gRegInfo.get_arg_reg(i); constr.desired_register = emitter::gRegInfo.get_arg_reg(i);
new_func_env->params[lambda.params.at(i).name] = ireg; 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 type = eval_args.at(i)->type();
auto copy = env->make_ireg(type, get_preferred_reg_kind(type)); auto copy = env->make_ireg(type, get_preferred_reg_kind(type));
env->emit(std::make_unique<IR_RegSet>(copy, eval_args.at(i))); 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; 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; std::vector<RegVal*> arg_outs;
for (auto& arg : args) { for (auto& arg : args) {
arg_outs.push_back(env->make_ireg(arg->type(), emitter::RegKind::GPR)); 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)); env->emit(std::make_unique<IR_RegSet>(arg_outs.back(), arg));
} }

View file

@ -442,6 +442,57 @@ Val* Compiler::compile_variable_shift(const RegVal* in,
return result; 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) { Val* Compiler::compile_mod(const goos::Object& form, const goos::Object& rest, Env* env) {
auto args = get_va(form, rest); auto args = get_va(form, rest);
va_check(form, args, {{}, {}}, {}); va_check(form, args, {{}, {}}, {});

View file

@ -34,8 +34,91 @@ bool integer_fits(s64 in, int size, bool is_signed) {
assert(false); assert(false);
} }
} }
u32 float_as_u32(float x) {
u32 result;
memcpy(&result, &x, 4);
return result;
}
} // namespace } // 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, Val* Compiler::compile_new_static_structure_or_basic(const goos::Object& form,
const TypeSpec& type, const TypeSpec& type,
const goos::Object& _field_defs, const goos::Object& _field_defs,

View file

@ -464,7 +464,13 @@ Val* Compiler::compile_deref(const goos::Object& form, const goos::Object& _rest
continue; 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); 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)) { if (m_ts.typecheck(m_ts.make_typespec("integer"), desired_ts, "", false, false)) {
auto result = number_to_integer(base, env); auto result = number_to_integer(base, env);
result->set_type(desired_ts); if (result != base) {
return result; 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)) { 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)) { if (is_structure(type_of_object)) {
return compile_new_static_structure_or_basic(form, type_of_object, *rest, env); 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") { } else if (allocation == "stack") {
auto type_of_object = m_ts.make_typespec(type_as_string); auto type_of_object = m_ts.make_typespec(type_as_string);

View file

@ -1528,9 +1528,10 @@ class IGen {
/*! /*!
* Shift 64-ptr left (logical) by the constant shift amount "sa". * 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); 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)); instr.set(Imm(1, sa));
return instr; return instr;
} }
@ -1538,9 +1539,10 @@ class IGen {
/*! /*!
* Shift 64-ptr right (logical) by the constant shift amount "sa". * 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); 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)); instr.set(Imm(1, sa));
return instr; return instr;
} }
@ -1548,9 +1550,10 @@ class IGen {
/*! /*!
* Shift 64-ptr right (arithmetic) by the constant shift amount "sa". * 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); 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)); instr.set(Imm(1, sa));
return instr; return instr;
} }

View file

@ -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

View file

@ -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))
)

View file

@ -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)

View 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)))
)

View file

@ -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)))
)

View file

@ -227,10 +227,15 @@ TEST_F(ArithmeticTests, NestedFunctionCall) {
runner.run_static_test(env, testCategory, "nested-function.static.gc", {"10\n"}); 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"}); 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) { TEST_F(ArithmeticTests, Subtraction) {
runner.run_static_test(env, testCategory, "subtract-1.static.gc", {"4\n"}); runner.run_static_test(env, testCategory, "subtract-1.static.gc", {"4\n"});
runner.run_static_test(env, testCategory, "subtract-2.static.gc", {"4\n"}); runner.run_static_test(env, testCategory, "subtract-2.static.gc", {"4\n"});

View file

@ -302,6 +302,24 @@ TEST_F(WithGameTests, GameCount) {
get_test_pass_string("game-count", 4)); 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) { TEST_F(WithGameTests, Math) {
runner.run_static_test(env, testCategory, "test-math.gc", get_test_pass_string("math", 31)); runner.run_static_test(env, testCategory, "test-math.gc", get_test_pass_string("math", 31));
} }