mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-24 06:39:51 +00:00
[Compiler] Add static data features (#162)
* support static pairs * also support strings * reorganize static stuff * add test * support inlined fields * add missing type check
This commit is contained in:
parent
cae3871730
commit
11a82bbf08
@ -14,6 +14,7 @@ enum LinkKind {
|
||||
LINK_TYPE_PTR = 2, //! link a pointer to a type.
|
||||
LINK_DISTANCE_TO_OTHER_SEG_64 = 3, //! link to another segment
|
||||
LINK_DISTANCE_TO_OTHER_SEG_32 = 4, //! link to another segment
|
||||
LINK_PTR = 5, //! link a pointer within this segment
|
||||
};
|
||||
|
||||
enum SegmentTypes { MAIN_SEGMENT = 0, DEBUG_SEGMENT = 1, TOP_LEVEL_SEGMENT = 2 };
|
||||
|
@ -11,9 +11,11 @@
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace versions {
|
||||
// language version
|
||||
// language version (OpenGOAL)
|
||||
constexpr s32 GOAL_VERSION_MAJOR = 0;
|
||||
constexpr s32 GOAL_VERSION_MINOR = 3;
|
||||
constexpr s32 GOAL_VERSION_MINOR = 4;
|
||||
|
||||
// these versions are from the game
|
||||
constexpr u32 ART_FILE_VERSION = 6;
|
||||
constexpr u32 LEVEL_FILE_VERSION = 30;
|
||||
constexpr u32 DGO_FILE_VERSION = 1;
|
||||
@ -21,7 +23,7 @@ constexpr u32 RES_FILE_VERSION = 1;
|
||||
constexpr u32 TX_PAGE_VERSION = 7;
|
||||
} // namespace versions
|
||||
|
||||
// GOAL kernel version
|
||||
// GOAL kernel version (OpenGOAL changes this version from the game's version)
|
||||
constexpr int KERNEL_VERSION_MAJOR = 2;
|
||||
constexpr int KERNEL_VERSION_MINOR = 0;
|
||||
|
||||
|
@ -80,4 +80,10 @@
|
||||
- Fixed a bug where the return instruction was still emitted and the overridden return type of `asm-func` was ignored for methods
|
||||
- Rearranged function stack frames so spilled register variable slots come after stack structures.
|
||||
- Added `stack` allocated and constructed basic/structure types.
|
||||
- Fixed a bug where functions with exactly 8 parameters created a compiler error.
|
||||
- Fixed a bug where functions with exactly 8 parameters created a compiler error.
|
||||
|
||||
## V0.4
|
||||
- Breaking change: added new link kind to link table format. Code compiled with previous versions will still work, but code compiled in V0.4 that uses static pairs will cause a "unknown link table code 5" error.
|
||||
- Added support for static pairs and lists. Symbols, integers, and strings are supported.
|
||||
- Added much better support for setting fields of statics. Now you can set constants, symbols, strings, pairs, integers, floats, or other statics, including inlined structures/basics! Also uses the full type system for typechecking
|
||||
- Fixed a bug where accessing an inline basic field did not apply the 4-byte basic offset.
|
@ -343,7 +343,7 @@ uint32_t symlink_v3(Ptr<uint8_t> link, Ptr<uint8_t> data) {
|
||||
}
|
||||
|
||||
/*!
|
||||
* Link a single pointer.
|
||||
* Link a single relative offset (used for RIP)
|
||||
*/
|
||||
uint32_t cross_seg_dist_link_v3(Ptr<uint8_t> link,
|
||||
ObjectFileHeader* ofh,
|
||||
@ -360,7 +360,8 @@ uint32_t cross_seg_dist_link_v3(Ptr<uint8_t> link,
|
||||
// printf("link object in seg %d diff %d at %d (%d + %d)\n", target_seg, diff, offset_of_patch,
|
||||
// link_data[2], ofh->code_infos[current_seg].offset);
|
||||
|
||||
// both 32-bit and 64-bit pointer links are supported, though 64-bit ones should disappear soon.
|
||||
// both 32-bit and 64-bit pointer links are supported, though 64-bit ones are no longer in use.
|
||||
// we still support it just in case we want to run ancient code.
|
||||
if (size == 4) {
|
||||
*Ptr<int32_t>(offset_of_patch).c() = diff;
|
||||
} else if (size == 8) {
|
||||
@ -372,6 +373,14 @@ uint32_t cross_seg_dist_link_v3(Ptr<uint8_t> link,
|
||||
return 1 + 3 * 4;
|
||||
}
|
||||
|
||||
uint32_t ptr_link_v3(Ptr<u8> link, ObjectFileHeader* ofh, int current_seg) {
|
||||
auto* link_data = link.cast<u32>().c();
|
||||
u32 patch_loc = link_data[0] + ofh->code_infos[current_seg].offset;
|
||||
u32 patch_value = link_data[1] + ofh->code_infos[current_seg].offset;
|
||||
*Ptr<u32>(patch_loc).c() = patch_value;
|
||||
return 8;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Run the linker. For now, all linking is done in two runs. If this turns out to be too slow,
|
||||
* this should be modified to do incremental linking over multiple runs.
|
||||
@ -468,16 +477,18 @@ uint32_t link_control::work_v3() {
|
||||
lp = lp + 1; // seek past id
|
||||
lp = lp + typelink_v3(lp, Ptr<u8>(ofh->code_infos[m_segment_process].offset));
|
||||
break;
|
||||
|
||||
case LINK_DISTANCE_TO_OTHER_SEG_64:
|
||||
lp = lp + 1;
|
||||
lp = lp + cross_seg_dist_link_v3(lp, ofh, m_segment_process, 8);
|
||||
break;
|
||||
|
||||
case LINK_DISTANCE_TO_OTHER_SEG_32:
|
||||
lp = lp + 1;
|
||||
lp = lp + cross_seg_dist_link_v3(lp, ofh, m_segment_process, 4);
|
||||
break;
|
||||
case LINK_PTR:
|
||||
lp = lp + 1;
|
||||
lp = lp + ptr_link_v3(lp, ofh, m_segment_process);
|
||||
break;
|
||||
default:
|
||||
printf("unknown link table thing %d\n", *lp);
|
||||
exit(0);
|
||||
|
@ -2298,8 +2298,7 @@
|
||||
(define *default-dead-pool* (the dead-pool *nk-dead-pool*))
|
||||
(define *pickup-dead-pool* (the dead-pool *nk-dead-pool*))
|
||||
|
||||
;; todo dead pool list
|
||||
|
||||
(define *dead-pool-list* '(*4k-dead-pool* *8k-dead-pool* *16k-dead-pool* *nk-dead-pool* *target-dead-pool* *camera-dead-pool* *camera-master-dead-pool*))
|
||||
|
||||
;; main active pool
|
||||
(define *active-pool* (new 'global 'process-tree 'active-pool))
|
||||
|
@ -94,6 +94,10 @@ class Compiler {
|
||||
bool is_basic(const TypeSpec& ts);
|
||||
bool is_structure(const TypeSpec& ts);
|
||||
bool is_bitfield(const TypeSpec& ts);
|
||||
bool is_pair(const TypeSpec& ts);
|
||||
std::vector<goos::Object> get_list_as_vector(const goos::Object& o,
|
||||
goos::Object* rest_out = nullptr,
|
||||
int max_length = -1);
|
||||
const goos::Object& pair_car(const goos::Object& o);
|
||||
const goos::Object& pair_cdr(const goos::Object& o);
|
||||
void expect_empty_list(const goos::Object& o);
|
||||
@ -210,6 +214,24 @@ class Compiler {
|
||||
const TypeSpec& type,
|
||||
const goos::Object& field_defs,
|
||||
Env* env);
|
||||
Val* compile_static_pair(const goos::Object& form, Env* env);
|
||||
StaticResult compile_static(const goos::Object& form, Env* env);
|
||||
StaticResult compile_static_no_eval_for_pairs(const goos::Object& form, Env* env);
|
||||
StaticResult compile_static_bitfield(const goos::Object& form,
|
||||
const TypeSpec& type,
|
||||
const goos::Object& _field_defs,
|
||||
Env* env);
|
||||
StaticResult compile_new_static_structure(const goos::Object& form,
|
||||
const TypeSpec& type,
|
||||
const goos::Object& _field_defs,
|
||||
Env* env);
|
||||
|
||||
void compile_static_structure_inline(const goos::Object& form,
|
||||
const TypeSpec& type,
|
||||
const goos::Object& _field_defs,
|
||||
StaticStructure* structure,
|
||||
int offset,
|
||||
Env* env);
|
||||
|
||||
template <typename... Args>
|
||||
void throw_compiler_error(const goos::Object& code, const std::string& str, Args&&... args) {
|
||||
|
@ -16,19 +16,13 @@ uint32_t push_data_to_byte_vector(T data, std::vector<uint8_t>& v) {
|
||||
////////////////
|
||||
// StaticString
|
||||
////////////////
|
||||
StaticString::StaticString(std::string data, int _seg) : text(std::move(data)), seg(_seg) {}
|
||||
StaticString::StaticString(std::string text_data, int _seg)
|
||||
: StaticBasic(_seg, "string"), text(std::move(text_data)) {}
|
||||
|
||||
std::string StaticString::print() const {
|
||||
return fmt::format("static-string \"{}\"", text);
|
||||
}
|
||||
|
||||
StaticObject::LoadInfo StaticString::get_load_info() const {
|
||||
LoadInfo info;
|
||||
info.requires_load = false;
|
||||
info.prefer_xmm = false;
|
||||
return info;
|
||||
}
|
||||
|
||||
void StaticString::generate(emitter::ObjectGenerator* gen) {
|
||||
rec = gen->add_static_to_seg(seg, 16);
|
||||
auto& d = gen->get_static_data(rec);
|
||||
@ -49,10 +43,6 @@ void StaticString::generate(emitter::ObjectGenerator* gen) {
|
||||
d.push_back(0);
|
||||
}
|
||||
|
||||
int StaticString::get_addr_offset() const {
|
||||
return BASIC_OFFSET;
|
||||
}
|
||||
|
||||
////////////////
|
||||
// StaticFloat
|
||||
////////////////
|
||||
@ -110,6 +100,14 @@ void StaticStructure::generate_structure(emitter::ObjectGenerator* gen) {
|
||||
for (auto& sym : symbols) {
|
||||
gen->link_static_symbol_ptr(rec, sym.offset, sym.name);
|
||||
}
|
||||
|
||||
for (auto& ptr : pointers) {
|
||||
gen->link_static_pointer(rec, ptr.offset_in_this, ptr.dest->rec, ptr.offset_in_dest);
|
||||
}
|
||||
|
||||
for (auto& type : types) {
|
||||
gen->link_static_type_ptr(rec, type.offset, type.name);
|
||||
}
|
||||
}
|
||||
|
||||
void StaticStructure::generate(emitter::ObjectGenerator* gen) {
|
||||
@ -123,18 +121,94 @@ void StaticStructure::add_symbol_record(std::string name, int offset) {
|
||||
symbols.push_back(srec);
|
||||
}
|
||||
|
||||
void StaticStructure::add_pointer_record(int offset_in_this,
|
||||
StaticStructure* dest,
|
||||
int offset_in_dest) {
|
||||
PointerRecord prec;
|
||||
prec.offset_in_this = offset_in_this;
|
||||
prec.dest = dest;
|
||||
prec.offset_in_dest = offset_in_dest;
|
||||
pointers.push_back(prec);
|
||||
}
|
||||
|
||||
void StaticStructure::add_type_record(std::string name, int offset) {
|
||||
SymbolRecord srec;
|
||||
srec.name = std::move(name);
|
||||
srec.offset = offset;
|
||||
types.push_back(srec);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
// StaticBasic
|
||||
///////////////////
|
||||
|
||||
StaticBasic::StaticBasic(int _seg, std::string _type_name)
|
||||
: StaticStructure(_seg), type_name(std::move(_type_name)) {}
|
||||
: StaticStructure(_seg), type_name(std::move(_type_name)) {
|
||||
add_type_record(type_name, 0);
|
||||
}
|
||||
|
||||
int StaticBasic::get_addr_offset() const {
|
||||
return BASIC_OFFSET;
|
||||
}
|
||||
|
||||
void StaticBasic::generate(emitter::ObjectGenerator* gen) {
|
||||
///////////////////
|
||||
// StaticPair
|
||||
///////////////////
|
||||
|
||||
StaticPair::StaticPair(StaticResult car, StaticResult cdr, int _seg)
|
||||
: StaticStructure(_seg), m_car(std::move(car)), m_cdr(std::move(cdr)) {}
|
||||
|
||||
int StaticPair::get_addr_offset() const {
|
||||
return PAIR_OFFSET;
|
||||
}
|
||||
|
||||
void StaticPair::generate(emitter::ObjectGenerator* gen) {
|
||||
data.resize(2 * POINTER_SIZE); // size of pair
|
||||
generate_item(m_car, 0);
|
||||
generate_item(m_cdr, 4);
|
||||
generate_structure(gen);
|
||||
gen->link_static_type_ptr(rec, 0, type_name);
|
||||
}
|
||||
|
||||
void StaticPair::generate_item(const StaticResult& item, int offset) {
|
||||
if (item.is_reference()) {
|
||||
add_pointer_record(offset, item.reference(), item.reference()->get_addr_offset());
|
||||
} else if (item.is_symbol()) {
|
||||
add_symbol_record(item.symbol_name(), offset);
|
||||
u32 symbol_placeholder = 0xffffffff;
|
||||
memcpy(data.data() + offset, &symbol_placeholder, POINTER_SIZE);
|
||||
} else if (item.is_constant_data()) {
|
||||
// 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();
|
||||
memcpy(data.data() + offset, &value, POINTER_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////
|
||||
// StaticResult
|
||||
///////////////////
|
||||
|
||||
StaticResult StaticResult::make_structure_reference(StaticStructure* structure, TypeSpec ts) {
|
||||
StaticResult result;
|
||||
result.m_kind = Kind::STRUCTURE_REFERENCE;
|
||||
result.m_struct = structure;
|
||||
result.m_ts = std::move(ts);
|
||||
return result;
|
||||
}
|
||||
|
||||
StaticResult StaticResult::make_constant_data(u64 value, TypeSpec ts) {
|
||||
StaticResult result;
|
||||
result.m_kind = Kind::CONSTANT_DATA;
|
||||
result.m_constant_data = value;
|
||||
result.m_ts = std::move(ts);
|
||||
return result;
|
||||
}
|
||||
|
||||
StaticResult StaticResult::make_symbol(const std::string& name) {
|
||||
StaticResult result;
|
||||
result.m_kind = Kind::SYMBOL;
|
||||
result.m_symbol = name;
|
||||
result.m_ts = TypeSpec("symbol");
|
||||
return result;
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/type_system/TypeSpec.h"
|
||||
#include "goalc/emitter/ObjectGenerator.h"
|
||||
|
||||
class StaticObject {
|
||||
@ -26,17 +27,6 @@ class StaticObject {
|
||||
emitter::StaticRecord rec;
|
||||
};
|
||||
|
||||
class StaticString : public StaticObject {
|
||||
public:
|
||||
explicit StaticString(std::string data, int _seg);
|
||||
std::string text;
|
||||
int seg = -1;
|
||||
std::string print() const override;
|
||||
LoadInfo get_load_info() const override;
|
||||
void generate(emitter::ObjectGenerator* gen) override;
|
||||
int get_addr_offset() const override;
|
||||
};
|
||||
|
||||
class StaticFloat : public StaticObject {
|
||||
public:
|
||||
explicit StaticFloat(float _value, int _seg);
|
||||
@ -63,9 +53,20 @@ class StaticStructure : public StaticObject {
|
||||
int offset = -1;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct PointerRecord {
|
||||
int offset_in_this = -1;
|
||||
StaticStructure* dest = nullptr;
|
||||
int offset_in_dest = -1;
|
||||
};
|
||||
|
||||
std::vector<SymbolRecord> types;
|
||||
std::vector<SymbolRecord> symbols;
|
||||
std::vector<PointerRecord> pointers;
|
||||
|
||||
void add_symbol_record(std::string name, int offset);
|
||||
void add_pointer_record(int offset_in_this, StaticStructure* dest, int offset_in_dest);
|
||||
void add_type_record(std::string name, int offset);
|
||||
};
|
||||
|
||||
class StaticBasic : public StaticStructure {
|
||||
@ -73,7 +74,83 @@ class StaticBasic : public StaticStructure {
|
||||
std::string type_name;
|
||||
StaticBasic(int _seg, std::string _type_name);
|
||||
int get_addr_offset() const override;
|
||||
};
|
||||
|
||||
class StaticString : public StaticBasic {
|
||||
public:
|
||||
explicit StaticString(std::string data, int _seg);
|
||||
std::string text;
|
||||
std::string print() const override;
|
||||
void generate(emitter::ObjectGenerator* gen) override;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Represents a "static value". Like a reference to a static structure (including pair, string,
|
||||
* basic), a symbol, or some constant (bitfield, integer, float). Cannot be used to store a static
|
||||
* structure itself, just a reference to one, meaning you cannot set an inlined structure to a
|
||||
* StaticResult.
|
||||
*/
|
||||
class StaticResult {
|
||||
public:
|
||||
StaticResult() = default;
|
||||
|
||||
static StaticResult make_structure_reference(StaticStructure* structure, TypeSpec ts);
|
||||
static StaticResult make_constant_data(u64 value, TypeSpec ts);
|
||||
static StaticResult make_symbol(const std::string& name);
|
||||
|
||||
std::string print() const;
|
||||
|
||||
const TypeSpec& typespec() const { return m_ts; }
|
||||
bool is_reference() const { return m_kind == Kind::STRUCTURE_REFERENCE; }
|
||||
bool is_constant_data() const { return m_kind == Kind::CONSTANT_DATA; }
|
||||
bool is_symbol() const { return m_kind == Kind::SYMBOL; }
|
||||
|
||||
StaticStructure* reference() const {
|
||||
assert(is_reference());
|
||||
return m_struct;
|
||||
}
|
||||
|
||||
s32 get_as_s32() const {
|
||||
assert(is_constant_data());
|
||||
// todo, check that it fits.
|
||||
return (s32)m_constant_data;
|
||||
}
|
||||
|
||||
const std::string& symbol_name() const {
|
||||
assert(is_symbol());
|
||||
return m_symbol;
|
||||
}
|
||||
|
||||
u64 constant_data() const {
|
||||
assert(is_constant_data());
|
||||
return m_constant_data;
|
||||
}
|
||||
|
||||
private:
|
||||
// used for all types
|
||||
TypeSpec m_ts;
|
||||
|
||||
// used for only STRUCTURE_REFERENCE
|
||||
StaticStructure* m_struct = nullptr;
|
||||
|
||||
// used for only constant data
|
||||
u64 m_constant_data = 0;
|
||||
|
||||
// used for only symbol
|
||||
std::string m_symbol;
|
||||
|
||||
enum class Kind { STRUCTURE_REFERENCE, CONSTANT_DATA, SYMBOL, INVALID } m_kind = Kind::INVALID;
|
||||
};
|
||||
|
||||
class StaticPair : public StaticStructure {
|
||||
public:
|
||||
StaticPair(StaticResult car, StaticResult cdr, int _seg);
|
||||
int get_addr_offset() const override;
|
||||
void generate(emitter::ObjectGenerator* gen) override;
|
||||
void generate_item(const StaticResult& item, int offset);
|
||||
|
||||
private:
|
||||
StaticResult m_car, m_cdr;
|
||||
};
|
||||
|
||||
#endif // JAK_STATICOBJECT_H
|
||||
|
@ -163,6 +163,10 @@ bool Compiler::is_bitfield(const TypeSpec& ts) {
|
||||
return m_ts.is_bitfield_type(ts.base_type());
|
||||
}
|
||||
|
||||
bool Compiler::is_pair(const TypeSpec& ts) {
|
||||
return m_ts.typecheck(m_ts.make_typespec("pair"), ts, "", false, false);
|
||||
}
|
||||
|
||||
bool Compiler::try_getting_constant_integer(const goos::Object& in, int64_t* out, Env* env) {
|
||||
(void)env;
|
||||
if (in.is_int()) {
|
||||
@ -223,4 +227,32 @@ bool Compiler::get_true_or_false(const goos::Object& form, const goos::Object& b
|
||||
}
|
||||
throw_compiler_error(form, "The value {} cannot be used as a boolean.", boolean.print());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<goos::Object> Compiler::get_list_as_vector(const goos::Object& o,
|
||||
goos::Object* rest_out,
|
||||
int max_length) {
|
||||
std::vector<goos::Object> result;
|
||||
|
||||
auto* cur = &o;
|
||||
int n = 0;
|
||||
while (true) {
|
||||
if (max_length >= 0 && n >= max_length) {
|
||||
if (rest_out) {
|
||||
*rest_out = *cur;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (cur->is_pair()) {
|
||||
result.push_back(cur->as_pair()->car);
|
||||
cur = &cur->as_pair()->cdr;
|
||||
n++;
|
||||
} else if (cur->is_empty_list()) {
|
||||
if (rest_out) {
|
||||
*rest_out = goos::EmptyListObject::make_new();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -106,7 +106,8 @@ Val* Compiler::compile_quote(const goos::Object& form, const goos::Object& rest,
|
||||
empty_pair->set_type(m_ts.make_typespec("pair"));
|
||||
return empty_pair;
|
||||
}
|
||||
// todo...
|
||||
case goos::ObjectType::PAIR:
|
||||
return compile_static_pair(thing, env);
|
||||
default:
|
||||
throw_compiler_error(form, "Quote is not yet implemented for {}.", thing.print());
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "common/goos/ParseHelpers.h"
|
||||
|
||||
namespace {
|
||||
bool integer_fits(s64 in, int size, bool is_signed) {
|
||||
@ -42,11 +43,193 @@ u32 float_as_u32(float x) {
|
||||
}
|
||||
} // 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);
|
||||
/*!
|
||||
* 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
|
||||
* structure within an existing one (set the offset to the offset of the inline field)
|
||||
*/
|
||||
void Compiler::compile_static_structure_inline(const goos::Object& form,
|
||||
const TypeSpec& type,
|
||||
const goos::Object& _field_defs,
|
||||
StaticStructure* structure,
|
||||
int offset,
|
||||
Env* env) {
|
||||
auto type_info = dynamic_cast<StructureType*>(m_ts.lookup_type(type));
|
||||
assert(type_info);
|
||||
|
||||
// make sure we have enough space
|
||||
if (int(structure->data.size()) < offset + type_info->get_size_in_memory()) {
|
||||
throw_compiler_error(form, "The structure does not fit in the type.");
|
||||
}
|
||||
|
||||
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_compiler_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_field_info(type_info->get_name(), field_name_def);
|
||||
|
||||
if (field_info.field.is_dynamic() || field_info.field.is_array()) {
|
||||
throw_compiler_error(form, "Static objects not yet implemented for dynamic/inline/array");
|
||||
}
|
||||
|
||||
auto field_offset = field_info.field.offset() + offset;
|
||||
|
||||
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_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());
|
||||
}
|
||||
// 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)) {
|
||||
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);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
if (field_info.field.is_inline()) {
|
||||
// for an inline field, we only accept (new 'static '<type> ...)
|
||||
if (!field_value.is_list()) {
|
||||
throw_compiler_error(field_value, "Inline field was not properly specified");
|
||||
}
|
||||
|
||||
goos::Object constructor_args;
|
||||
auto new_form = get_list_as_vector(field_value, &constructor_args, 3);
|
||||
if (new_form.size() != 3) {
|
||||
throw_compiler_error(field_value,
|
||||
"Inline field must be defined with (new 'static 'type-name ...)");
|
||||
}
|
||||
|
||||
if (!new_form.at(0).is_symbol() || new_form.at(0).as_symbol()->name != "new") {
|
||||
throw_compiler_error(field_value,
|
||||
"Inline field must be defined with (new 'static 'type-name ...)");
|
||||
}
|
||||
|
||||
if (!is_quoted_sym(new_form.at(1)) ||
|
||||
unquote(new_form.at(1)).as_symbol()->name != "static") {
|
||||
throw_compiler_error(field_value,
|
||||
"Inline field must be defined with (new 'static 'type-name ...)");
|
||||
}
|
||||
|
||||
auto inlined_type = parse_typespec(unquote(new_form.at(2)));
|
||||
if (inlined_type != field_info.type) {
|
||||
throw_compiler_error(field_value, "Cannot store a {} in an inline {}",
|
||||
inlined_type.print(), field_info.type.print());
|
||||
}
|
||||
compile_static_structure_inline(field_value, inlined_type, constructor_args, structure,
|
||||
field_offset, env);
|
||||
|
||||
if (is_basic(inlined_type)) {
|
||||
structure->add_type_record(inlined_type.base_type(), field_offset);
|
||||
}
|
||||
|
||||
} else {
|
||||
assert(field_info.needs_deref);
|
||||
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()));
|
||||
auto sr = compile_static(field_value, env);
|
||||
if (sr.is_symbol()) {
|
||||
if (sr.symbol_name() != "#f") {
|
||||
typecheck(form, field_info.type, sr.typespec());
|
||||
}
|
||||
structure->add_symbol_record(sr.symbol_name(), field_offset);
|
||||
assert(deref_info.mem_deref);
|
||||
assert(deref_info.can_deref);
|
||||
assert(deref_info.load_size == 4);
|
||||
// the linker needs to see a -1 in order to know to insert a symbol pointer
|
||||
// instead of just the symbol table offset.
|
||||
u32 linker_val = 0xffffffff;
|
||||
memcpy(structure->data.data() + field_offset, &linker_val, 4);
|
||||
} else if (sr.is_reference()) {
|
||||
typecheck(form, field_info.type, sr.typespec());
|
||||
structure->add_pointer_record(field_offset, sr.reference(),
|
||||
sr.reference()->get_addr_offset());
|
||||
} else {
|
||||
throw_compiler_error(form, "Unsupported field value {}.", field_value.print());
|
||||
}
|
||||
}
|
||||
} else if (is_float(field_info.type)) {
|
||||
assert(field_info.needs_deref);
|
||||
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_info.field.is_inline());
|
||||
auto sr = compile_static(field_value, env);
|
||||
if (!sr.is_constant_data()) {
|
||||
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();
|
||||
memcpy(structure->data.data() + field_offset, &value, sizeof(float));
|
||||
}
|
||||
|
||||
else {
|
||||
assert(false); // for now
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StaticResult Compiler::compile_new_static_structure(const goos::Object& form,
|
||||
const TypeSpec& type,
|
||||
const goos::Object& _field_defs,
|
||||
Env* env) {
|
||||
std::unique_ptr<StaticStructure> obj;
|
||||
if (is_basic(type)) {
|
||||
obj = std::make_unique<StaticBasic>(MAIN_SEGMENT, type.base_type());
|
||||
} else {
|
||||
// if we ever find this type of static data outside of MAIN_SEGMENT, we can create an option
|
||||
// in the new form to pick the segment.
|
||||
obj = std::make_unique<StaticStructure>(MAIN_SEGMENT);
|
||||
}
|
||||
|
||||
auto type_info = dynamic_cast<StructureType*>(m_ts.lookup_type(type));
|
||||
assert(type_info);
|
||||
|
||||
obj->data.resize(type_info->get_size_in_memory());
|
||||
compile_static_structure_inline(form, type, _field_defs, obj.get(), 0, env);
|
||||
auto fie = get_parent_env_of_type<FileEnv>(env);
|
||||
auto result = StaticResult::make_structure_reference(obj.get(), type);
|
||||
fie->add_static(std::move(obj));
|
||||
return result;
|
||||
}
|
||||
|
||||
StaticResult Compiler::compile_static_bitfield(const goos::Object& form,
|
||||
const TypeSpec& type,
|
||||
const goos::Object& _field_defs,
|
||||
Env* env) {
|
||||
u64 as_int = 0;
|
||||
|
||||
auto type_info = dynamic_cast<BitFieldType*>(m_ts.lookup_type(type));
|
||||
@ -117,120 +300,197 @@ Val* Compiler::compile_new_static_bitfield(const goos::Object& form,
|
||||
}
|
||||
}
|
||||
|
||||
return fe->alloc_val<IntegerConstantVal>(type, as_int);
|
||||
return StaticResult::make_constant_data(as_int, type);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Handles stuff in static pairs. Integers must be s32's.
|
||||
* - Pairs
|
||||
* - Empty Lists
|
||||
* - Symbols
|
||||
* - Integers
|
||||
* - Strings
|
||||
*/
|
||||
StaticResult Compiler::compile_static_no_eval_for_pairs(const goos::Object& form, Env* env) {
|
||||
auto fie = get_parent_env_of_type<FileEnv>(env);
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
auto segment = fe->segment;
|
||||
if (segment == TOP_LEVEL_SEGMENT) {
|
||||
segment = MAIN_SEGMENT;
|
||||
}
|
||||
if (form.is_pair()) {
|
||||
auto car = compile_static_no_eval_for_pairs(form.as_pair()->car, env);
|
||||
auto cdr = compile_static_no_eval_for_pairs(form.as_pair()->cdr, env);
|
||||
auto pair_structure = std::make_unique<StaticPair>(car, cdr, segment);
|
||||
auto result =
|
||||
StaticResult::make_structure_reference(pair_structure.get(), m_ts.make_typespec("pair"));
|
||||
fie->add_static(std::move(pair_structure));
|
||||
return result;
|
||||
} else if (form.is_int()) {
|
||||
if (!integer_fits(form.as_int(), 4, true)) {
|
||||
throw_compiler_error(
|
||||
form, "Cannot store {} (0x{:x}) in a pair because it overflows a signed 32-bit integer.",
|
||||
form.as_int(), form.as_int());
|
||||
}
|
||||
return StaticResult::make_constant_data(form.as_int(), TypeSpec("int32"));
|
||||
} else if (form.is_symbol()) {
|
||||
return StaticResult::make_symbol(form.as_symbol()->name);
|
||||
} else if (form.is_empty_list()) {
|
||||
return StaticResult::make_symbol("_empty_");
|
||||
} else if (form.is_string()) {
|
||||
// todo - this should eventually work with a string pool
|
||||
auto obj = std::make_unique<StaticString>(form.as_string()->data, segment);
|
||||
auto result = StaticResult::make_structure_reference(obj.get(), m_ts.make_typespec("string"));
|
||||
fie->add_static(std::move(obj));
|
||||
return result;
|
||||
} else {
|
||||
assert(false); // not yet implemented
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Generic copmilation function to handle:
|
||||
* - (new 'static <structure-or-basic>), a reference
|
||||
*
|
||||
* - (new 'static '<bitfield>), an integer constant
|
||||
* - (new 'static 'string), a string (not in the string pool, safe to modify)
|
||||
* - '(...) a quoted pair
|
||||
* - "a string" (goes in the string pool)
|
||||
* - 'a-symbol
|
||||
* - an integer
|
||||
* - a float
|
||||
* - a constant
|
||||
* - #t or #f
|
||||
*/
|
||||
StaticResult Compiler::compile_static(const goos::Object& form, Env* env) {
|
||||
auto fie = get_parent_env_of_type<FileEnv>(env);
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
auto segment = fe->segment;
|
||||
if (segment == TOP_LEVEL_SEGMENT) {
|
||||
segment = MAIN_SEGMENT;
|
||||
}
|
||||
|
||||
if (form.is_symbol()) {
|
||||
// constant, #t, or #f
|
||||
auto& name = form.as_symbol()->name;
|
||||
if (name == "#t" || name == "#f") {
|
||||
return StaticResult::make_symbol(name);
|
||||
}
|
||||
|
||||
// as a constant
|
||||
auto kv = m_global_constants.find(form.as_symbol());
|
||||
if (kv != m_global_constants.end()) {
|
||||
// expand constant and compile again.
|
||||
return compile_static(kv->second, env);
|
||||
} else {
|
||||
throw_compiler_error(form, "The symbol {} could not be evaluated at compile time",
|
||||
form.print());
|
||||
}
|
||||
} else if (form.is_float()) {
|
||||
u64 value = float_as_u32(form.as_float());
|
||||
return StaticResult::make_constant_data(value, TypeSpec("float"));
|
||||
} else if (form.is_int()) {
|
||||
return StaticResult::make_constant_data(form.as_int(), TypeSpec("integer"));
|
||||
} else if (is_quoted_sym(form)) {
|
||||
return StaticResult::make_symbol(unquote(form).as_symbol()->name);
|
||||
} else if (form.is_string()) {
|
||||
// todo string pool
|
||||
auto obj = std::make_unique<StaticString>(form.as_string()->data, segment);
|
||||
auto result = StaticResult::make_structure_reference(obj.get(), m_ts.make_typespec("string"));
|
||||
fie->add_static(std::move(obj));
|
||||
return result;
|
||||
} else if (form.is_pair()) {
|
||||
auto first = form.as_pair()->car;
|
||||
auto rest = form.as_pair()->cdr;
|
||||
if (first.is_symbol() && first.as_symbol()->name == "quote") {
|
||||
if (rest.is_pair()) {
|
||||
auto second = rest.as_pair()->car;
|
||||
if (!rest.as_pair()->cdr.is_empty_list()) {
|
||||
throw_compiler_error(form, "The form {} is an invalid quoted form.", form.print());
|
||||
}
|
||||
if (second.is_pair()) {
|
||||
return compile_static_no_eval_for_pairs(second, env);
|
||||
} else {
|
||||
throw_compiler_error(form, "Could not evaluate the quoted form {} at compile time.",
|
||||
second.print());
|
||||
}
|
||||
}
|
||||
throw_compiler_error(form, "The quoted form {} has no argument.", form.print());
|
||||
} else if (first.is_symbol() && first.as_symbol()->name == "new") {
|
||||
goos::Object constructor_args;
|
||||
auto args = get_list_as_vector(rest, &constructor_args, 2);
|
||||
if (args.size() < 2) {
|
||||
throw_compiler_error(form,
|
||||
"New form evaluated at compile must specify (new 'static <type> ...)");
|
||||
}
|
||||
if (!is_quoted_sym(args.at(0)) || unquote(args.at(0)).as_symbol()->name != "static") {
|
||||
throw_compiler_error(form, "New form evaluated at compile time must use 'static. Got {}.",
|
||||
args.at(0).print());
|
||||
}
|
||||
|
||||
if (!is_quoted_sym(args.at(1))) {
|
||||
throw_compiler_error(form, "New form evaluated at compile got an invalid type: {}",
|
||||
args.at(1).print());
|
||||
}
|
||||
|
||||
auto ts = parse_typespec(unquote(args.at(1)));
|
||||
if (ts == TypeSpec("string")) {
|
||||
// (new 'static 'string)
|
||||
if (rest.is_pair() && rest.as_pair()->cdr.is_empty_list() &&
|
||||
rest.as_pair()->car.is_string()) {
|
||||
auto obj = std::make_unique<StaticString>(rest.as_pair()->car.as_string()->data, segment);
|
||||
auto result =
|
||||
StaticResult::make_structure_reference(obj.get(), m_ts.make_typespec("string"));
|
||||
fie->add_static(std::move(obj));
|
||||
return result;
|
||||
} else {
|
||||
throw_compiler_error(form, "Invalid new static string");
|
||||
}
|
||||
} else if (is_bitfield(ts)) {
|
||||
return compile_static_bitfield(form, ts, constructor_args, env);
|
||||
} else if (is_structure(ts)) {
|
||||
return compile_new_static_structure(form, ts, constructor_args, env);
|
||||
} else {
|
||||
throw_compiler_error(form, "Cannot construct a static {}.", ts.print());
|
||||
}
|
||||
} else {
|
||||
// maybe an enum
|
||||
s64 int_out;
|
||||
if (try_getting_constant_integer(form, &int_out, env)) {
|
||||
return StaticResult::make_constant_data(int_out, TypeSpec("int"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw_compiler_error(form, "Could not evaluate {} at compile time.", form.print());
|
||||
return {};
|
||||
}
|
||||
|
||||
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);
|
||||
auto sr = compile_static_bitfield(form, type, _field_defs, env);
|
||||
assert(sr.is_constant_data());
|
||||
return fe->alloc_val<IntegerConstantVal>(sr.typespec(), sr.constant_data());
|
||||
}
|
||||
|
||||
Val* Compiler::compile_static_pair(const goos::Object& form, Env* env) {
|
||||
assert(form.is_pair()); // (quote PAIR)
|
||||
auto result = compile_static_no_eval_for_pairs(form, env);
|
||||
assert(result.is_reference());
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
auto static_result = fe->alloc_val<StaticVal>(result.reference(), result.typespec());
|
||||
return static_result;
|
||||
}
|
||||
|
||||
Val* Compiler::compile_new_static_structure_or_basic(const goos::Object& form,
|
||||
const TypeSpec& type,
|
||||
const goos::Object& _field_defs,
|
||||
const goos::Object& field_defs,
|
||||
Env* env) {
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
std::unique_ptr<StaticStructure> obj;
|
||||
if (is_basic(type)) {
|
||||
obj = std::make_unique<StaticBasic>(MAIN_SEGMENT, type.base_type());
|
||||
} else {
|
||||
// if we ever find this type of static data outside of MAIN_SEGMENT, we can create an option
|
||||
// in the new form to pick the segment.
|
||||
obj = std::make_unique<StaticStructure>(MAIN_SEGMENT);
|
||||
}
|
||||
|
||||
auto type_info = dynamic_cast<StructureType*>(m_ts.lookup_type(type));
|
||||
assert(type_info); // should always be at least a structure.
|
||||
obj->data.resize(type_info->get_size_in_memory());
|
||||
// the file env will end up owning the obj.
|
||||
auto result = fe->alloc_val<StaticVal>(obj.get(), type);
|
||||
|
||||
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_compiler_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_field_info(type_info->get_name(), field_name_def);
|
||||
|
||||
if (field_info.field.is_dynamic() || field_info.field.is_inline() ||
|
||||
field_info.field.is_array()) {
|
||||
throw_compiler_error(form, "Static objects not yet implemented for dynamic/inline/array");
|
||||
}
|
||||
|
||||
auto field_offset = field_info.field.offset();
|
||||
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(obj->data.size()));
|
||||
|
||||
if (is_integer(field_info.type)) {
|
||||
s64 value = 0;
|
||||
if (!try_getting_constant_integer(field_value, &value, env)) {
|
||||
throw_compiler_error(form,
|
||||
"Field {} is an integer, but the value given couldn't be "
|
||||
"converted to an integer at compile time.",
|
||||
field_name_def);
|
||||
}
|
||||
|
||||
if (!integer_fits(value, 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);
|
||||
}
|
||||
|
||||
if (field_size == 1 || field_size == 2 || field_size == 4 || field_size == 8) {
|
||||
memcpy(obj->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_basic(field_info.type)) {
|
||||
if (is_quoted_sym(field_value)) {
|
||||
obj->add_symbol_record(quoted_sym_as_string(field_value), field_offset);
|
||||
assert(deref_info.mem_deref);
|
||||
assert(deref_info.can_deref);
|
||||
assert(deref_info.load_size == 4);
|
||||
|
||||
// the linker needs to see a -1 in order to know to insert a symbol pointer
|
||||
// instead of just the symbol table offset.
|
||||
u32 linker_val = 0xffffffff;
|
||||
memcpy(obj->data.data() + field_offset, &linker_val, 4);
|
||||
} else if (field_value.is_symbol() &&
|
||||
(field_value.as_symbol()->name == "#t" || field_value.as_symbol()->name == "#f")) {
|
||||
obj->add_symbol_record(symbol_string(field_value), field_offset);
|
||||
assert(deref_info.mem_deref);
|
||||
assert(deref_info.can_deref);
|
||||
assert(deref_info.load_size == 4);
|
||||
|
||||
// the linker needs to see a -1 in order to know to insert a symbol pointer
|
||||
// instead of just the symbol table offset.
|
||||
u32 linker_val = 0xffffffff;
|
||||
memcpy(obj->data.data() + field_offset, &linker_val, 4);
|
||||
} else {
|
||||
throw_compiler_error(
|
||||
form, "Setting a basic field to anything other than a symbol is currently unsupported");
|
||||
}
|
||||
} else if (is_float(field_info.type)) {
|
||||
float value = 0.f;
|
||||
if (!try_getting_constant_float(field_value, &value, env)) {
|
||||
throw_compiler_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));
|
||||
}
|
||||
memcpy(obj->data.data() + field_offset, &value, sizeof(float));
|
||||
}
|
||||
|
||||
else {
|
||||
assert(false); // for now
|
||||
}
|
||||
}
|
||||
|
||||
auto fie = get_parent_env_of_type<FileEnv>(env);
|
||||
fie->add_static(std::move(obj));
|
||||
auto sr = compile_new_static_structure(form, type, field_defs, env);
|
||||
auto result = fe->alloc_val<StaticVal>(sr.reference(), type);
|
||||
return result;
|
||||
}
|
@ -412,8 +412,9 @@ Val* Compiler::get_field_of_structure(const StructureType* type,
|
||||
result = fe->alloc_val<MemoryDerefVal>(di.result_type, loc, MemLoadInfo(di));
|
||||
result->mark_as_settable();
|
||||
} else {
|
||||
result =
|
||||
fe->alloc_val<MemoryOffsetConstantVal>(field.type, object, field.field.offset() + offset);
|
||||
auto field_type_info = m_ts.lookup_type(field.type);
|
||||
result = fe->alloc_val<MemoryOffsetConstantVal>(
|
||||
field.type, object, field.field.offset() + offset + field_type_info->get_offset());
|
||||
result->mark_as_settable();
|
||||
}
|
||||
return result;
|
||||
|
@ -90,6 +90,7 @@ ObjectFileData ObjectGenerator::generate_data_v3() {
|
||||
handle_temp_instr_sym_links(seg);
|
||||
handle_temp_rip_func_links(seg);
|
||||
handle_temp_rip_data_links(seg);
|
||||
handle_temp_static_ptr_links(seg);
|
||||
}
|
||||
|
||||
// actual linking?
|
||||
@ -264,6 +265,23 @@ void ObjectGenerator::link_static_symbol_ptr(StaticRecord rec,
|
||||
m_static_sym_temp_links_by_seg.at(rec.seg)[name].push_back({rec, offset});
|
||||
}
|
||||
|
||||
/*!
|
||||
* Insert a pointer to other static data. This patching will happen during runtime linking.
|
||||
* The source and destination must be in the same segment.
|
||||
*/
|
||||
void ObjectGenerator::link_static_pointer(const StaticRecord& source,
|
||||
int source_offset,
|
||||
const StaticRecord& dest,
|
||||
int dest_offset) {
|
||||
StaticPointerLink link;
|
||||
link.source = source;
|
||||
link.dest = dest;
|
||||
link.offset_in_source = source_offset;
|
||||
link.offset_in_dest = dest_offset;
|
||||
assert(link.source.seg == link.dest.seg);
|
||||
m_static_temp_ptr_links_by_seg.at(source.seg).push_back(link);
|
||||
}
|
||||
|
||||
void ObjectGenerator::link_instruction_static(const InstructionRecord& instr,
|
||||
const StaticRecord& target_static,
|
||||
int offset) {
|
||||
@ -309,6 +327,21 @@ void ObjectGenerator::handle_temp_static_sym_links(int seg) {
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* m_static_temp_ptr_links_by_seg -> m_pointer_links_by_seg
|
||||
*/
|
||||
void ObjectGenerator::handle_temp_static_ptr_links(int seg) {
|
||||
for (const auto& link : m_static_temp_ptr_links_by_seg.at(seg)) {
|
||||
const auto& source_object = m_static_data_by_seg.at(seg).at(link.source.static_id);
|
||||
const auto& dest_object = m_static_data_by_seg.at(seg).at(link.dest.static_id);
|
||||
PointerLink result_link;
|
||||
result_link.segment = seg;
|
||||
result_link.source = source_object.location + link.offset_in_source;
|
||||
result_link.dest = dest_object.location + link.offset_in_dest;
|
||||
m_pointer_links_by_seg.at(seg).push_back(result_link);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* m_jump_temp_links_by_seg patching after memory layout is done
|
||||
*/
|
||||
@ -444,6 +477,17 @@ void ObjectGenerator::emit_link_symbol(int seg) {
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectGenerator::emit_link_ptr(int seg) {
|
||||
auto& out = m_link_by_seg.at(seg);
|
||||
for (auto& rec : m_pointer_links_by_seg.at(seg)) {
|
||||
out.push_back(LINK_PTR);
|
||||
assert(rec.dest >= 0);
|
||||
assert(rec.source >= 0);
|
||||
push_data<u32>(rec.source, out);
|
||||
push_data<u32>(rec.dest, out);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectGenerator::emit_link_rip(int seg) {
|
||||
auto& out = m_link_by_seg.at(seg);
|
||||
for (auto& rec : m_rip_links_by_seg.at(seg)) {
|
||||
@ -476,6 +520,7 @@ void ObjectGenerator::emit_link_table(int seg) {
|
||||
emit_link_symbol(seg);
|
||||
emit_link_type_pointer(seg);
|
||||
emit_link_rip(seg);
|
||||
emit_link_ptr(seg);
|
||||
m_link_by_seg.at(seg).push_back(LINK_TABLE_END);
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,10 @@ class ObjectGenerator {
|
||||
void link_instruction_symbol_mem(const InstructionRecord& rec, const std::string& name);
|
||||
void link_instruction_symbol_ptr(const InstructionRecord& rec, const std::string& name);
|
||||
void link_static_symbol_ptr(StaticRecord rec, int offset, const std::string& name);
|
||||
|
||||
void link_static_pointer(const StaticRecord& source,
|
||||
int source_offset,
|
||||
const StaticRecord& dest,
|
||||
int dest_offset);
|
||||
void link_instruction_static(const InstructionRecord& instr,
|
||||
const StaticRecord& target_static,
|
||||
int offset);
|
||||
@ -91,11 +94,13 @@ class ObjectGenerator {
|
||||
void handle_temp_static_sym_links(int seg);
|
||||
void handle_temp_rip_data_links(int seg);
|
||||
void handle_temp_rip_func_links(int seg);
|
||||
void handle_temp_static_ptr_links(int seg);
|
||||
|
||||
void emit_link_table(int seg);
|
||||
void emit_link_type_pointer(int seg);
|
||||
void emit_link_symbol(int seg);
|
||||
void emit_link_rip(int seg);
|
||||
void emit_link_ptr(int seg);
|
||||
std::vector<u8> generate_header_v3();
|
||||
|
||||
template <typename T>
|
||||
@ -139,6 +144,13 @@ class ObjectGenerator {
|
||||
int offset = -1;
|
||||
};
|
||||
|
||||
struct StaticPointerLink {
|
||||
StaticRecord source;
|
||||
int offset_in_source = -1;
|
||||
StaticRecord dest;
|
||||
int offset_in_dest = -1;
|
||||
};
|
||||
|
||||
struct SymbolInstrLink {
|
||||
InstructionRecord rec;
|
||||
bool is_mem_access = false;
|
||||
@ -166,6 +178,13 @@ class ObjectGenerator {
|
||||
IR_Record dest;
|
||||
};
|
||||
|
||||
struct PointerLink {
|
||||
int segment = -1;
|
||||
// both in bytes.
|
||||
int source = -1;
|
||||
int dest = -1;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using seg_vector = std::array<std::vector<T>, N_SEG>;
|
||||
|
||||
@ -185,6 +204,7 @@ class ObjectGenerator {
|
||||
seg_vector<JumpLink> m_jump_temp_links_by_seg;
|
||||
seg_map<SymbolInstrLink> m_symbol_instr_temp_links_by_seg;
|
||||
seg_map<StaticSymbolLink> m_static_sym_temp_links_by_seg;
|
||||
seg_vector<StaticPointerLink> m_static_temp_ptr_links_by_seg;
|
||||
seg_vector<RipFuncLink> m_rip_func_temp_links_by_seg;
|
||||
seg_vector<RipDataLink> m_rip_data_temp_links_by_seg;
|
||||
|
||||
@ -192,6 +212,7 @@ class ObjectGenerator {
|
||||
seg_map<int> m_type_ptr_links_by_seg;
|
||||
seg_map<int> m_sym_links_by_seg;
|
||||
seg_vector<RipLink> m_rip_links_by_seg;
|
||||
seg_vector<PointerLink> m_pointer_links_by_seg;
|
||||
|
||||
std::vector<FunctionRecord> m_all_function_records;
|
||||
};
|
||||
|
@ -0,0 +1,31 @@
|
||||
(deftype basic-field-test-bitfield (int32)
|
||||
((f1 uint8 :offset 0 :size 1)
|
||||
(f2 uint8 :offset 1 :size 1)
|
||||
(f3 uint8 :offset 2 :size 1)
|
||||
)
|
||||
)
|
||||
|
||||
(deftype basic-field-test-type (basic)
|
||||
((name string)
|
||||
(kc kernel-context)
|
||||
(kci kernel-context :inline)
|
||||
(pos float)
|
||||
(bf int32)
|
||||
(list pair))
|
||||
)
|
||||
|
||||
|
||||
(let ((obj (new 'static 'basic-field-test-type
|
||||
:pos 12.34
|
||||
:list '(a b c )
|
||||
:name "name"
|
||||
:bf (new 'static 'basic-field-test-bitfield :f1 1 :f3 1)
|
||||
:kc (new 'static 'kernel-context :next-pid 12)
|
||||
:kci (new 'static 'kernel-context :current-process 'asdf :relocating-min 33))))
|
||||
(format #t "~A ~D ~f ~A ~D" (-> obj name) (-> obj kc next-pid) (-> obj pos) (-> obj list) (-> obj bf))
|
||||
(format #t " ~D ~D ~A ~A~%"
|
||||
(-> obj kci relocating-min)
|
||||
(logand 15 (the int (-> obj kci)))
|
||||
(-> obj kci type)
|
||||
(-> obj kci current-process))
|
||||
)
|
@ -6,10 +6,11 @@
|
||||
(thing basic)
|
||||
(thing-2 basic)
|
||||
(thing-3 basic)
|
||||
(thing-4 float)
|
||||
(u64 uint64))
|
||||
)
|
||||
|
||||
(define test-static-basic (new 'static 'static-test-basic-type :s8 -122 :s16 -23123 :u64 434343 :thing 'bean :thing-2 #t :thing-3 #f))
|
||||
(define test-static-basic (new 'static 'static-test-basic-type :s8 -122 :s16 -23123 :thing-4 1.2 :u64 434343 :thing 'bean :thing-2 #t :thing-3 #f))
|
||||
|
||||
(expect-true (< (the int test-static-basic) (-> debug current))) ;; should be in debug segment?
|
||||
(expect-true (> (the int test-static-basic) (-> debug base)))
|
||||
@ -23,5 +24,6 @@
|
||||
(expect-true (neq? (-> test-static-basic thing) 'not-bean))
|
||||
(expect-true (eq? (-> test-static-basic type symbol) 'static-test-basic-type))
|
||||
(expect-true (eq? (-> test-static-basic type) static-test-basic-type))
|
||||
(expect-true (eq? (-> test-static-basic thing-4) 1.2))
|
||||
|
||||
(finish-test)
|
@ -0,0 +1,7 @@
|
||||
(defun test-static-pair-function ()
|
||||
(format #t "~A~%" '(8 ( w . a ) beans 16 (-8 -16) twelve ( a . "test")))
|
||||
0
|
||||
)
|
||||
|
||||
(test-static-pair-function)
|
||||
|
@ -259,7 +259,7 @@ TEST_F(WithGameTests, NewStaticStructureIntegers) {
|
||||
|
||||
TEST_F(WithGameTests, NewStaticBasic) {
|
||||
runner.run_static_test(env, testCategory, "test-new-static-basic.gc",
|
||||
get_test_pass_string("new-static-basic", 11));
|
||||
get_test_pass_string("new-static-basic", 12));
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, VectorDot) {
|
||||
@ -324,6 +324,16 @@ TEST_F(WithGameTests, Math) {
|
||||
runner.run_static_test(env, testCategory, "test-math.gc", get_test_pass_string("math", 31));
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, StaticPairs) {
|
||||
runner.run_static_test(env, testCategory, "test-static-pair-1.gc",
|
||||
{"(1 (w . a) beans 2 (-1 -2) twelve (a . \"test\"))\n0\n"});
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, FancyStatic) {
|
||||
runner.run_static_test(env, testCategory, "test-fancy-static-fields.gc",
|
||||
{"\"name\" 12 12.3400 (a b c) 5 33 4 kernel-context asdf\n0\n"});
|
||||
}
|
||||
|
||||
TEST(TypeConsistency, TypeConsistency) {
|
||||
Compiler compiler;
|
||||
compiler.enable_throw_on_redefines();
|
||||
|
Loading…
Reference in New Issue
Block a user