mirror of
https://github.com/open-goal/jak-project.git
synced 2025-02-06 13:49:15 +00:00
[compiler] support 128-bit bitfields (#500)
* support setting and accessing fields of a 128-bit bitfield * remove print * rework static constants * support 128-bit bitfields as part of static structures * dynamic construction
This commit is contained in:
parent
ec412c7777
commit
a6258f3654
@ -16,6 +16,7 @@ add_library(common
|
||||
type_system/TypeFieldLookup.cpp
|
||||
type_system/TypeSpec.cpp
|
||||
type_system/TypeSystem.cpp
|
||||
util/BitUtils.cpp
|
||||
util/dgo_util.cpp
|
||||
util/DgoReader.cpp
|
||||
util/DgoWriter.cpp
|
||||
|
@ -1322,6 +1322,14 @@ void TypeSystem::add_field_to_bitfield(BitFieldType* type,
|
||||
"type is {} bits)\n",
|
||||
type->get_name(), field_name, field_size + offset, type->get_load_size() * 8);
|
||||
}
|
||||
|
||||
// 128-bit bitfields have the limitation that fields cannot cross the 64-bit boundary.
|
||||
if (offset < 64 && offset + field_size > 64) {
|
||||
throw_typesystem_error(
|
||||
"Type {}'s bitfield {} will cross bit 64, which is not permitted. Range [{}, {})",
|
||||
type->get_name(), field_name, offset, offset + field_size);
|
||||
}
|
||||
|
||||
BitField field(field_type, field_name, offset, field_size);
|
||||
type->m_fields.push_back(field);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "defenum.h"
|
||||
#include "deftype.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "common/util/BitUtils.h"
|
||||
|
||||
namespace {
|
||||
const goos::Object& car(const goos::Object* x) {
|
||||
@ -30,34 +31,6 @@ bool is_type(const std::string& expected, const TypeSpec& actual, const TypeSyst
|
||||
return ts->tc(ts->make_typespec(expected), actual);
|
||||
}
|
||||
|
||||
bool integer_fits(s64 in, int size, bool is_signed) {
|
||||
switch (size) {
|
||||
case 1:
|
||||
if (is_signed) {
|
||||
return in >= INT8_MIN && in <= INT8_MAX;
|
||||
} else {
|
||||
return in >= 0 && in <= UINT8_MAX;
|
||||
}
|
||||
case 2:
|
||||
if (is_signed) {
|
||||
return in >= INT16_MIN && in <= INT16_MAX;
|
||||
} else {
|
||||
return in >= 0 && in <= UINT16_MAX;
|
||||
}
|
||||
case 4:
|
||||
if (is_signed) {
|
||||
return in >= INT32_MIN && in <= INT32_MAX;
|
||||
} else {
|
||||
return in >= 0 && in <= UINT32_MAX;
|
||||
}
|
||||
case 8:
|
||||
return true;
|
||||
default:
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string symbol_string(const goos::Object& obj) {
|
||||
if (obj.is_symbol()) {
|
||||
return obj.as_symbol()->name;
|
||||
|
@ -475,6 +475,9 @@ DeftypeResult parse_deftype(const goos::Object& deftype, TypeSystem* ts) {
|
||||
assert(pto);
|
||||
auto new_type = std::make_unique<BitFieldType>(
|
||||
parent_type_name, name, pto->get_size_in_memory(), pto->get_load_signed());
|
||||
auto parent_value = dynamic_cast<ValueType*>(pto);
|
||||
assert(parent_value);
|
||||
new_type->inherit(parent_value);
|
||||
new_type->set_runtime_type(pto->get_runtime_name());
|
||||
auto sr = parse_bitfield_type_def(new_type.get(), ts, field_list_obj, options_obj);
|
||||
result.flags = sr.flags;
|
||||
|
37
common/util/BitUtils.cpp
Normal file
37
common/util/BitUtils.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include "BitUtils.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
bool integer_fits(s64 in, int size, bool is_signed) {
|
||||
switch (size) {
|
||||
case 1:
|
||||
if (is_signed) {
|
||||
return in >= INT8_MIN && in <= INT8_MAX;
|
||||
} else {
|
||||
return in >= 0 && in <= UINT8_MAX;
|
||||
}
|
||||
case 2:
|
||||
if (is_signed) {
|
||||
return in >= INT16_MIN && in <= INT16_MAX;
|
||||
} else {
|
||||
return in >= 0 && in <= UINT16_MAX;
|
||||
}
|
||||
case 4:
|
||||
if (is_signed) {
|
||||
return in >= INT32_MIN && in <= INT32_MAX;
|
||||
} else {
|
||||
return in >= 0 && in <= UINT32_MAX;
|
||||
}
|
||||
case 8:
|
||||
return true;
|
||||
default:
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
u32 float_as_u32(float x) {
|
||||
u32 result;
|
||||
memcpy(&result, &x, 4);
|
||||
return result;
|
||||
}
|
@ -2,8 +2,8 @@
|
||||
|
||||
#include <optional>
|
||||
#include "common/util/assert.h"
|
||||
|
||||
#include "common/util/Range.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
constexpr int BITS_PER_BYTE = 8;
|
||||
template <typename T>
|
||||
@ -60,4 +60,7 @@ std::optional<int> get_power_of_two(T in) {
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool integer_fits(s64 in, int size, bool is_signed);
|
||||
u32 float_as_u32(float x);
|
||||
|
@ -148,4 +148,11 @@
|
||||
- Invalid static pairs now have nice errors instead of exiting the compiler
|
||||
- Added unsigned division (previously signed division was used for unsigned numbers)
|
||||
- Use shifts (64-bit) for positive power of two multiply and divide. Otherwise use 32-bit. This matches GOAL.
|
||||
- Allow setting a 64-bit or less memory location from a 128-bit variable (upper bits are discarded).
|
||||
- Allow setting a 64-bit or less memory location from a 128-bit variable (upper bits are discarded).
|
||||
- It is now a compiler error to declare a bitfield type where a field crosses bit 64.
|
||||
- Fixed a bug where a let/immediate lambda with an argument with type of child of int128/uint128 would end up in a 64 bit register.
|
||||
- Support accessing and setting fields of a 128-bit bitfield type.
|
||||
- Fixed a bug where the mask constant for clearing a bitfield was not computed correctly
|
||||
- Support 128-bit bitfields inside of static structure
|
||||
- Support 128-bit bitfield constants
|
||||
- Support dynamic construction of 128-bit bitfield values
|
||||
|
@ -40,6 +40,7 @@
|
||||
(water-tex1 60)
|
||||
;; merc1 61
|
||||
;; generic1 62
|
||||
;; debug text 68
|
||||
)
|
||||
|
||||
;; Information related to visibility data for a level.
|
||||
|
@ -64,6 +64,15 @@ class Compiler {
|
||||
goos::Object expand_macro_completely(const goos::Object& src, Env* env);
|
||||
|
||||
void set_bitfield(const goos::Object& form, BitFieldVal* dst, RegVal* src, Env* env);
|
||||
void set_bitfield_128(const goos::Object& form, BitFieldVal* dst, RegVal* src, Env* env);
|
||||
|
||||
void set_bits_in_bitfield(int size,
|
||||
int offset,
|
||||
RegVal* dst,
|
||||
RegVal* src,
|
||||
FunctionEnv* fe,
|
||||
Env* env);
|
||||
|
||||
Val* do_set(const goos::Object& form, Val* dst, RegVal* src_in_reg, Val* src, Env* env);
|
||||
Val* compile_goos_macro(const goos::Object& o,
|
||||
const goos::Object& macro_obj,
|
||||
@ -71,6 +80,7 @@ class Compiler {
|
||||
Env* env);
|
||||
Val* compile_pair(const goos::Object& code, Env* env);
|
||||
Val* compile_integer(const goos::Object& code, Env* env);
|
||||
Val* compile_integer(const U128& value, Env* env);
|
||||
Val* compile_integer(s64 value, Env* env);
|
||||
Val* compile_char(const goos::Object& code, Env* env);
|
||||
Val* compile_float(const goos::Object& code, Env* env);
|
||||
|
87
goalc/compiler/ConstantValue.h
Normal file
87
goalc/compiler/ConstantValue.h
Normal file
@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "common/util/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/util/BitUtils.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
struct U128 {
|
||||
U128() = default;
|
||||
U128(u64 _lo, u64 _hi) : lo(_lo), hi(_hi) {}
|
||||
u64 lo = 0;
|
||||
u64 hi = 0;
|
||||
};
|
||||
|
||||
class ConstantValue {
|
||||
public:
|
||||
ConstantValue(const void* data, int size) : m_size(size) {
|
||||
assert(size == 8 || size == 16);
|
||||
memcpy(m_value, data, size);
|
||||
}
|
||||
|
||||
std::string print() const {
|
||||
if (m_size == 8) {
|
||||
return std::to_string(value_64());
|
||||
} else {
|
||||
return fmt::format("0x{:08x}{:08x}", value_128_hi(), value_128_lo());
|
||||
}
|
||||
}
|
||||
|
||||
s64 value_64() const {
|
||||
assert(m_size == 8);
|
||||
s64 result;
|
||||
memcpy(&result, m_value, sizeof(s64));
|
||||
return result;
|
||||
}
|
||||
|
||||
u64 value_128_lo() const {
|
||||
assert(m_size == 16);
|
||||
s64 result;
|
||||
memcpy(&result, m_value, sizeof(s64));
|
||||
return result;
|
||||
}
|
||||
|
||||
u64 value_128_hi() const {
|
||||
assert(m_size == 16);
|
||||
s64 result;
|
||||
memcpy(&result, m_value + sizeof(s64), sizeof(s64));
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* 8 or 16 byte constant?
|
||||
*/
|
||||
int size() const { return m_size; }
|
||||
|
||||
/*!
|
||||
* Okay to put this in a gpr?
|
||||
*/
|
||||
bool uses_gpr() const { return m_size == 8; }
|
||||
|
||||
/*!
|
||||
* Try to copy data to destination.
|
||||
*/
|
||||
bool copy_to(void* destination, int size, bool is_signed) const {
|
||||
if (m_size == 8) {
|
||||
if (!integer_fits(value_64(), size, is_signed)) {
|
||||
return false;
|
||||
}
|
||||
memcpy(destination, m_value, size);
|
||||
return true;
|
||||
} else if (m_size == 16) {
|
||||
if (size != 16) {
|
||||
return false;
|
||||
}
|
||||
memcpy(destination, m_value, 16);
|
||||
return true;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
u8 m_value[16] = {0};
|
||||
int m_size;
|
||||
};
|
@ -180,7 +180,7 @@ void StaticPair::generate_item(const StaticResult& item, int offset) {
|
||||
// if it's a constant data, it should always be a boxed integer for a pair.
|
||||
// or I guess you could put a normal integer too. Either way, we assume signed here,
|
||||
// though we may need to allow overflow so you can store either signed/unsigned things in pairs
|
||||
s32 value = item.get_as_s32();
|
||||
s32 value = item.constant_s32();
|
||||
memcpy(data.data() + offset, &value, POINTER_SIZE);
|
||||
}
|
||||
}
|
||||
@ -197,14 +197,18 @@ StaticResult StaticResult::make_structure_reference(StaticStructure* structure,
|
||||
return result;
|
||||
}
|
||||
|
||||
StaticResult StaticResult::make_constant_data(u64 value, TypeSpec ts) {
|
||||
StaticResult StaticResult::make_constant_data(const ConstantValue& data, TypeSpec ts) {
|
||||
StaticResult result;
|
||||
result.m_kind = Kind::CONSTANT_DATA;
|
||||
result.m_constant_data = value;
|
||||
result.m_constant_data = data;
|
||||
result.m_ts = std::move(ts);
|
||||
return result;
|
||||
}
|
||||
|
||||
StaticResult StaticResult::make_constant_data(u64 data, const TypeSpec& ts) {
|
||||
return make_constant_data(ConstantValue((void*)&data, sizeof(u64)), ts);
|
||||
}
|
||||
|
||||
StaticResult StaticResult::make_symbol(const std::string& name) {
|
||||
StaticResult result;
|
||||
result.m_kind = Kind::SYMBOL;
|
||||
|
@ -1,12 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef JAK_STATICOBJECT_H
|
||||
#define JAK_STATICOBJECT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/type_system/TypeSpec.h"
|
||||
#include "goalc/emitter/ObjectGenerator.h"
|
||||
#include "goalc/compiler/ConstantValue.h"
|
||||
#include "common/util/BitUtils.h"
|
||||
|
||||
class StaticObject {
|
||||
public:
|
||||
@ -99,7 +98,8 @@ class StaticResult {
|
||||
StaticResult() = default;
|
||||
|
||||
static StaticResult make_structure_reference(StaticStructure* structure, TypeSpec ts);
|
||||
static StaticResult make_constant_data(u64 value, TypeSpec ts);
|
||||
static StaticResult make_constant_data(const ConstantValue& data, TypeSpec ts);
|
||||
static StaticResult make_constant_data(u64 data, const TypeSpec& ts);
|
||||
static StaticResult make_symbol(const std::string& name);
|
||||
|
||||
std::string print() const;
|
||||
@ -114,10 +114,10 @@ class StaticResult {
|
||||
return m_struct;
|
||||
}
|
||||
|
||||
s32 get_as_s32() const {
|
||||
assert(is_constant_data());
|
||||
// todo, check that it fits.
|
||||
return (s32)m_constant_data;
|
||||
s32 constant_s32() const {
|
||||
assert(is_constant_data() && m_constant_data && m_constant_data->size() == 8 &&
|
||||
integer_fits(m_constant_data->value_64(), 4, true));
|
||||
return (s32)m_constant_data->value_64();
|
||||
}
|
||||
|
||||
const std::string& symbol_name() const {
|
||||
@ -125,9 +125,14 @@ class StaticResult {
|
||||
return m_symbol;
|
||||
}
|
||||
|
||||
u64 constant_data() const {
|
||||
assert(is_constant_data());
|
||||
return m_constant_data;
|
||||
u64 constant_u64() const {
|
||||
assert(is_constant_data() && m_constant_data && m_constant_data->size() == 8);
|
||||
return m_constant_data->value_64();
|
||||
}
|
||||
|
||||
const ConstantValue& constant() const {
|
||||
assert(m_constant_data.has_value());
|
||||
return *m_constant_data;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -138,7 +143,7 @@ class StaticResult {
|
||||
StaticStructure* m_struct = nullptr;
|
||||
|
||||
// used for only constant data
|
||||
u64 m_constant_data = 0;
|
||||
std::optional<ConstantValue> m_constant_data;
|
||||
|
||||
// used for only symbol
|
||||
std::string m_symbol;
|
||||
@ -156,5 +161,3 @@ class StaticPair : public StaticStructure {
|
||||
private:
|
||||
StaticResult m_car, m_cdr;
|
||||
};
|
||||
|
||||
#endif // JAK_STATICOBJECT_H
|
||||
|
@ -7,7 +7,6 @@
|
||||
* Fallback to_gpr if a more optimized one is not provided.
|
||||
*/
|
||||
RegVal* Val::to_gpr(Env* fe) {
|
||||
// TODO - handle 128-bit stuff here!
|
||||
auto rv = to_reg(fe);
|
||||
if (rv->ireg().reg_class == RegClass::GPR_64) {
|
||||
return rv;
|
||||
@ -90,9 +89,23 @@ const std::optional<emitter::Register>& RegVal::rlet_constraint() const {
|
||||
}
|
||||
|
||||
RegVal* IntegerConstantVal::to_reg(Env* fe) {
|
||||
auto rv = fe->make_gpr(coerce_to_reg_type(m_ts));
|
||||
fe->emit(std::make_unique<IR_LoadConstant64>(rv, m_value));
|
||||
return rv;
|
||||
if (m_value.uses_gpr()) {
|
||||
auto rv = fe->make_gpr(coerce_to_reg_type(m_ts));
|
||||
fe->emit(std::make_unique<IR_LoadConstant64>(rv, m_value.value_64()));
|
||||
return rv;
|
||||
} else {
|
||||
auto rv = fe->make_ireg(m_ts, RegClass::INT_128);
|
||||
auto gpr = fe->make_gpr(TypeSpec("object"));
|
||||
auto xmm_temp = fe->make_ireg(TypeSpec("object"), RegClass::INT_128);
|
||||
|
||||
fe->emit_ir<IR_LoadConstant64>(gpr, m_value.value_128_lo());
|
||||
fe->emit_ir<IR_RegSet>(xmm_temp, gpr);
|
||||
fe->emit_ir<IR_LoadConstant64>(gpr, m_value.value_128_hi());
|
||||
fe->emit_ir<IR_RegSet>(rv, gpr);
|
||||
fe->emit_ir<IR_Int128Math3Asm>(true, rv, rv, xmm_temp, IR_Int128Math3Asm::Kind::PCPYLD);
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
RegVal* SymbolVal::to_reg(Env* fe) {
|
||||
@ -231,20 +244,34 @@ RegVal* StackVarAddrVal::to_reg(Env* fe) {
|
||||
}
|
||||
|
||||
std::string BitFieldVal::print() const {
|
||||
return fmt::format("[bitfield sz {} off {} sx {} of {}]", m_size, m_offset, m_sign_extend,
|
||||
m_parent->print());
|
||||
return fmt::format("[bitfield sz {} off {} sx {} of {} 128? {}]", m_size, m_offset, m_sign_extend,
|
||||
m_parent->print(), m_use_128);
|
||||
}
|
||||
|
||||
RegVal* BitFieldVal::to_reg(Env* env) {
|
||||
// first get the parent value
|
||||
auto parent_reg = m_parent->to_gpr(env);
|
||||
|
||||
int start_bit = -1;
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
auto result = fe->make_ireg(coerce_to_reg_type(m_ts), RegClass::GPR_64);
|
||||
env->emit(std::make_unique<IR_RegSet>(result, parent_reg));
|
||||
RegVal* result = fe->make_ireg(coerce_to_reg_type(m_ts), RegClass::GPR_64);
|
||||
|
||||
int start_bit = m_offset;
|
||||
int end_bit = m_offset + m_size;
|
||||
// this first step gets the right 64-bits into a GPR that is also used as the result.
|
||||
if (m_offset < 64) {
|
||||
// accessing in the lower 64 bits, we can just get the value in a GPR.
|
||||
start_bit = m_offset;
|
||||
RegVal* gpr = m_parent->to_gpr(env);
|
||||
env->emit(std::make_unique<IR_RegSet>(result, gpr));
|
||||
} else {
|
||||
// we need to get the value as a 128-bit integer
|
||||
auto xmm = m_parent->to_reg(env);
|
||||
assert(xmm->ireg().reg_class == RegClass::INT_128);
|
||||
auto xmm_temp = fe->make_ireg(TypeSpec("object"), RegClass::INT_128);
|
||||
env->emit_ir<IR_Int128Math3Asm>(true, xmm_temp, xmm, xmm, IR_Int128Math3Asm::Kind::PCPYUD);
|
||||
env->emit_ir<IR_RegSet>(result, xmm_temp);
|
||||
start_bit = m_offset - 64;
|
||||
}
|
||||
|
||||
// this second step does up to 2 shifts to extract the bitfield and sign extend as needed.
|
||||
int end_bit = start_bit + m_size;
|
||||
assert(end_bit <= 64); // should be checked by the type system.
|
||||
int epad = 64 - end_bit;
|
||||
assert(epad >= 0);
|
||||
int spad = start_bit;
|
||||
|
@ -5,9 +5,6 @@
|
||||
* The GOAL Value. A value represents a place (where the value is stored) and a type.
|
||||
*/
|
||||
|
||||
#ifndef JAK_VAL_H
|
||||
#define JAK_VAL_H
|
||||
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
@ -16,6 +13,7 @@
|
||||
#include "goalc/regalloc/IRegister.h"
|
||||
#include "Lambda.h"
|
||||
#include "StaticObject.h"
|
||||
#include "goalc/compiler/ConstantValue.h"
|
||||
|
||||
class RegVal;
|
||||
class Env;
|
||||
@ -240,13 +238,17 @@ class AliasVal : public Val {
|
||||
|
||||
class IntegerConstantVal : public Val {
|
||||
public:
|
||||
IntegerConstantVal(TypeSpec ts, s64 value) : Val(std::move(ts)), m_value(value) {}
|
||||
std::string print() const override { return "integer-constant-" + std::to_string(m_value); }
|
||||
IntegerConstantVal(TypeSpec ts, const void* data, int size)
|
||||
: Val(std::move(ts)), m_value(data, size) {
|
||||
assert(size == 8 || size == 16);
|
||||
}
|
||||
|
||||
std::string print() const override { return std::string("integer-constant-") + m_value.print(); }
|
||||
RegVal* to_reg(Env* fe) override;
|
||||
s64 value() const { return m_value; }
|
||||
const ConstantValue& value() const { return m_value; }
|
||||
|
||||
protected:
|
||||
s64 m_value = -1;
|
||||
ConstantValue m_value;
|
||||
};
|
||||
|
||||
class FloatConstantVal : public Val {
|
||||
@ -261,12 +263,13 @@ class FloatConstantVal : public Val {
|
||||
|
||||
class BitFieldVal : public Val {
|
||||
public:
|
||||
BitFieldVal(TypeSpec ts, Val* parent, int offset, int size, bool sign_extend)
|
||||
BitFieldVal(TypeSpec ts, Val* parent, int offset, int size, bool sign_extend, bool use128)
|
||||
: Val(std::move(ts)),
|
||||
m_parent(parent),
|
||||
m_offset(offset),
|
||||
m_size(size),
|
||||
m_sign_extend(sign_extend) {
|
||||
m_sign_extend(sign_extend),
|
||||
m_use_128(use128) {
|
||||
m_is_settable = parent->settable();
|
||||
}
|
||||
std::string print() const override;
|
||||
@ -274,6 +277,7 @@ class BitFieldVal : public Val {
|
||||
int offset() const { return m_offset; }
|
||||
int size() const { return m_size; }
|
||||
bool sext() const { return m_sign_extend; }
|
||||
bool use_128_bit() const { return m_use_128; }
|
||||
Val* parent() { return m_parent; }
|
||||
|
||||
protected:
|
||||
@ -281,6 +285,5 @@ class BitFieldVal : public Val {
|
||||
int m_offset = -1;
|
||||
int m_size = -1;
|
||||
bool m_sign_extend = false;
|
||||
bool m_use_128 = false;
|
||||
};
|
||||
|
||||
#endif // JAK_VAL_H
|
||||
|
@ -308,7 +308,12 @@ Val* Compiler::compile_char(const goos::Object& code, Env* env) {
|
||||
*/
|
||||
Val* Compiler::compile_integer(s64 value, Env* env) {
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
return fe->alloc_val<IntegerConstantVal>(m_ts.make_typespec("int"), value);
|
||||
return fe->alloc_val<IntegerConstantVal>(m_ts.make_typespec("int"), &value, 8);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_integer(const U128& value, Env* env) {
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
return fe->alloc_val<IntegerConstantVal>(m_ts.make_typespec("int"), &value, 16);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -99,30 +99,29 @@ 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) {
|
||||
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));
|
||||
|
||||
/*!
|
||||
* Modify dst by setting the bitfield with give size/offset to the value in src.
|
||||
*/
|
||||
void Compiler::set_bits_in_bitfield(int size,
|
||||
int offset,
|
||||
RegVal* dst,
|
||||
RegVal* src,
|
||||
FunctionEnv* fe,
|
||||
Env* env) {
|
||||
// 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());
|
||||
u64 mask_val = ~((((u64)1 << (u64)size) - (u64)1) << (u64)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));
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::AND_64, dst, 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();
|
||||
int left_shift_amnt = 64 - size;
|
||||
int right_shift_amnt = (64 - size) - offset;
|
||||
assert(right_shift_amnt >= 0);
|
||||
|
||||
if (left_shift_amnt > 0) {
|
||||
@ -133,10 +132,72 @@ void Compiler::set_bitfield(const goos::Object& form, BitFieldVal* dst, RegVal*
|
||||
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));
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::OR_64, dst, temp));
|
||||
}
|
||||
|
||||
void Compiler::set_bitfield(const goos::Object& form, BitFieldVal* dst, RegVal* src, Env* env) {
|
||||
if (dst->use_128_bit()) {
|
||||
set_bitfield_128(form, dst, src, env);
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
set_bits_in_bitfield(dst->size(), dst->offset(), original, src, fe, env);
|
||||
|
||||
do_set(form, dst->parent(), original, original, env);
|
||||
}
|
||||
|
||||
void Compiler::set_bitfield_128(const goos::Object& form, BitFieldVal* dst, RegVal* src, Env* env) {
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
|
||||
bool get_top = dst->offset() >= 64;
|
||||
|
||||
// first, get the value we want to modify:
|
||||
assert(m_ts.lookup_type(dst->parent()->type())->get_preferred_reg_class() == RegClass::INT_128);
|
||||
RegVal* original_original = dst->parent()->to_xmm128(env);
|
||||
|
||||
// next, get the 64-bit part we want to modify in the lower 64 bits of an XMM
|
||||
RegVal* xmm_temp = fe->make_ireg(original_original->type(), RegClass::INT_128);
|
||||
if (get_top) {
|
||||
env->emit_ir<IR_Int128Math3Asm>(true, xmm_temp, original_original, original_original,
|
||||
IR_Int128Math3Asm::Kind::PCPYUD);
|
||||
} else {
|
||||
env->emit_ir<IR_RegSet>(xmm_temp, original_original);
|
||||
}
|
||||
|
||||
// convert that xmm to a GPR.
|
||||
RegVal* gpr_64_section = fe->make_gpr(original_original->type());
|
||||
env->emit_ir<IR_RegSet>(gpr_64_section, xmm_temp);
|
||||
|
||||
// set the bits in the GPR
|
||||
int corrected_offset = get_top ? dst->offset() - 64 : dst->offset();
|
||||
set_bits_in_bitfield(dst->size(), corrected_offset, gpr_64_section, src, fe, env);
|
||||
|
||||
// back to xmm
|
||||
env->emit_ir<IR_RegSet>(xmm_temp, gpr_64_section);
|
||||
|
||||
// rebuild the xmm
|
||||
if (get_top) {
|
||||
env->emit_ir<IR_Int128Math3Asm>(true, xmm_temp, xmm_temp, original_original,
|
||||
IR_Int128Math3Asm::Kind::PCPYLD);
|
||||
} else {
|
||||
env->emit_ir<IR_Int128Math3Asm>(true, xmm_temp, xmm_temp, xmm_temp,
|
||||
IR_Int128Math3Asm::Kind::PCPYLD);
|
||||
env->emit_ir<IR_Int128Math3Asm>(true, xmm_temp, xmm_temp, original_original,
|
||||
IR_Int128Math3Asm::Kind::PCPYUD);
|
||||
}
|
||||
|
||||
// set
|
||||
do_set(form, dst->parent(), xmm_temp, xmm_temp, env);
|
||||
}
|
||||
|
||||
/*!
|
||||
* The internal "set" logic.
|
||||
* The source is provided both as the directly Val* from compilation and as a RegVal*.
|
||||
@ -187,7 +248,7 @@ Val* Compiler::do_set(const goos::Object& form, Val* dest, RegVal* src_in_reg, V
|
||||
src_in_reg, base_as_mco->offset, base_as_mco->base->to_gpr(env), ti->get_load_size()));
|
||||
return src_in_reg;
|
||||
} else {
|
||||
// nope, the pointer to dereference is some compliated thing.
|
||||
// nope, the pointer to dereference is some complicated thing.
|
||||
auto ti = m_ts.lookup_type(as_mem_deref->type());
|
||||
env->emit(std::make_unique<IR_StoreConstOffset>(src_in_reg, 0, base->to_gpr(env),
|
||||
ti->get_load_size()));
|
||||
|
@ -171,8 +171,8 @@ Val* Compiler::compile_mul(const goos::Object& form, const goos::Object& rest, E
|
||||
auto val = compile_error_guard(args.unnamed.at(i), env);
|
||||
auto val_as_int = dynamic_cast<IntegerConstantVal*>(val);
|
||||
int power_of_two = -1;
|
||||
if (val_as_int && val_as_int->value() > 0) {
|
||||
auto p = get_power_of_two(val_as_int->value());
|
||||
if (val_as_int && val_as_int->value().uses_gpr() && val_as_int->value().value_64() > 0) {
|
||||
auto p = get_power_of_two(val_as_int->value().value_64());
|
||||
if (p) {
|
||||
power_of_two = *p;
|
||||
}
|
||||
@ -390,8 +390,8 @@ Val* Compiler::compile_div(const goos::Object& form, const goos::Object& rest, E
|
||||
auto val = compile_error_guard(args.unnamed.at(1), env);
|
||||
auto val_as_int = dynamic_cast<IntegerConstantVal*>(val);
|
||||
int power_of_two = -1;
|
||||
if (val_as_int && val_as_int->value() > 0) {
|
||||
auto p = get_power_of_two(val_as_int->value());
|
||||
if (val_as_int && val_as_int->value().uses_gpr() && val_as_int->value().value_64() > 0) {
|
||||
auto p = get_power_of_two(val_as_int->value().value_64());
|
||||
if (p) {
|
||||
power_of_two = *p;
|
||||
}
|
||||
|
@ -8,42 +8,6 @@
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "common/goos/ParseHelpers.h"
|
||||
|
||||
namespace {
|
||||
bool integer_fits(s64 in, int size, bool is_signed) {
|
||||
switch (size) {
|
||||
case 1:
|
||||
if (is_signed) {
|
||||
return in >= INT8_MIN && in <= INT8_MAX;
|
||||
} else {
|
||||
return in >= 0 && in <= UINT8_MAX;
|
||||
}
|
||||
case 2:
|
||||
if (is_signed) {
|
||||
return in >= INT16_MIN && in <= INT16_MAX;
|
||||
} else {
|
||||
return in >= 0 && in <= UINT16_MAX;
|
||||
}
|
||||
case 4:
|
||||
if (is_signed) {
|
||||
return in >= INT32_MIN && in <= INT32_MAX;
|
||||
} else {
|
||||
return in >= 0 && in <= UINT32_MAX;
|
||||
}
|
||||
case 8:
|
||||
return true;
|
||||
default:
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
u32 float_as_u32(float x) {
|
||||
u32 result;
|
||||
memcpy(&result, &x, 4);
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/*!
|
||||
* Compile the fields of a static structure into the given StaticStructure*, applying an offset.
|
||||
* This can be used to generate an entire structure (set offset to 0), or to fill out an inline
|
||||
@ -159,10 +123,8 @@ void Compiler::compile_static_structure_inline(const goos::Object& form,
|
||||
} else if (is_integer(field_info.type)) {
|
||||
assert(field_info.needs_deref); // for now...
|
||||
auto deref_info = m_ts.get_deref_info(m_ts.make_pointer_typespec(field_info.type));
|
||||
auto field_size = deref_info.load_size;
|
||||
assert(field_offset + field_size <= int(structure->data.size()));
|
||||
assert(field_offset + deref_info.load_size <= int(structure->data.size()));
|
||||
assert(!field_info.field.is_inline());
|
||||
s64 value = 0;
|
||||
auto sr = compile_static(field_value, env);
|
||||
if (!sr.is_constant_data()) {
|
||||
throw_compiler_error(form, "Could not use {} for an integer field", field_value.print());
|
||||
@ -170,23 +132,17 @@ void Compiler::compile_static_structure_inline(const goos::Object& form,
|
||||
// we are not strict with the type checking here, as long as you give an "integer" and it
|
||||
// ends up fitting, it's okay.
|
||||
typecheck(form, TypeSpec("integer"), sr.typespec());
|
||||
value = sr.constant_data();
|
||||
|
||||
if (!integer_fits(value, deref_info.load_size, deref_info.sign_extend)) {
|
||||
if (!sr.constant().copy_to(structure->data.data() + field_offset, deref_info.load_size,
|
||||
deref_info.sign_extend)) {
|
||||
throw_compiler_error(form,
|
||||
"Field {} is set to a compile time integer value of {} which would "
|
||||
"overflow (size {} signed {})",
|
||||
field_name_def, value, deref_info.load_size, deref_info.sign_extend);
|
||||
field_name_def, sr.constant().print(), deref_info.load_size,
|
||||
deref_info.sign_extend);
|
||||
}
|
||||
|
||||
if (field_size == 1 || field_size == 2 || field_size == 4 || field_size == 8) {
|
||||
memcpy(structure->data.data() + field_offset, &value, field_size);
|
||||
} else {
|
||||
// not sure how we can create 128-bit integer constants at this point...
|
||||
assert(false);
|
||||
}
|
||||
} else if (is_structure(field_info.type) || is_pair(field_info.type)) {
|
||||
// todo - rewrite this to correctly handle structures within structures.
|
||||
if (is_pair(field_info.type)) {
|
||||
assert(!field_info.field.is_inline());
|
||||
}
|
||||
@ -264,7 +220,7 @@ void Compiler::compile_static_structure_inline(const goos::Object& form,
|
||||
throw_compiler_error(form, "Could not use {} for a float field", field_value.print());
|
||||
}
|
||||
typecheck(form, TypeSpec("float"), sr.typespec());
|
||||
u64 value = sr.constant_data();
|
||||
u64 value = sr.constant_u64();
|
||||
memcpy(structure->data.data() + field_offset, &value, sizeof(float));
|
||||
}
|
||||
|
||||
@ -311,12 +267,12 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form,
|
||||
bool allow_dynamic_construction,
|
||||
Env* env) {
|
||||
// unset fields are 0, so initialize our constant value to 0 here.
|
||||
u64 constant_integer_part = 0;
|
||||
U128 constant_integer_part;
|
||||
|
||||
// look up the bitfield type we're working with. For now, make sure it's under 8 bytes.
|
||||
auto type_info = dynamic_cast<BitFieldType*>(m_ts.lookup_type(type));
|
||||
assert(type_info);
|
||||
assert(type_info->get_load_size() <= 8);
|
||||
bool use_128 = type_info->get_load_size() == 16;
|
||||
|
||||
// We will construct this bitfield in two passes.
|
||||
// The first pass loops through definitions. If they can be evaluated at compile time, it adds
|
||||
@ -378,6 +334,7 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form,
|
||||
} else {
|
||||
u64 unsigned_value = value;
|
||||
u64 or_value = unsigned_value;
|
||||
assert(field_size <= 64);
|
||||
// shift us all the way left to clear upper bits.
|
||||
or_value <<= (64 - field_size);
|
||||
// and back right.
|
||||
@ -386,7 +343,14 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form,
|
||||
throw_compiler_error(form, "Field {}'s value doesn't fit.", field_name_def);
|
||||
}
|
||||
|
||||
constant_integer_part |= (or_value << field_offset);
|
||||
bool start_lo = field_offset < 64;
|
||||
bool end_lo = (field_offset + field_size) <= 64;
|
||||
assert(start_lo == end_lo);
|
||||
if (end_lo) {
|
||||
constant_integer_part.lo |= (or_value << field_offset);
|
||||
} else {
|
||||
constant_integer_part.hi |= (or_value << (field_offset - 64));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (is_float(field_info.result_type)) {
|
||||
@ -404,7 +368,14 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form,
|
||||
field_name_def);
|
||||
}
|
||||
u64 float_value = float_as_u32(value);
|
||||
constant_integer_part |= (float_value << field_offset);
|
||||
bool start_lo = field_offset < 64;
|
||||
bool end_lo = (field_offset + field_size) <= 64;
|
||||
assert(start_lo == end_lo);
|
||||
if (end_lo) {
|
||||
constant_integer_part.lo |= (float_value << field_offset);
|
||||
} else {
|
||||
constant_integer_part.hi |= (float_value << (field_offset - 64));
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
@ -413,39 +384,97 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form,
|
||||
}
|
||||
}
|
||||
|
||||
Val* integer;
|
||||
if (use_128) {
|
||||
integer = compile_integer(constant_integer_part, env);
|
||||
} else {
|
||||
integer = compile_integer(constant_integer_part.lo, env);
|
||||
assert(constant_integer_part.hi == 0);
|
||||
}
|
||||
|
||||
integer->set_type(type);
|
||||
|
||||
if (dynamic_defs.empty()) {
|
||||
auto integer = compile_integer(constant_integer_part, env);
|
||||
integer->set_type(type);
|
||||
return integer;
|
||||
} else {
|
||||
assert(allow_dynamic_construction);
|
||||
auto integer = compile_integer(constant_integer_part, env)->to_gpr(env);
|
||||
|
||||
for (auto& def : dynamic_defs) {
|
||||
auto field_val = compile_error_guard(def.definition, env)->to_gpr(env);
|
||||
if (!m_ts.tc(def.expected_type, field_val->type())) {
|
||||
throw_compiler_error(form, "Typecheck failed for bitfield {}! Got a {} but expected a {}",
|
||||
def.field_name, field_val->type().print(), def.expected_type.print());
|
||||
}
|
||||
int left_shift_amnt = 64 - def.field_size;
|
||||
int right_shift_amnt = (64 - def.field_size) - def.field_offset;
|
||||
assert(right_shift_amnt >= 0);
|
||||
if (use_128) {
|
||||
auto integer_lo = compile_integer(constant_integer_part.lo, env)->to_gpr(env);
|
||||
auto integer_hi = compile_integer(constant_integer_part.hi, env)->to_gpr(env);
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
auto rv = fe->make_ireg(type, RegClass::INT_128);
|
||||
auto xmm_temp = fe->make_ireg(TypeSpec("object"), RegClass::INT_128);
|
||||
|
||||
if (left_shift_amnt > 0) {
|
||||
env->emit(
|
||||
std::make_unique<IR_IntegerMath>(IntegerMathKind::SHL_64, field_val, left_shift_amnt));
|
||||
for (auto& def : dynamic_defs) {
|
||||
auto field_val = compile_error_guard(def.definition, env)->to_gpr(env);
|
||||
if (!m_ts.tc(def.expected_type, field_val->type())) {
|
||||
throw_compiler_error(form, "Typecheck failed for bitfield {}! Got a {} but expected a {}",
|
||||
def.field_name, field_val->type().print(),
|
||||
def.expected_type.print());
|
||||
}
|
||||
|
||||
bool start_lo = def.field_offset < 64;
|
||||
bool end_lo = def.field_offset + def.field_size <= 64;
|
||||
assert(start_lo == end_lo);
|
||||
assert(def.field_size <= 64);
|
||||
|
||||
int corrected_offset = def.field_offset;
|
||||
if (!start_lo) {
|
||||
corrected_offset -= 64;
|
||||
}
|
||||
|
||||
int left_shift_amnt = 64 - def.field_size;
|
||||
int right_shift_amnt = (64 - def.field_size) - corrected_offset;
|
||||
assert(right_shift_amnt >= 0);
|
||||
|
||||
if (left_shift_amnt > 0) {
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::SHL_64, field_val,
|
||||
left_shift_amnt));
|
||||
}
|
||||
|
||||
if (right_shift_amnt > 0) {
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::SHR_64, field_val,
|
||||
right_shift_amnt));
|
||||
}
|
||||
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::OR_64,
|
||||
start_lo ? integer_lo : integer_hi, field_val));
|
||||
}
|
||||
|
||||
if (right_shift_amnt > 0) {
|
||||
env->emit(
|
||||
std::make_unique<IR_IntegerMath>(IntegerMathKind::SHR_64, field_val, right_shift_amnt));
|
||||
fe->emit_ir<IR_RegSet>(xmm_temp, integer_lo);
|
||||
fe->emit_ir<IR_RegSet>(rv, integer_hi);
|
||||
fe->emit_ir<IR_Int128Math3Asm>(true, rv, rv, xmm_temp, IR_Int128Math3Asm::Kind::PCPYLD);
|
||||
return rv;
|
||||
} else {
|
||||
RegVal* integer_reg = integer->to_gpr(env);
|
||||
for (auto& def : dynamic_defs) {
|
||||
auto field_val = compile_error_guard(def.definition, env)->to_gpr(env);
|
||||
if (!m_ts.tc(def.expected_type, field_val->type())) {
|
||||
throw_compiler_error(form, "Typecheck failed for bitfield {}! Got a {} but expected a {}",
|
||||
def.field_name, field_val->type().print(),
|
||||
def.expected_type.print());
|
||||
}
|
||||
int left_shift_amnt = 64 - def.field_size;
|
||||
int right_shift_amnt = (64 - def.field_size) - def.field_offset;
|
||||
assert(right_shift_amnt >= 0);
|
||||
|
||||
if (left_shift_amnt > 0) {
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::SHL_64, field_val,
|
||||
left_shift_amnt));
|
||||
}
|
||||
|
||||
if (right_shift_amnt > 0) {
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::SHR_64, field_val,
|
||||
right_shift_amnt));
|
||||
}
|
||||
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::OR_64, integer_reg, field_val));
|
||||
}
|
||||
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::OR_64, integer, field_val));
|
||||
integer_reg->set_type(type);
|
||||
return integer_reg;
|
||||
}
|
||||
|
||||
integer->set_type(type);
|
||||
return integer;
|
||||
}
|
||||
}
|
||||
|
||||
@ -609,7 +638,6 @@ StaticResult Compiler::compile_static(const goos::Object& form_before_macro, Env
|
||||
} else if (is_bitfield(ts)) {
|
||||
auto val = dynamic_cast<const IntegerConstantVal*>(
|
||||
compile_bitfield_definition(form, ts, constructor_args, false, env));
|
||||
assert(val);
|
||||
return StaticResult::make_constant_data(val->value(), val->type());
|
||||
} else if (is_structure(ts)) {
|
||||
return compile_new_static_structure(form, ts, constructor_args, env);
|
||||
@ -682,12 +710,11 @@ void Compiler::fill_static_array_inline(const goos::Object& form,
|
||||
assert(deref_info.stride == 4);
|
||||
structure->add_pointer_record(elt_offset, sr.reference(), sr.reference()->get_addr_offset());
|
||||
} else if (sr.is_constant_data()) {
|
||||
if (!integer_fits(sr.constant_data(), deref_info.load_size, deref_info.sign_extend)) {
|
||||
if (!sr.constant().copy_to(structure->data.data() + elt_offset, deref_info.load_size,
|
||||
deref_info.sign_extend)) {
|
||||
throw_compiler_error(form, "The integer {} doesn't fit in element {} of array of {}",
|
||||
sr.constant_data(), arg_idx, content_type.print());
|
||||
sr.constant().print(), arg_idx, content_type.print());
|
||||
}
|
||||
u64 data = sr.constant_data();
|
||||
memcpy(structure->data.data() + elt_offset, &data, deref_info.load_size);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
@ -505,7 +505,8 @@ Val* Compiler::get_field_of_bitfield(const BitFieldType* type,
|
||||
Val* result = nullptr;
|
||||
auto bitfield_info = m_ts.lookup_bitfield_info(type->get_name(), field_name);
|
||||
result = fe->alloc_val<BitFieldVal>(bitfield_info.result_type, object, bitfield_info.offset,
|
||||
bitfield_info.size, bitfield_info.sign_extend);
|
||||
bitfield_info.size, bitfield_info.sign_extend,
|
||||
type->get_load_size() == 16);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -582,7 +583,8 @@ Val* Compiler::compile_deref(const goos::Object& form, const goos::Object& _rest
|
||||
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);
|
||||
bitfield_info.size, bitfield_info.sign_extend,
|
||||
type_info->get_load_size() == 16);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -745,7 +747,7 @@ Val* Compiler::compile_print_type(const goos::Object& form, const goos::Object&
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}}, {});
|
||||
auto result = compile(args.unnamed.at(0), env)->to_reg(env);
|
||||
fmt::print("[TYPE] {}\n", result->type().print());
|
||||
fmt::print("[TYPE] {} {}\n", result->type().print(), result->print());
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1107,7 +1109,7 @@ u64 Compiler::enum_lookup(const goos::Object& form,
|
||||
return;
|
||||
}
|
||||
}
|
||||
value |= (1 << kv->second);
|
||||
value |= ((u64)1 << (u64)kv->second);
|
||||
});
|
||||
|
||||
return value;
|
||||
|
@ -0,0 +1,95 @@
|
||||
(deftype b128 (uint128)
|
||||
((f1 int32 :offset 0)
|
||||
(f2 int32 :offset 32)
|
||||
(f3 int32 :offset 64)
|
||||
(f4 int32 :offset 96))
|
||||
)
|
||||
|
||||
(deftype b128-structure (structure)
|
||||
((val b128))
|
||||
)
|
||||
|
||||
|
||||
(let ((val (the b128 (make-u128 #xabcdbeef77777777 #x6666666612347890)))
|
||||
(struct (new 'stack 'b128-structure))
|
||||
)
|
||||
(set! (-> struct val) val)
|
||||
(format #t "~8x ~8x ~8x ~8x~%"
|
||||
(-> struct val f4)
|
||||
(-> struct val f3)
|
||||
(-> struct val f2)
|
||||
(-> struct val f1)
|
||||
)
|
||||
(set! (-> struct val f1) 1)
|
||||
(format #t "~8x ~8x ~8x ~8x~%"
|
||||
(-> struct val f4)
|
||||
(-> struct val f3)
|
||||
(-> struct val f2)
|
||||
(-> struct val f1)
|
||||
)
|
||||
(set! (-> struct val f2) 2)
|
||||
(format #t "~8x ~8x ~8x ~8x~%"
|
||||
(-> struct val f4)
|
||||
(-> struct val f3)
|
||||
(-> struct val f2)
|
||||
(-> struct val f1)
|
||||
)
|
||||
(set! (-> struct val f3) 3)
|
||||
(format #t "~8x ~8x ~8x ~8x~%"
|
||||
(-> struct val f4)
|
||||
(-> struct val f3)
|
||||
(-> struct val f2)
|
||||
(-> struct val f1)
|
||||
)
|
||||
(set! (-> struct val f4) 4)
|
||||
(format #t "~8x ~8x ~8x ~8x~%"
|
||||
(-> struct val f4)
|
||||
(-> struct val f3)
|
||||
(-> struct val f2)
|
||||
(-> struct val f1)
|
||||
)
|
||||
)
|
||||
|
||||
(let ((static-structure (new 'static 'b128-structure
|
||||
:val (new 'static 'b128
|
||||
:f1 #x12341234
|
||||
:f2 #x7
|
||||
:f3 #x666
|
||||
:f4 #xdeadbeef))))
|
||||
(format #t "~8x ~8x ~8x ~8x~%"
|
||||
(-> static-structure val f1)
|
||||
(-> static-structure val f2)
|
||||
(-> static-structure val f3)
|
||||
(-> static-structure val f4))
|
||||
)
|
||||
|
||||
(let ((val (new 'static 'b128
|
||||
:f1 #x23232323
|
||||
:f2 #x78787878
|
||||
:f3 #x92929292
|
||||
:f4 #x12124545))
|
||||
)
|
||||
(format #t "~8x ~8x ~8x ~8x~%"
|
||||
(-> val f4)
|
||||
(-> val f3)
|
||||
(-> val f2)
|
||||
(-> val f1)
|
||||
)
|
||||
)
|
||||
|
||||
(let* ((f1 #xffffffff00001212)
|
||||
(f4 #xffffffff00009878)
|
||||
(val (new 'static 'b128
|
||||
:f1 f1
|
||||
:f2 #x2222
|
||||
:f3 #x3333
|
||||
:f4 f4
|
||||
)))
|
||||
(format #t "~8x ~8x ~8x ~8x~%"
|
||||
(-> val f4)
|
||||
(-> val f3)
|
||||
(-> val f2)
|
||||
(-> val f1)
|
||||
)
|
||||
)
|
||||
|
@ -365,6 +365,19 @@ TEST_F(WithGameTests, TrickyBitField) {
|
||||
get_test_pass_string("bitfield-tricky-access", 14));
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, Bitfield128) {
|
||||
runner.run_static_test(env, testCategory, "test-access-bitfield128.gc",
|
||||
{"-abcdbeef 77777777 66666666 12347890\n"
|
||||
"-abcdbeef 77777777 66666666 00000001\n"
|
||||
"-abcdbeef 77777777 00000002 00000001\n"
|
||||
"-abcdbeef 00000003 00000002 00000001\n"
|
||||
"00000004 00000003 00000002 00000001\n"
|
||||
"12341234 00000007 00000666 -deadbeef\n"
|
||||
"12124545 -92929292 78787878 23232323\n"
|
||||
"00009878 00003333 00002222 00001212\n"
|
||||
"0\n"});
|
||||
}
|
||||
|
||||
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