[Compiler] Support array fields in static objects (#284)

* first part of static inline array fields

* level h
This commit is contained in:
water111 2021-02-25 22:49:46 -05:00 committed by GitHub
parent 791c4abfc0
commit cadd014add
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 408 additions and 73 deletions

View file

@ -207,8 +207,6 @@
)
(define-extern *level* level-group)
;; OpenGOAL doesn not yet support setting fields to arrays in statics.
#|
(if (zero? *level*)
(set! *level*
(new 'static 'level-group
@ -225,28 +223,28 @@
:name #f
:status 'inactive
:foreground-sink-group
(new 'static 'inline-array 'dma-foreground-sink-group 3
(new 'static 'inline-array dma-foreground-sink-group 3
(new 'static 'dma-foreground-sink-group
:sink (new 'static 'array 'dma-foreground-sink 3
:sink (new 'static 'array dma-foreground-sink 3
(new 'static 'dma-foreground-sink :bucket 10)
(new 'static 'generic-dma-foreground-sink
:bucket 11 :foreground-output-bucket 1
)
0)
)
)
(new 'static 'dma-foreground-sink-group
:sink (new 'static 'array 'dma-foreground-sink 3
:sink (new 'static 'array dma-foreground-sink 3
(new 'static 'dma-foreground-sink
:bucket 49
:foreground-texture-page 1
)
(new 'static 'generic-dma-foreground-sink :
bucket 50 :foreground-texture-page 1
(new 'static 'generic-dma-foreground-sink
:bucket 50 :foreground-texture-page 1
:foreground-output-bucket 1)
0)
)
)
(new 'static 'dma-foreground-sink-group
:sink (new 'static 'array 'dma-foreground-sink 3
:sink (new 'static 'array dma-foreground-sink 3
(new 'static 'dma-foreground-sink
:bucket 58
:foreground-texture-page 2
@ -256,7 +254,7 @@
:foreground-texture-page 2
:foreground-output-bucket 1
)
0)
)
)
)
:inside-sphere? #f
@ -269,9 +267,9 @@
:index 1
:status 'inactive
:foreground-sink-group
(new 'static 'inline-array 'dma-foreground-sink-group 3
(new 'static 'inline-array dma-foreground-sink-group 3
(new 'static 'dma-foreground-sink-group
:sink (new 'static 'array 'dma-foreground-sink 3
:sink (new 'static 'array dma-foreground-sink 3
(new 'static 'dma-foreground-sink
:bucket 17
:foreground-texture-level 1
@ -281,10 +279,10 @@
:foreground-texture-level 1
:foreground-output-bucket 1
)
0)
)
)
(new 'static 'dma-foreground-sink-group
:sink (new 'static 'array 'dma-foreground-sink 3
:sink (new 'static 'array dma-foreground-sink 3
(new 'static 'dma-foreground-sink
:bucket 52
:foreground-texture-page 1
@ -296,11 +294,11 @@
:foreground-texture-level 1
:foreground-output-bucket 1
)
0
)
)
(new 'static 'dma-foreground-sink-group
:sink (new 'static 'array 'dma-foreground-sink 3
:sink (new 'static 'array dma-foreground-sink 3
(new 'static 'dma-foreground-sink
:bucket 61
:foreground-texture-page 2
@ -312,7 +310,7 @@
:foreground-texture-level 1
:foreground-output-bucket 1
)
0)
)
)
)
:inside-sphere? #f
@ -325,9 +323,9 @@
:index 2
:status 'reserved
:foreground-sink-group
(new 'static 'inline-array 'dma-foreground-sink-group 3
(new 'static 'inline-array dma-foreground-sink-group 3
(new 'static 'dma-foreground-sink-group
:sink (new 'static 'array 'dma-foreground-sink 3
:sink (new 'static 'array dma-foreground-sink 3
(new 'static 'dma-foreground-sink
:bucket 45
:foreground-texture-level 2
@ -337,25 +335,25 @@
:foreground-texture-level 2
:foreground-output-bucket 1
)
0)
)
(new 'static 'dma-foreground-sink-group :
sink (new 'static 'array 'dma-foreground-sink 3
(new 'static 'dma-foreground-sink
:bucket 55
:foreground-texture-page 1
:foreground-texture-level 2
)
(new 'static 'generic-dma-foreground-sink
:bucket 56
:foreground-texture-page 1
:foreground-texture-level 2
:foreground-output-bucket 1
)
0)
)
)
(new 'static 'dma-foreground-sink-group
:sink (new 'static 'array 'dma-foreground-sink 3
:sink (new 'static 'array dma-foreground-sink 3
(new 'static 'dma-foreground-sink
:bucket 55
:foreground-texture-page 1
:foreground-texture-level 2
)
(new 'static 'generic-dma-foreground-sink
:bucket 56
:foreground-texture-page 1
:foreground-texture-level 2
:foreground-output-bucket 1
)
)
)
(new 'static 'dma-foreground-sink-group
:sink (new 'static 'array dma-foreground-sink 3
(new 'static 'dma-foreground-sink
:bucket 58
:foreground-texture-page 2
@ -367,7 +365,7 @@
:foreground-texture-level 2
:foreground-output-bucket 1
)
0
)
)
)
@ -378,5 +376,4 @@
)
)
)
|#

View file

@ -165,6 +165,23 @@ class Compiler {
bool boxed,
Env* env);
StaticResult fill_static_inline_array(const goos::Object& form,
const goos::Object& rest,
Env* env);
void fill_static_inline_array_inline(const goos::Object& form,
const TypeSpec& content_type,
const std::vector<goos::Object>& args,
StaticStructure* structure,
int offset,
Env* env);
void fill_static_array_inline(const goos::Object& form,
const TypeSpec& content_type,
const std::vector<goos::Object>& args,
StaticStructure* structure,
int offset,
Env* env);
TypeSystem m_ts;
std::unique_ptr<GlobalEnv> m_global_env = nullptr;
std::unique_ptr<None> m_none = nullptr;

View file

@ -90,7 +90,7 @@ StaticObject::LoadInfo StaticStructure::get_load_info() const {
}
int StaticStructure::get_addr_offset() const {
return 0;
return m_offset;
}
void StaticStructure::generate_structure(emitter::ObjectGenerator* gen) {

View file

@ -48,6 +48,7 @@ class StaticStructure : public StaticObject {
void generate_structure(emitter::ObjectGenerator* gen);
void generate(emitter::ObjectGenerator* gen) override;
int get_addr_offset() const override;
void set_offset(int offset) { m_offset = offset; }
struct SymbolRecord {
int offset = -1;
@ -67,6 +68,9 @@ class StaticStructure : public StaticObject {
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);
private:
int m_offset = 0;
};
class StaticBasic : public StaticStructure {

View file

@ -78,13 +78,82 @@ void Compiler::compile_static_structure_inline(const goos::Object& form,
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");
if (field_info.field.is_dynamic()) {
throw_compiler_error(form, "Dynamic fields are not supported for inline");
}
auto field_offset = field_info.field.offset() + offset;
if (is_integer(field_info.type)) {
if (field_info.field.is_array()) {
bool is_inline = field_info.field.is_inline();
// for an array field, we only accept (new 'static 'array <type> ...)
if (!field_value.is_list()) {
throw_compiler_error(field_value, "Array field was not properly specified");
}
goos::Object constructor_args;
auto new_form = get_list_as_vector(field_value, &constructor_args, 5);
if (new_form.size() != 5) {
throw_compiler_error(
field_value,
"Array field must be defined with (new 'static ['array, 'inline-array] type-name ...)");
}
if (!new_form.at(0).is_symbol() || new_form.at(0).as_symbol()->name != "new") {
throw_compiler_error(
field_value,
"Array field must be defined with (new 'static ['array, 'inline-array] type-name ...)");
}
if (!is_quoted_sym(new_form.at(1)) || unquote(new_form.at(1)).as_symbol()->name != "static") {
throw_compiler_error(
field_value,
"Array field must be defined with (new 'static ['array, 'inline-array] type-name ...)");
}
if (unquote(new_form.at(2)).print() != (is_inline ? "inline-array" : "array")) {
throw_compiler_error(
field_value,
"Array field must be defined with (new 'static ['array, 'inline-array] type-name ...)");
}
auto array_content_type = parse_typespec(new_form.at(3));
if (is_inline) {
if (field_info.field.type() != array_content_type) {
throw_compiler_error(field_value, "Inline array field must have the correct type");
}
} else {
// allow more specific types.
m_ts.typecheck(field_info.field.type(), array_content_type, "Array content type");
}
s64 elt_array_len;
if (!try_getting_constant_integer(new_form.at(4), &elt_array_len, env)) {
throw_compiler_error(field_value, "Array field size is invalid, got {}",
new_form.at(4).print());
}
if (elt_array_len != field_info.field.array_size()) {
throw_compiler_error(field_value, "Array field had an expected size of {} but got {}",
field_info.field.array_size(), elt_array_len);
}
auto arg_list = get_list_as_vector(field_value.as_pair()->cdr);
if (((int)arg_list.size() - 5) > elt_array_len) {
throw_compiler_error(field_value, "Array field definition has too many values in it.");
}
if (is_inline) {
fill_static_inline_array_inline(field_value, field_info.field.type(), arg_list, structure,
field_offset, env);
} else {
fill_static_array_inline(field_value, field_info.field.type(), arg_list, structure,
field_offset, env);
}
} 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;
@ -162,7 +231,7 @@ void Compiler::compile_static_structure_inline(const goos::Object& form,
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") {
if (sr.symbol_name() != "#f" && sr.symbol_name() != "_empty_") {
typecheck(form, field_info.type, sr.typespec());
}
structure->add_symbol_record(sr.symbol_name(), field_offset);
@ -437,6 +506,8 @@ StaticResult Compiler::compile_static(const goos::Object& form, Env* env) {
return fill_static_array(form, rest, true, env);
} else if (unquote(args.at(1)).as_symbol()->name == "array") {
return fill_static_array(form, rest, false, env);
} else if (unquote(args.at(1)).as_symbol()->name == "inline-array") {
return fill_static_inline_array(form, rest, env);
} else {
auto ts = parse_typespec(unquote(args.at(1)));
if (ts == TypeSpec("string")) {
@ -485,6 +556,46 @@ StaticResult Compiler::compile_static(const goos::Object& form, Env* env) {
return {};
}
void Compiler::fill_static_array_inline(const goos::Object& form,
const TypeSpec& content_type,
const std::vector<goos::Object>& args,
StaticStructure* structure,
int offset,
Env* env) {
auto pointer_type = m_ts.make_pointer_typespec(content_type);
auto deref_info = m_ts.get_deref_info(pointer_type);
assert(deref_info.can_deref);
assert(deref_info.mem_deref);
for (size_t i = 4; i < args.size(); i++) {
int arg_idx = i - 4;
int elt_offset = offset + arg_idx * deref_info.stride;
auto sr = compile_static(args.at(i), env);
if (is_integer(content_type)) {
typecheck(form, TypeSpec("integer"), sr.typespec());
} else {
typecheck(form, content_type, sr.typespec());
}
if (sr.is_symbol()) {
assert(deref_info.stride == 4);
structure->add_symbol_record(sr.symbol_name(), elt_offset);
u32 symbol_placeholder = 0xffffffff;
memcpy(structure->data.data() + elt_offset, &symbol_placeholder, 4);
} else if (sr.is_reference()) {
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)) {
throw_compiler_error(form, "The integer {} doesn't fit in element {} of array of {}",
sr.constant_data(), arg_idx, content_type.print());
}
u64 data = sr.constant_data();
memcpy(structure->data.data() + elt_offset, &data, deref_info.load_size);
} else {
assert(false);
}
}
}
StaticResult Compiler::fill_static_array(const goos::Object& form,
const goos::Object& rest,
bool boxed,
@ -530,34 +641,8 @@ StaticResult Compiler::fill_static_array(const goos::Object& form,
}
// now add arguments:
for (size_t i = 4; i < args.size(); i++) {
int arg_idx = i - 4;
int elt_offset = array_header_size + arg_idx * deref_info.stride;
auto sr = compile_static(args.at(i), env);
if (is_integer(content_type)) {
typecheck(form, TypeSpec("integer"), sr.typespec());
} else {
typecheck(form, content_type, sr.typespec());
}
if (sr.is_symbol()) {
assert(deref_info.stride == 4);
obj->add_symbol_record(sr.symbol_name(), elt_offset);
u32 symbol_placeholder = 0xffffffff;
memcpy(obj->data.data() + elt_offset, &symbol_placeholder, 4);
} else if (sr.is_reference()) {
assert(deref_info.stride == 4);
obj->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)) {
throw_compiler_error(form, "The integer {} doesn't fit in element {} of array of {}",
sr.constant_data(), arg_idx, content_type.print());
}
u64 data = sr.constant_data();
memcpy(obj->data.data() + elt_offset, &data, deref_info.load_size);
} else {
assert(false);
}
}
fill_static_array_inline(form, content_type, args, obj.get(), array_header_size, env);
TypeSpec result_type;
if (boxed) {
result_type = m_ts.make_array_typespec(content_type);
@ -569,6 +654,91 @@ StaticResult Compiler::fill_static_array(const goos::Object& form,
return result;
}
void Compiler::fill_static_inline_array_inline(const goos::Object& form,
const TypeSpec& content_type,
const std::vector<goos::Object>& args,
StaticStructure* structure,
int offset,
Env* env) {
auto inline_array_type = m_ts.make_inline_array_typespec(content_type);
auto deref_info = m_ts.get_deref_info(inline_array_type);
assert(deref_info.can_deref);
assert(!deref_info.mem_deref);
for (size_t i = 4; i < args.size(); i++) {
auto arg_idx = i - 4;
int elt_offset = arg_idx * deref_info.stride;
auto& elt_def = args.at(i);
if (!elt_def.is_list()) {
throw_compiler_error(form, "Element in static inline-array must be a {}. Got {}",
content_type.print(), elt_def.print());
}
goos::Object ctor_args;
auto new_form = get_list_as_vector(elt_def, &ctor_args, 3);
if (new_form.size() != 3) {
throw_compiler_error(
elt_def, "Inline array element 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(
elt_def, "Inline array element 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(
elt_def, "Inline array element must be defined with (new 'static 'type-name ...)");
}
auto inlined_type = parse_typespec(unquote(new_form.at(2)));
if (inlined_type != content_type) {
throw_compiler_error(elt_def, "Cannot store a {} in an inline array of {}",
inlined_type.print(), content_type.print());
}
compile_static_structure_inline(elt_def, content_type, ctor_args, structure,
elt_offset + offset, env);
if (is_basic(content_type)) {
structure->add_type_record(content_type.base_type(), elt_offset + offset);
}
}
}
StaticResult Compiler::fill_static_inline_array(const goos::Object& form,
const goos::Object& rest,
Env* env) {
auto fie = get_parent_env_of_type<FileEnv>(env);
// (new 'static 'inline-array ...)
// get all arguments now
auto args = get_list_as_vector(rest);
if (args.size() < 4) {
throw_compiler_error(form, "new static boxed array must have type and min-size arguments");
}
auto content_type = parse_typespec(args.at(2));
s64 min_size;
if (!try_getting_constant_integer(args.at(3), &min_size, env)) {
throw_compiler_error(form, "The length {} is not valid.", args.at(3).print());
}
s32 length = std::max(min_size, s64(args.size() - 4));
auto inline_array_type = m_ts.make_inline_array_typespec(content_type);
auto deref_info = m_ts.get_deref_info(inline_array_type);
assert(deref_info.can_deref);
assert(!deref_info.mem_deref);
// todo
auto obj = std::make_unique<StaticStructure>(MAIN_SEGMENT);
obj->set_offset(is_basic(content_type) ? 4 : 0);
obj->data.resize(length * deref_info.stride);
// now add elements:
fill_static_inline_array_inline(form, content_type, args, obj.get(), 0, env);
TypeSpec result_type = m_ts.make_inline_array_typespec(content_type);
auto result = StaticResult::make_structure_reference(obj.get(), result_type);
fie->add_static(std::move(obj));
return result;
}
Val* Compiler::compile_new_static_bitfield(const goos::Object& form,
const TypeSpec& type,
const goos::Object& _field_defs,

View file

@ -766,7 +766,8 @@ Val* Compiler::compile_static_new(const goos::Object& form,
Env* env) {
auto unquoted = unquote(type);
if (unquoted.is_symbol() &&
(unquoted.as_symbol()->name == "boxed-array" || unquoted.as_symbol()->name == "array")) {
(unquoted.as_symbol()->name == "boxed-array" || unquoted.as_symbol()->name == "array" ||
unquoted.as_symbol()->name == "inline-array")) {
auto fe = get_parent_env_of_type<FunctionEnv>(env);
auto sr = compile_static(form, env);
auto result = fe->alloc_val<StaticVal>(sr.reference(), sr.typespec());

View file

@ -0,0 +1,17 @@
(deftype test-basic-for-static-array-field (basic)
((value uint32)
(name string)
(arr basic 12)
(lst pair)
)
)
(let ((test (new 'static 'test-basic-for-static-array-field
:value 12
:arr (new 'static 'array basic 12
"asdf"
"ghjkl"
)
)))
(format #t "~A~%" (-> test arr 1))
)

View file

@ -0,0 +1,57 @@
(deftype basic-elt (basic)
((name string))
)
(deftype struct-elt (structure)
((thing symbol))
)
(deftype test-basic-for-static-inline-array-field (basic)
((value uint32)
(name string)
(arr basic-elt 12 :inline)
(arr2 struct-elt 3 :inline)
(lst pair)
)
)
(let ((test (new 'static 'test-basic-for-static-inline-array-field
:value 12
:arr (new 'static 'inline-array basic-elt 12
(new 'static 'basic-elt :name "first")
(new 'static 'basic-elt :name "second")
)
:arr2 (new 'static 'inline-array struct-elt 3
(new 'static 'struct-elt :thing 'asdf)
(new 'static 'struct-elt :thing 'two)
)
)))
(format #t "~A ~A~%" (-> test arr 1 name) (-> test arr 0 name))
(format #t "~A #x~X #x~X~%" (-> test arr 1 type) (logand 15 (the int (-> test arr))) (logand 15 (the int (-> test arr 1))))
(format #t "~A~%" (-> test arr2 1 thing))
)
(deftype test-struct-for-static-inline-array-field (structure)
((value uint32)
(name string)
(arr basic-elt 12 :inline)
(arr2 struct-elt 3 :inline)
(lst pair)
)
)
(let ((test (new 'static 'test-struct-for-static-inline-array-field
:value 12
:arr (new 'static 'inline-array basic-elt 12
(new 'static 'basic-elt :name "first")
(new 'static 'basic-elt :name "second")
)
:arr2 (new 'static 'inline-array struct-elt 3
(new 'static 'struct-elt :thing 'asdf)
(new 'static 'struct-elt :thing 'two)
)
)))
(format #t "~A ~A~%" (-> test arr 1 name) (-> test arr 0 name))
(format #t "~A #x~X #x~X~%" (-> test arr 1 type) (logand 15 (the int (-> test arr))) (logand 15 (the int (-> test arr 1))))
(format #t "~A~%" (-> test arr2 1 thing))
)

View file

@ -0,0 +1,47 @@
(deftype test-basic-for-static-inline (basic)
((value uint32)
(name string)
(thing symbol)
(lst pair)
)
)
(let ((test-arr (new 'static 'inline-array test-basic-for-static-inline 3
(new 'static 'test-basic-for-static-inline :value 12 :name "test-name" :thing 'beans :lst '("asdf" b ( c . d)))
(new 'static 'test-basic-for-static-inline :value 1235 :name "hello")
)
)
)
(format #t "~A ~A #x~X #x~X ~A~%"
(-> test-arr 0 type)
(-> test-arr 1 type)
(logand 15 (the int (-> test-arr 1)))
(logand 15 (the int test-arr))
(-> test-arr 1 name)
)
)
(deftype test-struct-for-static-inline (structure)
((value uint32)
(name string)
(thing symbol)
(lst pair)
)
)
(let ((test-arr (new 'static 'inline-array test-struct-for-static-inline 3
(new 'static 'test-struct-for-static-inline :value 12 :name "test-name" :thing 'beans :lst '("asdf" b ( c . d)))
(new 'static 'test-struct-for-static-inline :value 1235 :name "hello")
)
)
)
(format #t "#x~X #x~X ~A~%"
;;(-> test-arr 0 type)
;;(-> test-arr 1 type)
(logand 15 (the int (-> test-arr 1)))
(logand 15 (the int test-arr))
(-> test-arr 1 name)
)
)

View file

@ -493,6 +493,31 @@ TEST_F(WithGameTests, StaticArray) {
"0\n"});
}
TEST_F(WithGameTests, StaticInlineArray) {
runner.run_static_test(
env, testCategory, "test-static-inline-array.gc",
{"test-basic-for-static-inline test-basic-for-static-inline #x4 #x4 \"hello\"\n"
"#x0 #x0 \"hello\"\n"
"0\n"});
}
TEST_F(WithGameTests, StaticArrayField) {
runner.run_static_test(env, testCategory, "test-static-array-field.gc",
{"\"ghjkl\"\n"
"0\n"});
}
TEST_F(WithGameTests, StaticFieldInlineArray) {
runner.run_static_test(env, testCategory, "test-static-field-inline-arrays.gc",
{"\"second\" \"first\"\n"
"basic-elt #x4 #x4\n"
"two\n"
"\"second\" \"first\"\n"
"basic-elt #x4 #x4\n"
"two\n"
"0\n"});
}
TEST(TypeConsistency, TypeConsistency) {
Compiler compiler;
compiler.enable_throw_on_redefines();