diff --git a/common/versions.h b/common/versions.h index 76d3016102..17e58b86b8 100644 --- a/common/versions.h +++ b/common/versions.h @@ -5,9 +5,6 @@ * Version numbers for GOAL Language, Kernel, etc... */ -#ifndef JAK1_VERSIONS_H -#define JAK1_VERSIONS_H - #include "common/common_types.h" namespace versions { @@ -15,6 +12,8 @@ namespace versions { constexpr s32 GOAL_VERSION_MAJOR = 0; constexpr s32 GOAL_VERSION_MINOR = 6; +constexpr int DECOMPILER_VERSION = 1; + // these versions are from the game constexpr u32 ART_FILE_VERSION = 6; constexpr u32 LEVEL_FILE_VERSION = 30; @@ -26,5 +25,3 @@ constexpr u32 TX_PAGE_VERSION = 7; // GOAL kernel version (OpenGOAL changes this version from the game's version) constexpr int KERNEL_VERSION_MAJOR = 2; constexpr int KERNEL_VERSION_MINOR = 0; - -#endif // JAK1_VERSIONS_H diff --git a/decompiler/IR2/AtomicOpForm.cpp b/decompiler/IR2/AtomicOpForm.cpp index 62d5cbc19f..c9ca614cdf 100644 --- a/decompiler/IR2/AtomicOpForm.cpp +++ b/decompiler/IR2/AtomicOpForm.cpp @@ -77,11 +77,30 @@ FormElement* SetVarOp::get_as_form(FormPool& pool, const Env& env) const { } } + // create element auto source = pool.alloc_single_element_form(nullptr, m_src, m_my_idx); auto result = pool.alloc_element(m_dst, source, is_sequence_point()); - if (m_src.kind() == SimpleExpression::Kind::IDENTITY) { - if (env.has_local_vars() && env.op_id_is_eliminated_coloring_move(m_my_idx)) { + + // do some analysis to look for coloring moves which are already eliminated, + // dead sets, and dead set falses. + if (m_src.kind() == SimpleExpression::Kind::IDENTITY && env.has_local_vars() && + env.has_reg_use()) { + if (env.op_id_is_eliminated_coloring_move(m_my_idx)) { result->eliminate_as_coloring_move(); + } else if (m_src.get_arg(0).is_var()) { + auto& src_var = m_src.get_arg(0).var(); + auto& ri = env.reg_use().op.at(m_my_idx); + if (ri.consumes.find(src_var.reg()) != ri.consumes.end() && + ri.written_and_unused.find(dst().reg()) != ri.written_and_unused.end()) { + result->mark_as_dead_set(); + // fmt::print("marked {} as dead set\n", to_string(env)); + } + } else if (m_src.get_arg(0).is_sym_ptr() && m_src.get_arg(0).get_str() == "#f") { + auto& ri = env.reg_use().op.at(m_my_idx); + if (ri.written_and_unused.find(dst().reg()) != ri.written_and_unused.end()) { + result->mark_as_dead_false(); + // fmt::print("marked {} as dead set false\n", to_string(env)); + } } } diff --git a/decompiler/IR2/Form.cpp b/decompiler/IR2/Form.cpp index 0d0b4500a8..c9f6891505 100644 --- a/decompiler/IR2/Form.cpp +++ b/decompiler/IR2/Form.cpp @@ -32,10 +32,22 @@ void FormElement::push_to_stack(const Env& env, FormPool&, FormStack&) { throw std::runtime_error("push_to_stack not implemented for " + to_string(env)); } -goos::Object FormElement::to_form_as_condition(const Env& env) const { +goos::Object FormElement::to_form_as_condition_internal(const Env& env) const { return to_form(env); } +bool FormElement::active() const { + return true; +} + +goos::Object FormElement::to_form(const Env& env) const { + if (active()) { + return to_form_internal(env); + } else { + return pretty_print::build_list("empty-form"); + } +} + /////////////////// // Form ////////////////// @@ -48,7 +60,13 @@ goos::Object Form::to_form(const Env& env) const { std::vector forms; forms.push_back(pretty_print::to_symbol("begin")); for (auto& x : m_elements) { - forms.push_back(x->to_form(env)); + if (x->active()) { + forms.push_back(x->to_form_internal(env)); + } + } + + if (forms.size() == 2) { + return forms.at(1); } return pretty_print::build_list(forms); } @@ -57,16 +75,18 @@ goos::Object Form::to_form(const Env& env) const { goos::Object Form::to_form_as_condition(const Env& env) const { assert(!m_elements.empty()); if (m_elements.size() == 1) { - return m_elements.front()->to_form_as_condition(env); + return m_elements.front()->to_form_as_condition_internal(env); } else { std::vector forms; forms.push_back(pretty_print::to_symbol("begin")); for (size_t i = 0; i < m_elements.size(); i++) { const auto& x = m_elements.at(i); if (i == m_elements.size() - 1) { - forms.push_back(x->to_form_as_condition(env)); + forms.push_back(x->to_form_as_condition_internal(env)); } else { - forms.push_back(x->to_form(env)); + if (x->active()) { + forms.push_back(x->to_form_internal(env)); + } } } @@ -80,7 +100,9 @@ std::string Form::to_string(const Env& env) const { void Form::inline_forms(std::vector& forms, const Env& env) const { for (auto& x : m_elements) { - forms.push_back(x->to_form(env)); + if (x->active()) { + forms.push_back(x->to_form_internal(env)); + } } } @@ -116,7 +138,7 @@ void Form::get_modified_regs(RegSet& regs) const { SimpleExpressionElement::SimpleExpressionElement(SimpleExpression expr, int my_idx) : m_expr(std::move(expr)), m_my_idx(my_idx) {} -goos::Object SimpleExpressionElement::to_form(const Env& env) const { +goos::Object SimpleExpressionElement::to_form_internal(const Env& env) const { return m_expr.to_form(env.file->labels, env); } @@ -144,7 +166,7 @@ void SimpleExpressionElement::get_modified_regs(RegSet& regs) const { StoreElement::StoreElement(const StoreOp* op) : m_op(op) {} -goos::Object StoreElement::to_form(const Env& env) const { +goos::Object StoreElement::to_form_internal(const Env& env) const { return m_op->to_form(env.file->labels, env); } @@ -171,7 +193,7 @@ LoadSourceElement::LoadSourceElement(Form* addr, int size, LoadVarOp::Kind kind) m_addr->parent_element = this; } -goos::Object LoadSourceElement::to_form(const Env& env) const { +goos::Object LoadSourceElement::to_form_internal(const Env& env) const { switch (m_kind) { case LoadVarOp::Kind::FLOAT: assert(m_size == 4); @@ -230,7 +252,7 @@ void LoadSourceElement::get_modified_regs(RegSet& regs) const { SimpleAtomElement::SimpleAtomElement(const SimpleAtom& atom) : m_atom(atom) {} -goos::Object SimpleAtomElement::to_form(const Env& env) const { +goos::Object SimpleAtomElement::to_form_internal(const Env& env) const { return m_atom.to_form(env.file->labels, env); } @@ -257,7 +279,7 @@ SetVarElement::SetVarElement(const Variable& var, Form* value, bool is_sequence_ value->parent_element = this; } -goos::Object SetVarElement::to_form(const Env& env) const { +goos::Object SetVarElement::to_form_internal(const Env& env) const { return pretty_print::build_list("set!", m_dst.to_form(env), m_src->to_form(env)); } @@ -284,6 +306,14 @@ void SetVarElement::get_modified_regs(RegSet& regs) const { m_src->get_modified_regs(regs); } +bool SetVarElement::active() const { + if (is_eliminated_coloring_move() || is_dead_false_set()) { + return false; + } else { + return true; + } +} + ///////////////////////////// // SetFormFormElement ///////////////////////////// @@ -293,7 +323,7 @@ SetFormFormElement::SetFormFormElement(Form* dst, Form* src) : m_dst(dst), m_src m_src->parent_element = this; } -goos::Object SetFormFormElement::to_form(const Env& env) const { +goos::Object SetFormFormElement::to_form_internal(const Env& env) const { std::vector forms = {pretty_print::to_symbol("set!"), m_dst->to_form(env), m_src->to_form(env)}; return pretty_print::build_list(forms); @@ -328,7 +358,7 @@ void SetFormFormElement::get_modified_regs(RegSet& regs) const { AtomicOpElement::AtomicOpElement(const AtomicOp* op) : m_op(op) {} -goos::Object AtomicOpElement::to_form(const Env& env) const { +goos::Object AtomicOpElement::to_form_internal(const Env& env) const { return m_op->to_form(env.file->labels, env); } @@ -358,7 +388,7 @@ void AtomicOpElement::get_modified_regs(RegSet& regs) const { AsmOpElement::AsmOpElement(const AsmOp* op) : m_op(op) {} -goos::Object AsmOpElement::to_form(const Env& env) const { +goos::Object AsmOpElement::to_form_internal(const Env& env) const { return m_op->to_form(env.file->labels, env); } @@ -396,7 +426,7 @@ ConditionElement::ConditionElement(IR2_Condition::Kind kind, m_src[1] = src1; } -goos::Object ConditionElement::to_form(const Env& env) const { +goos::Object ConditionElement::to_form_internal(const Env& env) const { std::vector forms; forms.push_back(pretty_print::to_symbol(get_condition_kind_name(m_kind))); for (int i = 0; i < get_condition_num_args(m_kind); i++) { @@ -409,11 +439,11 @@ goos::Object ConditionElement::to_form(const Env& env) const { } } -goos::Object ConditionElement::to_form_as_condition(const Env& env) const { +goos::Object ConditionElement::to_form_as_condition_internal(const Env& env) const { if (m_kind == IR2_Condition::Kind::TRUTHY) { return m_src[0]->to_form(env.file->labels, env); } else { - return to_form(env); + return to_form_internal(env); } } @@ -445,7 +475,7 @@ void ConditionElement::get_modified_regs(RegSet& regs) const { FunctionCallElement::FunctionCallElement(const CallOp* op) : m_op(op) {} -goos::Object FunctionCallElement::to_form(const Env& env) const { +goos::Object FunctionCallElement::to_form_internal(const Env& env) const { return m_op->to_form(env.file->labels, env); } @@ -475,7 +505,7 @@ void FunctionCallElement::get_modified_regs(RegSet& regs) const { BranchElement::BranchElement(const BranchOp* op) : m_op(op) {} -goos::Object BranchElement::to_form(const Env& env) const { +goos::Object BranchElement::to_form_internal(const Env& env) const { return m_op->to_form(env.file->labels, env); } @@ -503,7 +533,7 @@ void BranchElement::get_modified_regs(RegSet& regs) const { // ReturnElement ///////////////////////////// -goos::Object ReturnElement::to_form(const Env& env) const { +goos::Object ReturnElement::to_form_internal(const Env& env) const { std::vector forms; forms.push_back(pretty_print::to_symbol("return")); forms.push_back(return_code->to_form(env)); @@ -546,7 +576,7 @@ void ReturnElement::get_modified_regs(RegSet& regs) const { // BreakElement ///////////////////////////// -goos::Object BreakElement::to_form(const Env& env) const { +goos::Object BreakElement::to_form_internal(const Env& env) const { std::vector forms; forms.push_back(pretty_print::to_symbol("break")); forms.push_back(pretty_print::build_list(return_code->to_form(env))); @@ -580,7 +610,7 @@ void BreakElement::get_modified_regs(RegSet& regs) const { // CondWithElseElement ///////////////////////////// -goos::Object CondWithElseElement::to_form(const Env& env) const { +goos::Object CondWithElseElement::to_form_internal(const Env& env) const { // for now we only turn it into an if statement if both cases won't require a begin at the top // level. I think it is more common to write these as a two-case cond instead of an if with begin. if (entries.size() == 1 && entries.front().body->is_single_element() && @@ -645,7 +675,7 @@ void CondWithElseElement::get_modified_regs(RegSet& regs) const { // EmptyElement ///////////////////////////// -goos::Object EmptyElement::to_form(const Env&) const { +goos::Object EmptyElement::to_form_internal(const Env&) const { return pretty_print::build_list("empty"); } @@ -668,7 +698,7 @@ void WhileElement::apply(const std::function& f) { condition->apply(f); } -goos::Object WhileElement::to_form(const Env& env) const { +goos::Object WhileElement::to_form_internal(const Env& env) const { std::vector list; list.push_back(pretty_print::to_symbol("while")); list.push_back(condition->to_form_as_condition(env)); @@ -702,7 +732,7 @@ void UntilElement::apply(const std::function& f) { condition->apply(f); } -goos::Object UntilElement::to_form(const Env& env) const { +goos::Object UntilElement::to_form_internal(const Env& env) const { std::vector list; list.push_back(pretty_print::to_symbol("until")); list.push_back(condition->to_form_as_condition(env)); @@ -750,7 +780,7 @@ void ShortCircuitElement::apply_form(const std::function& f) { } } -goos::Object ShortCircuitElement::to_form(const Env& env) const { +goos::Object ShortCircuitElement::to_form_internal(const Env& env) const { std::vector forms; switch (kind) { case UNKNOWN: @@ -788,7 +818,7 @@ void ShortCircuitElement::get_modified_regs(RegSet& regs) const { // CondNoElseElement ///////////////////////////// -goos::Object CondNoElseElement::to_form(const Env& env) const { +goos::Object CondNoElseElement::to_form_internal(const Env& env) const { if (entries.size() == 1 && entries.front().body->is_single_element()) { // print as an if statement if we can put the body in a single form. std::vector list; @@ -854,7 +884,7 @@ void CondNoElseElement::get_modified_regs(RegSet& regs) const { AbsElement::AbsElement(Variable _source, RegSet _consumed) : source(_source), consumed(std::move(_consumed)) {} -goos::Object AbsElement::to_form(const Env& env) const { +goos::Object AbsElement::to_form_internal(const Env& env) const { return pretty_print::build_list("abs", source.to_form(env)); } @@ -885,7 +915,7 @@ AshElement::AshElement(Variable _shift_amount, is_signed(_is_signed), consumed(_consumed) {} -goos::Object AshElement::to_form(const Env& env) const { +goos::Object AshElement::to_form_internal(const Env& env) const { return pretty_print::build_list(pretty_print::to_symbol(is_signed ? "ash.si" : "ash.ui"), value.to_form(env), shift_amount.to_form(env)); } @@ -912,7 +942,7 @@ TypeOfElement::TypeOfElement(Form* _value, std::optional _clobber) value->parent_element = this; } -goos::Object TypeOfElement::to_form(const Env& env) const { +goos::Object TypeOfElement::to_form_internal(const Env& env) const { return pretty_print::build_list("rtype-of", value->to_form(env)); } @@ -942,7 +972,7 @@ ConditionalMoveFalseElement::ConditionalMoveFalseElement(Variable _dest, source->parent_element = this; } -goos::Object ConditionalMoveFalseElement::to_form(const Env& env) const { +goos::Object ConditionalMoveFalseElement::to_form_internal(const Env& env) const { return pretty_print::build_list(on_zero ? "cmove-#f-zero" : "cmove-#f-nonzero", dest.to_form(env), source->to_form(env)); } @@ -1125,7 +1155,8 @@ std::string fixed_operator_to_string(FixedOperatorKind kind) { return "object-new"; case FixedOperatorKind::TYPE_NEW: return "type-new"; - + case FixedOperatorKind::CONS: + return "cons"; case FixedOperatorKind::LT: return "<"; case FixedOperatorKind::GT: @@ -1178,7 +1209,7 @@ GenericElement::GenericElement(GenericOperator op, std::vector forms) } } -goos::Object GenericElement::to_form(const Env& env) const { +goos::Object GenericElement::to_form_internal(const Env& env) const { if (m_head.kind() == GenericOperator::Kind::CONDITION_OPERATOR && m_head.condition_kind() == IR2_Condition::Kind::TRUTHY) { assert(m_elts.size() == 1); @@ -1231,7 +1262,7 @@ CastElement::CastElement(TypeSpec type, Form* source, bool numeric) source->parent_element = this; } -goos::Object CastElement::to_form(const Env& env) const { +goos::Object CastElement::to_form_internal(const Env& env) const { return pretty_print::build_list(m_numeric ? "the" : "the-as", m_type.print(), m_source->to_form(env)); } @@ -1375,7 +1406,7 @@ DerefElement::DerefElement(Form* base, bool is_addr_of, std::vector } } -goos::Object DerefElement::to_form(const Env& env) const { +goos::Object DerefElement::to_form_internal(const Env& env) const { std::vector forms = {pretty_print::to_symbol(m_is_addr_of ? "&->" : "->"), m_base->to_form(env)}; for (auto& tok : m_tokens) { @@ -1419,7 +1450,7 @@ void DerefElement::get_modified_regs(RegSet& regs) const { DynamicMethodAccess::DynamicMethodAccess(Variable source) : m_source(source) {} -goos::Object DynamicMethodAccess::to_form(const Env& env) const { +goos::Object DynamicMethodAccess::to_form_internal(const Env& env) const { return pretty_print::build_list("dyn-method-access", m_source.to_form(env)); } @@ -1447,7 +1478,7 @@ ArrayFieldAccess::ArrayFieldAccess(Variable source, m_expected_stride(expected_stride), m_constant_offset(constant_offset) {} -goos::Object ArrayFieldAccess::to_form(const Env& env) const { +goos::Object ArrayFieldAccess::to_form_internal(const Env& env) const { std::vector elts; elts.push_back(pretty_print::to_symbol("dynamic-array-field-access")); elts.push_back(m_source.to_form(env)); @@ -1492,7 +1523,7 @@ GetMethodElement::GetMethodElement(Form* in, std::string name, bool is_object) in->parent_element = this; } -goos::Object GetMethodElement::to_form(const Env& env) const { +goos::Object GetMethodElement::to_form_internal(const Env& env) const { return pretty_print::build_list(m_is_object ? "method-of-object" : "method-of-type", m_in->to_form(env), m_name); } @@ -1520,7 +1551,7 @@ void GetMethodElement::get_modified_regs(RegSet& regs) const { StringConstantElement::StringConstantElement(const std::string& value) : m_value(value) {} -goos::Object StringConstantElement::to_form(const Env&) const { +goos::Object StringConstantElement::to_form_internal(const Env&) const { return goos::StringObject::make_new(m_value); } diff --git a/decompiler/IR2/Form.h b/decompiler/IR2/Form.h index d25230afa2..d8619f1221 100644 --- a/decompiler/IR2/Form.h +++ b/decompiler/IR2/Form.h @@ -21,14 +21,17 @@ class FormElement { public: Form* parent_form = nullptr; - virtual goos::Object to_form(const Env& env) const = 0; - virtual goos::Object to_form_as_condition(const Env& env) const; + goos::Object to_form(const Env& env) const; + virtual goos::Object to_form_internal(const Env& env) const = 0; + virtual goos::Object to_form_as_condition_internal(const Env& env) const; virtual ~FormElement() = default; virtual void apply(const std::function& f) = 0; virtual void apply_form(const std::function& f) = 0; virtual bool is_sequence_point() const { return true; } virtual void collect_vars(VariableSet& vars) const = 0; virtual void get_modified_regs(RegSet& regs) const = 0; + virtual bool active() const; + std::string to_string(const Env& env) const; bool has_side_effects(); @@ -52,11 +55,12 @@ class SimpleExpressionElement : public FormElement { public: explicit SimpleExpressionElement(SimpleExpression expr, int my_idx); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; bool is_sequence_point() const override; void collect_vars(VariableSet& vars) const override; + void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; // void push_to_stack(const Env& env, FormStack& stack) override; void update_from_stack(const Env& env, FormPool& pool, @@ -150,7 +154,7 @@ class StoreElement : public FormElement { public: explicit StoreElement(const StoreOp* op); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -169,7 +173,7 @@ class StoreElement : public FormElement { class LoadSourceElement : public FormElement { public: LoadSourceElement(Form* addr, int size, LoadVarOp::Kind kind); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -196,12 +200,18 @@ class LoadSourceElement : public FormElement { class SimpleAtomElement : public FormElement { public: explicit SimpleAtomElement(const SimpleAtom& var); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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 get_modified_regs(RegSet& regs) const override; const SimpleAtom& atom() const { return m_atom; } + void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; + void update_from_stack(const Env& env, + FormPool& pool, + FormStack& stack, + std::vector* result, + bool allow_side_effects) override; // void push_to_stack(const Env& env, FormStack& stack) override; private: @@ -214,7 +224,7 @@ class SimpleAtomElement : public FormElement { class SetVarElement : public FormElement { public: SetVarElement(const Variable& var, Form* value, bool is_sequence_point); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; bool is_sequence_point() const override; @@ -226,6 +236,7 @@ class SetVarElement : public FormElement { std::vector* result, bool allow_side_effects) override; void get_modified_regs(RegSet& regs) const override; + bool active() const override; const Variable& dst() const { return m_dst; } const Form* src() const { return m_src; } @@ -233,11 +244,25 @@ class SetVarElement : public FormElement { bool is_eliminated_coloring_move() const { return m_is_eliminated_coloring_move; } void eliminate_as_coloring_move() { m_is_eliminated_coloring_move = true; } + bool is_dead_set() const { return m_is_dead_set; } + void mark_as_dead_set() { m_is_dead_set = true; } + + bool is_dead_false_set() const { return m_is_dead_false; } + void mark_as_dead_false() { m_is_dead_false = true; } + private: Variable m_dst; Form* m_src = nullptr; bool m_is_sequence_point = true; + + // is this a compiler-inserted move at the beginning of a function + // that should be eliminated? bool m_is_eliminated_coloring_move = false; + // is this a (set! var expr) which consumes the reg for expr, + // and var is written and unused? + bool m_is_dead_set = false; + // is this a (set! var #f) where the value of #f isn't used? + bool m_is_dead_false = false; }; /*! @@ -248,7 +273,7 @@ class SetVarElement : public FormElement { class SetFormFormElement : public FormElement { public: SetFormFormElement(Form* dst, Form* src); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; bool is_sequence_point() const override; @@ -273,7 +298,7 @@ class SetFormFormElement : public FormElement { class AtomicOpElement : public FormElement { public: explicit AtomicOpElement(const AtomicOp* op); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -291,7 +316,7 @@ class AtomicOpElement : public FormElement { class AsmOpElement : public FormElement { public: explicit AsmOpElement(const AsmOp* op); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -318,8 +343,8 @@ class ConditionElement : public FormElement { std::optional src1, RegSet consumed, bool flipped); - goos::Object to_form(const Env& env) const override; - goos::Object to_form_as_condition(const Env& env) const override; + goos::Object to_form_internal(const Env& env) const override; + goos::Object to_form_as_condition_internal(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; @@ -351,7 +376,7 @@ class ConditionElement : public FormElement { class FunctionCallElement : public FormElement { public: explicit FunctionCallElement(const CallOp* op); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -374,7 +399,7 @@ class FunctionCallElement : public FormElement { class BranchElement : public FormElement { public: explicit BranchElement(const BranchOp* op); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -397,7 +422,7 @@ class ReturnElement : public FormElement { Form* dead_code = nullptr; ReturnElement(Form* _return_code, Form* _dead_code) : return_code(_return_code), dead_code(_dead_code) {} - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -432,7 +457,7 @@ class BreakElement : public FormElement { Form* dead_code = nullptr; BreakElement(Form* _return_code, Form* _dead_code) : return_code(_return_code), dead_code(_dead_code) {} - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -466,7 +491,7 @@ class CondWithElseElement : public FormElement { bool already_rewritten = false; CondWithElseElement(std::vector _entries, Form* _else_ir) : entries(std::move(_entries)), else_ir(_else_ir) {} - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -485,7 +510,7 @@ class CondWithElseElement : public FormElement { class EmptyElement : public FormElement { public: EmptyElement() = default; - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -501,7 +526,7 @@ class EmptyElement : public FormElement { class WhileElement : public FormElement { public: WhileElement(Form* _condition, Form* _body) : condition(_condition), body(_body) {} - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -520,7 +545,7 @@ class WhileElement : public FormElement { class UntilElement : public FormElement { public: UntilElement(Form* _condition, Form* _body) : condition(_condition), body(_body) {} - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -555,7 +580,7 @@ class ShortCircuitElement : public FormElement { bool already_rewritten = false; explicit ShortCircuitElement(std::vector _entries) : entries(std::move(_entries)) {} - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -587,7 +612,7 @@ class CondNoElseElement : public FormElement { bool already_rewritten = false; std::vector entries; explicit CondNoElseElement(std::vector _entries) : entries(std::move(_entries)) {} - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -601,7 +626,7 @@ class CondNoElseElement : public FormElement { class AbsElement : public FormElement { public: explicit AbsElement(Variable _source, RegSet _consumed); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -631,7 +656,7 @@ class AshElement : public FormElement { std::optional _clobber, bool _is_signed, RegSet _consumed); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -652,7 +677,7 @@ class TypeOfElement : public FormElement { Form* value; std::optional clobber; TypeOfElement(Form* _value, std::optional _clobber); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -688,7 +713,7 @@ class ConditionalMoveFalseElement : public FormElement { Form* source = nullptr; bool on_zero = false; ConditionalMoveFalseElement(Variable _dest, Form* _source, bool _on_zero); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -752,7 +777,7 @@ class GenericElement : public FormElement { GenericElement(GenericOperator op, Form* arg0, Form* arg1); GenericElement(GenericOperator op, std::vector forms); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -775,7 +800,7 @@ class GenericElement : public FormElement { class CastElement : public FormElement { public: explicit CastElement(TypeSpec type, Form* source, bool numeric = false); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -837,7 +862,7 @@ class DerefElement : public FormElement { public: DerefElement(Form* base, bool is_addr_of, DerefToken token); DerefElement(Form* base, bool is_addr_of, std::vector tokens); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -862,7 +887,7 @@ class DerefElement : public FormElement { class DynamicMethodAccess : public FormElement { public: explicit DynamicMethodAccess(Variable source); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -883,7 +908,7 @@ class ArrayFieldAccess : public FormElement { const std::vector& deref_tokens, int expected_stride, int constant_offset); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -904,7 +929,7 @@ class ArrayFieldAccess : public FormElement { class GetMethodElement : public FormElement { public: GetMethodElement(Form* in, std::string name, bool is_object); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -919,7 +944,7 @@ class GetMethodElement : public FormElement { class StringConstantElement : public FormElement { public: StringConstantElement(const std::string& value); - goos::Object to_form(const Env& env) const override; + goos::Object to_form_internal(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; @@ -958,7 +983,7 @@ class Form { } bool is_single_element() const { return m_elements.size() == 1; } FormElement* operator[](int idx) { return m_elements.at(idx); } - FormElement* at(int idx) { return m_elements.at(idx); } + FormElement*& at(int idx) { return m_elements.at(idx); } const FormElement* operator[](int idx) const { return m_elements.at(idx); } int size() const { return int(m_elements.size()); } FormElement* back() const { diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index ba8cf8239b..ffd55b638a 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -910,7 +910,7 @@ void FunctionCallElement::update_from_stack(const Env& env, match_result = match(matcher, temp_form); if (match_result.matched) { auto alloc = match_result.maps.strings.at(allocation); - if (alloc != "global" && alloc != "debug") { + if (alloc != "global" && alloc != "debug" && alloc != "process") { throw std::runtime_error("Unrecognized heap symbol for new: " + alloc); } auto type_2 = match_result.maps.strings.at(type_for_arg); @@ -922,13 +922,25 @@ void FunctionCallElement::update_from_stack(const Env& env, auto quoted_type = pool.alloc_single_element_form( nullptr, SimpleAtom::make_sym_ptr(type_2)); - std::vector new_args = dynamic_cast(new_form)->elts(); - new_args.at(1) = quoted_type; + if (alloc == "global" && type_1 == "pair") { + // cons! + // (new 'global 'pair a b) -> (cons a b) + std::vector cons_args = {dynamic_cast(new_form)->elts().at(2), + dynamic_cast(new_form)->elts().at(3)}; + auto cons_op = pool.alloc_element( + GenericOperator::make_fixed(FixedOperatorKind::CONS), cons_args); + result->push_back(cons_op); + return; + } else { + // just normal construction on the heap + std::vector new_args = dynamic_cast(new_form)->elts(); + new_args.at(1) = quoted_type; - auto new_op = pool.alloc_element( - GenericOperator::make_fixed(FixedOperatorKind::NEW), new_args); - result->push_back(new_op); - return; + auto new_op = pool.alloc_element( + GenericOperator::make_fixed(FixedOperatorKind::NEW), new_args); + result->push_back(new_op); + return; + } } else { throw std::runtime_error("Failed to match new method"); } @@ -1035,6 +1047,7 @@ void WhileElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stac void CondNoElseElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) { if (already_rewritten) { stack.push_form_element(this, true); + return; } for (auto& entry : entries) { for (auto form : {entry.condition, entry.body}) { @@ -1057,6 +1070,17 @@ void CondNoElseElement::push_to_stack(const Env& env, FormPool& pool, FormStack& } } + // raise expression. + auto top_condition = entries.front().condition; + if (!top_condition->is_single_element()) { + auto real_condition = top_condition->back(); + top_condition->pop_back(); + for (auto x : top_condition->elts()) { + x->push_to_stack(env, pool, stack); + } + top_condition->elts() = {real_condition}; + } + if (used_as_value) { stack.push_value_to_reg(final_destination, pool.alloc_single_form(nullptr, this), true); } else { @@ -1068,6 +1092,7 @@ void CondNoElseElement::push_to_stack(const Env& env, FormPool& pool, FormStack& void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) { if (already_rewritten) { stack.push_form_element(this, true); + return; } // first, let's try to detect if all bodies write the same value std::optional last_var; @@ -1129,6 +1154,7 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac } } + // process else. FormStack temp_stack; for (auto& elt : else_ir->elts()) { elt->push_to_stack(env, pool, temp_stack); @@ -1146,6 +1172,7 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac else_ir->push_back(e); } + // raise expression. auto top_condition = entries.front().condition; if (!top_condition->is_single_element()) { auto real_condition = top_condition->back(); @@ -1181,6 +1208,7 @@ void ShortCircuitElement::push_to_stack(const Env& env, FormPool& pool, FormStac } else { if (already_rewritten) { stack.push_form_element(this, true); + return; } for (int i = 0; i < int(entries.size()); i++) { auto& entry = entries.at(i); @@ -1201,6 +1229,17 @@ void ShortCircuitElement::push_to_stack(const Env& env, FormPool& pool, FormStac entry.condition->push_back(e); } } + + auto top_condition = entries.front().condition; + if (!top_condition->is_single_element()) { + auto real_condition = top_condition->back(); + top_condition->pop_back(); + for (auto x : top_condition->elts()) { + x->push_to_stack(env, pool, stack); + } + top_condition->elts() = {real_condition}; + } + assert(used_as_value.has_value()); stack.push_value_to_reg(final_result, pool.alloc_single_form(nullptr, this), true); already_rewritten = true; @@ -1438,7 +1477,8 @@ void AtomicOpElement::push_to_stack(const Env& env, FormPool&, FormStack& stack) auto as_special = dynamic_cast(m_op); if (as_special) { - if (as_special->kind() == SpecialOp::Kind::NOP) { + if (as_special->kind() == SpecialOp::Kind::NOP || + as_special->kind() == SpecialOp::Kind::BREAK) { stack.push_form_element(this, true); return; } @@ -1687,4 +1727,20 @@ void ConditionalMoveFalseElement::push_to_stack(const Env&, FormPool&, FormStack stack.push_form_element(this, true); } +void SimpleAtomElement::push_to_stack(const Env&, FormPool&, FormStack& stack) { + stack.push_form_element(this, true); +} + +void SimpleAtomElement::update_from_stack(const Env&, + FormPool&, + FormStack&, + std::vector* result, + bool) { + result->push_back(this); +} + +void SimpleExpressionElement::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 ae51909e39..a1c945487a 100644 --- a/decompiler/IR2/IR2_common.h +++ b/decompiler/IR2/IR2_common.h @@ -120,6 +120,7 @@ enum class FixedOperatorKind { GEQ, EQ, NEQ, + CONS, INVALID }; diff --git a/decompiler/analysis/cfg_builder.cpp b/decompiler/analysis/cfg_builder.cpp index 2af8851bad..db361b0911 100644 --- a/decompiler/analysis/cfg_builder.cpp +++ b/decompiler/analysis/cfg_builder.cpp @@ -1204,6 +1204,76 @@ Form* merge_cond_else_with_sc_cond(FormPool& pool, return result; } +namespace { +template +bool contains(const std::vector& vec, const T& val) { + for (auto& x : vec) { + if (val == x) { + return true; + } + } + return false; +} +} // namespace + +template +void convert_and_inline(FormPool& pool, Function& f, const BlockVtx* as_block, T* output) { + auto start_op = f.ir2.atomic_ops->block_id_to_first_atomic_op.at(as_block->block_id); + auto end_op = f.ir2.atomic_ops->block_id_to_end_atomic_op.at(as_block->block_id); + std::vector add_map; + for (auto i = start_op; i < end_op; i++) { + // convert to a form. + auto op = f.ir2.atomic_ops->ops.at(i)->get_as_form(pool, f.ir2.env); + bool add = true; + + // check if we got a set, which we might want to eliminate + auto op_as_set = dynamic_cast(op); + if (op_as_set) { + // check for deadness + if (op_as_set->is_dead_set()) { + // we want to eliminate, but we should we fix up the register info. + add = false; + auto consumed_expr = + dynamic_cast(op_as_set->src()->try_as_single_element()); + assert(consumed_expr); + auto& consumed = consumed_expr->expr().get_arg(0).var(); + for (int j = i; j-- > start_op;) { + auto& ri = f.ir2.env.reg_use().op.at(j); + auto& ao = f.ir2.atomic_ops->ops.at(j); + if (contains(ao->write_regs(), consumed.reg())) { + ri.written_and_unused.insert(consumed.reg()); + // fmt::print("GOT 3, making {} wau by {}\n", consumed.reg().to_charp(), + // ao->to_string(f.ir2.env)); + // HACK - regenerate: + if (add_map.at(j - start_op) != -1) { + // fmt::print("regenerating {} to ", output->at(add_map.at(j - + // start_op))->to_string(f.ir2.env)); + output->at(add_map.at(j - start_op)) = + f.ir2.atomic_ops->ops.at(j)->get_as_form(pool, f.ir2.env); + // fmt::print("{}\n", output->at(add_map.at(j - + // start_op))->to_string(f.ir2.env)); + } + break; + } + + if (contains(ao->read_regs(), consumed.reg())) { + // fmt::print("GOT 2, making {} consumed by {}\n", consumed.reg().to_charp(), + // ao->to_string(f.ir2.env)); + ri.consumes.insert(consumed.reg()); + break; + } + } + } + } + if (add) { + add_map.push_back(output->size()); + output->push_back(op); + } else { + add_map.push_back(-1); + } + } +} + void insert_cfg_into_list(FormPool& pool, Function& f, const CfgVtx* vtx, @@ -1216,12 +1286,7 @@ void insert_cfg_into_list(FormPool& pool, insert_cfg_into_list(pool, f, x, output); } } else if (as_block) { - // inline the ops. - auto start_op = f.ir2.atomic_ops->block_id_to_first_atomic_op.at(as_block->block_id); - auto end_op = f.ir2.atomic_ops->block_id_to_end_atomic_op.at(as_block->block_id); - for (auto i = start_op; i < end_op; i++) { - output->push_back(f.ir2.atomic_ops->ops.at(i)->get_as_form(pool, f.ir2.env)); - } + convert_and_inline(pool, f, as_block, output); } else { auto ir = cfg_to_ir(pool, f, vtx); for (auto x : ir->elts()) { @@ -1233,16 +1298,9 @@ void insert_cfg_into_list(FormPool& pool, Form* cfg_to_ir(FormPool& pool, Function& f, const CfgVtx* vtx) { if (dynamic_cast(vtx)) { auto* bv = dynamic_cast(vtx); - Form* output = pool.alloc_empty_form(); - auto start_op = f.ir2.atomic_ops->block_id_to_first_atomic_op.at(bv->block_id); - auto end_op = f.ir2.atomic_ops->block_id_to_end_atomic_op.at(bv->block_id); - for (auto i = start_op; i < end_op; i++) { - output->push_back(f.ir2.atomic_ops->ops.at(i)->get_as_form(pool, f.ir2.env)); - } - + convert_and_inline(pool, f, bv, output); return output; - } else if (dynamic_cast(vtx)) { auto* sv = dynamic_cast(vtx); Form* output = pool.alloc_empty_form(); diff --git a/decompiler/analysis/expression_build.cpp b/decompiler/analysis/expression_build.cpp index e7e5cbe099..3790e66411 100644 --- a/decompiler/analysis/expression_build.cpp +++ b/decompiler/analysis/expression_build.cpp @@ -7,70 +7,13 @@ namespace decompiler { -// TODO - remove all these and put them in the analysis methods instead. -void clean_up_ifs(Form* top_level_form, const Env&) { +/*void clean_up_ifs(Form* top_level_form, const Env&) { bool changed = true; while (changed) { for (auto x : top_level_form->elts()) { assert(x->parent_form == top_level_form); } changed = false; - top_level_form->apply([&](FormElement* elt) { - auto as_cne = dynamic_cast(elt); - if (!as_cne) { - return; - } - - auto top_condition = as_cne->entries.front().condition; - if (!top_condition->is_single_element() && elt->parent_form) { - auto real_condition = top_condition->back(); - top_condition->pop_back(); - - auto& parent_vector = elt->parent_form->elts(); - // find us in the parent vector - auto me = std::find_if(parent_vector.begin(), parent_vector.end(), - [&](FormElement* x) { return x == elt; }); - 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; - } - }); - - for (auto x : top_level_form->elts()) { - assert(x->parent_form == top_level_form); - } - top_level_form->apply([&](FormElement* elt) { - auto as_sc = dynamic_cast(elt); - if (!as_sc) { - return; - } - - auto top_condition = as_sc->entries.front().condition; - if (!top_condition->is_single_element() && elt->parent_form) { - auto real_condition = top_condition->back(); - top_condition->pop_back(); - - auto& parent_vector = elt->parent_form->elts(); - // find us in the parent vector - auto me = std::find_if(parent_vector.begin(), parent_vector.end(), - [&](FormElement* x) { return x == elt; }); - 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; - } - }); for (auto x : top_level_form->elts()) { assert(x->parent_form == top_level_form); @@ -107,7 +50,7 @@ void clean_up_ifs(Form* top_level_form, const Env&) { } }); } -} +}*/ bool convert_to_expressions(Form* top_level_form, FormPool& pool, @@ -118,27 +61,6 @@ bool convert_to_expressions(Form* top_level_form, // fmt::print("Before anything:\n{}\n", // pretty_print::to_string(top_level_form->to_form(f.ir2.env))); try { - // top_level_form->apply_form([&](Form* form) { - // if (form == top_level_form || !form->is_single_element()) { - // FormStack stack; - // for (auto& entry : form->elts()) { - // fmt::print("push {} to stack\n", entry->to_form(f.ir2.env).print()); - // entry->push_to_stack(f.ir2.env, pool, stack); - // } - // std::vector new_entries; - // if (form == top_level_form && f.type.last_arg() != TypeSpec("none")) { - // new_entries = stack.rewrite_to_get_reg(pool, Register(Reg::GPR, Reg::V0)); - // } else { - // new_entries = stack.rewrite(pool); - // } - // assert(!new_entries.empty()); - // form->clear(); - // for (auto x : new_entries) { - // form->push_back(x); - // } - // } - // }); - FormStack stack; for (auto& entry : top_level_form->elts()) { // fmt::print("push {} to stack\n", entry->to_form(f.ir2.env).print()); @@ -172,10 +94,10 @@ bool convert_to_expressions(Form* top_level_form, assert(x->parent_form == top_level_form); } // fix up stuff - clean_up_ifs(top_level_form, f.ir2.env); } catch (std::exception& e) { - std::string warning = fmt::format("Expression building failed: {}", e.what()); + std::string warning = + fmt::format("Expression building failed in {}: {}", f.guessed_name.to_string(), e.what()); lg::warn(warning); f.warnings.append(";; " + warning); return false; @@ -206,16 +128,6 @@ bool convert_to_expressions(Form* top_level_form, } } - // strip out coloring moves - for (auto it = top_level_form->elts().begin(); it != top_level_form->elts().end();) { - auto as_x = dynamic_cast(*it); - if (as_x && as_x->is_eliminated_coloring_move()) { - it = top_level_form->elts().erase(it); - } else { - it++; - } - } - return true; } } // namespace decompiler diff --git a/decompiler/config/jak1_ntsc_black_label/type_hints.jsonc b/decompiler/config/jak1_ntsc_black_label/type_hints.jsonc index 1314a882f3..1ac50d3a91 100644 --- a/decompiler/config/jak1_ntsc_black_label/type_hints.jsonc +++ b/decompiler/config/jak1_ntsc_black_label/type_hints.jsonc @@ -80,8 +80,9 @@ ], "(method 14 dead-pool)":[ - [23, ["v1", "process"]], // bad visit order with #f? - [28, ["s4", "(pointer process-tree)"]] // bug in real game, see gkernel.gc + // bug in game! + [25, ["v1", "(pointer process-tree)"]], + [30, ["s4", "(pointer process-tree)"]] ], "throw":[ diff --git a/decompiler/main.cpp b/decompiler/main.cpp index 1b0027273d..b3d4285b94 100644 --- a/decompiler/main.cpp +++ b/decompiler/main.cpp @@ -5,6 +5,7 @@ #include "common/log/log.h" #include "config.h" #include "common/util/FileUtil.h" +#include "common/versions.h" int main(int argc, char** argv) { using namespace decompiler; @@ -13,12 +14,13 @@ int main(int argc, char** argv) { lg::set_stdout_level(lg::level::info); lg::set_flush_level(lg::level::info); lg::initialize(); + lg::info("GOAL Decompiler version {}\n", versions::DECOMPILER_VERSION); file_util::init_crc(); init_opcode_info(); if (argc != 4) { - printf("Usage: jak_disassembler \n"); + printf("Usage: decompiler \n"); return 1; } diff --git a/doc/decompiler_changelog.md b/doc/decompiler_changelog.md new file mode 100644 index 0000000000..2d0c373d61 --- /dev/null +++ b/doc/decompiler_changelog.md @@ -0,0 +1,9 @@ +## Version 1 +- Fixed bug where eliminated moves would appear in output in the first condition of a function as `(set! a a)`. +- Improved expression building immediately before the condition of `if`/`cond` +- Recognize `(new 'global 'pair )` as `(cons )` +- Remove useless `(set! #f)` +- Remove useless `(set! )` and eliminate useless temporaries created by these. +- Remove useless `set!`s sometimes appearing around functions `(set! (some-function ...))` when the result is unused, but moved into a different register. +- Recognize `(break!)` (GOAL breakpoint) +- Support `(new 'process ...)` \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 66cc571989..3b91759a54 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -23,6 +23,7 @@ add_executable(goalc-test ${CMAKE_CURRENT_LIST_DIR}/decompiler/test_FormExpressionBuild.cpp ${CMAKE_CURRENT_LIST_DIR}/decompiler/test_FormExpressionBuildLong.cpp ${CMAKE_CURRENT_LIST_DIR}/decompiler/test_InstructionParser.cpp + ${CMAKE_CURRENT_LIST_DIR}/decompiler/test_gkernel_decomp.cpp ${GOALC_TEST_FRAMEWORK_SOURCES} ${GOALC_TEST_CASES}) diff --git a/test/decompiler/test_FormBeforeExpressions.cpp b/test/decompiler/test_FormBeforeExpressions.cpp index cbcbdfa792..23b786c471 100644 --- a/test/decompiler/test_FormBeforeExpressions.cpp +++ b/test/decompiler/test_FormBeforeExpressions.cpp @@ -187,7 +187,6 @@ TEST_F(FormRegressionTest, FormatString) { std::string type = "(function bfloat bfloat)"; std::string expected = "(begin\n" - " (set! a0-0 a0-0)\n" " (set! t9-0 format)\n" " (set! a0-1 '#t)\n" " (set! a1-0 L343)\n" @@ -381,7 +380,6 @@ TEST_F(FormRegressionTest, DynamicMethodAccess) { // nothing. " )\n" " )\n" - " (set! v1-5 '#f)\n" " (ret-value v0-0)\n" " )"; test_no_expr(func, type, expected); @@ -423,8 +421,6 @@ TEST_F(FormRegressionTest, SimpleLoopMergeCheck) { " (set! a0-0 (cdr a0-0))\n" // should have merged " (set! v1-0 (+ v1-0 1))\n" // also should have merged " )\n" - " (set! v1-1 '#f)\n" - " (set! v1-2 '#f)\n" " (set! v0-0 (car a0-0))\n" " (ret-value v0-0)\n" " )"; @@ -495,7 +491,6 @@ TEST_F(FormRegressionTest, And) { " (set! v0-0 (+ v0-0 1))\n" // merged (and the result) " (set! v1-1 (cdr v1-1))\n" // also merged. " )\n" - " (set! v1-2 '#f)\n" // while's false, I think. " )\n" " )" "(ret-value v0-0))\n"; @@ -558,8 +553,6 @@ TEST_F(FormRegressionTest, FunctionCall) { std::string expected = "(begin (if\n" // this if needs regrouping. " (begin\n" - " (set! a0-0 a0-0)\n" // s5-0 is the thing to check - " (set! a1-0 a1-0)\n" // gp-0 is the list " (while\n" " (begin\n" " (or\n" @@ -576,7 +569,6 @@ TEST_F(FormRegressionTest, FunctionCall) { " )\n" " (set! a1-0 (cdr a1-0))\n" // get next (merged) " )\n" - " (set! v1-2 '#f)\n" // while loop thing " (set! v1-3 '())\n" // " (!= a1-0 v1-3)\n" // IF CONDITION " )\n" @@ -692,9 +684,7 @@ TEST_F(FormRegressionTest, NestedAndOr) { std::string type = "(function object (function object object object) object)"; std::string expected = "(begin\n" - " (set! a0-0 a0-0)\n" // gp-0 = list - " (set! a1-0 a1-0)\n" // s5-0 = func - " (set! s4-0 -1)\n" // s4-0 = flag + " (set! s4-0 -1)\n" // s4-0 = flag " (while\n" " (nonzero? s4-0)\n" // there is stuff to do... " (set! s4-0 0)\n" // flag = 0 @@ -744,14 +734,10 @@ TEST_F(FormRegressionTest, NestedAndOr) { " (set! (car s3-0) s1-0)\n" // set iter's car to cadr " (set! v1-4 (cdr s3-0))\n" // current cdr " (set! (car v1-4) s2-0)\n" // set cadr - " (set! v1-5 s2-0)\n" // iteration thing? " )\n" " (set! s3-0 (cdr s3-0))\n" // increment! " )\n" - " (set! v1-10 '#f)\n" - " (set! v1-11 '#f)\n" " )\n" - " (set! v1-12 '#f)\n" " (set! v0-1 a0-0)\n" " (ret-value v0-1)\n" " )"; @@ -795,7 +781,6 @@ TEST_F(FormRegressionTest, NewMethod) { std::string expected = "(begin (when\n" " (begin\n" - " (set! a2-0 a2-0)\n" // gp-0 is size " (set! v1-0 object)\n" " (set! t9-0 (-> v1-0 methods-by-name new))\n" // object new " (set! v1-1 a1-0)\n" // ? @@ -847,7 +832,7 @@ TEST_F(FormRegressionTest, Recursive) { std::string type = "(function int int)"; std::string expected = "(begin (cond\n" - " ((begin (set! a0-0 a0-0) (set! v1-0 1) (= a0-0 v1-0)) (set! v0-0 1))\n" // base + " ((begin (set! v1-0 1) (= a0-0 v1-0)) (set! v0-0 1))\n" // base " (else\n" " (set! t9-0 fact)\n" // recurse! " (set! a0-1 (+ a0-0 -1))\n" diff --git a/test/decompiler/test_FormExpressionBuild.cpp b/test/decompiler/test_FormExpressionBuild.cpp index 82a3e5115b..1da94773e6 100644 --- a/test/decompiler/test_FormExpressionBuild.cpp +++ b/test/decompiler/test_FormExpressionBuild.cpp @@ -1276,7 +1276,7 @@ TEST_F(FormRegressionTest, ExprAppend) { " (set! v1-1 arg0)\n" " (while (!= (cdr v1-1) '()) (nop!) (nop!) (set! v1-1 (cdr v1-1)))\n" " (set! a2-1 '#f)\n" - " (when (!= v1-1 '()) (set! (cdr v1-1) arg1) (set! v1-2 arg1))\n" + " (if (!= v1-1 '()) (set! (cdr v1-1) arg1))\n" " arg0\n" " )\n" " )"; @@ -1471,7 +1471,7 @@ TEST_F(FormRegressionTest, ExprInsertCons) { std::string expected = "(begin\n" " (set! a3-0 (delete-car! (car arg0) arg1))\n" - " (new 'global 'pair arg0 a3-0)\n" + " (cons arg0 a3-0)\n" " )"; test_with_expr(func, type, expected, true, ""); } @@ -1599,7 +1599,6 @@ TEST_F(FormRegressionTest, ExprSort) { " (set! s4-0 (+ s4-0 1))\n" " (set! (car s3-0) s1-0)\n" " (set! (car (cdr s3-0)) s2-0)\n" - " (set! v1-5 s2-0)\n" " )\n" " (set! s3-0 (cdr s3-0))\n" " )\n" @@ -2103,7 +2102,7 @@ TEST_F(FormRegressionTest, ExprPrintl) { std::string expected = "(begin\n" " (set! a0-1 arg0)\n" - " (set! v1-2 ((method-of-type (rtype-of a0-1) print) a0-1))\n" + " ((method-of-type (rtype-of a0-1) print) a0-1)\n" " (format (quote #t) \"~%\")\n" " arg0\n" " )"; diff --git a/test/decompiler/test_FormExpressionBuildLong.cpp b/test/decompiler/test_FormExpressionBuildLong.cpp index 7084a99ce6..77b35443df 100644 --- a/test/decompiler/test_FormExpressionBuildLong.cpp +++ b/test/decompiler/test_FormExpressionBuildLong.cpp @@ -1974,16 +1974,10 @@ TEST_F(FormRegressionTest, ExprValid) { "(begin\n" " (set!\n" " v1-1\n" - " (begin\n" - " (set! arg0 arg0)\n" - " (set! arg1 arg1)\n" - " (set! arg2 arg2)\n" - " (set! arg4 arg4)\n" " (and\n" " (>= (the-as uint arg0) (the-as uint __START-OF-TABLE__))\n" " (< (the-as uint arg0) (the-as uint 134217728))\n" " )\n" - " )\n" " )\n" " (cond\n" " ((not arg1)\n" @@ -1991,30 +1985,24 @@ TEST_F(FormRegressionTest, ExprValid) { " ((nonzero? (logand (the-as int arg0) 3))\n" " (if\n" " arg2\n" - " (set!\n" - " v1-4\n" " (format\n" " arg4\n" " \"ERROR: object #x~X ~S is not a valid object (misaligned)~%\"\n" " arg0\n" " arg2\n" " )\n" - " )\n" " )\n" " (quote #f)\n" " )\n" " ((not v1-1)\n" " (if\n" " arg2\n" - " (set!\n" - " v1-6\n" " (format\n" " arg4\n" " \"ERROR: object #x~X ~S is not a valid object (bad address)~%\"\n" " arg0\n" " arg2\n" " )\n" - " )\n" " )\n" " (quote #f)\n" " )\n" @@ -2029,8 +2017,6 @@ TEST_F(FormRegressionTest, ExprValid) { " ((nonzero? (logand (the-as int arg0) 15))\n" " (if\n" " arg2\n" - " (set!\n" - " v1-8\n" " (format\n" " arg4\n" " \"ERROR: object #x~X ~S is not a valid object of type '~A' (misaligned)~%\"\n" @@ -2038,7 +2024,6 @@ TEST_F(FormRegressionTest, ExprValid) { " arg2\n" " arg1\n" " )\n" - " )\n" " )\n" " (quote #f)\n" " )\n" @@ -2052,8 +2037,6 @@ TEST_F(FormRegressionTest, ExprValid) { " )\n" " (if\n" " arg2\n" - " (set!\n" - " v1-13\n" " (format\n" " arg4\n" " \"ERROR: object #x~X ~S is not a valid object of type '~A' (bad address)~%\"\n" @@ -2061,7 +2044,6 @@ TEST_F(FormRegressionTest, ExprValid) { " arg2\n" " arg1\n" " )\n" - " )\n" " )\n" " (quote #f)\n" " )\n" @@ -2073,8 +2055,6 @@ TEST_F(FormRegressionTest, ExprValid) { " ((!= (logand (the-as int arg0) 7) 2)\n" " (if\n" " arg2\n" - " (set!\n" - " v1-15\n" " (format\n" " arg4\n" " \"ERROR: object #x~X ~S is not a valid object of type '~A' (misaligned)~%\"\n" @@ -2082,15 +2062,12 @@ TEST_F(FormRegressionTest, ExprValid) { " arg2\n" " arg1\n" " )\n" - " )\n" " )\n" " (quote #f)\n" " )\n" " ((not v1-1)\n" " (if\n" " arg2\n" - " (set!\n" - " v1-17\n" " (format\n" " arg4\n" " \"ERROR: object #x~X ~S is not a valid object of type '~A' (bad address)~%\"\n" @@ -2098,7 +2075,6 @@ TEST_F(FormRegressionTest, ExprValid) { " arg2\n" " arg1\n" " )\n" - " )\n" " )\n" " (quote #f)\n" " )\n" @@ -2111,8 +2087,6 @@ TEST_F(FormRegressionTest, ExprValid) { " (else\n" " (if\n" " arg2\n" - " (set!\n" - " v1-20\n" " (format\n" " arg4\n" " \"ERROR: object #x~X ~S is not a valid object of type '~A' (misaligned)~%\"\n" @@ -2120,7 +2094,6 @@ TEST_F(FormRegressionTest, ExprValid) { " arg2\n" " arg1\n" " )\n" - " )\n" " )\n" " (quote #f)\n" " )\n" @@ -2129,8 +2102,6 @@ TEST_F(FormRegressionTest, ExprValid) { " ((!= (logand (the-as int arg0) 7) 4)\n" " (if\n" " arg2\n" - " (set!\n" - " v1-22\n" " (format\n" " arg4\n" " \"ERROR: object #x~X ~S is not a valid object of type '~A' (misaligned)~%\"\n" @@ -2138,15 +2109,12 @@ TEST_F(FormRegressionTest, ExprValid) { " arg2\n" " arg1\n" " )\n" - " )\n" " )\n" " (quote #f)\n" " )\n" " ((not v1-1)\n" " (if\n" " arg2\n" - " (set!\n" - " v1-24\n" " (format\n" " arg4\n" " \"ERROR: object #x~X ~S is not a valid object of type '~A' (bad address)~%\"\n" @@ -2154,15 +2122,12 @@ TEST_F(FormRegressionTest, ExprValid) { " arg2\n" " arg1\n" " )\n" - " )\n" " )\n" " (quote #f)\n" " )\n" " ((and (= arg1 type) (!= (rtype-of arg0) type))\n" " (if\n" " arg2\n" - " (set!\n" - " v1-31\n" " (format\n" " arg4\n" " \"ERROR: object #x~X ~S is not a valid object of type '~A' (invalid type " @@ -2172,11 +2137,11 @@ TEST_F(FormRegressionTest, ExprValid) { " arg1\n" " (rtype-of arg0)\n" " )\n" - " )\n" " )\n" " (quote #f)\n" " )\n" " (else\n" + // todo - why isn't this compacted? " (set!\n" " v1-33\n" " (and\n" @@ -2188,8 +2153,6 @@ TEST_F(FormRegressionTest, ExprValid) { " (v1-33\n" " (if\n" " arg2\n" - " (set!\n" - " v1-37\n" " (format\n" " arg4\n" " \"ERROR: object #x~X ~S is not a valid object of type '~A' (invalid type " @@ -2199,15 +2162,12 @@ TEST_F(FormRegressionTest, ExprValid) { " arg1\n" " (rtype-of arg0)\n" " )\n" - " )\n" " )\n" " (quote #f)\n" " )\n" " ((not (type-type? (rtype-of arg0) arg1))\n" " (if\n" " arg2\n" - " (set!\n" - " v1-41\n" " (format\n" " arg4\n" " \"ERROR: object #x~X ~S is not a valid object of type '~A' (is type '~A' " @@ -2217,7 +2177,6 @@ TEST_F(FormRegressionTest, ExprValid) { " arg1\n" " (rtype-of arg0)\n" " )\n" - " )\n" " )\n" " (quote #f)\n" " )\n" @@ -2228,8 +2187,6 @@ TEST_F(FormRegressionTest, ExprValid) { " ((>= (the-as uint arg0) (the-as uint v1-44))\n" " (if\n" " arg2\n" - " (set!\n" - " v1-46\n" " (format\n" " arg4\n" " \"ERROR: object #x~X ~S is not a valid object of type '~A' (not in symbol " @@ -2238,7 +2195,6 @@ TEST_F(FormRegressionTest, ExprValid) { " arg2\n" " arg1\n" " )\n" - " )\n" " )\n" " (quote #f)\n" " )\n" @@ -2252,8 +2208,6 @@ TEST_F(FormRegressionTest, ExprValid) { " )\n" " (if\n" " arg2\n" - " (set!\n" - " v1-50\n" " (format\n" " arg4\n" " \"ERROR: object #x~X ~S is not a valid object of type '~A' (inside symbol " @@ -2262,7 +2216,6 @@ TEST_F(FormRegressionTest, ExprValid) { " arg2\n" " arg1\n" " )\n" - " )\n" " )\n" " (quote #f)\n" " )\n" diff --git a/test/decompiler/test_gkernel_decomp.cpp b/test/decompiler/test_gkernel_decomp.cpp new file mode 100644 index 0000000000..4730d3e698 --- /dev/null +++ b/test/decompiler/test_gkernel_decomp.cpp @@ -0,0 +1,108 @@ +#include "gtest/gtest.h" +#include "FormRegressionTest.h" + +using namespace decompiler; + +TEST_F(FormRegressionTest, ExprMethod7Object) { + std::string func = + " sll r0, r0, 0\n" + " or v0, a0, r0\n" + " jr ra\n" + " daddu sp, sp, r0\n"; + std::string type = "(function object int object)"; + std::string expected = "arg0"; + test_with_expr(func, type, expected); +} + +TEST_F(FormRegressionTest, ExprLoadPackage) { + std::string func = + " sll r0, r0, 0\n" + "L278:\n" + " daddiu sp, sp, -48\n" + " sd ra, 0(sp)\n" + " sq s5, 16(sp)\n" + " sq gp, 32(sp)\n" + + " or gp, a0, r0\n" + " or s5, a1, r0\n" + " lw t9, nmember(s7)\n" + " or a0, gp, r0\n" + " lw a1, *kernel-packages*(s7)\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + + " bne s7, v0, L279\n" + " or v0, s7, r0\n" + + " lw t9, dgo-load(s7)\n" + " or a0, gp, r0\n" + " addiu a2, r0, 15\n" + " lui a3, 32\n" + " or a1, s5, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + + " lw v1, pair(s7)\n" + " lwu t9, 16(v1)\n" + " daddiu a0, s7, global\n" + " lw a1, pair(s7)\n" + " lw a3, *kernel-packages*(s7)\n" + " or a2, gp, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + + " sw v0, *kernel-packages*(s7)\n" + "L279:\n" + " ld ra, 0(sp)\n" + " lq gp, 32(sp)\n" + " lq s5, 16(sp)\n" + " jr ra\n" + " daddiu sp, sp, 48\n"; + std::string type = "(function string kheap pair)"; + std::string expected = + "(when\n" + " (not (nmember arg0 *kernel-packages*))\n" + " (dgo-load arg0 arg1 15 2097152)\n" + " (set! v0-1 (cons arg0 *kernel-packages*))\n" + " (set! *kernel-packages* v0-1)\n" + " v0-1\n" + " )"; + test_with_expr(func, type, expected); +} + +TEST_F(FormRegressionTest, ExprUnloadPackage) { + std::string func = + " sll r0, r0, 0\n" + " daddiu sp, sp, -16\n" + " sd ra, 0(sp)\n" + " lw t9, nmember(s7)\n" + " lw a1, *kernel-packages*(s7)\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + + " or v1, v0, r0\n" + " beq s7, v1, L277\n" + " or a0, s7, r0\n" + + " lw t9, delete!(s7)\n" + " lw a0, -2(v1)\n" + " lw a1, *kernel-packages*(s7)\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + + " sw v0, *kernel-packages*(s7)\n" + " or v1, v0, r0\n" + "L277:\n" + " lw v0, *kernel-packages*(s7)\n" + " ld ra, 0(sp)\n" + " jr ra\n" + " daddiu sp, sp, 16\n"; + std::string type = "(function string pair)"; + std::string expected = + "(begin\n" + " (set! v1-0 (nmember arg0 *kernel-packages*))\n" + " (if v1-0 (set! *kernel-packages* (delete! (car v1-0) *kernel-packages*)))\n" + " *kernel-packages*\n" + " )"; + test_with_expr(func, type, expected, true); +} \ No newline at end of file