Add compiler features for gkernel (#83)

* add some type stuff for gkernel

* more tweaks

* blah

* more little tweaks and more of gkernel

* add static structures with integer fields but not basics

* static structures

* update gkernel
This commit is contained in:
water111 2020-10-16 17:08:26 -04:00 committed by GitHub
parent 118a1b29e3
commit d5d0d7f924
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 985 additions and 92 deletions

View File

@ -224,6 +224,7 @@ class StructureType : public ReferenceType {
bool lookup_field(const std::string& name, Field* out);
bool is_dynamic() const { return m_dynamic; }
~StructureType() = default;
void set_pack(bool pack) { m_pack = pack; }
protected:
friend class TypeSystem;

View File

@ -68,8 +68,32 @@ Type* TypeSystem::add_type(const std::string& name, std::unique_ptr<Type> type)
* This will allow the type system to generate TypeSpecs for this type, but not access detailed
* information, or know the exact size.
*/
void TypeSystem::forward_declare_type(std::string name) {
m_forward_declared_types.insert(std::move(name));
void TypeSystem::forward_declare_type(const std::string& name) {
if (m_types.find(name) == m_types.end()) {
m_forward_declared_types[name] = TYPE;
}
}
/*!
* Inform the type system that there will eventually be a type named "name" and that it's a basic.
* This allows the type to be used in a few specific places. For instance a basic can have
* a field where an element is the same type.
*/
void TypeSystem::forward_declare_type_as_basic(const std::string& name) {
if (m_types.find(name) == m_types.end()) {
m_forward_declared_types[name] = BASIC;
}
}
/*!
* Inform the type system that there will eventually be a type named "name" and that it's a
* structure. This allows the type to be used in a few specific places. For instance a structure can
* have a field where an element is the same type.
*/
void TypeSystem::forward_declare_type_as_structure(const std::string& name) {
if (m_types.find(name) == m_types.end()) {
m_forward_declared_types[name] = STRUCTURE;
}
}
/*!
@ -121,7 +145,7 @@ DerefInfo TypeSystem::get_deref_info(const TypeSpec& ts) {
} else if (ts.base_type() == "pointer") {
info.can_deref = true;
info.result_type = ts.get_single_arg();
auto result_type = lookup_type(info.result_type);
auto result_type = lookup_type_allow_partial_def(info.result_type);
if (result_type->is_reference()) {
// in memory, an array of pointers
info.stride = POINTER_SIZE;
@ -160,6 +184,10 @@ bool TypeSystem::fully_defined_type_exists(const std::string& name) const {
return m_types.find(name) != m_types.end();
}
bool TypeSystem::partially_defined_type_exists(const std::string& name) const {
return m_forward_declared_types.find(name) != m_forward_declared_types.end();
}
/*!
* Create a typespec for a function. If the function doesn't return anything, use "none" as the
* return type.
@ -233,6 +261,42 @@ Type* TypeSystem::lookup_type(const TypeSpec& ts) const {
return lookup_type(ts.base_type());
}
/*!
* Get type info. If the type is not fully defined (ie, we are parsing its deftype now) and its
* forward defined as a basic or structure, just get basic/structure.
*/
Type* TypeSystem::lookup_type_allow_partial_def(const TypeSpec& ts) const {
return lookup_type_allow_partial_def(ts.base_type());
}
/*!
* Get type info. If the type is not fully defined (ie, we are parsing its deftype now) and its
* forward defined as a basic or structure, just get basic/structure.
*/
Type* TypeSystem::lookup_type_allow_partial_def(const std::string& name) const {
// look up fully defined types first:
auto kv = m_types.find(name);
if (kv != m_types.end()) {
return kv->second.get();
}
auto fwd_dec = m_forward_declared_types.find(name);
if (fwd_dec != m_forward_declared_types.end()) {
if (fwd_dec->second == STRUCTURE) {
return lookup_type("structure");
} else if (fwd_dec->second == BASIC) {
return lookup_type("basic");
} else {
fmt::print("[TypeSystem] The type {} is not fully define (allow partial).\n", name);
}
} else {
fmt::print("[TypeSystem] The type {} is not defined.\n", name);
}
throw std::runtime_error("lookup_type_allow_partial_def failed");
}
MethodInfo TypeSystem::add_method(const std::string& type_name,
const std::string& method_name,
const TypeSpec& ts,
@ -412,7 +476,7 @@ FieldLookupInfo TypeSystem::lookup_field_info(const std::string& type_name,
info.array_size = info.field.array_size();
}
auto base_type = lookup_type(info.field.type());
auto base_type = lookup_type_allow_partial_def(info.field.type());
if (base_type->is_reference()) {
if (info.field.is_inline()) {
if (info.field.is_array()) {
@ -542,11 +606,7 @@ void TypeSystem::add_builtin_types() {
auto kheap_type = add_builtin_structure("structure", "kheap");
auto array_type = add_builtin_basic("basic", "array");
auto pair_type = add_builtin_structure("object", "pair", true);
auto process_tree_type = add_builtin_basic("basic", "process-tree");
auto process_type = add_builtin_basic("process-tree", "process");
auto thread_type = add_builtin_basic("basic", "thread");
auto connectable_type = add_builtin_structure("structure", "connectable");
auto stack_frame_type = add_builtin_basic("basic", "stack-frame");
auto file_stream_type = add_builtin_basic("basic", "file-stream");
add_builtin_value_type("object", "pointer", 4);
auto inline_array_type = add_builtin_value_type("object", "inline-array", 4);
@ -566,7 +626,7 @@ void TypeSystem::add_builtin_types() {
add_builtin_value_type("uinteger", "uint8", 1);
add_builtin_value_type("uinteger", "uint16", 2);
add_builtin_value_type("uinteger", "uint32", 4);
add_builtin_value_type("uinteger", "uint64", 81);
add_builtin_value_type("uinteger", "uint64", 8);
add_builtin_value_type("uinteger", "uint128", 16, false, false, RegKind::INT_128);
auto int_type = add_builtin_value_type("integer", "int", 8, false, true);
@ -585,7 +645,7 @@ void TypeSystem::add_builtin_types() {
make_function_typespec({"_type_"}, "int")); // todo - this integer type?
add_method(obj_type, "asize-of", make_function_typespec({"_type_"}, "int"));
add_method(obj_type, "copy", make_function_typespec({"_type_", "symbol"}, "_type_"));
add_method(obj_type, "relocate", make_function_typespec({"_type_", "int32"}, "_type_"));
add_method(obj_type, "relocate", make_function_typespec({"_type_", "int"}, "_type_"));
add_method(obj_type, "mem-usage",
make_function_typespec({"_type_"}, "int32")); // todo - this is a guess.
@ -660,11 +720,7 @@ void TypeSystem::add_builtin_types() {
add_field_to_type(pair_type, "cdr", make_typespec("object"));
// todo, with kernel
(void)process_tree_type;
(void)process_type;
(void)thread_type;
(void)connectable_type;
(void)stack_frame_type;
(void)file_stream_type;
}
@ -717,7 +773,7 @@ Field TypeSystem::lookup_field(const std::string& type_name, const std::string&
* Get the minimum required aligment of a field.
*/
int TypeSystem::get_alignment_in_type(const Field& field) {
auto field_type = lookup_type(field.type());
auto field_type = lookup_type_allow_partial_def(field.type());
if (field.is_inline()) {
if (field.is_array()) {
@ -727,7 +783,7 @@ int TypeSystem::get_alignment_in_type(const Field& field) {
} else {
// it is an inlined field, so return the alignment in memory
// TODO - for inline, but not inline array, do we use structure alignment always?
return field_type->get_in_memory_alignment();
return field_type->get_inline_array_alignment();
}
}
@ -740,18 +796,31 @@ int TypeSystem::get_alignment_in_type(const Field& field) {
return POINTER_SIZE;
}
namespace {
bool allow_inline(const Type* type) {
auto name = type->get_name();
return name != "basic" && name != "structure";
}
} // namespace
/*!
* Get the size of a field in a type. The array sizes should be consistent with get_deref_info's
* stride.
*/
int TypeSystem::get_size_in_type(const Field& field) {
int TypeSystem::get_size_in_type(const Field& field) const {
if (field.is_dynamic()) {
return 0;
}
auto field_type = lookup_type(field.type());
auto field_type = lookup_type_allow_partial_def(field.type());
if (field.is_array()) {
if (field.is_inline()) {
if (!allow_inline(field_type)) {
fmt::print(
"[Type System] Attempted to use {} inline, this probably isn't what you wanted.\n",
field_type->get_name());
throw std::runtime_error("bad get size in type");
}
assert(field_type->is_reference());
return field.array_size() *
align(field_type->get_size_in_memory(), field_type->get_inline_array_alignment());
@ -766,8 +835,16 @@ int TypeSystem::get_size_in_type(const Field& field) {
} else {
// not an array
if (field.is_inline()) {
if (!allow_inline(field_type)) {
fmt::print(
"[Type System] Attempted to use {} inline, this probably isn't what you wanted.\n",
field_type->get_name());
throw std::runtime_error("bad get size in type");
}
assert(field_type->is_reference());
return align(field_type->get_size_in_memory(), field_type->get_in_memory_alignment());
// return align(field_type->get_size_in_memory(), field_type->get_in_memory_alignment());
// looking at dead-pool-heap we tightly pack in this case
return field_type->get_size_in_memory();
} else {
if (field_type->is_reference()) {
return POINTER_SIZE;
@ -888,16 +965,16 @@ bool TypeSystem::typecheck_base_types(const std::string& expected,
// declared, but not defined?)
lookup_type(expected);
if (expected == actual) {
lookup_type(actual); // make sure it exists
if (expected == actual || expected == lookup_type_allow_partial_def(actual)->get_name()) {
lookup_type_allow_partial_def(actual); // make sure it exists
return true;
}
std::string actual_name = actual;
auto actual_type = lookup_type(actual_name);
auto actual_type = lookup_type_allow_partial_def(actual_name);
while (actual_type->has_parent()) {
actual_name = actual_type->get_parent();
actual_type = lookup_type(actual_name);
actual_type = lookup_type_allow_partial_def(actual_name);
if (expected == actual_name) {
return true;
@ -991,12 +1068,12 @@ TypeSpec TypeSystem::lowest_common_ancestor(const std::vector<TypeSpec>& types)
TypeSpec coerce_to_reg_type(const TypeSpec& in) {
if (in.arg_count() == 0) {
if (in.base_type() == "int8" || in.base_type() == "int16" || in.base_type() == "int32" ||
in.base_type() == "int16") {
in.base_type() == "int64") {
return TypeSpec("int");
}
if (in.base_type() == "uint8" || in.base_type() == "uint16" || in.base_type() == "uint32" ||
in.base_type() == "uint16") {
in.base_type() == "uint64") {
return TypeSpec("uint");
}
}

View File

@ -34,12 +34,15 @@ class TypeSystem {
TypeSystem();
Type* add_type(const std::string& name, std::unique_ptr<Type> type);
void forward_declare_type(std::string name);
void forward_declare_type(const std::string& name);
void forward_declare_type_as_basic(const std::string& name);
void forward_declare_type_as_structure(const std::string& name);
std::string get_runtime_type(const TypeSpec& ts);
DerefInfo get_deref_info(const TypeSpec& ts);
bool fully_defined_type_exists(const std::string& name) const;
bool partially_defined_type_exists(const std::string& name) const;
TypeSpec make_typespec(const std::string& name) const;
TypeSpec make_function_typespec(const std::vector<std::string>& arg_types,
const std::string& return_type);
@ -52,6 +55,9 @@ class TypeSystem {
Type* lookup_type(const TypeSpec& ts) const;
Type* lookup_type(const std::string& name) const;
Type* lookup_type_allow_partial_def(const TypeSpec& ts) const;
Type* lookup_type_allow_partial_def(const std::string& name) const;
MethodInfo add_method(const std::string& type_name,
const std::string& method_name,
const TypeSpec& ts,
@ -105,7 +111,7 @@ class TypeSystem {
private:
std::string lca_base(const std::string& a, const std::string& b);
bool typecheck_base_types(const std::string& expected, const std::string& actual) const;
int get_size_in_type(const Field& field);
int get_size_in_type(const Field& field) const;
int get_alignment_in_type(const Field& field);
Field lookup_field(const std::string& type_name, const std::string& field_name);
StructureType* add_builtin_structure(const std::string& parent,
@ -120,8 +126,10 @@ class TypeSystem {
RegKind reg = RegKind::GPR_64);
void builtin_structure_inherit(StructureType* st);
enum ForwardDeclareKind { TYPE, STRUCTURE, BASIC };
std::unordered_map<std::string, std::unique_ptr<Type>> m_types;
std::unordered_set<std::string> m_forward_declared_types;
std::unordered_map<std::string, ForwardDeclareKind> m_forward_declared_types;
std::vector<std::unique_ptr<Type>> m_old_types;
bool m_allow_redefinition = false;

View File

@ -178,11 +178,17 @@ void declare_method(StructureType* type, TypeSystem* type_system, const goos::Ob
});
}
TypeFlags parse_structure_def(StructureType* type,
TypeSystem* ts,
const goos::Object& fields,
const goos::Object& options) {
(void)options;
struct StructureDefResult {
TypeFlags flags;
bool generate_runtime_type = true;
bool pack_me = false;
};
StructureDefResult parse_structure_def(StructureType* type,
TypeSystem* ts,
const goos::Object& fields,
const goos::Object& options) {
StructureDefResult result;
for_each_in_list(fields, [&](const goos::Object& o) { add_field(type, ts, o); });
TypeFlags flags;
flags.heap_base = 0;
@ -229,6 +235,10 @@ TypeFlags parse_structure_def(StructureType* type,
flag_assert = get_int(car(rest));
flag_assert_set = true;
rest = cdr(rest);
} else if (opt_name == ":no-runtime-type") {
result.generate_runtime_type = false;
} else if (opt_name == ":pack-me") {
result.pack_me = true;
} else {
throw std::runtime_error("Invalid option in field specification: " + opt_name);
}
@ -255,7 +265,8 @@ TypeFlags parse_structure_def(StructureType* type,
flags.flag, flag_assert));
}
return flags;
result.flags = flags;
return result;
}
} // namespace
@ -303,14 +314,28 @@ DeftypeResult parse_deftype(const goos::Object& deftype, TypeSystem* ts) {
auto pto = dynamic_cast<BasicType*>(ts->lookup_type(parent_type));
assert(pto);
new_type->inherit(pto);
result.flags = parse_structure_def(new_type.get(), ts, field_list_obj, options_obj);
ts->forward_declare_type_as_basic(name);
auto sr = parse_structure_def(new_type.get(), ts, field_list_obj, options_obj);
result.flags = sr.flags;
result.create_runtime_type = sr.generate_runtime_type;
if (sr.pack_me) {
fmt::print("[TypeSystem] :pack-me was set on {}, which is a basic and cannot be packed.",
name);
throw std::runtime_error("invalid pack option on basic");
}
ts->add_type(name, std::move(new_type));
} else if (is_type("structure", parent_type, ts)) {
auto new_type = std::make_unique<StructureType>(parent_type_name, name);
auto pto = dynamic_cast<StructureType*>(ts->lookup_type(parent_type));
assert(pto);
new_type->inherit(pto);
result.flags = parse_structure_def(new_type.get(), ts, field_list_obj, options_obj);
ts->forward_declare_type_as_structure(name);
auto sr = parse_structure_def(new_type.get(), ts, field_list_obj, options_obj);
result.flags = sr.flags;
result.create_runtime_type = sr.generate_runtime_type;
if (sr.pack_me) {
new_type->set_pack(true);
}
ts->add_type(name, std::move(new_type));
} else if (is_type("integer", parent_type, ts)) {
throw std::runtime_error("Creating a child type of integer is not supported yet.");

View File

@ -19,6 +19,7 @@ struct DeftypeResult {
TypeFlags flags;
TypeSpec type;
Type* type_info = nullptr;
bool create_runtime_type = true;
};
DeftypeResult parse_deftype(const goos::Object& deftype, TypeSystem* ts);

View File

@ -21,4 +21,17 @@
- The `&->` operator has been added
- The `new` operator can create arrays and inline arrays on heaps
- The value of `deftype` is now `none`
- Creating a method with more than 8 arguments is an error instead of a crash.
- Creating a method with more than 8 arguments is an error instead of a crash.
- The `defconstant` form for defining a constant in GOAL but not GOOS has been added
- Both `defconstant` and `defglobalconstant` throw an error if you define a constant with the same name as a symbol.
- The `uint64` type now uses 8 bytes instead of 81 in a type (this was a typo)
- `deftype` allows basics/structures with a field that is the same type as the basic/structure.
- Doing a `define-extern` with a type of `type` will forward declare the type.
- `deftype` now has a `:no-runtime-type` flag to disable the creation of a runtime type.
- There is a `declare-type` form for forward declaring types to allow circular dependencies.
- Types that are `structure` but not `basic` can request that they be tightly packed when possible with `:pack-me`.
- Using `method` on a forward declared type is an error. The old behavior was to get a method of `type`, which is confusing.
- Loading an `int64`/`uint64` gives a `int`/`uint`, like the other register integers.
- Defining a type with `deftype` will auto-generate an inspect method.
- The `new` operator can now create static structures and basics and set fields to integers or symbols.
- The `neq?` operator now works when used outside of a branch condition (previously it generated a syntax error)

View File

@ -5,5 +5,312 @@
;; name in dgo: gkernel-h
;; dgos: KERNEL
(defglobalconstant *kernel-major-version* 2)
(defglobalconstant *kernel-minor-version* 0)
;; Type definitions for the GOAL Kernel.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CONSTANTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; -hardware-
;; the end of the 16 kB fast "scratchpad" memory of the PS2.
;; this memory is mapped to 0x70000000 in the PS2.
(defconstant *scratch-memory-top* (the pointer #x70004000))
;; -versions-
;; the version of the kernel. This is checked in the C Kernel.
;; This must match the version in common/versions.h when building gk
(defconstant *kernel-major-version* 2)
(defconstant *kernel-minor-version* 0)
;; the version of the OVERLORD I/O driver.
;; this may be unused.
(defconstant *irx-major-version* 1)
(defconstant *irx-minor-version* 2)
;; -memory-
;; the size of the execution stack (~14 kB) shared by all threads
(defconstant DPROCESS_STACK_SIZE #x3800)
;; another stack size used as a maximum for temporary threads
(defconstant PROCESS_STACK_SIZE #x1c00)
;; default size of stack to backup for a process
(defconstant PROCESS_STACK_SAVE_SIZE 256)
;; the size of the shared heap used by dynamically sized processes
(defconstant PROCESS_HEAP_SIZE (* 984 1024))
;; -system-
;; tab size for printing.
(defconstant *tab-size* (the binteger 8))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ENUMS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; todo, processs mask
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; MACROS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; trigger an exception. (GOAL used lw r0, 2(r0))
(defmacro break ()
`(/ 0 0)
)
;; todo, process check and set
(defmacro msg-err (&rest args)
;"Print a message to stdout immediately. This won't appear in the compiler."
`(format 0 ,@args)
)
;; todo process pointer
;; todo process memory usage
;; with pp
;; todo suspend
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TYPES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; this stores the current state of the kernel.
(deftype kernel-context (basic)
((prevent-from-run int32 :offset-assert 4) ;; actually a process-mask
(require-for-run int32 :offset-assert 8) ;; actually a process-mask, unused?
(allow-to-run int32 :offset-assert 12) ;; actually a process-mask, unused?
(next-pid int32 :offset-assert 16) ;; next PID to give out
(fast-stack-top pointer :offset-assert 20) ;; scratchpad stack (unused?)
(current-process basic :offset-assert 24) ;; process?
(relocating-process basic :offset-assert 28) ;; process?
(relocating-min int32 :offset-assert 32) ;; print hex
(relocating-max int32 :offset-assert 36) ;; print hex
(relocating-offset int32 :offset-assert 40) ;; ?
(low-memory-message basic :offset-assert 44) ;; boolean?
)
:size-assert #x30
:method-count-assert 9
:flag-assert #x900000030
)
; A thread belongs to a process and has a reference to a stack.
; they have an "execution stack", which is where the stack goes when the thread runs.
; and also a "backup stack", which stores the stack when the thread doesn't run.
; this means threads can't leak pointers to stack variables to other threads...
; optionally, threads may know how to suspend/resume themselves.
(declare-type process basic)
; DANGER - this type is created in kscheme.cpp. It has room for 12 methods and size 0x28 bytes.
(deftype thread (basic)
((name basic :offset-assert 4) ;; name of the thread (usually a symbol?)
(process process :offset-assert 8) ;; process that the thread belongs to
(previous thread :offset-assert 12) ;; previous thread that was running in the process
(suspend-hook basic :offset-assert 16) ;; function to suspend this thread
(resume-hook basic :offset-assert 20) ;; function to resume this thread
(pc pointer :offset-assert 24) ;; program counter of the thread
(sp pointer :offset-assert 28) ;; stack pointer of the thread (actual stack)
(stack-top pointer :offset-assert 32) ;; top of the thread's stack (actual stack)
(stack-size int32 :offset-assert 36) ;; size of the thread's stack (backup stack)
)
(:methods
;; todo, triple check these method numbers.
(stack-size-set! ((this thread) (stack-size integer)) none 9)
(thread-suspend ((this _type_)) none 10) ;; only safe on a cpu-thread, but slot exists for thread
(thread-resume ((to-resume _type_)) none 11) ;; only safe on a cpu-thread, but slot exists for thread
)
:size-assert #x28
:method-count-assert 12
:flag-assert #xc00000028
;; is already defined in kscheme but we define it again.
)
;; A CPU thread is a thread which has some memory to save registers and a stack
(deftype cpu-thread (thread)
((rreg uint64 8 :offset-assert 40) ;; general purpose saved registers
(freg float 6 :offset-assert 104) ;; floating point registers
(stack uint8 :dynamic :offset-assert 128) ;; stack memory (dynamic array)
)
(:methods
(thread-suspend ((this _type_)) none 10)
(thread-resume ((to-resume _type_)) none 11)
)
:size-assert #x80
:method-count-assert 12
:flag-assert #xc00000080
)
;; Parent type of all process tree nodes.
;; A process-tree is a left-child right-sibling binary tree
;; (except GOAL is old and it looks like they called them left-child right-brother trees back then)
(deftype process-tree (basic)
((name basic :offset-assert 4)
(mask int32 :offset-assert 8)
(parent pointer :offset-assert 12)
(brother pointer :offset-assert 16)
(child pointer :offset-assert 20)
(ppointer pointer :offset-assert 24)
(self basic :offset-assert 28)
)
(:methods
(activate ((obj process-tree) (dest process-tree) (name basic) (stack-top pointer)) basic 9)
(deactivate ((obj process-tree)) basic 10)
(dummy-method-11 () none 11)
(run-logic? ((obj process-tree)) symbol 12)
(dummy-method () none 13)
)
:size-assert #x20
:method-count-assert 14
:no-runtime-type ;; already defined by kscheme. Don't do it again.
)
;; A dead pool is simply a process-tree node which contains all dead processes.
;; It supports getting and returning processes.
(deftype dead-pool (process-tree)
(
;; nothing new!
)
(:methods
(get-process ((pool dead-pool) (type-to-make type) (stack-size integer)) process 14)
(return-process ((pool dead-pool) (proc process)) process-tree 15)
)
:size-assert #x20
:method-count-assert 16
:flag-assert #x1000000020
)
;; A dead-pool-heap-rec is a record for a process which lives on a dead-pool-heap.
;; these processes can move around in memory, but the records can't.
;; Therefore a pointer to these can be used as a handle for the process, so you can
;; find it after it moves
(deftype dead-pool-heap-rec (structure)
((process process :offset-assert 0) ;; the process of this record
(prev dead-pool-heap-rec :offset-assert 4) ;; next rec in the linked list
(next dead-pool-heap-rec :offset-assert 8) ;; prev. rec in the linked list
)
:pack-me ; don't worry about aligning me to 16-bytes in arrays and types.
:size-assert #xc
:method-count-assert 9
:flag-assert #x90000000c
)
;; This is a pool of dead processes which can be dynamically sized and allocated from a common heap.
;; Alive processess in a dead-pool-heap can be relocated and compacted to reduce heap fragmentation.
(deftype dead-pool-heap (dead-pool)
((allocated-length int32 :offset-assert #x20) ;; size of heap
(compact-time uint32 :offset-assert #x24) ;; ??
(compact-count-targ uint32 :offset-assert #x28) ;; ??
(compact-count uint32 :offset-assert #x2c) ;; ??
(fill-percent float :offset-assert #x30) ;; ??
(first-gap dead-pool-heap-rec :offset-assert #x34) ;; ??
(first-shrink dead-pool-heap-rec :offset-assert #x38) ;; ??
(heap kheap :inline :offset-assert 64) ;; ??
(alive-list dead-pool-heap-rec :inline :offset-assert 80) ;; ??
(last dead-pool-heap-rec :offset #x54 :offset-assert #x54) ;; overlay of (-> alive-list prev)
;; note - the placement of dead-list at 92 here is used to determine the packing behavior.
;; see TypeSystem::get_size_in_type().
(dead-list dead-pool-heap-rec :inline :offset-assert 92) ;; ??
(process-list dead-pool-heap-rec :inline :dynamic :offset-assert 104)
)
(:methods
(compact ((this dead-pool-heap) (count integer)) none 16)
(shrink-heap ((this dead-pool-heap) (proc process)) dead-pool-heap 17)
(churn ((this dead-pool-heap) (count integer)) none 18)
(memory-used ((this dead-pool-heap)) integer 19)
(memory-total ((this dead-pool-heap)) integer 20)
(gap-size ((this dead-pool-heap) (rec dead-pool-heap-rec)) integer 21)
(gap-location ((this dead-pool-heap) (rec dead-pool-heap-rec)) pointer 22)
(find-gap ((this dead-pool-heap) (rec dead-pool-heap-rec)) dead-pool-heap-rec 23)
(find-gap-by-size ((this dead-pool-heap) (size integer)) dead-pool-heap-rec 24)
(memory-free ((this dead-pool-heap)) integer 25)
(compact-time ((this dead-pool-heap)) integer 26)
)
:size-assert #x68
:method-count-assert #x1b
:flag-assert #x1b00000068
)
;; GOAL can create a series of stack frames for unwinding/cleaning up.
;; This is the parent type for any stack frame.
(deftype stack-frame (basic)
((name basic :offset 4)
(next stack-frame :offset 8) ;; which way does this point?
)
:size-assert #xc
:method-count-assert 9
:flag-assert #x90000000c
)
;; A catch frame is a frame you can "throw" to, by name.
;; You can "throw" out of a function and into another function.
(deftype catch-frame (stack-frame)
((sp pointer :offset 12) ;; where to reset the stack when throwing.
(ra pointer :offset 16) ;; where to jump when throwing
;; todo - rework for x86-64.
(freg float 6 :offset 20) ;; saved floating point registers from "catch" statement
(rreg uint128 8 :offset 48) ;; saved GPRs from "catch" statement (ugh they are 128s)
)
:size-assert #xb0
:method-count-assert 9
:flag-assert #x9000000b0
)
;; A protect frame is a frame which has a cleanup function called on exit.
(deftype protect-frame (stack-frame)
((exit function :offset-assert 12)) ;; function to call to clean up
:size-assert 16
:method-count-assert 9
:flag-assert #x900000010
)
;; handle (todo, need bitfield types)
;; GOAL State. A Process can be in a State.
(deftype state (protect-frame) ; state is a protect frame so we can "exit" it with cleanup
((code function :offset-assert 16) ;; main code to run in this state
(trans function :offset-assert 20) ;; ? run once and return ? when ?
(post function :offset-assert 24) ;; ?
(enter function :offset-assert 28) ;; ?
(event basic :offset-assert 32) ;; event handler function?
)
:size-assert #x24
:method-count-assert 9
:flag-assert #x900000024
)
;; Not sure what this is, but it's probably used in the event system.
(deftype event-message-block (structure)
((to basic :offset-assert 0)
(from basic :offset-assert 4)
(num-params int32 :offset-assert 8)
(message basic :offset-assert 12)
(param uint64 7 :offset-assert 16)
)
:size-assert #x48
:method-count-assert 9
:flag-assert #x900000048
)

View File

@ -5,7 +5,98 @@
;; name in dgo: gkernel
;; dgos: KERNEL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; System Globals
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set version number symbols
(define *kernel-version* (the binteger (logior (ash *kernel-major-version* 16) *kernel-minor-version*)))
(define *irx-version* (the binteger (logior (ash *irx-major-version* 16) *irx-minor-version*)))
;; Set default options. The C Kernel may modify these before loading the engine.
;; Can be 'boot, 'listener, or 'debug-boot
;; set to 'boot when DiskBooting.
(define *kernel-boot-mode* 'listener)
;; DebugBootLevel in C Kernel
(define *kernel-boot-level* (the symbol #f))
;; The number of DECI messages received.
;; The C Kernel increments this.
(define *deci-count* 0)
;; Some debug stats. Unused?
(define *last-loado-length* 0)
(define *last-loado-global-usage* 0)
(define *last-loado-debug-usage* 0)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Relocate
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Objects on a dynamic process heap may be relocated.
;; They should provide their own relocate method to do any fixups
;; for any references.
(defmethod relocate object ((this object) (offset int))
this
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Kernel Package System
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The kernel has a weird package system. It's not really used and doesn't do much.
;; Both the C Kernel and GOAL Kernel update the kernel-packages list.
;; The list is used to avoid loading the same package multiple times.
(define *kernel-packages* '())
(defun load-package ((package string) (allocation kheap))
"Load a Package from a CGO/DGO"
(unless (nmember package *kernel-packages*)
;; #xf = OUTPUT_LOAD, OUTPUT_TRUE, EXECUTE, PRINT_LOGIN
(dgo-load package allocation #xf #x200000)
(set! *kernel-packages* (cons package *kernel-packages*))
)
)
(defun unload-package ((package string))
"Mark a package as unloaded, if it was loaded previously"
(let ((pack (nmember package *kernel-packages*)))
(when pack
(set! *kernel-packages* (delete! (car pack) *kernel-packages*))
)
*kernel-packages*
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Kernel Globals
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The kernel context is a global which stores the state of the kernel.
(define *kernel-context* (new 'static 'kernel-context
:prevent-from-run #x41 ;; todo, bitfield enum types
:next-pid 2
:current-process '#f
:relocating-process '#f
:low-memory-message '#t
)
)
;; the main stack for running GOAL code!
(define *dram-stack* (new 'global 'array 'uint8 #x3800)) ;;DPROCESS_STACK_SIZE
;; I don't think this stack is used, but I'm not sure.
(set! (-> *kernel-context* fast-stack-top) *scratch-memory-top*)
;; A context with all process masks set to 0. This can be used to iterate through a process tree
;; without executing anything, to find a process for instance.
(define *null-kernel-context* (new 'static 'kernel-context))
(defun kernel-dispatcher ()
"Kernel Dispatcher Function. This gets called from the main loop in kboot.cpp's KernelCheckAndDispatch"
@ -24,4 +115,4 @@
;; clear the pending function.
(set! *listener-function* (the (function object) #f))
)
)
)

View File

@ -22,6 +22,7 @@ add_library(compiler
compiler/compilation/Function.cpp
compiler/compilation/ControlFlow.cpp
compiler/compilation/Type.cpp
compiler/compilation/Static.cpp
compiler/Util.cpp
logger/Logger.cpp
regalloc/IRegister.cpp

View File

@ -72,7 +72,9 @@ class Compiler {
std::string as_string(const goos::Object& o);
std::string symbol_string(const goos::Object& o);
std::string quoted_sym_as_string(const goos::Object& o);
bool is_quoted_sym(const goos::Object& o);
bool is_basic(const TypeSpec& ts);
bool is_structure(const TypeSpec& ts);
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);
@ -118,20 +120,29 @@ class Compiler {
Val* compile_format_string(const goos::Object& form,
Env* env,
std::string& fmt_template,
const std::string& fmt_template,
std::vector<RegVal*> args,
const std::string& out_stream = "#t");
void generate_field_description(StructureType* type,
void generate_field_description(const goos::Object& form,
StructureType* type,
Env* env,
RegVal* reg,
Field f,
std::vector<RegVal*> format_args,
std::string& str_template);
const Field& f);
Val* generate_inspector_for_type(const goos::Object& form, Env* env, Type* type);
RegVal* compile_get_method_of_type(const TypeSpec& type,
const std::string& method_name,
Env* env);
RegVal* compile_get_method_of_object(RegVal* object, const std::string& method_name, Env* env);
Val* compile_define_constant(const goos::Object& form,
const goos::Object& rest,
Env* env,
bool goos,
bool goal);
Val* compile_new_static_structure_or_basic(const goos::Object& form,
const TypeSpec& type,
const goos::Object& field_defs,
Env* env);
public:
// Atoms
@ -171,6 +182,7 @@ class Compiler {
Val* compile_gscond(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_quote(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_defglobalconstant(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_defconstant(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_mlet(const goos::Object& form, const goos::Object& rest, Env* env);
// Math
@ -205,6 +217,7 @@ class Compiler {
Val* compile_cdr(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_method(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_addr_of(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_declare_type(const goos::Object& form, const goos::Object& rest, Env* env);
};
#endif // JAK_COMPILER_H

View File

@ -80,4 +80,61 @@ int StaticFloat::get_addr_offset() const {
std::string StaticFloat::print() const {
return fmt::format("(sf {})", value);
}
///////////////////
// StaticStructure
///////////////////
StaticStructure::StaticStructure(int _seg) : seg(_seg) {}
std::string StaticStructure::print() const {
return "static-structure";
}
StaticObject::LoadInfo StaticStructure::get_load_info() const {
LoadInfo info;
info.requires_load = false;
info.prefer_xmm = false;
return info;
}
int StaticStructure::get_addr_offset() const {
return 0;
}
void StaticStructure::generate_structure(emitter::ObjectGenerator* gen) {
rec = gen->add_static_to_seg(seg, 16);
auto& d = gen->get_static_data(rec);
d.insert(d.end(), data.begin(), data.end());
for (auto& sym : symbols) {
gen->link_static_symbol_ptr(rec, sym.offset, sym.name);
}
}
void StaticStructure::generate(emitter::ObjectGenerator* gen) {
generate_structure(gen);
}
void StaticStructure::add_symbol_record(std::string name, int offset) {
SymbolRecord srec;
srec.name = std::move(name);
srec.offset = offset;
symbols.push_back(srec);
}
///////////////////
// StaticBasic
///////////////////
StaticBasic::StaticBasic(int _seg, std::string _type_name)
: StaticStructure(_seg), type_name(std::move(_type_name)) {}
int StaticBasic::get_addr_offset() const {
return BASIC_OFFSET;
}
void StaticBasic::generate(emitter::ObjectGenerator* gen) {
generate_structure(gen);
gen->link_static_type_ptr(rec, 0, type_name);
}

View File

@ -4,6 +4,7 @@
#define JAK_STATICOBJECT_H
#include <string>
#include <vector>
#include "goalc/emitter/ObjectGenerator.h"
class StaticObject {
@ -46,4 +47,32 @@ class StaticFloat : public StaticObject {
int get_addr_offset() const override;
};
class StaticStructure : public StaticObject {
public:
StaticStructure(int _seg);
std::vector<u8> data;
int seg = -1;
std::string print() const override;
LoadInfo get_load_info() const override;
void generate_structure(emitter::ObjectGenerator* gen);
void generate(emitter::ObjectGenerator* gen) override;
int get_addr_offset() const override;
struct SymbolRecord {
int offset = -1;
std::string name;
};
std::vector<SymbolRecord> symbols;
void add_symbol_record(std::string name, int offset);
};
class StaticBasic : public StaticStructure {
public:
std::string type_name;
StaticBasic(int _seg, std::string _type_name);
int get_addr_offset() const override;
void generate(emitter::ObjectGenerator* gen) override;
};
#endif // JAK_STATICOBJECT_H

View File

@ -113,6 +113,24 @@ std::string Compiler::quoted_sym_as_string(const goos::Object& o) {
return symbol_string(args.unnamed.at(1));
}
bool Compiler::is_quoted_sym(const goos::Object& o) {
if (o.is_pair()) {
auto car = pair_car(o);
auto cdr = pair_cdr(o);
if (car.is_symbol() && car.as_symbol()->name == "quote") {
if (cdr.is_pair()) {
auto thing = pair_car(cdr);
if (thing.is_symbol()) {
if (pair_cdr(cdr).is_empty_list()) {
return true;
}
}
}
}
}
return false;
}
const goos::Object& Compiler::pair_car(const goos::Object& o) {
return o.as_pair()->car;
}
@ -186,6 +204,10 @@ bool Compiler::is_basic(const TypeSpec& ts) {
return m_ts.typecheck(m_ts.make_typespec("basic"), ts, "", false, false);
}
bool Compiler::is_structure(const TypeSpec& ts) {
return m_ts.typecheck(m_ts.make_typespec("structure"), ts, "", false, false);
}
bool Compiler::try_getting_constant_integer(const goos::Object& in, int64_t* out, Env* env) {
(void)env;
if (in.is_int()) {
@ -193,6 +215,6 @@ bool Compiler::try_getting_constant_integer(const goos::Object& in, int64_t* out
return true;
}
// todo, try more things before giving up.
// todo, try more things like constants before giving up.
return false;
}

View File

@ -65,6 +65,7 @@ static const std::unordered_map<
{"car", &Compiler::compile_car},
{"cdr", &Compiler::compile_cdr},
{"method", &Compiler::compile_method},
{"declare-type", &Compiler::compile_declare_type},
// LAMBDA
{"lambda", &Compiler::compile_lambda},
@ -77,7 +78,7 @@ static const std::unordered_map<
// MACRO
{"quote", &Compiler::compile_quote},
{"mlet", &Compiler::compile_mlet},
// {"defconstant", &Compiler::compile_defconstant},
{"defconstant", &Compiler::compile_defconstant},
// OBJECT
// {"current-method-type", &Compiler::compile_current_method_type},
@ -101,6 +102,7 @@ static const std::unordered_map<
{"=", &Compiler::compile_condition_as_bool},
{"!=", &Compiler::compile_condition_as_bool},
{"eq?", &Compiler::compile_condition_as_bool},
{"neq?", &Compiler::compile_condition_as_bool},
{"not", &Compiler::compile_condition_as_bool},
{"<=", &Compiler::compile_condition_as_bool},
{">=", &Compiler::compile_condition_as_bool},

View File

@ -75,6 +75,11 @@ Val* Compiler::compile_define_extern(const goos::Object& form, const goos::Objec
symbol_string(sym).c_str(), existing_type->second.print().c_str(),
new_type.print().c_str());
}
if (new_type == m_ts.make_typespec("type")) {
m_ts.forward_declare_type(symbol_string(sym));
}
m_symbol_types[symbol_string(sym)] = new_type;
return get_none();
}

View File

@ -444,6 +444,10 @@ Val* Compiler::compile_real_function_call(const goos::Object& form,
}
}
if (args.size() > 8) {
throw_compile_error(form, "Function call cannot use more than 8 parameters");
}
// set args (introducing a move here makes coloring more likely to be possible)
std::vector<RegVal*> arg_outs;
for (auto& arg : args) {

View File

@ -112,16 +112,15 @@ Val* Compiler::compile_quote(const goos::Object& form, const goos::Object& rest,
return get_none();
}
/*!
* Compile defglobalconstant forms, which define a constant in both GOOS and GOAL.
*/
Val* Compiler::compile_defglobalconstant(const goos::Object& form,
const goos::Object& _rest,
Env* env) {
Val* Compiler::compile_define_constant(const goos::Object& form,
const goos::Object& _rest,
Env* env,
bool goos,
bool goal) {
auto rest = &_rest;
(void)env;
if (!rest->is_pair()) {
throw_compile_error(form, "invalid defglobalconstant");
throw_compile_error(form, "invalid constant definition");
}
auto sym = pair_car(*rest).as_symbol();
@ -130,18 +129,43 @@ Val* Compiler::compile_defglobalconstant(const goos::Object& form,
rest = &rest->as_pair()->cdr;
if (!rest->is_empty_list()) {
throw_compile_error(form, "invalid defglobalconstant");
throw_compile_error(form, "invalid constant definition");
}
// GOAL constant
m_global_constants[sym] = value;
if (goal) {
if (m_symbol_types.find(sym->name) != m_symbol_types.end()) {
throw_compile_error(form, fmt::format("The name {} cannot be defined as a constant because "
"it is already the name of a symbol of type {}",
sym->name, m_symbol_types.at(sym->name).print()));
}
m_global_constants[sym] = value;
}
// GOOS constant
m_goos.global_environment.as_env()->vars[sym] = value;
if (goos) {
m_goos.global_environment.as_env()->vars[sym] = value;
}
return get_none();
}
/*!
* Compile defglobalconstant forms, which define a constant in both GOOS and GOAL.
*/
Val* Compiler::compile_defglobalconstant(const goos::Object& form,
const goos::Object& rest,
Env* env) {
return compile_define_constant(form, rest, env, true, true);
}
/*!
* Compile a defconstant form, which defines a constant that is only in GOAL.
*/
Val* Compiler::compile_defconstant(const goos::Object& form, const goos::Object& rest, Env* env) {
return compile_define_constant(form, rest, env, false, true);
}
/*!
* Compile an "mlet" scoped constant/symbol macro form
*/

View File

@ -0,0 +1,132 @@
/*!
* @file Static.cpp
* Compiler helper functions for creating static data.
* This is the front end for things in StaticObject.cpp
*/
#include "goalc/compiler/Compiler.h"
namespace {
bool integer_fits(s64 in, int size, bool is_signed) {
switch (size) {
case 1:
if (is_signed) {
return in >= INT8_MIN && in <= INT8_MAX;
} else {
return in >= 0 && in <= UINT8_MAX;
}
case 2:
if (is_signed) {
return in >= INT16_MIN && in <= INT16_MAX;
} else {
return in >= 0 && in <= UINT16_MAX;
}
case 4:
if (is_signed) {
return in >= INT32_MIN && in <= INT32_MAX;
} else {
return in >= 0 && in <= UINT32_MAX;
}
case 8:
return true;
default:
assert(false);
}
}
} // namespace
Val* Compiler::compile_new_static_structure_or_basic(const goos::Object& form,
const TypeSpec& type,
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_compile_error(form,
"expected field def name to start with :, instead got " + field_name_def);
}
field_name_def = field_name_def.substr(1);
auto field_info = m_ts.lookup_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_compile_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_compile_error(form,
fmt::format("Field {} is an integer, but the value given couldn't be "
"converted to an integer at compile time.",
field_name_def));
}
if (!integer_fits(value, deref_info.load_size, deref_info.sign_extend)) {
throw_compile_error(
form, fmt::format("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 {
throw_compile_error(
form, "Setting a basic field to anything other than a symbol is currently unsupported");
}
}
else {
assert(false); // for now
}
}
auto fie = get_parent_env_of_type<FileEnv>(env);
fie->add_static(std::move(obj));
return result;
}

View File

@ -87,7 +87,7 @@ RegVal* Compiler::compile_get_method_of_object(RegVal* object,
Val* Compiler::compile_format_string(const goos::Object& form,
Env* env,
std::string& fmt_template,
const std::string& fmt_template,
std::vector<RegVal*> args,
const std::string& out_stream) {
// Add first two format args
@ -101,12 +101,13 @@ Val* Compiler::compile_format_string(const goos::Object& form,
return compile_real_function_call(form, format_function, args, env);
}
void Compiler::generate_field_description(StructureType* type,
void Compiler::generate_field_description(const goos::Object& form,
StructureType* type,
Env* env,
RegVal* reg,
Field f,
std::vector<RegVal*> format_args,
std::string& str_template) {
const Field& f) {
std::string str_template;
std::vector<RegVal*> format_args = {};
if (m_ts.typecheck(m_ts.make_typespec("type"), f.type(), "", false, false)) {
// type
return;
@ -114,36 +115,38 @@ void Compiler::generate_field_description(StructureType* type,
m_ts.typecheck(m_ts.make_typespec("binteger"), f.type(), "", false, false) ||
m_ts.typecheck(m_ts.make_typespec("pair"), f.type(), "", false, false)) {
// basic, binteger, pair
str_template += fmt::format("~T- {}: ~T~A~%", f.name());
str_template += fmt::format("~T{}: ~A~%", f.name());
format_args.push_back(get_field_of_structure(type, reg, f.name(), env)->to_gpr(env));
} else if (m_ts.typecheck(m_ts.make_typespec("integer"), f.type(), "", false, false)) {
// Integer
str_template += fmt::format("~T- {}: ~T~D~%", f.name());
str_template += fmt::format("~T{}: ~D~%", f.name());
format_args.push_back(get_field_of_structure(type, reg, f.name(), env)->to_gpr(env));
} else if (m_ts.typecheck(m_ts.make_typespec("float"), f.type(), "", false, false)) {
// Float
str_template += fmt::format("~T- {}: ~T~f~%", f.name());
str_template += fmt::format("~T{}: ~f~%", f.name());
format_args.push_back(get_field_of_structure(type, reg, f.name(), env)->to_gpr(env));
} else if (m_ts.typecheck(m_ts.make_typespec("pointer"), f.type(), "", false, false)) {
// Pointers
str_template += fmt::format("~T- {}: ~T#x~X~%", f.name());
str_template += fmt::format("~T{}: #x~X~%", f.name());
format_args.push_back(get_field_of_structure(type, reg, f.name(), env)->to_gpr(env));
} else if (f.is_array()) {
} else if (f.is_array() && !f.is_dynamic()) {
// Arrays
str_template += fmt::format("~T- {}[{}]: ~T@ #x~X~%", f.name(), f.array_size());
str_template += fmt::format("~T{}[{}] @ #x~X~%", f.name(), f.array_size());
format_args.push_back(get_field_of_structure(type, reg, f.name(), env)->to_gpr(env));
} else if (f.is_dynamic()) {
// Dynamic Field
str_template += fmt::format("~T- {}[0]: ~T@ #x~X~%", f.name());
str_template += fmt::format("~T{}[0] @ #x~X~%", f.name());
format_args.push_back(get_field_of_structure(type, reg, f.name(), env)->to_gpr(env));
} else if (f.is_dynamic()) {
// Structure
str_template += fmt::format("~T- {}: ~T#<{} @ #x~X>~%", f.name(), f.type().print());
str_template += fmt::format("~T{}: #<{} @ #x~X>~%", f.name(), f.type().print());
format_args.push_back(get_field_of_structure(type, reg, f.name(), env)->to_gpr(env));
} else {
// Otherwise, we havn't implemented it!
str_template += fmt::format("~T- {}: ~TUndefined!~%", f.name());
str_template += fmt::format("~T{}: Undefined!~%", f.name());
}
compile_format_string(form, env, str_template, format_args);
}
Val* Compiler::generate_inspector_for_type(const goos::Object& form, Env* env, Type* type) {
@ -171,20 +174,20 @@ Val* Compiler::generate_inspector_for_type(const goos::Object& form, Env* env, T
// Inform the compiler that `input`'s value will be written to `rdi` (first arg register)
method_env->emit(std::make_unique<IR_FunctionStart>(std::vector<RegVal*>{input}));
std::string str_template = fmt::format("~T- type: ~T{}~%", type->get_name());
std::vector<RegVal*> format_args = {};
// Check if there are no fields
if (structured_type->fields().empty()) {
str_template += "~T- No fields!~%";
RegVal* type_name = nullptr;
if (dynamic_cast<BasicType*>(structured_type)) {
type_name = get_field_of_structure(structured_type, input, "type", method_env.get())
->to_gpr(method_env.get());
} else {
for (Field f : structured_type->fields()) {
generate_field_description(structured_type, method_env.get(), input, f, format_args,
str_template);
}
type_name = compile_get_sym_obj(structured_type->get_name(), method_env.get())
->to_gpr(method_env.get());
}
compile_format_string(form, method_env.get(), "[~8x] ~A~%", {input, type_name});
for (const Field& f : structured_type->fields()) {
generate_field_description(form, structured_type, method_env.get(), input, f);
}
compile_format_string(form, method_env.get(), str_template, format_args);
method_env->emit(std::make_unique<IR_Return>(method_env->make_gpr(input->type()), input));
// add this function to the object file
@ -223,13 +226,16 @@ Val* Compiler::compile_deftype(const goos::Object& form, const goos::Object& res
// remember that this is a type
m_symbol_types[result.type.base_type()] = m_ts.make_typespec("type");
// get the new method of type object. this is new_type in kscheme.cpp
auto new_type_method = compile_get_method_of_type(m_ts.make_typespec("type"), "new", env);
// call (new 'type 'type-name parent-type flags)
auto new_type_symbol = compile_get_sym_obj(result.type.base_type(), env)->to_gpr(env);
auto parent_type = compile_get_symbol_value(result.type_info->get_parent(), env)->to_gpr(env);
auto flags_int = compile_integer(result.flags.flag, env)->to_gpr(env);
compile_real_function_call(form, new_type_method, {new_type_symbol, parent_type, flags_int}, env);
if (result.create_runtime_type) {
// get the new method of type object. this is new_type in kscheme.cpp
auto new_type_method = compile_get_method_of_type(m_ts.make_typespec("type"), "new", env);
// call (new 'type 'type-name parent-type flags)
auto new_type_symbol = compile_get_sym_obj(result.type.base_type(), env)->to_gpr(env);
auto parent_type = compile_get_symbol_value(result.type_info->get_parent(), env)->to_gpr(env);
auto flags_int = compile_integer(result.flags.flag, env)->to_gpr(env);
compile_real_function_call(form, new_type_method, {new_type_symbol, parent_type, flags_int},
env);
}
// Auto-generate (inspect) method
generate_inspector_for_type(form, env, result.type_info);
@ -620,13 +626,15 @@ Val* Compiler::compile_new(const goos::Object& form, const goos::Object& _rest,
});
auto new_method = compile_get_method_of_type(type_of_obj, "new", env);
auto new_obj = compile_real_function_call(form, new_method, args, env);
new_obj->set_type(type_of_obj);
return new_obj;
}
} else if (allocation == "static") {
assert(false);
auto type_of_object = m_ts.make_typespec(type_as_string);
if (is_structure(type_of_object)) {
return compile_new_static_structure_or_basic(form, type_of_object, *rest, env);
}
}
throw_compile_error(form, "unsupported new form");
@ -670,9 +678,31 @@ Val* Compiler::compile_method(const goos::Object& form, const goos::Object& rest
if (arg.is_symbol()) {
if (m_ts.fully_defined_type_exists(symbol_string(arg))) {
return compile_get_method_of_type(m_ts.make_typespec(symbol_string(arg)), method_name, env);
} else if (m_ts.partially_defined_type_exists(symbol_string(arg))) {
throw_compile_error(form,
"The method form is ambiguous when used on a forward declared type.");
}
}
auto obj = compile_error_guard(arg, env)->to_gpr(env);
return compile_get_method_of_object(obj, method_name, env);
}
Val* Compiler::compile_declare_type(const goos::Object& form, const goos::Object& rest, Env* env) {
(void)env;
auto args = get_va(form, rest);
va_check(form, args, {goos::ObjectType::SYMBOL, goos::ObjectType::SYMBOL}, {});
auto kind = symbol_string(args.unnamed.at(1));
auto type_name = symbol_string(args.unnamed.at(0));
if (kind == "basic") {
m_ts.forward_declare_type_as_basic(type_name);
} else if (kind == "structure") {
m_ts.forward_declare_type_as_structure(type_name);
} else {
throw_compile_error(form, "Invalid declare-type form");
}
return get_none();
}

View File

@ -74,6 +74,7 @@ ObjectFileData ObjectGenerator::generate_data_v3() {
// step 3, cleaning up things now that we know the memory layout
for (int seg = N_SEG; seg-- > 0;) {
handle_temp_static_type_links(seg);
handle_temp_static_sym_links(seg);
handle_temp_jump_links(seg);
handle_temp_instr_sym_links(seg);
handle_temp_rip_func_links(seg);

View File

@ -1,6 +1,8 @@
(define format _format)
(format #t "~A~A~%" (eq? (-> process method-table 2) (method process print))
; no longer use process as a test type here because it's no longer built-in so is
; only forward declared at this point.
(format #t "~A~A~%" (eq? (-> type method-table 2) (method type print))
(eq? (-> string method-table 3) (method "test" inspect))
)
0

View File

@ -0,0 +1,23 @@
(start-test "new-static-basic")
(deftype static-test-basic-type (basic)
((s8 int8)
(s16 int16)
(thing basic)
(u64 uint64))
)
(define test-static-basic (new 'static 'static-test-basic-type :s8 -122 :s16 -23123 :u64 434343 :thing 'bean))
(expect-true (< (the int test-static-basic) (-> debug current))) ;; should be in debug segment?
(expect-true (> (the int test-static-basic) (-> debug base)))
(expect-true (= (-> test-static-basic s8) -122))
(expect-true (= (-> test-static-basic s16) -23123))
(expect-true (= (-> test-static-basic u64) 434343))
(expect-true (eq? (-> test-static-basic thing) 'bean))
(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))
(finish-test)

View File

@ -0,0 +1,21 @@
(start-test "new-static-structure-integers")
(deftype static-test-structure-type (structure)
((s8 int8)
(s16 int16)
(thing basic)
(u64 uint64))
)
(define test-static-structure (new 'static 'static-test-structure-type :s8 -122 :s16 -23123 :u64 434343 :thing 'bean))
(expect-true (< (the int test-static-structure) (-> debug current))) ;; should be in debug segment?
(expect-true (> (the int test-static-structure) (-> debug base)))
(expect-true (= (-> test-static-structure s8) -122))
(expect-true (= (-> test-static-structure s16) -23123))
(expect-true (= (-> test-static-structure u64) 434343))
(expect-true (eq? (-> test-static-structure thing) 'bean))
(expect-true (neq? (-> test-static-structure thing) 'not-bean))
(finish-test)

View File

@ -127,4 +127,8 @@ TEST_F(WithGameTests, All) {
runner.run_static_test(env, testCategory, "test-set-self.gc", {"#t\n0\n"});
runner.run_static_test(env, testCategory, "test-new-array.gc",
get_test_pass_string("new-array", 8));
runner.run_static_test(env, testCategory, "test-new-static-structure-integers.gc",
get_test_pass_string("new-static-structure-integers", 7));
runner.run_static_test(env, testCategory, "test-new-static-basic.gc",
get_test_pass_string("new-static-basic", 9));
}