[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:
water111 2020-12-19 15:21:29 -05:00 committed by GitHub
parent cae3871730
commit 11a82bbf08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 758 additions and 156 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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());
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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