mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
Add defmethod and some uses of the deref operator (#51)
* add tests for various xmms * use unaligned stores and loads to back up and restore xmm128s and also fix argument spilling bug * add deftype * add deref and fix some method _type_ things
This commit is contained in:
parent
abcd444a3b
commit
c7c342ce7e
|
@ -44,3 +44,20 @@ TypeSpec TypeSpec::substitute_for_method_call(const std::string& method_type) co
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TypeSpec::is_compatible_child_method(const TypeSpec& implementation,
|
||||
const std::string& child_type) const {
|
||||
bool ok = implementation.m_type == m_type ||
|
||||
(m_type == "_type_" && implementation.m_type == child_type);
|
||||
if (!ok || implementation.m_arguments.size() != m_arguments.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_arguments.size(); i++) {
|
||||
if (!m_arguments[i].is_compatible_child_method(implementation.m_arguments[i], child_type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -30,6 +30,8 @@ class TypeSpec {
|
|||
|
||||
bool operator!=(const TypeSpec& other) const;
|
||||
bool operator==(const TypeSpec& other) const;
|
||||
bool is_compatible_child_method(const TypeSpec& implementation,
|
||||
const std::string& child_type) const;
|
||||
std::string print() const;
|
||||
|
||||
void add_arg(const TypeSpec& ts) { m_arguments.push_back(ts); }
|
||||
|
|
|
@ -228,6 +228,12 @@ Type* TypeSystem::lookup_type(const TypeSpec& ts) const {
|
|||
return lookup_type(ts.base_type());
|
||||
}
|
||||
|
||||
MethodInfo TypeSystem::add_method(const std::string& type_name,
|
||||
const std::string& method_name,
|
||||
const TypeSpec& ts) {
|
||||
return add_method(lookup_type(make_typespec(type_name)), method_name, ts);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Add a method, if it doesn't exist. If the method already exists (possibly in a parent), checks to
|
||||
* see if this is an identical definition. If not, it's an error, and if so, nothing happens.
|
||||
|
@ -263,7 +269,7 @@ MethodInfo TypeSystem::add_method(Type* type, const std::string& method_name, co
|
|||
|
||||
if (got_existing) {
|
||||
// make sure we aren't changing anything.
|
||||
if (ts != existing_info.type) {
|
||||
if (!existing_info.type.is_compatible_child_method(ts, type->get_name())) {
|
||||
fmt::print(
|
||||
"[TypeSystem] The method {} of type {} was originally defined as {}, but has been "
|
||||
"redefined as {}\n",
|
||||
|
|
|
@ -51,6 +51,9 @@ class TypeSystem {
|
|||
Type* lookup_type(const TypeSpec& ts) const;
|
||||
Type* lookup_type(const std::string& name) const;
|
||||
|
||||
MethodInfo add_method(const std::string& type_name,
|
||||
const std::string& method_name,
|
||||
const TypeSpec& ts);
|
||||
MethodInfo add_method(Type* type, const std::string& method_name, const TypeSpec& ts);
|
||||
MethodInfo add_new_method(Type* type, const TypeSpec& ts);
|
||||
MethodInfo lookup_method(const std::string& type_name, const std::string& method_name);
|
||||
|
|
|
@ -174,3 +174,31 @@
|
|||
)
|
||||
)
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;
|
||||
;; Math Macros
|
||||
;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defmacro +1 (var)
|
||||
`(+ ,var 1)
|
||||
)
|
||||
|
||||
(defmacro +! (place amount)
|
||||
`(set! ,place (+ ,place ,amount))
|
||||
)
|
||||
|
||||
(defmacro +1! (place)
|
||||
`(set! ,place (+ 1 ,place))
|
||||
)
|
||||
|
||||
(defmacro -! (place amount)
|
||||
`(set! ,place (- ,place ,amount))
|
||||
)
|
||||
|
||||
(defmacro *! (place amount)
|
||||
`(set! ,place (* ,place ,amount))
|
||||
)
|
||||
|
||||
(defmacro 1- (var)
|
||||
`(- ,var 1)
|
||||
)
|
|
@ -208,41 +208,59 @@
|
|||
|
||||
;; TODO - vec4s
|
||||
|
||||
;; The "boxed float" type bfloat is just a float wrapped in a basic (structure type that has runtime type information)
|
||||
;; The "boxed float" type "bfloat" is just a float wrapped in a basic (structure type that has runtime type information)
|
||||
;; it's a way to have a floating point number that knows its a floating point number and can print/inspect itself
|
||||
;; Compared to a normal float, it's much less efficient, so this is used extremely rarely.
|
||||
|
||||
;; a GOAL deftype contains the following:
|
||||
;; - type name
|
||||
;; - parent type name
|
||||
;; - field list
|
||||
;; - method declarations
|
||||
;; - additional options
|
||||
;; It has "asserts" that can be used to make sure that the type is laid out in memory in the same way as the game.
|
||||
;; You provide the actual offsets/sizes/method ids, and if there is a mismatch, it throws a compiler error.
|
||||
;; The decompile will generate these automatically in the future.
|
||||
|
||||
;; Type Name: should be a unique name. Can't be the name of a function or global variable. In this case, it's bfloat
|
||||
;; Parent Type: Should be the name of the parent type ("basic" in this case). Will inherit fields and methods from the parent.
|
||||
;; children of "basic" are structure types with runtime type information.
|
||||
;; Field List: each field of the type, listed as (name type-name [options])
|
||||
;; use the :offset-assert X to do a check at comile-time that the OpenGOAL compiler places the field at the given offset.
|
||||
;; if the compiler came up with a different offset, it will create an error. This used to make sure the memory layout matches
|
||||
;; the original game.
|
||||
;; Method Declarations: Any methods which are defined in this type but not the parent must be declared here.
|
||||
;; you may optionally declare methods defined only in the parent, or defined in both the parent and child (overridden methods)
|
||||
;; the method declarations is (method-name (arg-list) return-type [optional-id-assert])
|
||||
;; the optional id assert is used to check that the compiler places the method in the given slot of the method table.
|
||||
;; like the offset-assert, it's used to make sure the type hierarchy matches the game.
|
||||
;; Note that the special type "_type_" can be used in methods args/returns to indicate "the type of the object method is called on".
|
||||
;; this is used for 2 things:
|
||||
;; 1. Child who overrides it can use their own type as an argument, rather than a less specific parent type.
|
||||
;; 2. Caller who calls an overriden method and knows it at compile time can know a return type more specifically.
|
||||
|
||||
|
||||
;; define a type called "bfloat" which is a child of "basic".
|
||||
;; "basic" is just a type with structures and runtime type information
|
||||
(deftype bfloat (basic)
|
||||
;; the field list.
|
||||
;; there is a single field named data, of type float.
|
||||
;; the :offset-assert makes sure that OpenGOAL's type layout places the field at the given offset.
|
||||
;; if not, it creates a compiler error. This is used to make sure we exactly copy the game's memory layout,
|
||||
;; as we can get the exact offset of fields from the disassembly
|
||||
((data float :offset-assert 4))
|
||||
|
||||
;; declare methods. If you are overriding a parent method, you don't have to declare it, but you can if you want
|
||||
;; The number after the return type is the method ID, that can be checked against the disassembly to make sure
|
||||
;; the type and method hierarchy is correct. If OpenGOAL's method table layout doesn't match, it will create
|
||||
;; compiler error.
|
||||
|
||||
(:methods (print (_type_) _type_ 2) ;; we will override print later on
|
||||
(inspect (_type_) _type_ 3) ;; this is a parent method we won't override. It's fine to put it here anyway.
|
||||
;; fields
|
||||
((data float :offset-assert 4)) ;; field "data" is a float.
|
||||
;; methods
|
||||
(:methods (print (_type_) _type_ 2) ;; we will override print later on. This is optional to include
|
||||
(inspect (_type_) _type_ 3) ;; this is a parent method we won't override. This is also optional to inlcude
|
||||
)
|
||||
|
||||
;; options
|
||||
|
||||
;; Note that the special type "_type_" can be used in methods to indicate "the type of the object method is called on".
|
||||
;; this is used for 2 things:
|
||||
;; 1. Child who overrides it can use their own type as an argument, rather than a less specific parent type.
|
||||
;; 2. Caller who calls an overriden method and knows it at compile time can know a return type more specifically.
|
||||
|
||||
|
||||
;; make sure the size of the type is correct (this is stored in the type structure, so we can check it)
|
||||
;; make sure the size of the type is correct (compare to value from game)
|
||||
:size-assert 8
|
||||
;; make sure method count is correct (again, stored in the type structure)
|
||||
;; make sure method count is correct (again, compare to value from game)
|
||||
:method-count-assert 9
|
||||
;; flags passed to the new_type function in the runtime.
|
||||
;; flags passed to the new_type function in the runtime, compare from game
|
||||
:flag-assert #x900000008
|
||||
)
|
||||
|
||||
|
||||
;; todo print bfloat
|
||||
(defmethod print bfloat ((obj bfloat))
|
||||
"Override the default print method to print a bfloat like a normal float"
|
||||
(format #t "~f" (-> obj data))
|
||||
obj
|
||||
)
|
1
goal_src/test/test-deref-simple.gc
Normal file
1
goal_src/test/test-deref-simple.gc
Normal file
|
@ -0,0 +1 @@
|
|||
(print (-> type parent parent)) 0
|
8
goal_src/test/test-float-function.gc
Normal file
8
goal_src/test/test-float-function.gc
Normal file
|
@ -0,0 +1,8 @@
|
|||
(defun float-testing-function ((x float) (y float))
|
||||
(* x y (* x x))
|
||||
)
|
||||
|
||||
(let ((x (float-testing-function (* 1.2 1.2) 3.4)))
|
||||
(format #t "~,,3f~%" x)
|
||||
)
|
||||
0
|
2
goal_src/test/test-float-in-symbol.gc
Normal file
2
goal_src/test/test-float-in-symbol.gc
Normal file
|
@ -0,0 +1,2 @@
|
|||
(define float-symbol 2345.6)
|
||||
(format #t "~f~%" float-symbol)
|
12
goal_src/test/test-float-pow-function.gc
Normal file
12
goal_src/test/test-float-pow-function.gc
Normal file
|
@ -0,0 +1,12 @@
|
|||
(defun pow-test ((base float) (exponent integer))
|
||||
(let ((result base))
|
||||
(while (> exponent 1)
|
||||
(*! result base)
|
||||
(-! exponent 1)
|
||||
)
|
||||
result
|
||||
)
|
||||
)
|
||||
|
||||
(format #t "~,,0f~%" (pow-test 2.0 8))
|
||||
0
|
1
goal_src/test/test-float-product.gc
Normal file
1
goal_src/test/test-float-product.gc
Normal file
|
@ -0,0 +1 @@
|
|||
(format #t "~f~%" (* 1.2 25.0 4.0))
|
5
goal_src/test/test-function-return-constant-float.gc
Normal file
5
goal_src/test/test-function-return-constant-float.gc
Normal file
|
@ -0,0 +1,5 @@
|
|||
(defun return-const-float ()
|
||||
3.1415
|
||||
)
|
||||
(format #t "~,,5f~%" (return-const-float))
|
||||
0
|
1
goal_src/test/test-min-max.gc
Normal file
1
goal_src/test/test-min-max.gc
Normal file
|
@ -0,0 +1 @@
|
|||
(+ (min 1 2) (min 2 (min -1 -4)) (max 10 (min 2 23)) (max 3 1))
|
15
goal_src/test/test-nested-float-functions.gc
Normal file
15
goal_src/test/test-nested-float-functions.gc
Normal file
|
@ -0,0 +1,15 @@
|
|||
(define-extern _format function)
|
||||
(define format _format)
|
||||
|
||||
(defun float-testing-function-2 ((x float) (y float))
|
||||
(format #t "i ~f ~f~%" x y)
|
||||
(let ((result (* x y (* x x))))
|
||||
(format #t "r ~f~%" result)
|
||||
result
|
||||
)
|
||||
)
|
||||
|
||||
(let ((x (float-testing-function-2 (* 1.2 1.2) 3.4)))
|
||||
(format #t "~,,3f ~,,3f~%" (float-testing-function-2 1.2000 x) x)
|
||||
)
|
||||
0
|
4
goal_src/test/test-quote-symbol.gc
Normal file
4
goal_src/test/test-quote-symbol.gc
Normal file
|
@ -0,0 +1,4 @@
|
|||
(define my-thing 'apple)
|
||||
(set! my-thing 'banana)
|
||||
(format #t "~A~%" my-thing)
|
||||
0
|
|
@ -151,6 +151,13 @@ void Compiler::color_object_file(FileEnv* env) {
|
|||
input.instructions.push_back(i->to_rai());
|
||||
input.debug_instruction_names.push_back(i->print());
|
||||
}
|
||||
|
||||
// temp hack
|
||||
{
|
||||
for (auto& arg : f->params) {
|
||||
input.instructions.front().write.push_back(arg.second->ireg());
|
||||
}
|
||||
}
|
||||
input.max_vars = f->max_vars();
|
||||
input.constraints = f->constraints();
|
||||
|
||||
|
|
|
@ -170,6 +170,8 @@ class Compiler {
|
|||
|
||||
// Type
|
||||
Val* compile_deftype(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_defmethod(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_deref(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
};
|
||||
|
||||
#endif // JAK_COMPILER_H
|
||||
|
|
|
@ -219,6 +219,8 @@ void IR_RegSet::do_codegen(emitter::ObjectGenerator* gen,
|
|||
gen->add_instr(IGen::movd_gpr32_xmm32(dest_reg, val_reg), irec);
|
||||
} else if (val_reg.is_gpr() && dest_reg.is_xmm()) {
|
||||
gen->add_instr(IGen::movd_xmm32_gpr32(dest_reg, val_reg), irec);
|
||||
} else if (val_reg.is_xmm() && dest_reg.is_xmm()) {
|
||||
gen->add_instr(IGen::mov_xmm32_xmm32(dest_reg, val_reg), irec);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
@ -504,6 +506,8 @@ std::string IR_FloatMath::print() {
|
|||
switch (m_kind) {
|
||||
case FloatMathKind::DIV_SS:
|
||||
return fmt::format("divss {}, {}", m_dest->print(), m_arg->print());
|
||||
case FloatMathKind::MUL_SS:
|
||||
return fmt::format("mulss {}, {}", m_dest->print(), m_arg->print());
|
||||
default:
|
||||
throw std::runtime_error("Unsupported FloatMathKind");
|
||||
}
|
||||
|
@ -525,6 +529,10 @@ void IR_FloatMath::do_codegen(emitter::ObjectGenerator* gen,
|
|||
gen->add_instr(
|
||||
IGen::divss_xmm_xmm(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec);
|
||||
break;
|
||||
case FloatMathKind::MUL_SS:
|
||||
gen->add_instr(
|
||||
IGen::mulss_xmm_xmm(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
|
|
@ -223,7 +223,7 @@ class IR_IntegerMath : public IR {
|
|||
RegVal* m_arg;
|
||||
};
|
||||
|
||||
enum class FloatMathKind { DIV_SS };
|
||||
enum class FloatMathKind { DIV_SS, MUL_SS };
|
||||
|
||||
class IR_FloatMath : public IR {
|
||||
public:
|
||||
|
|
|
@ -94,7 +94,20 @@ RegVal* FloatConstantVal::to_reg(Env* fe) {
|
|||
}
|
||||
|
||||
RegVal* MemoryOffsetConstantVal::to_reg(Env* fe) {
|
||||
auto re = fe->make_gpr(deref_type);
|
||||
fe->emit(std::make_unique<IR_LoadConstOffset>(re, offset, base, info));
|
||||
return re;
|
||||
(void)fe;
|
||||
assert(false);
|
||||
throw std::runtime_error("MemoryOffsetConstantVal::to_reg not yet implemented");
|
||||
}
|
||||
|
||||
RegVal* MemoryDerefVal::to_reg(Env* fe) {
|
||||
auto base_as_co = dynamic_cast<MemoryOffsetConstantVal*>(base);
|
||||
if (base_as_co) {
|
||||
auto re = fe->make_gpr(m_ts);
|
||||
fe->emit(std::make_unique<IR_LoadConstOffset>(re, base_as_co->offset,
|
||||
base_as_co->base->to_gpr(fe), info));
|
||||
return re;
|
||||
} else {
|
||||
assert(false);
|
||||
throw std::runtime_error("MemoryDerefVal::to_reg not yet implemented for this case");
|
||||
}
|
||||
}
|
|
@ -126,35 +126,45 @@ class StaticVal : public Val {
|
|||
};
|
||||
|
||||
struct MemLoadInfo {
|
||||
MemLoadInfo() = default;
|
||||
explicit MemLoadInfo(const DerefInfo& di) {
|
||||
assert(di.can_deref);
|
||||
assert(di.mem_deref);
|
||||
sign_extend = di.sign_extend;
|
||||
size = di.load_size;
|
||||
reg = di.reg;
|
||||
}
|
||||
|
||||
RegKind reg = RegKind::INVALID;
|
||||
bool sign_extend = false;
|
||||
int size = -1;
|
||||
};
|
||||
|
||||
class MemoryOffsetConstantVal : public Val {
|
||||
public:
|
||||
MemoryOffsetConstantVal(TypeSpec ts,
|
||||
RegVal* _base,
|
||||
int _offset,
|
||||
MemLoadInfo _info,
|
||||
TypeSpec _deref_type)
|
||||
: Val(std::move(ts)),
|
||||
base(_base),
|
||||
offset(_offset),
|
||||
info(_info),
|
||||
deref_type(std::move(_deref_type)) {}
|
||||
MemoryOffsetConstantVal(TypeSpec ts, Val* _base, int _offset)
|
||||
: Val(std::move(ts)), base(_base), offset(_offset) {}
|
||||
std::string print() const override {
|
||||
return "(" + base->print() + " + " + std::to_string(offset) + ")";
|
||||
}
|
||||
RegVal* to_reg(Env* fe) override;
|
||||
RegVal* base = nullptr;
|
||||
Val* base = nullptr;
|
||||
int offset = 0;
|
||||
MemLoadInfo info;
|
||||
TypeSpec deref_type;
|
||||
};
|
||||
|
||||
// MemOffConstant
|
||||
// MemOffVar
|
||||
// MemDeref
|
||||
|
||||
class MemoryDerefVal : public Val {
|
||||
public:
|
||||
MemoryDerefVal(TypeSpec ts, Val* _base, MemLoadInfo _info)
|
||||
: Val(std::move(ts)), base(_base), info(_info) {}
|
||||
std::string print() const override { return "[" + base->print() + "]"; }
|
||||
RegVal* to_reg(Env* fe) override;
|
||||
Val* base = nullptr;
|
||||
MemLoadInfo info;
|
||||
};
|
||||
|
||||
// PairEntry
|
||||
// Alias
|
||||
|
||||
|
|
|
@ -57,8 +57,9 @@ static const std::unordered_map<
|
|||
//
|
||||
// TYPE
|
||||
{"deftype", &Compiler::compile_deftype},
|
||||
{"defmethod", &Compiler::compile_defmethod},
|
||||
// {"defenum", &Compiler::compile_defenum},
|
||||
// {"->", &Compiler::compile_deref},
|
||||
{"->", &Compiler::compile_deref},
|
||||
// {"&", &Compiler::compile_addr_of},
|
||||
//
|
||||
//
|
||||
|
@ -83,7 +84,7 @@ static const std::unordered_map<
|
|||
// {"the", &Compiler::compile_the},
|
||||
// {"the-as", &Compiler::compile_the_as},
|
||||
//
|
||||
// {"defmethod", &Compiler::compile_defmethod},
|
||||
//
|
||||
//
|
||||
// {"current-method-type", &Compiler::compile_current_method_type},
|
||||
// {"new", &Compiler::compile_new},
|
||||
|
|
|
@ -91,9 +91,8 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest
|
|||
|
||||
if (!inline_only) {
|
||||
// compile a function! First create env
|
||||
// auto new_func_env = fe->alloc_env<FunctionEnv>(env, lambda.debug_name);
|
||||
auto new_func_env = std::make_unique<FunctionEnv>(env, lambda.debug_name);
|
||||
new_func_env->set_segment(MAIN_SEGMENT);
|
||||
new_func_env->set_segment(MAIN_SEGMENT); // todo, how do we set debug?
|
||||
|
||||
// set up arguments
|
||||
assert(lambda.params.size() < 8); // todo graceful error
|
||||
|
@ -216,7 +215,7 @@ Val* Compiler::compile_function_or_method_call(const goos::Object& form, Env* en
|
|||
head_as_lambda = dynamic_cast<LambdaVal*>(head);
|
||||
}
|
||||
|
||||
if (!head_as_lambda) {
|
||||
if (!head_as_lambda && !is_method_call) {
|
||||
head = head->to_gpr(env);
|
||||
}
|
||||
|
||||
|
@ -282,12 +281,13 @@ Val* Compiler::compile_function_or_method_call(const goos::Object& form, Env* en
|
|||
} else {
|
||||
// not an inline call
|
||||
if (is_method_call) {
|
||||
// determine the method to call by looking at the type of first argument
|
||||
if (eval_args.empty()) {
|
||||
throw_compile_error(form, "0 argument method call is impossible to figure out");
|
||||
}
|
||||
printf("BAD %s\n", uneval_head.print().c_str());
|
||||
assert(false); // nyi
|
||||
throw_compile_error(form, "Unrecognized symbol " + uneval_head.print() + " as head of form");
|
||||
// // determine the method to call by looking at the type of first argument
|
||||
// if (eval_args.empty()) {
|
||||
//
|
||||
// }
|
||||
// printf("BAD %s\n", uneval_head.print().c_str());
|
||||
// assert(false); // nyi
|
||||
// head = compile_get_method_of_object(eval_args.front(), symbol_string(uneval_head), env);
|
||||
}
|
||||
|
||||
|
|
|
@ -148,6 +148,18 @@ Val* Compiler::compile_mul(const goos::Object& form, const goos::Object& rest, E
|
|||
}
|
||||
return result;
|
||||
}
|
||||
case MATH_FLOAT: {
|
||||
auto result = env->make_xmm(first_type);
|
||||
env->emit(std::make_unique<IR_RegSet>(result, first_val->to_xmm(env)));
|
||||
|
||||
for (size_t i = 1; i < args.unnamed.size(); i++) {
|
||||
env->emit(std::make_unique<IR_FloatMath>(
|
||||
FloatMathKind::MUL_SS, result,
|
||||
to_math_type(compile_error_guard(args.unnamed.at(i), env), math_type, env)
|
||||
->to_xmm(env)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case MATH_INVALID:
|
||||
throw_compile_error(
|
||||
form, "Cannot determine the math mode for object of type " + first_type.print());
|
||||
|
|
|
@ -13,9 +13,16 @@ RegVal* Compiler::compile_get_method_of_type(const TypeSpec& type,
|
|||
load_info.sign_extend = false;
|
||||
load_info.size = POINTER_SIZE;
|
||||
|
||||
return fe
|
||||
->alloc_val<MemoryOffsetConstantVal>(typ->type(), typ, offset_of_method, load_info, info.type)
|
||||
->to_reg(env);
|
||||
auto loc_type = m_ts.make_pointer_typespec(info.type);
|
||||
auto loc = fe->alloc_val<MemoryOffsetConstantVal>(loc_type, typ, offset_of_method);
|
||||
auto di = m_ts.get_deref_info(loc_type);
|
||||
assert(di.can_deref);
|
||||
assert(di.mem_deref);
|
||||
assert(di.sign_extend == false);
|
||||
assert(di.load_size == 4);
|
||||
|
||||
auto deref = fe->alloc_val<MemoryDerefVal>(di.result_type, loc, MemLoadInfo(di));
|
||||
return deref->to_reg(env);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_deftype(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
|
@ -38,3 +45,180 @@ Val* Compiler::compile_deftype(const goos::Object& form, const goos::Object& res
|
|||
return compile_real_function_call(form, new_type_method,
|
||||
{new_type_symbol, parent_type, flags_int}, env);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _rest, Env* env) {
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
auto* rest = &_rest;
|
||||
|
||||
auto& method_name = pair_car(*rest);
|
||||
rest = &pair_cdr(*rest);
|
||||
auto& type_name = pair_car(*rest);
|
||||
rest = &pair_cdr(*rest);
|
||||
auto& arg_list = pair_car(*rest);
|
||||
auto body = &pair_cdr(*rest);
|
||||
|
||||
if (!method_name.is_symbol()) {
|
||||
throw_compile_error(form, "method name must be a symbol, got " + method_name.print());
|
||||
}
|
||||
if (!type_name.is_symbol()) {
|
||||
throw_compile_error(form, "method type must be a symbol, got " + method_name.print());
|
||||
}
|
||||
|
||||
auto place = fe->alloc_val<LambdaVal>(get_none()->type());
|
||||
auto& lambda = place->lambda;
|
||||
auto lambda_ts = m_ts.make_typespec("function");
|
||||
|
||||
// parse the argument list.
|
||||
for_each_in_list(arg_list, [&](const goos::Object& o) {
|
||||
if (o.is_symbol()) {
|
||||
// if it has no type, assume object.
|
||||
lambda.params.push_back({symbol_string(o), m_ts.make_typespec("object")});
|
||||
lambda_ts.add_arg(m_ts.make_typespec("object"));
|
||||
} else {
|
||||
auto param_args = get_va(o, o);
|
||||
va_check(o, param_args, {goos::ObjectType::SYMBOL, goos::ObjectType::SYMBOL}, {});
|
||||
|
||||
GoalArg parm;
|
||||
parm.name = symbol_string(param_args.unnamed.at(0));
|
||||
parm.type = parse_typespec(param_args.unnamed.at(1));
|
||||
lambda_ts.add_arg(parm.type);
|
||||
parm.type = parm.type.substitute_for_method_call(symbol_string(type_name));
|
||||
lambda.params.push_back(parm);
|
||||
}
|
||||
});
|
||||
assert(lambda.params.size() == lambda_ts.arg_count());
|
||||
// todo, verify argument list types (check that first arg is _type_ for methods that aren't "new")
|
||||
lambda.debug_name = fmt::format("(method {} {})", method_name.print(), type_name.print());
|
||||
|
||||
// skip docstring
|
||||
if (body->as_pair()->car.is_string() && !body->as_pair()->cdr.is_empty_list()) {
|
||||
body = &pair_cdr(*body);
|
||||
}
|
||||
|
||||
lambda.body = *body;
|
||||
place->func = nullptr;
|
||||
|
||||
auto new_func_env = std::make_unique<FunctionEnv>(env, lambda.debug_name);
|
||||
new_func_env->set_segment(MAIN_SEGMENT); // todo, how do we set debug?
|
||||
|
||||
// set up arguments
|
||||
assert(lambda.params.size() < 8); // todo graceful error
|
||||
for (u32 i = 0; i < lambda.params.size(); i++) {
|
||||
IRegConstraint constr;
|
||||
constr.instr_idx = 0; // constraint at function start
|
||||
auto ireg = new_func_env->make_ireg(lambda.params.at(i).type, emitter::RegKind::GPR);
|
||||
constr.ireg = ireg->ireg();
|
||||
constr.desired_register = emitter::gRegInfo.get_arg_reg(i);
|
||||
new_func_env->params[lambda.params.at(i).name] = ireg;
|
||||
new_func_env->constrain(constr);
|
||||
}
|
||||
|
||||
place->func = new_func_env.get();
|
||||
|
||||
// nasty function block env setup
|
||||
auto return_reg = new_func_env->make_ireg(get_none()->type(), emitter::RegKind::GPR);
|
||||
auto func_block_env = new_func_env->alloc_env<BlockEnv>(new_func_env.get(), "#f");
|
||||
func_block_env->return_value = return_reg;
|
||||
func_block_env->end_label = Label(new_func_env.get());
|
||||
|
||||
// compile the function!
|
||||
Val* result = nullptr;
|
||||
bool first_thing = true;
|
||||
for_each_in_list(lambda.body, [&](const goos::Object& o) {
|
||||
result = compile_error_guard(o, func_block_env);
|
||||
if (first_thing) {
|
||||
first_thing = false;
|
||||
// you could probably cheat and do a (begin (blorp) (declare ...)) to get around this.
|
||||
new_func_env->settings.is_set = true;
|
||||
}
|
||||
});
|
||||
if (result) {
|
||||
auto final_result = result->to_gpr(new_func_env.get());
|
||||
new_func_env->emit(std::make_unique<IR_Return>(return_reg, final_result));
|
||||
// new_func_env->emit(std::make_unique<IR_Null>())???
|
||||
new_func_env->finish();
|
||||
lambda_ts.add_arg(final_result->type());
|
||||
} else {
|
||||
lambda_ts.add_arg(m_ts.make_typespec("none"));
|
||||
}
|
||||
func_block_env->end_label.idx = new_func_env->code().size();
|
||||
|
||||
auto obj_env = get_parent_env_of_type<FileEnv>(new_func_env.get());
|
||||
assert(obj_env);
|
||||
if (new_func_env->settings.save_code) {
|
||||
obj_env->add_function(std::move(new_func_env));
|
||||
}
|
||||
place->set_type(lambda_ts);
|
||||
|
||||
auto info = m_ts.add_method(symbol_string(type_name), symbol_string(method_name), lambda_ts);
|
||||
auto type_obj = compile_get_symbol_value(symbol_string(type_name), env)->to_gpr(env);
|
||||
auto id_val = compile_integer(info.id, env)->to_gpr(env);
|
||||
auto method_val = place->to_gpr(env);
|
||||
auto method_set_val = compile_get_symbol_value("method-set!", env)->to_gpr(env);
|
||||
return compile_real_function_call(form, method_set_val, {type_obj, id_val, method_val}, env);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_deref(const goos::Object& form, const goos::Object& _rest, Env* env) {
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
if (_rest.is_empty_list()) {
|
||||
throw_compile_error(form, "-> must get at least one argument");
|
||||
}
|
||||
|
||||
auto& first_arg = pair_car(_rest);
|
||||
auto rest = &pair_cdr(_rest);
|
||||
|
||||
// eval the first thing
|
||||
auto result = compile_error_guard(first_arg, env);
|
||||
|
||||
if (rest->is_empty_list()) {
|
||||
// one argument, do a pointer deref
|
||||
auto deref_info = m_ts.get_deref_info(result->type());
|
||||
if (!deref_info.can_deref) {
|
||||
throw_compile_error(form, "Cannot dereference a " + result->type().print());
|
||||
}
|
||||
|
||||
if (deref_info.mem_deref) {
|
||||
result =
|
||||
fe->alloc_val<MemoryDerefVal>(deref_info.result_type, result, MemLoadInfo(deref_info));
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// compound, is field access/nested access
|
||||
while (!rest->is_empty_list()) {
|
||||
auto field_obj = pair_car(*rest);
|
||||
rest = &pair_cdr(*rest);
|
||||
auto type_info = m_ts.lookup_type(result->type());
|
||||
|
||||
// attempt to treat it as a field. May not succeed if we're actually an array.
|
||||
if (field_obj.is_symbol()) {
|
||||
auto field_name = symbol_string(field_obj);
|
||||
auto struct_type = dynamic_cast<StructureType*>(type_info);
|
||||
|
||||
if (struct_type) {
|
||||
int offset = -struct_type->get_offset();
|
||||
auto field = m_ts.lookup_field_info(type_info->get_name(), field_name);
|
||||
if (field.needs_deref) {
|
||||
TypeSpec loc_type = m_ts.make_pointer_typespec(field.type);
|
||||
auto loc = fe->alloc_val<MemoryOffsetConstantVal>(loc_type, result,
|
||||
field.field.offset() + offset);
|
||||
auto di = m_ts.get_deref_info(loc_type);
|
||||
assert(di.can_deref);
|
||||
assert(di.mem_deref);
|
||||
result = fe->alloc_val<MemoryDerefVal>(di.result_type, loc, MemLoadInfo(di));
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// todo try bitfield
|
||||
}
|
||||
|
||||
// todo array or other
|
||||
assert(false);
|
||||
}
|
||||
return result;
|
||||
}
|
|
@ -115,6 +115,7 @@ class IGen {
|
|||
instr.set_op2(0x0f);
|
||||
instr.set_op3(0x10);
|
||||
instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, false);
|
||||
instr.swap_op0_rex();
|
||||
return instr;
|
||||
}
|
||||
|
||||
|
@ -996,7 +997,8 @@ class IGen {
|
|||
static Instruction store128_gpr64_xmm128(Register gpr_addr, Register xmm_value) {
|
||||
assert(gpr_addr.is_gpr());
|
||||
assert(xmm_value.is_xmm());
|
||||
Instruction instr(0x66);
|
||||
// Instruction instr(0x66);
|
||||
Instruction instr(0xf3);
|
||||
instr.set_op2(0x0f);
|
||||
instr.set_op3(0x7f);
|
||||
instr.set_modrm_and_rex_for_reg_addr(xmm_value.hw_id(), gpr_addr.hw_id(), false);
|
||||
|
@ -1007,7 +1009,8 @@ class IGen {
|
|||
static Instruction load128_xmm128_gpr64(Register xmm_dest, Register gpr_addr) {
|
||||
assert(gpr_addr.is_gpr());
|
||||
assert(xmm_dest.is_xmm());
|
||||
Instruction instr(0x66);
|
||||
// Instruction instr(0x66);
|
||||
Instruction instr(0xf3);
|
||||
instr.set_op2(0x0f);
|
||||
instr.set_op3(0x6f);
|
||||
instr.set_modrm_and_rex_for_reg_addr(xmm_dest.hw_id(), gpr_addr.hw_id(), false);
|
||||
|
|
|
@ -23,6 +23,23 @@ RegisterInfo RegisterInfo::make_register_info() {
|
|||
info.m_info[R14] = {-1, false, true, "r14"}; // st?
|
||||
info.m_info[R15] = {-1, false, true, "r15"}; // offset.
|
||||
|
||||
info.m_info[XMM0] = {-1, false, false, "xmm0"};
|
||||
info.m_info[XMM1] = {-1, false, false, "xmm1"};
|
||||
info.m_info[XMM2] = {-1, false, false, "xmm2"};
|
||||
info.m_info[XMM3] = {-1, false, false, "xmm3"};
|
||||
info.m_info[XMM4] = {-1, false, false, "xmm4"};
|
||||
info.m_info[XMM5] = {-1, false, false, "xmm5"};
|
||||
info.m_info[XMM6] = {-1, false, false, "xmm6"};
|
||||
info.m_info[XMM7] = {-1, false, false, "xmm7"};
|
||||
info.m_info[XMM8] = {-1, true, false, "xmm8"};
|
||||
info.m_info[XMM9] = {-1, true, false, "xmm9"};
|
||||
info.m_info[XMM10] = {-1, true, false, "xmm10"};
|
||||
info.m_info[XMM11] = {-1, true, false, "xmm11"};
|
||||
info.m_info[XMM12] = {-1, true, false, "xmm12"};
|
||||
info.m_info[XMM13] = {-1, true, false, "xmm13"};
|
||||
info.m_info[XMM14] = {-1, true, false, "xmm14"};
|
||||
info.m_info[XMM15] = {-1, true, false, "xmm15"};
|
||||
|
||||
info.m_arg_regs = std::array<Register, N_ARGS>({RDI, RSI, RDX, RCX, R8, R9, R10, R11});
|
||||
info.m_saved_gprs = std::array<Register, N_SAVED_GPRS>({RBX, RBP, R10, R11, R12});
|
||||
info.m_saved_xmms =
|
||||
|
|
|
@ -57,36 +57,68 @@ TEST(CodeTester, xmm_store_128) {
|
|||
// movdqa [r14], xmm3
|
||||
// movdqa [rbx], xmm14
|
||||
// movdqa [r14], xmm13
|
||||
// tester.emit(IGen::store128_gpr64_xmm128(RBX, XMM3));
|
||||
// tester.emit(IGen::store128_gpr64_xmm128(R14, XMM3));
|
||||
// tester.emit(IGen::store128_gpr64_xmm128(RBX, XMM14));
|
||||
// tester.emit(IGen::store128_gpr64_xmm128(R14, XMM13));
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(),
|
||||
// "66 0f 7f 1b 66 41 0f 7f 1e 66 44 0f 7f 33 66 45 0f 7f 2e");
|
||||
//
|
||||
// tester.clear();
|
||||
// tester.emit(IGen::store128_gpr64_xmm128(RSP, XMM1));
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 7f 0c 24"); // requires SIB byte.
|
||||
//
|
||||
// tester.clear();
|
||||
// tester.emit(IGen::store128_gpr64_xmm128(R12, XMM13));
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 7f 2c 24"); // requires SIB byte and REX
|
||||
// byte
|
||||
//
|
||||
// tester.clear();
|
||||
// tester.emit(IGen::store128_gpr64_xmm128(RBP, XMM1));
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 7f 4d 00");
|
||||
//
|
||||
// tester.clear();
|
||||
// tester.emit(IGen::store128_gpr64_xmm128(RBP, XMM11));
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(), "66 44 0f 7f 5d 00");
|
||||
//
|
||||
// tester.clear();
|
||||
// tester.emit(IGen::store128_gpr64_xmm128(R13, XMM2));
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(), "66 41 0f 7f 55 00");
|
||||
//
|
||||
// tester.clear();
|
||||
// tester.emit(IGen::store128_gpr64_xmm128(R13, XMM12));
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 7f 65 00");
|
||||
|
||||
tester.emit(IGen::store128_gpr64_xmm128(RBX, XMM3));
|
||||
tester.emit(IGen::store128_gpr64_xmm128(R14, XMM3));
|
||||
tester.emit(IGen::store128_gpr64_xmm128(RBX, XMM14));
|
||||
tester.emit(IGen::store128_gpr64_xmm128(R14, XMM13));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(),
|
||||
"66 0f 7f 1b 66 41 0f 7f 1e 66 44 0f 7f 33 66 45 0f 7f 2e");
|
||||
"f3 0f 7f 1b f3 41 0f 7f 1e f3 44 0f 7f 33 f3 45 0f 7f 2e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::store128_gpr64_xmm128(RSP, XMM1));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 7f 0c 24"); // requires SIB byte.
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 7f 0c 24"); // requires SIB byte.
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::store128_gpr64_xmm128(R12, XMM13));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 7f 2c 24"); // requires SIB byte and REX byte
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 45 0f 7f 2c 24"); // requires SIB byte and REX byte
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::store128_gpr64_xmm128(RBP, XMM1));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 7f 4d 00");
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 7f 4d 00");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::store128_gpr64_xmm128(RBP, XMM11));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 44 0f 7f 5d 00");
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 44 0f 7f 5d 00");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::store128_gpr64_xmm128(R13, XMM2));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 41 0f 7f 55 00");
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 41 0f 7f 55 00");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::store128_gpr64_xmm128(R13, XMM12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 7f 65 00");
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 45 0f 7f 65 00");
|
||||
}
|
||||
|
||||
TEST(CodeTester, sub_gpr64_imm8) {
|
||||
|
@ -114,36 +146,68 @@ TEST(CodeTester, add_gpr64_imm8) {
|
|||
TEST(CodeTester, xmm_load_128) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM3, RBX));
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM3, R14));
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM14, RBX));
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM13, R14));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(),
|
||||
"66 0f 6f 1b 66 41 0f 6f 1e 66 44 0f 6f 33 66 45 0f 6f 2e");
|
||||
"f3 0f 6f 1b f3 41 0f 6f 1e f3 44 0f 6f 33 f3 45 0f 6f 2e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM1, RSP));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 6f 0c 24"); // requires SIB byte.
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 6f 0c 24"); // requires SIB byte.
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM13, R12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 6f 2c 24"); // requires SIB byte and REX byte
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 45 0f 6f 2c 24"); // requires SIB byte and REX byte
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM1, RBP));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 6f 4d 00");
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 6f 4d 00");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM11, RBP));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 44 0f 6f 5d 00");
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 44 0f 6f 5d 00");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM2, R13));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 41 0f 6f 55 00");
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 41 0f 6f 55 00");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM12, R13));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 6f 65 00");
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 45 0f 6f 65 00");
|
||||
// tester.emit(IGen::load128_xmm128_gpr64(XMM3, RBX));
|
||||
// tester.emit(IGen::load128_xmm128_gpr64(XMM3, R14));
|
||||
// tester.emit(IGen::load128_xmm128_gpr64(XMM14, RBX));
|
||||
// tester.emit(IGen::load128_xmm128_gpr64(XMM13, R14));
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(),
|
||||
// "66 0f 6f 1b 66 41 0f 6f 1e 66 44 0f 6f 33 66 45 0f 6f 2e");
|
||||
//
|
||||
// tester.clear();
|
||||
// tester.emit(IGen::load128_xmm128_gpr64(XMM1, RSP));
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 6f 0c 24"); // requires SIB byte.
|
||||
//
|
||||
// tester.clear();
|
||||
// tester.emit(IGen::load128_xmm128_gpr64(XMM13, R12));
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 6f 2c 24"); // requires SIB byte and REX
|
||||
// byte
|
||||
//
|
||||
// tester.clear();
|
||||
// tester.emit(IGen::load128_xmm128_gpr64(XMM1, RBP));
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 6f 4d 00");
|
||||
//
|
||||
// tester.clear();
|
||||
// tester.emit(IGen::load128_xmm128_gpr64(XMM11, RBP));
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(), "66 44 0f 6f 5d 00");
|
||||
//
|
||||
// tester.clear();
|
||||
// tester.emit(IGen::load128_xmm128_gpr64(XMM2, R13));
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(), "66 41 0f 6f 55 00");
|
||||
//
|
||||
// tester.clear();
|
||||
// tester.emit(IGen::load128_xmm128_gpr64(XMM12, R13));
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 6f 65 00");
|
||||
}
|
||||
|
||||
TEST(CodeTester, push_pop_xmms) {
|
||||
|
|
|
@ -125,6 +125,10 @@ TEST(CompilerAndRuntime, BuildGameAndTest) {
|
|||
}
|
||||
|
||||
// todo, tests after loading the game.
|
||||
CompilerTestRunner runner;
|
||||
runner.c = &compiler;
|
||||
|
||||
runner.run_test("test-min-max.gc", {"10\n"});
|
||||
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
|
@ -254,6 +258,7 @@ TEST(CompilerAndRuntime, CompilerTests) {
|
|||
runner.run_test("test-protect.gc", {"33\n"});
|
||||
|
||||
runner.run_test("test-format-reg-order.gc", {"test 1 2 3 4 5 6\n0\n"});
|
||||
runner.run_test("test-quote-symbol.gc", {"banana\n0\n"});
|
||||
|
||||
// expected =
|
||||
// "test newline\nnewline\ntest tilde ~ \ntest A print boxed-string: \"boxed string!\"\ntest
|
||||
|
@ -270,6 +275,15 @@ TEST(CompilerAndRuntime, CompilerTests) {
|
|||
// // todo, finish format testing.
|
||||
// runner.run_test("test-format.gc", {expected}, expected.size());
|
||||
|
||||
runner.run_test("test-float-product.gc", {"120.0000\n0\n"});
|
||||
runner.run_test("test-float-in-symbol.gc", {"2345.6000\n0\n"});
|
||||
runner.run_test("test-function-return-constant-float.gc", {"3.14149\n0\n"});
|
||||
runner.run_test("test-float-function.gc", {"10.152\n0\n"});
|
||||
runner.run_test("test-float-pow-function.gc", {"256\n0\n"});
|
||||
runner.run_test("test-nested-float-functions.gc",
|
||||
{"i 1.4400 3.4000\nr 10.1523\ni 1.2000 10.1523\nr 17.5432\n17.543 10.152\n0\n"});
|
||||
runner.run_test("test-deref-simple.gc", {"structure\n0\n"});
|
||||
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
runner.print_summary();
|
||||
|
|
Loading…
Reference in a new issue