From e01e065170ef674d7e84a2de6a2ca05241b297a9 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Sun, 7 Feb 2021 18:21:00 -0500 Subject: [PATCH] [gcommon decomp] compiler and decompiler fixes (#239) * wip * decompile file-io * a * fix --- common/type_system/TypeFieldLookup.cpp | 21 +- common/type_system/TypeSystem.cpp | 8 +- decompiler/IR2/AtomicOp.h | 1 + decompiler/IR2/AtomicOpForm.cpp | 2 +- decompiler/IR2/AtomicOpTypeAnalysis.cpp | 10 +- decompiler/IR2/Form.cpp | 55 ++++- decompiler/IR2/Form.h | 34 +++ decompiler/IR2/FormExpressionAnalysis.cpp | 133 ++++++++++- decompiler/IR2/IR2_common.h | 9 +- decompiler/analysis/cfg_builder.cpp | 45 +++- decompiler/analysis/expression_build.cpp | 13 +- decompiler/config/all-types.gc | 220 ++++++++++-------- doc/goal_doc.md | 18 +- game/kernel/kmachine.cpp | 3 +- goal_src/engine/game/main-h.gc | 130 +++++++++++ goal_src/engine/load/file-io.gc | 183 +++++++++++++++ goal_src/goal-lib.gc | 4 +- goal_src/kernel-defs.gc | 5 +- goal_src/kernel/gcommon.gc | 66 +++++- goal_src/kernel/gstring.gc | 6 +- goalc/compiler/Compiler.h | 1 + goalc/compiler/IR.cpp | 20 ++ goalc/compiler/IR.h | 10 + goalc/compiler/compilation/Atoms.cpp | 2 + goalc/compiler/compilation/Block.cpp | 7 + goalc/emitter/IGen.h | 6 + .../decompiler/test_FormBeforeExpressions.cpp | 18 +- test/decompiler/test_FormExpressionBuild.cpp | 41 ++-- .../test_FormExpressionBuildLong.cpp | 12 +- .../source_templates/with_game/test-memset.gc | 4 +- .../with_game/test-vector-dot.gc | 2 + 31 files changed, 925 insertions(+), 164 deletions(-) diff --git a/common/type_system/TypeFieldLookup.cpp b/common/type_system/TypeFieldLookup.cpp index 813b906c3..cff9b76b3 100644 --- a/common/type_system/TypeFieldLookup.cpp +++ b/common/type_system/TypeFieldLookup.cpp @@ -13,12 +13,18 @@ bool debug_reverse_lookup = false; /*! * Is the actual dereference compatible with the expected? */ -bool deref_matches(const DerefInfo& expected, const DerefKind& actual, bool is_integer) { +bool deref_matches(const DerefInfo& expected, + const DerefKind& actual, + bool is_integer, + bool is_basic) { assert(expected.mem_deref); assert(expected.can_deref); if (actual.is_store || actual.size >= 8 || !is_integer) { // don't check sign extension return expected.load_size == actual.size; + } else if (is_basic) { + // this is kinda weird, but it seems like GOAL uses lw and lwu for loading basics. + return expected.load_size == actual.size; } else { return expected.load_size == actual.size && expected.sign_extend == actual.sign_extend; } @@ -105,6 +111,7 @@ bool TypeSystem::try_reverse_lookup_pointer(const FieldReverseLookupInput& input auto di = get_deref_info(input.base_type); bool is_integer = typecheck(TypeSpec("integer"), input.base_type.get_single_arg(), "", false, false); + bool is_basic = typecheck(TypeSpec("basic"), input.base_type.get_single_arg(), "", false, false); assert(di.mem_deref); // it's accessing a pointer. auto elt_type = di.result_type; if (input.stride) { @@ -123,7 +130,7 @@ bool TypeSystem::try_reverse_lookup_pointer(const FieldReverseLookupInput& input token.kind = FieldReverseLookupOutput::Token::Kind::VAR_IDX; path->push_back(token); if (input.deref.has_value()) { - if (deref_matches(di, input.deref.value(), is_integer)) { + if (deref_matches(di, input.deref.value(), is_integer, is_basic)) { // access element of array *addr_of = false; *result_type = elt_type; @@ -151,7 +158,7 @@ bool TypeSystem::try_reverse_lookup_pointer(const FieldReverseLookupInput& input token.kind = FieldReverseLookupOutput::Token::Kind::CONSTANT_IDX; token.idx = elt_idx; if (input.deref.has_value()) { - if (!deref_matches(di, input.deref.value(), is_integer)) { + if (!deref_matches(di, input.deref.value(), is_integer, is_basic)) { // this isn't the right type of dereference return false; } @@ -200,6 +207,7 @@ bool TypeSystem::try_reverse_lookup_array(const FieldReverseLookupInput& input, auto di = get_deref_info(array_data_type); bool is_integer = typecheck(TypeSpec("integer"), input.base_type.get_single_arg(), "", false, false); + bool is_basic = typecheck(TypeSpec("basic"), input.base_type.get_single_arg(), "", false, false); assert(di.mem_deref); // it's accessing a pointer. auto elt_type = di.result_type; if (input.stride) { @@ -218,7 +226,7 @@ bool TypeSystem::try_reverse_lookup_array(const FieldReverseLookupInput& input, token.kind = FieldReverseLookupOutput::Token::Kind::VAR_IDX; path->push_back(token); if (input.deref.has_value()) { - if (deref_matches(di, input.deref.value(), is_integer)) { + if (deref_matches(di, input.deref.value(), is_integer, is_basic)) { // access element of array *addr_of = false; *result_type = elt_type; @@ -248,7 +256,7 @@ bool TypeSystem::try_reverse_lookup_array(const FieldReverseLookupInput& input, // always put array index, even if it's zero. path->push_back(token); if (input.deref.has_value()) { - if (!deref_matches(di, input.deref.value(), is_integer)) { + if (!deref_matches(di, input.deref.value(), is_integer, is_basic)) { // this isn't the right type of dereference return false; } @@ -391,7 +399,8 @@ bool TypeSystem::try_reverse_lookup_other(const FieldReverseLookupInput& input, TypeSpec loc_type = make_pointer_typespec(field_deref.type); auto di = get_deref_info(loc_type); bool is_integer = typecheck(TypeSpec("integer"), field_deref.type, "", false, false); - if (!deref_matches(di, input.deref.value(), is_integer)) { + bool is_basic = typecheck(TypeSpec("basic"), field_deref.type, "", false, false); + if (!deref_matches(di, input.deref.value(), is_integer, is_basic)) { continue; // try another field! } // it's a match, just access the field like normal! diff --git a/common/type_system/TypeSystem.cpp b/common/type_system/TypeSystem.cpp index b64661264..3f994807c 100644 --- a/common/type_system/TypeSystem.cpp +++ b/common/type_system/TypeSystem.cpp @@ -820,7 +820,13 @@ void TypeSystem::add_builtin_types() { add_field_to_type(connectable_type, "prev1", make_typespec("connectable")); // todo - (void)file_stream_type; + builtin_structure_inherit(file_stream_type); + add_field_to_type(file_stream_type, "flags", make_typespec("uint32")); + add_field_to_type(file_stream_type, "mode", make_typespec("basic")); + add_field_to_type(file_stream_type, "name", make_typespec("string")); + add_field_to_type(file_stream_type, "file", make_typespec("uint32")); + add_method(file_stream_type, "new", + make_function_typespec({"symbol", "type", "string", "basic"}, "_type_")); } /*! diff --git a/decompiler/IR2/AtomicOp.h b/decompiler/IR2/AtomicOp.h index 4f2da9199..73aaf265f 100644 --- a/decompiler/IR2/AtomicOp.h +++ b/decompiler/IR2/AtomicOp.h @@ -291,6 +291,7 @@ class AsmOp : public AtomicOp { const Env& env, DecompilerTypeSystem& dts) override; void collect_vars(VariableSet& vars) const override; + const Instruction& instruction() const { return m_instr; } private: Instruction m_instr; diff --git a/decompiler/IR2/AtomicOpForm.cpp b/decompiler/IR2/AtomicOpForm.cpp index 34c952416..dddebc031 100644 --- a/decompiler/IR2/AtomicOpForm.cpp +++ b/decompiler/IR2/AtomicOpForm.cpp @@ -79,7 +79,7 @@ FormElement* SetVarOp::get_as_form(FormPool& pool, const Env& env) const { } FormElement* AsmOp::get_as_form(FormPool& pool, const Env&) const { - return pool.alloc_element(this); + return pool.alloc_element(this); } FormElement* SetVarConditionOp::get_as_form(FormPool& pool, const Env& env) const { diff --git a/decompiler/IR2/AtomicOpTypeAnalysis.cpp b/decompiler/IR2/AtomicOpTypeAnalysis.cpp index 8a218fbd2..c9dbf5abe 100644 --- a/decompiler/IR2/AtomicOpTypeAnalysis.cpp +++ b/decompiler/IR2/AtomicOpTypeAnalysis.cpp @@ -112,7 +112,9 @@ TP_Type SimpleAtom::get_type(const TypeState& input, } else if ((label.offset & 7) == PAIR_OFFSET) { return TP_Type::make_from_ts(TypeSpec("pair")); } - throw std::runtime_error("IR_StaticAddress couldn't figure out the type: " + label.name); + // throw std::runtime_error("IR_StaticAddress couldn't figure out the type: " + label.name); + lg::error("IR_StaticAddress doesn't know the type of {}", label.name); + return TP_Type::make_from_ts("object"); } case Kind::INVALID: default: @@ -136,6 +138,12 @@ TP_Type SimpleExpression::get_type(const TypeState& input, } case Kind::FPR_TO_GPR: case Kind::DIV_S: + case Kind::SUB_S: + case Kind::MUL_S: + case Kind::ADD_S: + case Kind::SQRT_S: + case Kind::ABS_S: + case Kind::NEG_S: return TP_Type::make_from_ts("float"); case Kind::ADD: case Kind::SUB: diff --git a/decompiler/IR2/Form.cpp b/decompiler/IR2/Form.cpp index da3ba4afc..11d3f5a98 100644 --- a/decompiler/IR2/Form.cpp +++ b/decompiler/IR2/Form.cpp @@ -349,6 +349,36 @@ void AtomicOpElement::get_modified_regs(RegSet& regs) const { } } +///////////////////////////// +// AsmOpElement +///////////////////////////// + +AsmOpElement::AsmOpElement(const AsmOp* op) : m_op(op) {} + +goos::Object AsmOpElement::to_form(const Env& env) const { + return m_op->to_form(env.file->labels, env); +} + +void AsmOpElement::apply(const std::function& f) { + f(this); +} + +void AsmOpElement::apply_form(const std::function&) {} + +void AsmOpElement::collect_vars(VariableSet& vars) const { + m_op->collect_vars(vars); +} + +void AsmOpElement::get_modified_regs(RegSet& regs) const { + for (auto r : m_op->write_regs()) { + regs.insert(r); + } + + for (auto r : m_op->clobber_regs()) { + regs.insert(r); + } +} + ///////////////////////////// // ConditionElement ///////////////////////////// @@ -474,29 +504,38 @@ goos::Object ReturnElement::to_form(const Env& env) const { std::vector forms; forms.push_back(pretty_print::to_symbol("return")); forms.push_back(return_code->to_form(env)); - forms.push_back(dead_code->to_form(env)); + if (dead_code) { + forms.push_back(dead_code->to_form(env)); + } return pretty_print::build_list(forms); } void ReturnElement::apply(const std::function& f) { f(this); return_code->apply(f); - dead_code->apply(f); + if (dead_code) { + dead_code->apply(f); + } } void ReturnElement::apply_form(const std::function& f) { return_code->apply_form(f); - dead_code->apply_form(f); + if (dead_code) { + dead_code->apply_form(f); + } } void ReturnElement::collect_vars(VariableSet& vars) const { return_code->collect_vars(vars); - dead_code->collect_vars(vars); + if (dead_code) { + dead_code->collect_vars(vars); + } } void ReturnElement::get_modified_regs(RegSet& regs) const { - for (auto x : {return_code, dead_code}) { - x->get_modified_regs(regs); + return_code->get_modified_regs(regs); + if (dead_code) { + dead_code->get_modified_regs(regs); } } @@ -874,7 +913,7 @@ TypeOfElement::TypeOfElement(Form* _value, std::optional _clobber) } goos::Object TypeOfElement::to_form(const Env& env) const { - return pretty_print::build_list("type-of", value->to_form(env)); + return pretty_print::build_list("rtype-of", value->to_form(env)); } void TypeOfElement::apply(const std::function& f) { @@ -1048,6 +1087,8 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) { return "-"; case FixedOperatorKind::MULTIPLICATION: return "*"; + case FixedOperatorKind::SQRT: + return "sqrt"; case FixedOperatorKind::ARITH_SHIFT: return "ash"; case FixedOperatorKind::MOD: diff --git a/decompiler/IR2/Form.h b/decompiler/IR2/Form.h index bf13b543c..43759bfea 100644 --- a/decompiler/IR2/Form.h +++ b/decompiler/IR2/Form.h @@ -84,6 +84,18 @@ class SimpleExpressionElement : public FormElement { FormStack& stack, std::vector* result, bool allow_side_effects); + void update_from_stack_float_2(const Env& env, + FixedOperatorKind kind, + FormPool& pool, + FormStack& stack, + std::vector* result, + bool allow_side_effects); + void update_from_stack_float_1(const Env& env, + FixedOperatorKind kind, + FormPool& pool, + FormStack& stack, + std::vector* result, + bool allow_side_effects); void update_from_stack_add_i(const Env& env, FormPool& pool, FormStack& stack, @@ -259,6 +271,24 @@ class AtomicOpElement : public FormElement { const AtomicOp* m_op; }; +/*! + * A wrapper around a single AsmOp + */ +class AsmOpElement : public FormElement { + public: + explicit AsmOpElement(const AsmOp* op); + goos::Object to_form(const Env& env) const override; + void apply(const std::function& f) override; + void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; + void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; + void get_modified_regs(RegSet& regs) const override; + const AsmOp* op() const { return m_op; } + + private: + const AsmOp* m_op; +}; + /*! * A "condition" like (< a b). This can be used as a boolean value directly: (set! a (< b c)) * or it can be used as a branch condition: (if (< a b)). @@ -344,6 +374,8 @@ class BranchElement : public FormElement { /*! * Represents a (return-from #f x) form, which immediately returns from the function. * This always has some "dead code" after it that can't be reached, which is the "dead_code". + * We store the dead code because it may contain an unreachable jump to the next place that can + * be stripped away in later analysis passes. Or they may have written code after the return. */ class ReturnElement : public FormElement { public: @@ -444,6 +476,7 @@ class EmptyElement : public FormElement { void apply_form(const std::function& f) override; void collect_vars(VariableSet& vars) const override; void get_modified_regs(RegSet& regs) const override; + void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; }; /*! @@ -646,6 +679,7 @@ class ConditionalMoveFalseElement : public FormElement { void apply_form(const std::function& f) override; void collect_vars(VariableSet& vars) const override; void get_modified_regs(RegSet& regs) const override; + void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; }; std::string fixed_operator_to_string(FixedOperatorKind kind); diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index 07cff720c..6c3cbadf7 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -2,6 +2,7 @@ #include "FormStack.h" #include "GenericElementMatcher.h" #include "common/goos/PrettyPrinter.h" +#include "decompiler/util/DecompilerTypeSystem.h" /* * TODO @@ -335,6 +336,42 @@ void SimpleExpressionElement::update_from_stack_div_s(const Env& env, } } +void SimpleExpressionElement::update_from_stack_float_2(const Env& env, + FixedOperatorKind kind, + FormPool& pool, + FormStack& stack, + std::vector* result, + bool allow_side_effects) { + if (is_float_type(env, m_my_idx, m_expr.get_arg(0).var()) && + is_float_type(env, m_my_idx, m_expr.get_arg(1).var())) { + // todo - check the order here + + auto args = pop_to_forms({m_expr.get_arg(0).var(), m_expr.get_arg(1).var()}, env, pool, stack, + allow_side_effects); + auto new_form = pool.alloc_element(GenericOperator::make_fixed(kind), + args.at(0), args.at(1)); + result->push_back(new_form); + } else { + throw std::runtime_error(fmt::format("Floating point math attempted on invalid types.")); + } +} + +void SimpleExpressionElement::update_from_stack_float_1(const Env& env, + FixedOperatorKind kind, + FormPool& pool, + FormStack& stack, + std::vector* result, + bool allow_side_effects) { + if (is_float_type(env, m_my_idx, m_expr.get_arg(0).var())) { + auto args = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects); + auto new_form = + pool.alloc_element(GenericOperator::make_fixed(kind), args.at(0)); + result->push_back(new_form); + } else { + throw std::runtime_error(fmt::format("Floating point division attempted on invalid types.")); + } +} + void SimpleExpressionElement::update_from_stack_add_i(const Env& env, FormPool& pool, FormStack& stack, @@ -546,6 +583,30 @@ void SimpleExpressionElement::update_from_stack(const Env& env, case SimpleExpression::Kind::DIV_S: update_from_stack_div_s(env, pool, stack, result, allow_side_effects); break; + case SimpleExpression::Kind::SUB_S: + update_from_stack_float_2(env, FixedOperatorKind::SUBTRACTION, pool, stack, result, + allow_side_effects); + break; + case SimpleExpression::Kind::MUL_S: + update_from_stack_float_2(env, FixedOperatorKind::MULTIPLICATION, pool, stack, result, + allow_side_effects); + break; + case SimpleExpression::Kind::ADD_S: + update_from_stack_float_2(env, FixedOperatorKind::ADDITION, pool, stack, result, + allow_side_effects); + break; + case SimpleExpression::Kind::SQRT_S: + update_from_stack_float_1(env, FixedOperatorKind::SQRT, pool, stack, result, + allow_side_effects); + break; + case SimpleExpression::Kind::ABS_S: + update_from_stack_float_1(env, FixedOperatorKind::ABS, pool, stack, result, + allow_side_effects); + break; + case SimpleExpression::Kind::NEG_S: + update_from_stack_float_1(env, FixedOperatorKind::SUBTRACTION, pool, stack, result, + allow_side_effects); + break; case SimpleExpression::Kind::ADD: update_from_stack_add_i(env, pool, stack, result, allow_side_effects); break; @@ -702,7 +763,29 @@ void FunctionCallElement::update_from_stack(const Env& env, } auto unstacked = pop_to_forms(all_pop_vars, env, pool, stack, allow_side_effects); std::vector arg_forms; - arg_forms.insert(arg_forms.begin(), unstacked.begin() + 1, unstacked.end()); + TypeSpec function_type; + if (env.has_type_analysis()) { + function_type = + env.get_types_before_op(all_pop_vars.at(0).idx()).get(all_pop_vars.at(0).reg()).typespec(); + } + + for (size_t arg_id = 0; arg_id < nargs; arg_id++) { + auto val = unstacked.at(arg_id + 1); // first is the function itself. + auto& var = all_pop_vars.at(arg_id + 1); + if (env.has_type_analysis() && function_type.arg_count() == nargs + 1) { + auto actual_arg_type = env.get_types_before_op(var.idx()).get(var.reg()).typespec(); + auto desired_arg_type = function_type.get_arg(arg_id); + if (!env.dts->ts.typecheck(desired_arg_type, actual_arg_type, "", false, false)) { + arg_forms.push_back( + pool.alloc_single_element_form(nullptr, desired_arg_type, val)); + } else { + arg_forms.push_back(val); + } + } else { + arg_forms.push_back(val); + } + } + auto new_form = pool.alloc_element( GenericOperator::make_function(unstacked.at(0)), arg_forms); @@ -1161,6 +1244,36 @@ FormElement* ConditionElement::make_generic(const Env&, casted); } + case IR2_Condition::Kind::FLOAT_NOT_EQUAL: { + auto casted = make_cast(source_forms, types, TypeSpec("float"), pool); + return pool.alloc_element(GenericOperator::make_fixed(FixedOperatorKind::NEQ), + casted); + } + + case IR2_Condition::Kind::FLOAT_EQUAL: { + auto casted = make_cast(source_forms, types, TypeSpec("float"), pool); + return pool.alloc_element(GenericOperator::make_fixed(FixedOperatorKind::EQ), + casted); + } + + case IR2_Condition::Kind::FLOAT_LEQ: { + auto casted = make_cast(source_forms, types, TypeSpec("float"), pool); + return pool.alloc_element(GenericOperator::make_fixed(FixedOperatorKind::LEQ), + casted); + } + + case IR2_Condition::Kind::FLOAT_LESS_THAN: { + auto casted = make_cast(source_forms, types, TypeSpec("float"), pool); + return pool.alloc_element(GenericOperator::make_fixed(FixedOperatorKind::LT), + casted); + } + + case IR2_Condition::Kind::FLOAT_GEQ: { + auto casted = make_cast(source_forms, types, TypeSpec("float"), pool); + return pool.alloc_element(GenericOperator::make_fixed(FixedOperatorKind::GEQ), + casted); + } + default: throw std::runtime_error("ConditionElement::make_generic NYI for kind " + get_condition_kind_name(m_kind)); @@ -1254,6 +1367,10 @@ void AtomicOpElement::push_to_stack(const Env& env, FormPool&, FormStack& stack) throw std::runtime_error("Can't push atomic op to stack: " + m_op->to_string(env)); } +void AsmOpElement::push_to_stack(const Env&, FormPool&, FormStack& stack) { + stack.push_form_element(this, true); +} + void GenericElement::update_from_stack(const Env& env, FormPool& pool, FormStack& stack, @@ -1305,7 +1422,7 @@ void DynamicMethodAccess::update_from_stack(const Env& env, auto deref = pool.alloc_element( var_to_form(base.value(), pool), false, - std::vector{DerefToken::make_field_name("methods"), + std::vector{DerefToken::make_field_name("method-table"), DerefToken::make_int_expr(var_to_form(idx.value(), pool))}); result->push_back(deref); } @@ -1450,4 +1567,16 @@ void TypeOfElement::update_from_stack(const Env& env, result->push_back(this); } +//////////////////////// +// EmptyElement +//////////////////////// + +void EmptyElement::push_to_stack(const Env&, FormPool&, FormStack& stack) { + stack.push_form_element(this, true); +} + +void ConditionalMoveFalseElement::push_to_stack(const Env&, FormPool&, FormStack& stack) { + stack.push_form_element(this, true); +} + } // namespace decompiler diff --git a/decompiler/IR2/IR2_common.h b/decompiler/IR2/IR2_common.h index ccf6df3e8..ef8245315 100644 --- a/decompiler/IR2/IR2_common.h +++ b/decompiler/IR2/IR2_common.h @@ -95,6 +95,7 @@ enum class FixedOperatorKind { ADDITION, SUBTRACTION, MULTIPLICATION, + SQRT, ARITH_SHIFT, MOD, ABS, @@ -125,9 +126,15 @@ enum class FixedOperatorKind { struct VariableNames { struct VarInfo { VarInfo() = default; - std::string name() const { return fmt::format("{}-{}", reg_id.reg.to_charp(), reg_id.id); } + std::string name() const { + if (!override_name.empty()) { + return override_name; + } + return fmt::format("{}-{}", reg_id.reg.to_charp(), reg_id.id); + } TP_Type type; RegId reg_id; + std::string override_name; bool initialized = false; }; diff --git a/decompiler/analysis/cfg_builder.cpp b/decompiler/analysis/cfg_builder.cpp index 54ddeffbc..2af8851ba 100644 --- a/decompiler/analysis/cfg_builder.cpp +++ b/decompiler/analysis/cfg_builder.cpp @@ -150,6 +150,30 @@ void clean_up_return(FormPool& pool, ReturnElement* ir) { } } +void clean_up_return_final(const Function& f, ReturnElement* ir) { + SetVarElement* dead = dynamic_cast(ir->dead_code->try_as_single_element()); + if (!dead) { + dead = dynamic_cast(ir->dead_code->elts().front()); + for (int i = 1; i < ir->dead_code->size(); i++) { + if (!dynamic_cast(ir->dead_code->at(i))) { + dead = nullptr; + break; + } + } + } + + if (!dead) { + lg::error("failed to recognize dead code after return, got {}", + ir->dead_code->to_string(f.ir2.env)); + } + assert(dead); + auto src = dynamic_cast(dead->src()->try_as_single_element()); + assert(src); + assert(src->expr().is_identity() && src->expr().get_arg(0).is_int() && + src->expr().get_arg(0).get_int() == 0); + ir->dead_code = nullptr; +} + /*! * Remove the branch in a break (really return-from nonfunction scope) */ @@ -1355,8 +1379,22 @@ Form* cfg_to_ir(FormPool& pool, Function& f, const CfgVtx* vtx) { return result; } else if (dynamic_cast(vtx)) { auto* cvtx = dynamic_cast(vtx); + + // dead code should always be (set! var 0) + auto dead_code = cfg_to_ir(pool, f, cvtx->unreachable_block); + // auto dead = dynamic_cast(dead_code->try_as_single_element()); + // if (!dead) { + // lg::error("failed to recognize dead code after return, got {}", + // dead_code->to_string(f.ir2.env)); + // } + // assert(dead); + // auto src = dynamic_cast(dead->src()->try_as_single_element()); + // assert(src); + // assert(src->expr().is_identity() && src->expr().get_arg(0).is_int() && + // src->expr().get_arg(0).get_int() == 0); + auto result = pool.alloc_single_element_form( - nullptr, cfg_to_ir(pool, f, cvtx->body), cfg_to_ir(pool, f, cvtx->unreachable_block)); + nullptr, cfg_to_ir(pool, f, cvtx->body), dead_code); clean_up_return(pool, dynamic_cast(result->try_as_single_element())); return result; } else if (dynamic_cast(vtx)) { @@ -1433,6 +1471,11 @@ void build_initial_forms(Function& function) { if (as_cne) { clean_up_cond_no_else_final(function, as_cne); } + + auto as_return = dynamic_cast(form); + if (as_return) { + clean_up_return_final(function, as_return); + } }); function.ir2.top_form = result; diff --git a/decompiler/analysis/expression_build.cpp b/decompiler/analysis/expression_build.cpp index 400d8d386..4507791a6 100644 --- a/decompiler/analysis/expression_build.cpp +++ b/decompiler/analysis/expression_build.cpp @@ -8,7 +8,7 @@ namespace decompiler { // TODO - remove all these and put them in the analysis methods instead. -void clean_up_ifs(Form* top_level_form) { +void clean_up_ifs(Form* top_level_form, const Env&) { bool changed = true; while (changed) { changed = false; @@ -30,6 +30,9 @@ void clean_up_ifs(Form* top_level_form) { assert(me != parent_vector.end()); // now insert the fake condition + for (auto& x : top_condition->elts()) { + x->parent_form = elt->parent_form; + } parent_vector.insert(me, top_condition->elts().begin(), top_condition->elts().end()); top_condition->elts() = {real_condition}; changed = true; @@ -54,6 +57,9 @@ void clean_up_ifs(Form* top_level_form) { assert(me != parent_vector.end()); // now insert the fake condition + for (auto& x : top_condition->elts()) { + x->parent_form = elt->parent_form; + } parent_vector.insert(me, top_condition->elts().begin(), top_condition->elts().end()); top_condition->elts() = {real_condition}; changed = true; @@ -81,6 +87,9 @@ void clean_up_ifs(Form* top_level_form) { assert(me != parent_vector.end()); // now insert the fake condition + for (auto& x : top_condition->elts()) { + x->parent_form = elt->parent_form; + } parent_vector.insert(me, top_condition->elts().begin(), top_condition->elts().end()); top_condition->elts() = {real_condition}; changed = true; @@ -153,7 +162,7 @@ bool convert_to_expressions(Form* top_level_form, // fmt::print("Before clean:\n{}\n", // pretty_print::to_string(top_level_form->to_form(f.ir2.env))); // fix up stuff - clean_up_ifs(top_level_form); + clean_up_ifs(top_level_form, f.ir2.env); } catch (std::exception& e) { std::string warning = fmt::format("Expression building failed: {}", e.what()); diff --git a/decompiler/config/all-types.gc b/decompiler/config/all-types.gc index cd95ff458..4e7449be5 100644 --- a/decompiler/config/all-types.gc +++ b/decompiler/config/all-types.gc @@ -56,6 +56,10 @@ (define-extern *enable-method-set* int) (define-extern install-debug-handler (function int object symbol)) +(define-extern file-stream-open (function file-stream basic basic file-stream)) +(define-extern file-stream-length (function file-stream int)) +(define-extern file-stream-read (function file-stream pointer int int)) + ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; GCOMMON ;;;;;;;;;;;;;;;;;;; @@ -740,6 +744,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; + (deftype bit-array (basic) ((length int32 :offset-assert 4) (allocated-length int32 :offset-assert 8) @@ -914,7 +919,8 @@ ) (deftype vector (structure) - ((data float 4 :offset-assert 0) + ( + (data float 4 :offset-assert 0) (x float :offset 0) (y float :offset 4) (z float :offset 8) @@ -1793,6 +1799,7 @@ ; ;; likely a bitfield type ; ) + ; ;; dma-h (deftype dma-bucket (structure) ((tag uint64 :offset-assert 0) @@ -2219,24 +2226,109 @@ (define-extern *display* display) -;;;;;;;;;;;;;;; -;; file-io -;;;;;;;;;;;;;;; +;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; VECTOR ;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; -;; todo -; (deftype file-stream (basic) -; ((flags uint32 :offset-assert 4) -; (mode basic :offset-assert 8) -; (name basic :offset-assert 12) -; (file uint32 :offset-assert 16) -; ) -; :method-count-assert 9 -; :size-assert #x14 -; :flag-assert #x900000014 -; ) +(define-extern sin (function float float)) +(define-extern cos (function float float)) +(define-extern atan (function float float float)) + +(define-extern vector-cross! (function vector vector vector vector)) +(define-extern vector+float! (function vector vector float vector)) +(define-extern vector*! (function vector vector vector vector)) +(define-extern vector+*! (function vector vector vector float vector)) +(define-extern vector-*! (function vector vector vector float vector)) +(define-extern vector/! (function vector vector vector vector)) +(define-extern vector-float*! (function vector vector float vector)) +(define-extern vector-average! (function vector vector vector vector)) +(define-extern vector+float*! (function vector vector vector float vector)) +(define-extern vector--float*! (function vector vector vector float vector)) +(define-extern vector-float/! (function vector vector float vector)) +(define-extern vector-negate! (function vector vector vector)) +(define-extern vector-negate-in-place! (function vector vector)) +(define-extern vector= (function vector vector symbol)) +(define-extern vector-delta (function vector vector float)) +(define-extern vector-seek! (function vector vector float vector)) +(define-extern vector-seek-2d-xz-smooth! (function vector vector float float vector)) +(define-extern vector-seek-2d-yz-smooth! (function vector vector float float vector)) +(define-extern vector-seek-3d-smooth! (function vector vector float float vector)) +(define-extern seek-with-smooth (function float float float float float float)) +(define-extern vector-identity! (function vector vector)) +(define-extern vector-seconds (function vector vector vector)) +(define-extern vector-seconds! (function vector vector)) +(define-extern vector-v! (function vector vector)) +(define-extern vector-v+! (function vector vector vector vector)) +(define-extern vector-v*float+! (function vector vector vector float vector)) +(define-extern vector-v++! (function vector vector vector)) +(define-extern vector-v*float! (function vector float float vector)) +(define-extern vector-v*float++! (function vector vector float vector)) +(define-extern vector-to-ups! (function vector vector vector)) +(define-extern vector-from-ups! (function vector vector vector)) +(define-extern vector-length (function vector float)) +(define-extern vector-length-squared (function vector float)) +(define-extern vector-xz-length-squared (function vector float)) +(define-extern vector-xz-length (function vector float)) +(define-extern vector-vector-distance (function vector vector float)) +(define-extern vector-vector-distance-squared (function vector vector float)) +(define-extern vector-vector-xz-distance (function vector vector float)) +(define-extern vector-vector-xz-distance-squared (function vector vector float)) +(define-extern vector-normalize! (function vector vector vector)) +(define-extern vector-normalize-ret-len! (function vector float float)) +(define-extern vector-normalize-copy! (function vector vector float vector)) +(define-extern vector-xz-normalize! (function vector float vector)) +(define-extern vector-length-max! (function vector float vector)) +(define-extern vector-xz-length-max! (function vector float vector)) +(define-extern vector-rotate-around-y! (function vector vector float vector)) +(define-extern rotate-y<-vector+vector (function vector vector float)) +(define-extern vector-cvt.w.s! (function vector vector vector)) +(define-extern vector-cvt.s.w! (function vector vector vector)) +(define-extern rot-zxy-from-vector! (function vector vector vector)) +(define-extern rot-zyx-from-vector! (function vector vector vector)) +(define-extern vector-lerp! (function vector vector vector)) +(define-extern vector-lerp-clamp! (function vector vector float float vector)) +(define-extern vector4-lerp! (function vector vector vector)) +(define-extern vector4-lerp-clamp! (function vector vector float float vector)) +(define-extern vector-degi (function vector vector vector)) +(define-extern vector-degf (function vector vector vector)) +(define-extern vector-degmod (function vector vector vector)) +(define-extern vector-deg-diff (function vector vector vector vector)) +(define-extern vector-deg-lerp-clamp! function) ;; todo +(define-extern vector3s-copy! (function vector vector vector)) +(define-extern vector3s+! (function vector vector vector vector)) +(define-extern vector3s*float! (function vector vector float vector)) +(define-extern vector3s-! (function vector vector vector vector)) +(define-extern spheres-overlap? (function vector vector symbol)) +(define-extern sphere<-vector! (function sphere vector sphere)) +(define-extern sphere<-vector+r! (function sphere vector float sphere)) +(define-extern rand-vu-sphere-point! function) ;; todo + +;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; FILE-IO ;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; + +(deftype file-stream (basic) + ((flags uint32 :offset-assert 4) + (mode basic :offset-assert 8) + (name string :offset-assert 12) + (file uint32 :offset-assert 16) + ) + (:methods + (new ((allocation symbol) (type-to-make type) (name string) (mode basic)) _type_) + ) + :method-count-assert 9 + :size-assert #x14 + :flag-assert #x900000014 + ) + +(define-extern file-stream-read-string (function file-stream string string)) (deftype file-info (basic) - ((file-type basic :offset-assert 4) + ((file-type symbol :offset-assert 4) (file-name basic :offset-assert 8) (major-version uint32 :offset-assert 12) (minor-version uint32 :offset-assert 16) @@ -2249,6 +2341,12 @@ :flag-assert #x900000020 ) +(define-extern *file-temp-string* string) + +(define-extern make-file-name (function int string int string)) +(define-extern make-vfile-name (function int string string)) +(define-extern file-info-correct-version? (function file-info int int symbol)) + ;;;;;;;;;;;;;;; ;; loader-h ;;;;;;;;;;;;;;; @@ -31396,14 +31494,11 @@ (define-extern matrix-translate! function) (define-extern matrix-4x4-inverse! function) (define-extern matrix-! function) -(define-extern atan function) (define-extern vector-sincos! function) -(define-extern cos function) -(define-extern sin function) (define-extern trs-matrix-calc! function) (define-extern transform-matrix-parent-calc! function) (define-extern transform-matrix-calc! function) -(define-extern vector-identity! function) + (define-extern quaternion-zero! function) (define-extern quaternion-set! function) (define-extern matrix->quaternion function) @@ -31457,15 +31552,15 @@ (define-extern quaternion-i! function) (define-extern sincos-rad! function) (define-extern vector-sincos-rad! function) -(define-extern vector-xz-normalize! function) + (define-extern atan-series-rad function) (define-extern atan2-rad function) (define-extern quaternion-from-two-vectors-max-angle! function) -(define-extern vector-length function) + (define-extern vector-rad<-vector-deg/2! function) (define-extern vector-sin-rad! function) (define-extern acos-rad function) -(define-extern vector-xz-length function) + (define-extern acos function) (define-extern quat->eul function) (define-extern set-eul! function) @@ -31514,15 +31609,7 @@ (define-extern forward-down-nopitch->inv-matrix function) (define-extern vector-reflect-flat-above! function) (define-extern vector-circle-tangent-new function) -(define-extern vector-vector-distance-squared function) -(define-extern vector-cross! function) -(define-extern vector+float*! function) -(define-extern vector-normalize! function) -(define-extern vector-negate! function) -(define-extern vector-vector-distance function) -(define-extern vector-normalize-ret-len! function) -(define-extern vector-normalize-copy! function) -(define-extern vector-float*! function) + (define-extern coserp function) (define-extern sinerp-clamp function) ;;(define-extern exp-slead object) ;; unknown type @@ -31773,72 +31860,9 @@ ;;(define-extern draw-env object) ;; unknown type ;;(define-extern *pre-draw-hook* object) ;; unknown type (define-extern set-display function) -(define-extern vector4-lerp! function) -(define-extern rotate-y<-vector+vector function) -(define-extern rot-zxy-from-vector! function) -(define-extern vector3s-copy! function) -(define-extern vector-seek-2d-yz-smooth! function) -(define-extern vector-from-ups! function) -(define-extern vector-seek-2d-xz-smooth! function) -(define-extern vector/! function) -(define-extern rand-vu-sphere-point! function) -(define-extern vector-vector-xz-distance function) -(define-extern vector-v*float+! function) -(define-extern vector-length-max! function) -(define-extern vector*! function) -(define-extern vector+*! function) -(define-extern vector-deg-lerp-clamp! function) -(define-extern vector-negate-in-place! function) -(define-extern vector--float*! function) -(define-extern rot-zyx-from-vector! function) -(define-extern vector4-lerp-clamp! function) -(define-extern vector-seconds! function) -(define-extern vector-degmod function) -(define-extern vector-v*float++! function) -(define-extern vector-degi function) -(define-extern sphere<-vector+r! function) -(define-extern vector-xz-length-max! function) -(define-extern vector3s-! function) -(define-extern vector3s+! function) -(define-extern vector-average! function) -(define-extern vector-cvt.w.s! function) -(define-extern vector-v! function) -(define-extern vector-degf function) -(define-extern vector-vector-xz-distance-squared function) -(define-extern vector-xz-length-squared function) -(define-extern vector-lerp-clamp! function) -(define-extern vector-cvt.s.w! function) -(define-extern vector-to-ups! function) -(define-extern vector-seek! function) -(define-extern vector-seconds function) -(define-extern vector-deg-diff function) -(define-extern vector-delta function) -(define-extern vector+float! function) -(define-extern vector-v+! function) -(define-extern vector3s*float! function) -(define-extern spheres-overlap? function) -(define-extern vector-length-squared function) -(define-extern vector-lerp! function) -(define-extern vector-rotate-around-y! function) -(define-extern vector-*! function) -(define-extern seek-with-smooth function) -(define-extern vector-v*float! function) -(define-extern vector-v++! function) -(define-extern vector-seek-3d-smooth! function) -(define-extern sphere<-vector! function) -(define-extern vector-float/! function) -(define-extern vector= function) + ;;(define-extern *display* object) ;; unknown type -(define-extern make-vfile-name function) -;;(define-extern file-stream object) ;; unknown type -(define-extern file-info type) -(define-extern make-file-name function) -(define-extern file-info-correct-version? function) -;;(define-extern *file-temp-string* object) ;; unknown type -(define-extern file-stream-read-string function) -;;(define-extern file-stream-length object) ;; unknown type -;;(define-extern file-stream-read object) ;; unknown type -;;(define-extern file-stream-open object) ;; unknown type + ;;(define-extern load-dir object) ;; unknown type ;;(define-extern external-art-buffer object) ;; unknown type ;;(define-extern load-dir-art-group object) ;; unknown type @@ -32085,7 +32109,7 @@ ;;(define-extern *display-strip-lines* object) ;; unknown type ;;(define-extern *display-process-anim* object) ;; unknown type ;;(define-extern *display-actor-marks* object) ;; unknown type -(define-extern *menu-hook* function) +(define-extern *menu-hook* (function none)) ;;(define-extern *display-load-boundaries* object) ;; unknown type ;;(define-extern *display-water-marks* object) ;; unknown type ;;(define-extern *artist-flip-visible* object) ;; unknown type diff --git a/doc/goal_doc.md b/doc/goal_doc.md index dd9561836..008be0968 100644 --- a/doc/goal_doc.md +++ b/doc/goal_doc.md @@ -574,6 +574,13 @@ if `x` is a match, returns `x` from the function (not shown) immediately. The `return-from` form is very rarely used to return from a block, but sometimes used to return from a function. +## `return` +Exit a function early. +```lisp +(return value) +``` +Has the same behavior as `(return-from #f value)`. + ## `label` Create a named label for `goto` or `goto-when`. ```lisp @@ -1290,7 +1297,16 @@ This code generation is identical to using a `(set! dst src)` form. (.nop.vf) ``` -Inserts a `FNOP` assembly instruction, which is fundamentally the same as a `NOP`. +Inserts a `FNOP` assembly instruction, which is fundamentally the same as a `NOP`. It is a 2-byte instruction. + +## `.nop` or `(nop!)` +```lisp +(.nop) +;; or +(nop!) +``` + +Inserts a single-byte `nop`. ## `.lvf` ```lisp diff --git a/game/kernel/kmachine.cpp b/game/kernel/kmachine.cpp index 5482db03f..29d209076 100644 --- a/game/kernel/kmachine.cpp +++ b/game/kernel/kmachine.cpp @@ -425,7 +425,8 @@ u64 kopen(u64 fs, u64 name, u64 mode) { file_stream->flags = 0; printf("****** CALL TO kopen() ******\n"); char buffer[128]; - sprintf(buffer, "host:%s", Ptr(name)->data()); + // sprintf(buffer, "host:%s", Ptr(name)->data()); + sprintf(buffer, "%s", Ptr(name)->data()); if (!strcmp(info(Ptr(mode))->str->data(), "read")) { file_stream->file = sceOpen(buffer, SCE_RDONLY); } else { diff --git a/goal_src/engine/game/main-h.gc b/goal_src/engine/game/main-h.gc index 1b0447e06..09426d749 100644 --- a/goal_src/engine/game/main-h.gc +++ b/goal_src/engine/game/main-h.gc @@ -5,3 +5,133 @@ ;; name in dgo: main-h ;; dgos: GAME, ENGINE +(define *stats-poly* '#f) +(define *stats-memory* '#f) +(define *stats-memory-short* '#f) +(define *stats-collide* '#f) +(define *stats-bsp* '#f) +(define *stats-buffer* '#f) +(define *stats-target* '#f) +(define *stats-dma-test* '#f) +(define *artist-all-visible* '#f) +(define *artist-flip-visible* '#f) +(define *artist-fix-visible* '#f) +(define *artist-fix-frustum* '#f) +(define *artist-error-spheres* '#f) +(define *artist-use-menu-subdiv* '#f) +(define *display-profile* '#t) +(define *display-sidekick-stats* '#f) +(define *display-quad-stats* '#f) +(define *display-tri-stats* '#f) +(define *display-perf-stats* '#f) +(define *display-ground-stats* '#f) +(define *display-collision-marks* '#f) +(define *display-collide-cache* '#f) +(define *display-render-collision* '#f) +(define *display-hipri-collision-marks* '#f) +(define *display-edge-collision-marks* '#f) +(define *display-geo-marks* '#f) +(define *display-target-marks* '#f) +(define *display-collide-history* 0) +(define *display-xyz-axes* '#f) +(define *display-cam-collide-history* '#f) +(define *record-cam-collide-history* '#f) +(define *display-cam-master-marks* '#f) +(define *display-cam-other* '#f) +(define *display-camera-marks* '#f) +(define *camera-no-mip-correction* '#f) +(define *display-cam-los-info* '#f) +(define *display-cam-los-debug* '#f) +(define *display-cam-los-marks* '#f) +(define *display-cam-coll-marks* '#f) +(define *display-camera-info* '#f) +(define *display-camera-old-stats* '#f) +(define *display-camera-last-attacker* '#f) +(define *display-file-info* '#f) +(define *display-actor-marks* '#f) +(define *display-ambient-hint-marks* '#f) +(define *display-ambient-sound-marks* '#f) +(define *display-ambient-poi-marks* '#f) +(define *display-ambient-light-marks* '#f) +(define *display-ambient-dark-marks* '#f) +(define *display-ambient-weather-off-marks* '#f) +(define *display-ambient-ocean-off-marks* '#f) +(define *display-ambient-ocean-near-off-marks* '#f) +(define *display-ambient-music-marks* '#f) +(define *display-sprite-info* '#f) +(define *display-entity-errors* '#t) +(define *display-lights* '#f) +(define *display-instance-info* '#f) +(define *display-deci-count* '#f) +(define *sync-dma* '#f) +(define *display-strip-lines* 0) +(define *display-nav-marks* '#f) +(define *display-path-marks* '#f) +(define *display-vol-marks* '#f) +(define *display-water-marks* '#f) +(define *display-actor-anim* '#f) +(define *display-process-anim* '#f) +(define *display-actor-vis* '#f) +(define *display-actor-graph* '#f) +(define *display-level-border* '#f) +(define *display-load-boundaries* '#f) +(define *display-memcard-info* '#f) +(define *display-split-boxes* '#f) +(define *display-split-box-info* '#f) +(define *display-texture-download* '#f) +(define *display-art-control* '#f) +(define *display-level-spheres* '#f) +(define *time-of-day-effects* '#t) +(define *time-of-day-fast* '#t) +(define *display-iop-info* '#f) +(define *ambient-sound-class* '#t) +(define *slow-frame-rate* '#f) +(define *weather-off* '#f) +(define *debug-pause* '#f) +(define *subdivide-draw-mode* 0) +(define *ocean-subdivide-draw-mode* 0) + +;; this is a bit of a trick. +;; I believe *dproc* is the display process. +;; if it is already created and this file is reloaded, we don't want to write over it. +;; so we only set it to #f if we think it hasn't been set before. +(define-extern *dproc* process) +(when (or (not *dproc*) + (zero? *dproc*)) + ;; no dproc, safe to write over it. + (set! *dproc* #f) + ) + +(define *run* '#f) +(define *teleport* '#f) +(define *teleport-count* 0) +(define *draw-hook* nothing) +(define *debug-hook* nothing) +(define *menu-hook* nothing) +(define *progress-hook* nothing) +(define *dma-timeout-hook* nothing) + +(deftype frame-stats (structure) + ((field-time uint64 2 :offset-assert 0) + (field int32 :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x14 + :flag-assert #x900000014 + ) + +(define *frame-stats* (new 'static 'frame-stats)) + + +(deftype screen-filter (basic) + ((draw? basic :offset-assert 4) + (color uint32 :offset-assert 8) + ) + :method-count-assert 10 + :size-assert #xc + :flag-assert #xa0000000c + (:methods + (dummy-9 () none 9) + ) + ) + diff --git a/goal_src/engine/load/file-io.gc b/goal_src/engine/load/file-io.gc index cbc5b6310..00c1ce79d 100644 --- a/goal_src/engine/load/file-io.gc +++ b/goal_src/engine/load/file-io.gc @@ -5,3 +5,186 @@ ;; name in dgo: file-io ;; dgos: GAME, ENGINE +;; represents a file that can be read/written, similar to FILE* in C. +;; NOTE: this is a special type in three ways: +;; 1). It is used in the C runtime. This must be kept in sync with kmachine.h's FileStream +;; 2). This type is built-in to the compiler (see TypeSystem.cpp, add_builtin_types) +;; It must be kept up to date with that definition as well. +;; 3). The C runtime constructs this type before anything is loaded. The sizes +;; must be kept up to date there as well. +(deftype file-stream (basic) + ((flags uint32 :offset-assert 4) + (mode basic :offset-assert 8) + (name string :offset-assert 12) + (file uint32 :offset-assert 16) + ) + (:methods + (new ((allocation symbol) (type-to-make type) (name string) (mode basic)) _type_) + ) + :method-count-assert 9 + :size-assert #x14 + :flag-assert #x900000014 + ) + + +(defmethod new file-stream ((allocation symbol) (type-to-make type) (name string) (mode basic)) + "Allocate a file-stream and open it." + (let ((stream (object-new))) + (file-stream-open stream name mode) + stream + ) + ) + +;; we already have a length method for a file-stream defined in C. +;; just store that in the method table. +(set! (-> file-stream method-table 4) file-stream-length) + +(defun file-stream-read-string ((stream file-stream) (str string)) + "Fill a string with data from a file stream. + Note: this function does not work." + ;; makes the length of the string 0. + (clear str) + ;; so this will read nothing. + (file-stream-read stream (-> str data) (length str)) + str + ) + + +;; A common file header found in GOAL files. +(deftype file-info (basic) + ((file-type symbol :offset-assert 4) + (file-name basic :offset-assert 8) + (major-version uint32 :offset-assert 12) + (minor-version uint32 :offset-assert 16) + (maya-file-name basic :offset-assert 20) + (tool-debug basic :offset-assert 24) + (mdb-file-name basic :offset-assert 28) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +(defmethod print file-info ((obj file-info)) + "Print information about a file" + (format #t "#<~A ~A :version ~D.~D @ #x~X>" + (-> obj type) (-> obj file-name) (-> obj major-version) (-> obj minor-version) obj) + obj + ) + +;; allocate a temporary string +(define *file-temp-string* (new 'global 'string 128 (the string #f))) + + +(defun make-file-name ((kind int) (name string) (art-group-version int)) + "Make a file name. Similar to MakeFileName in C. + Note: file type enum is different between C and GOAL. + File versions should match those in versions.h. + Uses a single *file-temp-string* buffer, shared with make-vfile-name." + + (clear *file-temp-string*) + (cond + ((= kind 3) + (format *file-temp-string* "texture-page~D/dir-tpages" 7)) + ((= kind 2) + (format *file-temp-string* "texture-page~D/tpage-~S" 7 name)) + ((zero? kind) + (format *file-temp-string* "level~D/~S-bt" 30 name)) + ((= kind 5) + (format *file-temp-string* "res~D/~S-tx" 1 name)) + ((= kind 4) + (format *file-temp-string* "level~D/~S-vs" 30 name)) + ((= kind 6) + (format *file-temp-string* "~S.VIS" name)) + ((= kind 1) + (format *file-temp-string* "art-group~D/~S-ag" + (if (> art-group-version 0) art-group-version 6) + name + ) + ) + ) + *file-temp-string* + ) + + + +(defun make-vfile-name ((a0-0 int) (a1-0 string)) + "Make another type of file name." + (local-vars + (s5-0 int) + (gp-0 string) + ) + (set! s5-0 a0-0) + (set! gp-0 a1-0) + (clear *file-temp-string*) + (cond + ((zero? s5-0) (format *file-temp-string* "$LEVEL/~S" gp-0)) + ((= s5-0 1) (format *file-temp-string* "$ART_GROUP/~S" gp-0)) + ) + *file-temp-string* + ) + +(defun file-info-correct-version? ((info file-info) (kind int) (version int)) + "Check if the file info is valid. If you call this with version = 0, + it will pick the right version for the kind automatically." + (local-vars + (v1-0 int) + (v1-1 int) + (expected-kind string) + (expected-version int) + ) + + ;; figure out the expected major version + (set! expected-version + (cond + ((zero? version) ;; version not specified. + (set! v1-0 kind) + (cond + ((or (zero? (+ v1-0 -2)) (zero? (+ v1-0 -3))) 7) ;; textures. + ((zero? v1-0) 30) ;; level + ((= v1-0 1) 6) ;; art-group + ) + ) + (else version) ;; version was specified + ) + ) + + ;; figure out the expected kind + (set! expected-kind + (begin + (set! v1-1 kind) + (cond ((= v1-1 2) "texture-page") + ((zero? v1-1) "bsp-header") + ((= v1-1 1) "art-group")) + ) + ) + + ;; check: + (cond + ;; first, check the name is right: + ;; not clear why we dereference the symbol like this. + ((not (name= (the-as basic (-> info file-type value)) expected-kind)) + + (format 0 "ERROR: file ~A is of type ~S but needs to be ~S.~%" + (-> info file-name) (-> info file-type) expected-kind) + ;; FAIL + '#f + ) + + ;; check versions (only major) + ((!= expected-version (-> info major-version)) + (format + 0 + "ERROR: file ~A is version ~D.~D, but needs to be ~D.x~%" + (-> info file-name) + (-> info major-version) + (-> info minor-version) + expected-version + ) + '#f + ) + + ;; both tests pass! + (else '#t) + ) + ) diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index 5381c4da4..4f8a92c94 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -302,7 +302,9 @@ ) ) -;; TODO - these work but aren't very efficient. +(defmacro return (val) + `(return-from #f ,val) + ) ;;;;;;;;;;;;;;;;;;; diff --git a/goal_src/kernel-defs.gc b/goal_src/kernel-defs.gc index e709f374a..dff74da1a 100644 --- a/goal_src/kernel-defs.gc +++ b/goal_src/kernel-defs.gc @@ -126,10 +126,11 @@ ;; install-handler ;; install-debug-handler ;; file-stream-open +(define-extern file-stream-open (function file-stream basic basic file-stream)) ;; file-stream-close -;; file-stream-length +(define-extern file-stream-length (function file-stream int)) ;; file-stream-seek -;; file-stream-read +(define-extern file-stream-read (function file-stream pointer int int)) ;; file-stream-write ;; scf-get-language ;; scf-get-time diff --git a/goal_src/kernel/gcommon.gc b/goal_src/kernel/gcommon.gc index eeb5cb8f7..b492814a6 100644 --- a/goal_src/kernel/gcommon.gc +++ b/goal_src/kernel/gcommon.gc @@ -389,9 +389,67 @@ ) ) -;; todo -;; nassoc -;; nassce +(defun nassoc ((a0-0 string) (a1-0 object)) + (local-vars + (v0-2 object) + (v1-1 object) + (v1-3 symbol) + (a1-1 object) + (s5-0 string) + (gp-0 object) + ) + (begin + (set! s5-0 a0-0) + (set! gp-0 a1-0) + (while + (not + (or + (= gp-0 '()) + (begin + (set! a1-1 (car (car gp-0))) + (if (pair? a1-1) (nmember s5-0 a1-1) (name= (the basic a1-1) s5-0)) + ) + ) + ) + (set! gp-0 (cdr gp-0)) + ) + (set! v1-3 '#f) + (if (!= gp-0 '()) (car gp-0)) + ) + ) + +(defun nassoce ((a0-0 string) (a1-0 object)) + (local-vars + (v0-2 object) + (v1-1 object) + (v1-4 symbol) + (s4-0 object) + (s5-0 string) + (gp-0 object) + ) + (begin + (set! s5-0 a0-0) + (set! gp-0 a1-0) + (while + (not + (or + (= gp-0 '()) + (begin + (set! s4-0 (car (car gp-0))) + (if + (pair? s4-0) + (nmember s5-0 s4-0) + (or (name= (the basic s4-0) s5-0) (= s4-0 'else)) + ) + ) + ) + ) + (set! gp-0 (cdr gp-0)) + ) + (set! v1-4 '#f) + (if (!= gp-0 '()) (car gp-0)) + ) + ) (defun append! ((front object) (back object)) "Append back to front." @@ -605,7 +663,7 @@ dst ) -(defun mem-set32! ((dst pointer) (value int) (n int)) +(defun mem-set32! ((dst pointer) (n int) (value int)) "Memset a 32-bit value n times. Total memory filled is 4 * n bytes." (let ((p (the pointer dst)) (i 0)) diff --git a/goal_src/kernel/gstring.gc b/goal_src/kernel/gstring.gc index 19add59a4..6cacfa7b8 100644 --- a/goal_src/kernel/gstring.gc +++ b/goal_src/kernel/gstring.gc @@ -85,7 +85,11 @@ ;; cat-string<-string_to_charp ;; append-character-to-string ;; charp-basename -;; clear + +(defun clear ((a0-0 string)) + (set! (-> a0-0 data 0) 0) a0-0 + ) + ;; string? ;; string<=? diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index fcea3d375..fe81bbe09 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -338,6 +338,7 @@ class Compiler { Val* compile_return_from(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_label(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_goto(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_nop(const goos::Object& form, const goos::Object& rest, Env* env); // CompilerControl Val* compile_seval(const goos::Object& form, const goos::Object& rest, Env* env); diff --git a/goalc/compiler/IR.cpp b/goalc/compiler/IR.cpp index 25acd626b..4a9f991a6 100644 --- a/goalc/compiler/IR.cpp +++ b/goalc/compiler/IR.cpp @@ -1023,6 +1023,26 @@ void IR_GetStackAddr::do_codegen(emitter::ObjectGenerator* gen, } } +/////////////////////// +// Nop +/////////////////////// + +IR_Nop::IR_Nop() {} + +std::string IR_Nop::print() { + return fmt::format("nop"); +} + +RegAllocInstr IR_Nop::to_rai() { + return {}; +} + +void IR_Nop::do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult&, + emitter::IR_Record irec) { + gen->add_instr(IGen::nop(), irec); +} + /////////////////////// // Asm /////////////////////// diff --git a/goalc/compiler/IR.h b/goalc/compiler/IR.h index 80f884839..35d8c3f2e 100644 --- a/goalc/compiler/IR.h +++ b/goalc/compiler/IR.h @@ -340,6 +340,16 @@ class IR_GetStackAddr : public IR { int m_slot = -1; }; +class IR_Nop : public IR { + public: + IR_Nop(); + std::string print() override; + RegAllocInstr to_rai() override; + void do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) override; +}; + class IR_Asm : public IR { public: explicit IR_Asm(bool use_coloring); diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index 126fd40cb..232e2d61e 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -26,6 +26,7 @@ static const std::unordered_map< // INLINE ASM - VECTOR FLOAT OPERATIONS {".nop.vf", &Compiler::compile_asm_nop_vf}, + {".nop", &Compiler::compile_nop}, {".lvf", &Compiler::compile_asm_lvf}, {".svf", &Compiler::compile_asm_svf}, {".xor.vf", &Compiler::compile_asm_xor_vf}, @@ -70,6 +71,7 @@ static const std::unordered_map< {"return-from", &Compiler::compile_return_from}, {"label", &Compiler::compile_label}, {"goto", &Compiler::compile_goto}, + {"nop!", &Compiler::compile_nop}, // COMPILER CONTROL {"gs", &Compiler::compile_gs}, diff --git a/goalc/compiler/compilation/Block.cpp b/goalc/compiler/compilation/Block.cpp index 81bac91b3..f4ab79154 100644 --- a/goalc/compiler/compilation/Block.cpp +++ b/goalc/compiler/compilation/Block.cpp @@ -185,4 +185,11 @@ Val* Compiler::compile_goto(const goos::Object& form, const goos::Object& rest, get_parent_env_of_type(env)->unresolved_gotos.push_back({ir_goto.get(), label_name}); env->emit(std::move(ir_goto)); return get_none(); +} + +Val* Compiler::compile_nop(const goos::Object& form, const goos::Object& rest, Env* env) { + auto args = get_va(form, rest); + va_check(form, args, {}, {}); + env->emit_ir(); + return get_none(); } \ No newline at end of file diff --git a/goalc/emitter/IGen.h b/goalc/emitter/IGen.h index b26cc9ef8..51a557f4f 100644 --- a/goalc/emitter/IGen.h +++ b/goalc/emitter/IGen.h @@ -2009,6 +2009,12 @@ class IGen { return instr; } + static Instruction nop() { + // NOP + Instruction instr(0x90); + return instr; + } + static Instruction nop_vf() { // FNOP Instruction instr(0xd9); diff --git a/test/decompiler/test_FormBeforeExpressions.cpp b/test/decompiler/test_FormBeforeExpressions.cpp index 141b3b537..1fc222bdc 100644 --- a/test/decompiler/test_FormBeforeExpressions.cpp +++ b/test/decompiler/test_FormBeforeExpressions.cpp @@ -234,7 +234,7 @@ TEST_F(FormRegressionTest, WhileLoop) { " (begin (set! v1-0 (-> v1-0 parent)) (= v1-0 a0-1))\n" " (if\n" " (= v1-0 a1-0)\n" - " (return (begin (set! v1-1 '#t) (set! v0-0 v1-1)) (set! v1-0 0))\n" + " (return (begin (set! v1-1 '#t) (set! v0-0 v1-1)))\n" " )\n" " )\n" " (set! v0-0 '#f)\n" @@ -300,7 +300,7 @@ TEST_F(FormRegressionTest, Or) { " )\n" " (if\n" " (= a0-0 a1-0)\n" - " (return (begin (set! v1-1 '#t) (set! v0-0 v1-1)) (set! v1-0 0))\n" + " (return (begin (set! v1-1 '#t) (set! v0-0 v1-1)))\n" " )\n" " )\n" " (set! v0-0 '#f)\n" @@ -365,9 +365,9 @@ TEST_F(FormRegressionTest, DynamicMethodAccess) { " (if\n" " (begin\n" " (if\n" - " (begin (set! a2-0 object) (= a0-0 a2-0))\n" // if we reached the top - " (return (begin (set! v1-3 nothing) (set! v0-0 v1-3)) (set! v1-2 0))\n" // return - // nothing. + " (begin (set! a2-0 object) (= a0-0 a2-0))\n" // if we reached the top + " (return (begin (set! v1-3 nothing) (set! v0-0 v1-3)))\n" // return + // nothing. " )\n" " (set! a0-0 (-> a0-0 parent))\n" // get next parent type " (set! a2-2 (sll a1-0 2))\n" // fancy access @@ -376,9 +376,9 @@ TEST_F(FormRegressionTest, DynamicMethodAccess) { // condition) " (zero? v0-0)\n" // is it defined? " )\n" - " (return (begin (set! v1-4 nothing) (set! v0-0 v1-4)) (set! v1-2 0))\n" // also - // return - // nothing. + " (return (begin (set! v1-4 nothing) (set! v0-0 v1-4)))\n" // also + // return + // nothing. " )\n" " )\n" " (set! v1-5 '#f)\n" @@ -887,7 +887,7 @@ TEST_F(FormRegressionTest, TypeOf) { std::string type = "(function object object)"; std::string expected = "(begin\n" - " (set! v1-1 (type-of a0-0))\n" + " (set! v1-1 (rtype-of a0-0))\n" " (set! t9-0 (-> v1-1 methods-by-name print))\n" // print method. " (set! v0-0 (call! a0-0))\n" " (ret-value v0-0)\n" diff --git a/test/decompiler/test_FormExpressionBuild.cpp b/test/decompiler/test_FormExpressionBuild.cpp index 46a5ff799..4cb41504a 100644 --- a/test/decompiler/test_FormExpressionBuild.cpp +++ b/test/decompiler/test_FormExpressionBuild.cpp @@ -461,7 +461,7 @@ TEST_F(FormRegressionTest, ExprBasicTypeP) { // don't plan on supporting this. " (if\n" " (= v1-0 a1-0)\n" - " (return '#t (set! v1-0 0))\n" + " (return '#t)\n" " )\n" " )\n" " '#f\n" @@ -507,7 +507,7 @@ TEST_F(FormRegressionTest, FinalBasicTypeP) { " (set! a0-1 object)\n" " (until\n" " (begin (set! v1-0 (-> v1-0 parent)) (= v1-0 a0-1))\n" - " (if (= v1-0 a1-0) (return (quote #t) (set! v1-0 0)))\n" + " (if (= v1-0 a1-0) (return (quote #t)))\n" " )\n" " (quote #f)\n" " )\n" @@ -562,7 +562,7 @@ TEST_F(FormRegressionTest, ExprTypeTypep) { " (set! a0-0 (-> a0-0 parent))\n" " (or (= a0-0 v1-0) (zero? a0-0))\n" " )\n" - " (if (= a0-0 a1-0) (return '#t (set! v1-0 0)))\n" + " (if (= a0-0 a1-0) (return '#t))\n" " )\n" " '#f\n" " )"; @@ -618,13 +618,13 @@ TEST_F(FormRegressionTest, ExprFindParentMethod) { std::string expected = "(begin\n" - " (set! v1-2 (-> a0-0 methods a1-0))\n" + " (set! v1-2 (-> a0-0 method-table a1-0))\n" " (until\n" " (!= v0-0 v1-2)\n" - " (if (= a0-0 object) (return nothing (set! v1-2 0)))\n" + " (if (= a0-0 object) (return nothing))\n" " (set! a0-0 (-> a0-0 parent))\n" - " (set! v0-0 (-> a0-0 methods a1-0))\n" - " (if (zero? v0-0) (return nothing (set! v1-2 0)))\n" + " (set! v0-0 (-> a0-0 method-table a1-0))\n" + " (if (zero? v0-0) (return nothing))\n" " )\n" " (set! v1-5 '#f)\n" " v0-0\n" @@ -902,7 +902,7 @@ TEST_F(FormRegressionTest, ExprNmember) { " (set! s5-0 a0-0)\n" " (set! gp-0 a1-0)\n" " (while\n" - " (not (or (= gp-0 '()) (name= (car gp-0) s5-0)))\n" + " (not (or (= gp-0 '()) (name= (the-as basic (car gp-0)) s5-0)))\n" " (set! gp-0 (cdr gp-0))\n" " )\n" " (set! v1-2 '#f)\n" @@ -1110,7 +1110,11 @@ TEST_F(FormRegressionTest, ExprNassoc) { " (= gp-0 (quote ()))\n" " (begin\n" " (set! a1-1 (car (car gp-0)))\n" - " (if (pair? a1-1) (nmember s5-0 a1-1) (name= a1-1 s5-0))\n" + " (if " + " (pair? a1-1)\n" + " (nmember (the-as basic s5-0) a1-1)\n" + " (name= (the-as basic a1-1) (the-as basic s5-0))" + " )\n" " )\n" " )\n" " )\n" @@ -1213,8 +1217,11 @@ TEST_F(FormRegressionTest, ExprNassoce) { " (set! s4-0 (car (car gp-0)))\n" " (if\n" " (pair? s4-0)\n" - " (nmember s5-0 s4-0)\n" - " (or (name= s4-0 s5-0) (= s4-0 (quote else)))\n" + " (nmember (the-as basic s5-0) s4-0)\n" + " (or\n" + " (name= (the-as basic s4-0) (the-as basic s5-0))\n" + " (= s4-0 (quote else))\n" + " )\n" " )\n" " )\n" " )\n" @@ -2063,7 +2070,7 @@ TEST_F(FormRegressionTest, ExprPrint) { " daddiu sp, sp, 16"; std::string type = "(function object object)"; - std::string expected = "((method-of-type (type-of a0-0) print) a0-0)"; + std::string expected = "((method-of-type (rtype-of a0-0) print) a0-0)"; test_with_expr(func, type, expected); } @@ -2112,7 +2119,7 @@ TEST_F(FormRegressionTest, ExprPrintl) { "(begin\n" " (set! gp-0 a0-0)\n" " (set! a0-1 gp-0)\n" - " (set! v1-2 ((method-of-type (type-of a0-1) print) a0-1))\n" + " (set! v1-2 ((method-of-type (rtype-of a0-1) print) a0-1))\n" " (format (quote #t) L324)\n" " gp-0\n" " )"; @@ -2143,7 +2150,7 @@ TEST_F(FormRegressionTest, ExprInspect) { " daddiu sp, sp, 16"; std::string type = "(function object object)"; - std::string expected = "((method-of-type (type-of a0-0) inspect) a0-0)"; + std::string expected = "((method-of-type (rtype-of a0-0) inspect) a0-0)"; test_with_expr(func, type, expected); } @@ -2334,13 +2341,13 @@ TEST_F(FormRegressionTest, ExprPrintName) { "(cond\n" " ((= a0-0 a1-0) (quote #t))\n" " ((and (= (-> a0-0 type) string) (= (-> a1-0 type) string))\n" - " (string= a0-0 a1-0)\n" + " (string= (the-as string a0-0) (the-as string a1-0))\n" " )\n" " ((and (= (-> a0-0 type) string) (= (-> a1-0 type) symbol))\n" - " (string= a0-0 (-> (+ 65336 (the-as int a1-0)) 0))\n" + " (string= (the-as string a0-0) (-> (+ 65336 (the-as int a1-0)) 0))\n" " )\n" " ((and (= (-> a1-0 type) string) (= (-> a0-0 type) symbol))\n" - " (string= a1-0 (-> (+ 65336 (the-as int a0-0)) 0))\n" + " (string= (the-as string a1-0) (-> (+ 65336 (the-as int a0-0)) 0))\n" " )\n" " )"; test_with_expr(func, type, expected, false, "", {}, diff --git a/test/decompiler/test_FormExpressionBuildLong.cpp b/test/decompiler/test_FormExpressionBuildLong.cpp index c3cc5752f..103fcc828 100644 --- a/test/decompiler/test_FormExpressionBuildLong.cpp +++ b/test/decompiler/test_FormExpressionBuildLong.cpp @@ -2002,8 +2002,8 @@ TEST_F(FormRegressionTest, ExprValid) { " (if s4-0 (set! v1-24 (format s5-0 L318 gp-0 s4-0 s3-0)))\n" " (quote #f)\n" " )\n" - " ((and (= s3-0 type) (!= (type-of gp-0) type))\n" - " (if s4-0 (set! v1-31 (format s5-0 L317 gp-0 s4-0 s3-0 (type-of gp-0))))\n" + " ((and (= s3-0 type) (!= (rtype-of gp-0) type))\n" + " (if s4-0 (set! v1-31 (format s5-0 L317 gp-0 s4-0 s3-0 (rtype-of gp-0))))\n" " (quote #f)\n" " )\n" " (else\n" @@ -2011,16 +2011,16 @@ TEST_F(FormRegressionTest, ExprValid) { " v1-33\n" " (and\n" " (!= s3-0 type)\n" - " (not (valid? (type-of gp-0) type (quote #f) (quote #t) 0))\n" + " (not (valid? (rtype-of gp-0) type (quote #f) (quote #t) 0))\n" " )\n" " )\n" " (cond\n" " (v1-33\n" - " (if s4-0 (set! v1-37 (format s5-0 L317 gp-0 s4-0 s3-0 (type-of gp-0))))\n" + " (if s4-0 (set! v1-37 (format s5-0 L317 gp-0 s4-0 s3-0 (rtype-of gp-0))))\n" " (quote #f)\n" " )\n" - " ((not (type-type? (type-of gp-0) s3-0))\n" - " (if s4-0 (set! v1-41 (format s5-0 L316 gp-0 s4-0 s3-0 (type-of gp-0))))\n" + " ((not (type-type? (rtype-of gp-0) s3-0))\n" + " (if s4-0 (set! v1-41 (format s5-0 L316 gp-0 s4-0 s3-0 (rtype-of gp-0))))\n" " (quote #f)\n" " )\n" " ((= s3-0 symbol)\n" diff --git a/test/goalc/source_templates/with_game/test-memset.gc b/test/goalc/source_templates/with_game/test-memset.gc index 90c9e8402..972882f5a 100644 --- a/test/goalc/source_templates/with_game/test-memset.gc +++ b/test/goalc/source_templates/with_game/test-memset.gc @@ -3,9 +3,9 @@ (let* ((base-addr #x6000000) (word-cnt 23) (base (the (pointer int32) base-addr)) - (foo (mem-set32! base #x0 (+ 1 word-cnt))) + (foo (mem-set32! base (+ 1 word-cnt) #x0)) (last-byte (the (pointer uint8) (+ base-addr 3 (* 4 (- word-cnt 1))))) - (dst (mem-set32! base #x0badbeef word-cnt)) + (dst (mem-set32! base word-cnt #x0badbeef)) ) (if (!= dst base) diff --git a/test/goalc/source_templates/with_game/test-vector-dot.gc b/test/goalc/source_templates/with_game/test-vector-dot.gc index 22f115ad4..96cdcf724 100644 --- a/test/goalc/source_templates/with_game/test-vector-dot.gc +++ b/test/goalc/source_templates/with_game/test-vector-dot.gc @@ -8,6 +8,8 @@ (set! (-> b x) 2.) (set! (-> b y) 3.) (set! (-> b z) 4.) + (.nop) + (nop!) (expect-true (= 20.0 (vector-dot-vu a b))) )