mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-27 16:31:14 +00:00
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:
parent
118a1b29e3
commit
d5d0d7f924
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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.");
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
)
|
@ -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))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
@ -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},
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
*/
|
||||
|
132
goalc/compiler/compilation/Static.cpp
Normal file
132
goalc/compiler/compilation/Static.cpp
Normal 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;
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
@ -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)
|
@ -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)
|
@ -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));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user