diff --git a/decompiler/IR2/AtomicOp.cpp b/decompiler/IR2/AtomicOp.cpp index fb1a2d9c35..e1bbe6fef9 100644 --- a/decompiler/IR2/AtomicOp.cpp +++ b/decompiler/IR2/AtomicOp.cpp @@ -12,7 +12,7 @@ namespace decompiler { // VARIABLE ///////////////////////////// -Variable::Variable(VariableMode mode, Register reg, int atomic_idx, bool allow_all) +RegisterAccess::RegisterAccess(AccessMode mode, Register reg, int atomic_idx, bool allow_all) : m_mode(mode), m_reg(reg), m_atomic_idx(atomic_idx) { // make sure we're using a valid GPR. if (reg.get_kind() == Reg::GPR && !allow_all) { @@ -23,13 +23,13 @@ Variable::Variable(VariableMode mode, Register reg, int atomic_idx, bool allow_a } } -goos::Object Variable::to_form(const Env& env, Print mode) const { +goos::Object RegisterAccess::to_form(const Env& env, Print mode) const { switch (mode) { case Print::AS_REG: return pretty_print::to_symbol(m_reg.to_string()); case Print::FULL: return pretty_print::to_symbol(fmt::format("{}-{:03d}-{}", m_reg.to_charp(), m_atomic_idx, - m_mode == VariableMode::READ ? 'r' : 'w')); + m_mode == AccessMode::READ ? 'r' : 'w')); case Print::AS_VARIABLE: return env.get_variable_name(m_reg, m_atomic_idx, m_mode); case Print::AUTOMATIC: @@ -43,11 +43,15 @@ goos::Object Variable::to_form(const Env& env, Print mode) const { } } -bool Variable::operator==(const Variable& other) const { - return m_mode == other.m_mode && m_reg == other.m_reg && m_atomic_idx && other.m_atomic_idx; +std::string RegisterAccess::to_string(const Env& env, Print mode) const { + return to_form(env, mode).print(); } -bool Variable::operator!=(const Variable& other) const { +bool RegisterAccess::operator==(const RegisterAccess& other) const { + return m_mode == other.m_mode && m_reg == other.m_reg && m_atomic_idx == other.m_atomic_idx; +} + +bool RegisterAccess::operator!=(const RegisterAccess& other) const { return !((*this) == other); } @@ -82,7 +86,7 @@ void AtomicOp::clobber_temps() { // SimpleAtom ///////////////////////////// -SimpleAtom SimpleAtom::make_var(const Variable& var) { +SimpleAtom SimpleAtom::make_var(const RegisterAccess& var) { SimpleAtom result; result.m_kind = Kind::VARIABLE; result.m_variable = var; @@ -143,7 +147,7 @@ goos::Object SimpleAtom::to_form(const std::vector& labels, con } } -void SimpleAtom::collect_vars(VariableSet& vars) const { +void SimpleAtom::collect_vars(RegAccessSet& vars) const { if (is_var()) { vars.insert(var()); } @@ -360,7 +364,7 @@ void SimpleExpression::get_regs(std::vector* out) const { } } -void SimpleExpression::collect_vars(VariableSet& vars) const { +void SimpleExpression::collect_vars(RegAccessSet& vars) const { for (int i = 0; i < args(); i++) { get_arg(i).collect_vars(vars); } @@ -398,7 +402,7 @@ bool SetVarOp::is_sequence_point() const { return true; } -Variable SetVarOp::get_set_destination() const { +RegisterAccess SetVarOp::get_set_destination() const { return m_dst; } @@ -407,7 +411,7 @@ void SetVarOp::update_register_info() { m_src.get_regs(&m_read_regs); } -void SetVarOp::collect_vars(VariableSet& vars) const { +void SetVarOp::collect_vars(RegAccessSet& vars) const { vars.insert(m_dst); m_src.collect_vars(vars); } @@ -423,7 +427,7 @@ AsmOp::AsmOp(Instruction instr, int my_idx) : AtomicOp(my_idx), m_instr(std::mov if (dst.is_reg()) { auto reg = dst.get_reg(); if (reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR || reg.get_kind() == Reg::VF) { - m_dst = Variable(VariableMode::WRITE, reg, my_idx, true); + m_dst = RegisterAccess(AccessMode::WRITE, reg, my_idx, true); } } } @@ -434,7 +438,7 @@ AsmOp::AsmOp(Instruction instr, int my_idx) : AtomicOp(my_idx), m_instr(std::mov if (src.is_reg()) { auto reg = src.get_reg(); if (reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR || reg.get_kind() == Reg::VF) { - m_src[i] = Variable(VariableMode::READ, reg, my_idx, true); + m_src[i] = RegisterAccess(AccessMode::READ, reg, my_idx, true); } } } @@ -483,7 +487,7 @@ bool AsmOp::is_sequence_point() const { return true; } -Variable AsmOp::get_set_destination() const { +RegisterAccess AsmOp::get_set_destination() const { throw std::runtime_error("AsmOp cannot be treated as a set! operation"); } @@ -595,7 +599,7 @@ void AsmOp::update_register_info() { } } -void AsmOp::collect_vars(VariableSet& vars) const { +void AsmOp::collect_vars(RegAccessSet& vars) const { if (m_dst.has_value()) { vars.insert(*m_dst); } @@ -862,7 +866,7 @@ void IR2_Condition::get_regs(std::vector* out) const { } } -void IR2_Condition::collect_vars(VariableSet& vars) const { +void IR2_Condition::collect_vars(RegAccessSet& vars) const { for (int i = 0; i < get_condition_num_args(m_kind); i++) { m_src[i].collect_vars(vars); } @@ -872,7 +876,7 @@ void IR2_Condition::collect_vars(VariableSet& vars) const { // SetVarConditionOp ///////////////////////////// -SetVarConditionOp::SetVarConditionOp(Variable dst, IR2_Condition condition, int my_idx) +SetVarConditionOp::SetVarConditionOp(RegisterAccess dst, IR2_Condition condition, int my_idx) : AtomicOp(my_idx), m_dst(dst), m_condition(std::move(condition)) {} goos::Object SetVarConditionOp::to_form(const std::vector& labels, @@ -895,7 +899,7 @@ bool SetVarConditionOp::is_sequence_point() const { return true; } -Variable SetVarConditionOp::get_set_destination() const { +RegisterAccess SetVarConditionOp::get_set_destination() const { return m_dst; } @@ -904,7 +908,7 @@ void SetVarConditionOp::update_register_info() { m_condition.get_regs(&m_read_regs); } -void SetVarConditionOp::collect_vars(VariableSet& vars) const { +void SetVarConditionOp::collect_vars(RegAccessSet& vars) const { vars.insert(m_dst); m_condition.collect_vars(vars); } @@ -963,7 +967,7 @@ bool StoreOp::is_sequence_point() const { return true; } -Variable StoreOp::get_set_destination() const { +RegisterAccess StoreOp::get_set_destination() const { throw std::runtime_error("StoreOp cannot be treated as a set! operation"); } @@ -972,7 +976,7 @@ void StoreOp::update_register_info() { m_value.get_regs(&m_read_regs); } -void StoreOp::collect_vars(VariableSet& vars) const { +void StoreOp::collect_vars(RegAccessSet& vars) const { m_addr.collect_vars(vars); m_value.collect_vars(vars); } @@ -981,7 +985,7 @@ void StoreOp::collect_vars(VariableSet& vars) const { // LoadVarOp ///////////////////////////// -LoadVarOp::LoadVarOp(Kind kind, int size, Variable dst, SimpleExpression src, int my_idx) +LoadVarOp::LoadVarOp(Kind kind, int size, RegisterAccess dst, SimpleExpression src, int my_idx) : AtomicOp(my_idx), m_kind(kind), m_size(size), m_dst(dst), m_src(std::move(src)) {} goos::Object LoadVarOp::to_form(const std::vector& labels, const Env& env) const { @@ -1045,7 +1049,7 @@ bool LoadVarOp::is_sequence_point() const { return true; } -Variable LoadVarOp::get_set_destination() const { +RegisterAccess LoadVarOp::get_set_destination() const { return m_dst; } @@ -1054,7 +1058,7 @@ void LoadVarOp::update_register_info() { m_write_regs.push_back(m_dst.reg()); } -void LoadVarOp::collect_vars(VariableSet& vars) const { +void LoadVarOp::collect_vars(RegAccessSet& vars) const { vars.insert(m_dst); m_src.collect_vars(vars); } @@ -1067,27 +1071,31 @@ IR2_BranchDelay::IR2_BranchDelay(Kind kind) : m_kind(kind) { assert(m_kind == Kind::NOP || m_kind == Kind::NO_DELAY || m_kind == Kind::UNKNOWN); } -IR2_BranchDelay::IR2_BranchDelay(Kind kind, Variable var0) : m_kind(kind) { +IR2_BranchDelay::IR2_BranchDelay(Kind kind, RegisterAccess var0) : m_kind(kind) { assert(m_kind == Kind::SET_REG_FALSE || m_kind == Kind::SET_REG_TRUE || m_kind == Kind::SET_BINTEGER || m_kind == Kind::SET_PAIR); - assert(var0.mode() == VariableMode::WRITE); + assert(var0.mode() == AccessMode::WRITE); m_var[0] = var0; } -IR2_BranchDelay::IR2_BranchDelay(Kind kind, Variable var0, Variable var1) : m_kind(kind) { +IR2_BranchDelay::IR2_BranchDelay(Kind kind, RegisterAccess var0, RegisterAccess var1) + : m_kind(kind) { assert(m_kind == Kind::NEGATE || m_kind == Kind::SET_REG_REG); - assert(var0.mode() == VariableMode::WRITE); - assert(var1.mode() == VariableMode::READ); + assert(var0.mode() == AccessMode::WRITE); + assert(var1.mode() == AccessMode::READ); m_var[0] = var0; m_var[1] = var1; } -IR2_BranchDelay::IR2_BranchDelay(Kind kind, Variable var0, Variable var1, Variable var2) +IR2_BranchDelay::IR2_BranchDelay(Kind kind, + RegisterAccess var0, + RegisterAccess var1, + RegisterAccess var2) : m_kind(kind) { assert(m_kind == Kind::DSLLV); - assert(var0.mode() == VariableMode::WRITE); - assert(var1.mode() == VariableMode::READ); - assert(var2.mode() == VariableMode::READ); + assert(var0.mode() == AccessMode::WRITE); + assert(var1.mode() == AccessMode::READ); + assert(var2.mode() == AccessMode::READ); m_var[0] = var0; m_var[1] = var1; m_var[2] = var2; @@ -1171,7 +1179,7 @@ void IR2_BranchDelay::get_regs(std::vector* write, std::vectorcollect_vars(vars); } @@ -1349,7 +1357,7 @@ bool SpecialOp::is_sequence_point() const { return true; } -Variable SpecialOp::get_set_destination() const { +RegisterAccess SpecialOp::get_set_destination() const { throw std::runtime_error("SpecialOp cannot be treated as a set! operation"); } @@ -1371,7 +1379,7 @@ void SpecialOp::update_register_info() { } } -void SpecialOp::collect_vars(VariableSet&) const {} +void SpecialOp::collect_vars(RegAccessSet&) const {} ///////////////////////////// // CallOp @@ -1379,8 +1387,8 @@ void SpecialOp::collect_vars(VariableSet&) const {} CallOp::CallOp(int my_idx) : AtomicOp(my_idx), - m_function_var(VariableMode::READ, Register(Reg::GPR, Reg::T9), my_idx), - m_return_var(VariableMode::WRITE, Register(Reg::GPR, Reg::V0), my_idx) {} + m_function_var(AccessMode::READ, Register(Reg::GPR, Reg::T9), my_idx), + m_return_var(AccessMode::WRITE, Register(Reg::GPR, Reg::V0), my_idx) {} goos::Object CallOp::to_form(const std::vector& labels, const Env& env) const { (void)labels; @@ -1407,7 +1415,7 @@ bool CallOp::is_sequence_point() const { return true; } -Variable CallOp::get_set_destination() const { +RegisterAccess CallOp::get_set_destination() const { throw std::runtime_error("CallOp cannot be treated as a set! operation"); } @@ -1421,7 +1429,7 @@ void CallOp::update_register_info() { clobber_temps(); } -void CallOp::collect_vars(VariableSet& vars) const { +void CallOp::collect_vars(RegAccessSet& vars) const { vars.insert(m_function_var); for (auto& e : m_arg_vars) { vars.insert(e); @@ -1436,9 +1444,9 @@ void CallOp::collect_vars(VariableSet& vars) const { // ConditionalMoveFalseOp ///////////////////////////// -ConditionalMoveFalseOp::ConditionalMoveFalseOp(Variable dst, - Variable src, - Variable old_value, +ConditionalMoveFalseOp::ConditionalMoveFalseOp(RegisterAccess dst, + RegisterAccess src, + RegisterAccess old_value, bool on_zero, int my_idx) : AtomicOp(my_idx), m_dst(dst), m_src(src), m_old_value(old_value), m_on_zero(on_zero) {} @@ -1465,7 +1473,7 @@ bool ConditionalMoveFalseOp::is_sequence_point() const { return true; } -Variable ConditionalMoveFalseOp::get_set_destination() const { +RegisterAccess ConditionalMoveFalseOp::get_set_destination() const { throw std::runtime_error("ConditionalMoveFalseOp cannot be treated as a set! operation"); } @@ -1475,7 +1483,7 @@ void ConditionalMoveFalseOp::update_register_info() { m_read_regs.push_back(m_old_value.reg()); } -void ConditionalMoveFalseOp::collect_vars(VariableSet& vars) const { +void ConditionalMoveFalseOp::collect_vars(RegAccessSet& vars) const { vars.insert(m_dst); vars.insert(m_src); vars.insert(m_old_value); @@ -1504,7 +1512,7 @@ bool get_as_reg_offset(const SimpleExpression& expr, IR2_RegOffset* out) { ///////////////////////////// FunctionEndOp::FunctionEndOp(int my_idx) - : AtomicOp(my_idx), m_return_reg(VariableMode::READ, Register(Reg::GPR, Reg::V0), my_idx) {} + : AtomicOp(my_idx), m_return_reg(AccessMode::READ, Register(Reg::GPR, Reg::V0), my_idx) {} goos::Object FunctionEndOp::to_form(const std::vector&, const Env& env) const { if (m_function_has_return_value) { @@ -1528,7 +1536,7 @@ bool FunctionEndOp::is_sequence_point() const { return true; } -Variable FunctionEndOp::get_set_destination() const { +RegisterAccess FunctionEndOp::get_set_destination() const { throw std::runtime_error("FunctionEndOp cannot be treated as a set! operation"); } @@ -1536,7 +1544,7 @@ void FunctionEndOp::update_register_info() { m_read_regs.push_back(Register(Reg::GPR, Reg::V0)); } -void FunctionEndOp::collect_vars(VariableSet& vars) const { +void FunctionEndOp::collect_vars(RegAccessSet& vars) const { if (m_function_has_return_value) { vars.insert(m_return_reg); } diff --git a/decompiler/IR2/AtomicOp.h b/decompiler/IR2/AtomicOp.h index a650aca9a8..389d96c597 100644 --- a/decompiler/IR2/AtomicOp.h +++ b/decompiler/IR2/AtomicOp.h @@ -58,7 +58,7 @@ class AtomicOp { virtual bool is_sequence_point() const = 0; // get the variable being set by this operation. Only call this if is_variable_set returns true. - virtual Variable get_set_destination() const = 0; + virtual RegisterAccess get_set_destination() const = 0; // convert me to an expression. If I'm a set!, this will produce a (set! x y), which may be // undesirable when expression stacking. @@ -70,7 +70,7 @@ class AtomicOp { // read twice. virtual void update_register_info() = 0; - virtual void collect_vars(VariableSet& vars) const = 0; + virtual void collect_vars(RegAccessSet& vars) const = 0; TypeState propagate_types(const TypeState& input, const Env& env, DecompilerTypeSystem& dts); @@ -122,18 +122,18 @@ class SimpleAtom { }; SimpleAtom() = default; - static SimpleAtom make_var(const Variable& var); + static SimpleAtom make_var(const RegisterAccess& var); static SimpleAtom make_sym_ptr(const std::string& name); static SimpleAtom make_sym_val(const std::string& name); static SimpleAtom make_empty_list(); static SimpleAtom make_int_constant(s64 value); static SimpleAtom make_static_address(int static_label_id); goos::Object to_form(const std::vector& labels, const Env& env) const; - void collect_vars(VariableSet& vars) const; + void collect_vars(RegAccessSet& vars) const; bool is_var() const { return m_kind == Kind::VARIABLE; } bool is_label() const { return m_kind == Kind::STATIC_ADDRESS; } - const Variable& var() const { + const RegisterAccess& var() const { assert(is_var()); return m_variable; } @@ -166,7 +166,7 @@ class SimpleAtom { Kind m_kind = Kind::INVALID; std::string m_string; // for symbol ptr and symbol val s64 m_int = -1; // for integer constant and static address label id - Variable m_variable; + RegisterAccess m_variable; }; /*! @@ -233,7 +233,7 @@ class SimpleExpression { bool operator==(const SimpleExpression& other) const; bool is_identity() const { return m_kind == Kind::IDENTITY; } bool is_var() const { return is_identity() && get_arg(0).is_var(); } - const Variable& var() const { + const RegisterAccess& var() const { assert(is_var()); return get_arg(0).var(); } @@ -245,7 +245,7 @@ class SimpleExpression { TP_Type get_type_int1(const TypeState& input, const Env& env, const DecompilerTypeSystem& dts) const; - void collect_vars(VariableSet& vars) const; + void collect_vars(RegAccessSet& vars) const; private: Kind m_kind = Kind::INVALID; @@ -260,7 +260,7 @@ int get_simple_expression_arg_count(SimpleExpression::Kind kind); */ class SetVarOp : public AtomicOp { public: - SetVarOp(const Variable& dst, SimpleExpression src, int my_idx) + SetVarOp(const RegisterAccess& dst, SimpleExpression src, int my_idx) : AtomicOp(my_idx), m_dst(dst), m_src(std::move(src)) { assert(my_idx == dst.idx()); } @@ -268,18 +268,18 @@ class SetVarOp : public AtomicOp { const Env& env) const override; bool operator==(const AtomicOp& other) const override; bool is_sequence_point() const override; - Variable get_set_destination() const override; + RegisterAccess get_set_destination() const override; FormElement* get_as_form(FormPool& pool, const Env& env) const override; void update_register_info() override; TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; - void collect_vars(VariableSet& vars) const override; - const Variable& dst() const { return m_dst; } + void collect_vars(RegAccessSet& vars) const override; + const RegisterAccess& dst() const { return m_dst; } const SimpleExpression& src() const { return m_src; } private: - Variable m_dst; + RegisterAccess m_dst; SimpleExpression m_src; }; @@ -296,19 +296,19 @@ class AsmOp : public AtomicOp { goos::Object to_form(const std::vector& labels, const Env& env) const override; bool operator==(const AtomicOp& other) const override; bool is_sequence_point() const override; - Variable get_set_destination() const override; + RegisterAccess get_set_destination() const override; FormElement* get_as_form(FormPool& pool, const Env& env) const override; void update_register_info() override; TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; - void collect_vars(VariableSet& vars) const override; + void collect_vars(RegAccessSet& vars) const override; const Instruction& instruction() const { return m_instr; } private: Instruction m_instr; - std::optional m_dst; - std::optional m_src[4]; + std::optional m_dst; + std::optional m_src[4]; }; /*! @@ -369,7 +369,7 @@ class IR2_Condition { Kind kind() const { return m_kind; } const SimpleAtom& src(int i) const { return m_src[i]; } ConditionElement* get_as_form(FormPool& pool, const Env& env, int my_idx) const; - void collect_vars(VariableSet& vars) const; + void collect_vars(RegAccessSet& vars) const; void make_flipped() { m_flipped_eval = true; } bool flipped() const { return m_flipped_eval; } @@ -389,21 +389,21 @@ bool condition_uses_float(IR2_Condition::Kind kind); */ class SetVarConditionOp : public AtomicOp { public: - SetVarConditionOp(Variable dst, IR2_Condition condition, int my_idx); + SetVarConditionOp(RegisterAccess dst, IR2_Condition condition, int my_idx); goos::Object to_form(const std::vector& labels, const Env& env) const override; bool operator==(const AtomicOp& other) const override; bool is_sequence_point() const override; - Variable get_set_destination() const override; + RegisterAccess get_set_destination() const override; FormElement* get_as_form(FormPool& pool, const Env& env) const override; void update_register_info() override; void invert() { m_condition.invert(); } TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; - void collect_vars(VariableSet& vars) const override; + void collect_vars(RegAccessSet& vars) const override; private: - Variable m_dst; + RegisterAccess m_dst; IR2_Condition m_condition; }; @@ -418,13 +418,13 @@ class StoreOp : public AtomicOp { goos::Object to_form(const std::vector& labels, const Env& env) const override; bool operator==(const AtomicOp& other) const override; bool is_sequence_point() const override; - Variable get_set_destination() const override; + RegisterAccess get_set_destination() const override; FormElement* get_as_form(FormPool& pool, const Env& env) const override; void update_register_info() override; TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; - void collect_vars(VariableSet& vars) const override; + void collect_vars(RegAccessSet& vars) const override; const SimpleExpression& addr() const { return m_addr; } const SimpleAtom& value() const { return m_value; } @@ -442,23 +442,23 @@ class StoreOp : public AtomicOp { class LoadVarOp : public AtomicOp { public: enum class Kind { UNSIGNED, SIGNED, FLOAT }; - LoadVarOp(Kind kind, int size, Variable dst, SimpleExpression src, int my_idx); + LoadVarOp(Kind kind, int size, RegisterAccess dst, SimpleExpression src, int my_idx); goos::Object to_form(const std::vector& labels, const Env& env) const override; bool operator==(const AtomicOp& other) const override; bool is_sequence_point() const override; - Variable get_set_destination() const override; + RegisterAccess get_set_destination() const override; FormElement* get_as_form(FormPool& pool, const Env& env) const override; void update_register_info() override; TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; TP_Type get_src_type(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) const; - void collect_vars(VariableSet& vars) const override; + void collect_vars(RegAccessSet& vars) const override; private: Kind m_kind; int m_size = -1; - Variable m_dst; + RegisterAccess m_dst; SimpleExpression m_src; }; @@ -485,9 +485,9 @@ class IR2_BranchDelay { }; explicit IR2_BranchDelay(Kind kind); - IR2_BranchDelay(Kind kind, Variable var0); - IR2_BranchDelay(Kind kind, Variable var0, Variable var1); - IR2_BranchDelay(Kind kind, Variable var0, Variable var1, Variable var2); + IR2_BranchDelay(Kind kind, RegisterAccess var0); + IR2_BranchDelay(Kind kind, RegisterAccess var0, RegisterAccess var1); + IR2_BranchDelay(Kind kind, RegisterAccess var0, RegisterAccess var1, RegisterAccess var2); goos::Object to_form(const std::vector& labels, const Env& env) const; bool operator==(const IR2_BranchDelay& other) const; void get_regs(std::vector* write, std::vector* read) const; @@ -495,16 +495,16 @@ class IR2_BranchDelay { TypeState propagate_types(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) const; - void collect_vars(VariableSet& vars) const; + void collect_vars(RegAccessSet& vars) const; Kind kind() const { return m_kind; } - const Variable& var(int idx) const { + const RegisterAccess& var(int idx) const { assert(idx < 3); assert(m_var[idx].has_value()); return m_var[idx].value(); } private: - std::optional m_var[3]; + std::optional m_var[3]; Kind m_kind = Kind::UNKNOWN; }; @@ -522,13 +522,13 @@ class BranchOp : public AtomicOp { goos::Object to_form(const std::vector& labels, const Env& env) const override; bool operator==(const AtomicOp& other) const override; bool is_sequence_point() const override; - Variable get_set_destination() const override; + RegisterAccess get_set_destination() const override; FormElement* get_as_form(FormPool& pool, const Env& env) const override; void update_register_info() override; TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; - void collect_vars(VariableSet& vars) const override; + void collect_vars(RegAccessSet& vars) const override; const IR2_BranchDelay& branch_delay() const { return m_branch_delay; } const IR2_Condition& condition() const { return m_condition; } ConditionElement* get_condition_as_form(FormPool& pool, const Env& env) const; @@ -554,13 +554,13 @@ class AsmBranchOp : public AtomicOp { goos::Object to_form(const std::vector& labels, const Env& env) const override; bool operator==(const AtomicOp& other) const override; bool is_sequence_point() const override; - Variable get_set_destination() const override; + RegisterAccess get_set_destination() const override; FormElement* get_as_form(FormPool& pool, const Env& env) const override; void update_register_info() override; TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; - void collect_vars(VariableSet& vars) const override; + void collect_vars(RegAccessSet& vars) const override; private: bool m_likely = false; @@ -586,13 +586,13 @@ class SpecialOp : public AtomicOp { goos::Object to_form(const std::vector& labels, const Env& env) const override; bool operator==(const AtomicOp& other) const override; bool is_sequence_point() const override; - Variable get_set_destination() const override; + RegisterAccess get_set_destination() const override; FormElement* get_as_form(FormPool& pool, const Env& env) const override; void update_register_info() override; TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; - void collect_vars(VariableSet& vars) const override; + void collect_vars(RegAccessSet& vars) const override; Kind kind() const { return m_kind; } private: @@ -609,15 +609,15 @@ class CallOp : public AtomicOp { goos::Object to_form(const std::vector& labels, const Env& env) const override; bool operator==(const AtomicOp& other) const override; bool is_sequence_point() const override; - Variable get_set_destination() const override; + RegisterAccess get_set_destination() const override; FormElement* get_as_form(FormPool& pool, const Env& env) const override; void update_register_info() override; TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; - void collect_vars(VariableSet& vars) const override; - const std::vector& arg_vars() const { return m_arg_vars; } - Variable function_var() const { return m_function_var; } + void collect_vars(RegAccessSet& vars) const override; + const std::vector& arg_vars() const { return m_arg_vars; } + RegisterAccess function_var() const { return m_function_var; } bool is_method() const { return m_is_virtual_method; } protected: @@ -625,9 +625,9 @@ class CallOp : public AtomicOp { bool m_call_type_set = false; bool m_is_virtual_method = false; - std::vector m_arg_vars; - Variable m_function_var; - Variable m_return_var; + std::vector m_arg_vars; + RegisterAccess m_function_var; + RegisterAccess m_return_var; }; /*! @@ -644,26 +644,30 @@ class CallOp : public AtomicOp { */ class ConditionalMoveFalseOp : public AtomicOp { public: - ConditionalMoveFalseOp(Variable dst, Variable src, Variable old_value, bool on_zero, int my_idx); + ConditionalMoveFalseOp(RegisterAccess dst, + RegisterAccess src, + RegisterAccess old_value, + bool on_zero, + int my_idx); goos::Object to_form(const std::vector& labels, const Env& env) const override; bool operator==(const AtomicOp& other) const override; bool is_sequence_point() const override; - Variable get_set_destination() const override; + RegisterAccess get_set_destination() const override; FormElement* get_as_form(FormPool& pool, const Env& env) const override; void update_register_info() override; TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; - void collect_vars(VariableSet& vars) const override; + void collect_vars(RegAccessSet& vars) const override; private: - Variable m_dst, m_src, m_old_value; + RegisterAccess m_dst, m_src, m_old_value; bool m_on_zero; }; struct IR2_RegOffset { Register reg; - Variable var; + RegisterAccess var; int offset; }; @@ -680,22 +684,22 @@ class FunctionEndOp : public AtomicOp { const Env& env) const override; bool operator==(const AtomicOp& other) const override; bool is_sequence_point() const override; - Variable get_set_destination() const override; + RegisterAccess get_set_destination() const override; FormElement* get_as_form(FormPool& pool, const Env& env) const override; void update_register_info() override; TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; - void collect_vars(VariableSet& vars) const override; + void collect_vars(RegAccessSet& vars) const override; void mark_function_as_no_return_value(); - const Variable& return_var() const { + const RegisterAccess& return_var() const { assert(m_function_has_return_value); return m_return_reg; } private: bool m_function_has_return_value = true; - Variable m_return_reg; + RegisterAccess m_return_reg; }; bool get_as_reg_offset(const SimpleExpression& expr, IR2_RegOffset* out); diff --git a/decompiler/IR2/AtomicOpForm.cpp b/decompiler/IR2/AtomicOpForm.cpp index c4234a3663..4b93bb2214 100644 --- a/decompiler/IR2/AtomicOpForm.cpp +++ b/decompiler/IR2/AtomicOpForm.cpp @@ -407,7 +407,7 @@ FormElement* CallOp::get_as_form(FormPool& pool, const Env& env) const { // this is a little scary in the case that type analysis doesn't run and relies on the fact // that CallOp falls back to writing v0 in the case where the function type isn't known. - Variable out_var(VariableMode::WRITE, Register(Reg::GPR, Reg::V0), m_my_idx); + RegisterAccess out_var(AccessMode::WRITE, Register(Reg::GPR, Reg::V0), m_my_idx); return pool.alloc_element(out_var, pool.alloc_single_form(nullptr, call), true); } else { throw std::runtime_error("CallOp::get_as_expr not yet implemented"); diff --git a/decompiler/IR2/AtomicOpTypeAnalysis.cpp b/decompiler/IR2/AtomicOpTypeAnalysis.cpp index 441980f763..42420532ff 100644 --- a/decompiler/IR2/AtomicOpTypeAnalysis.cpp +++ b/decompiler/IR2/AtomicOpTypeAnalysis.cpp @@ -789,7 +789,7 @@ TypeState CallOp::propagate_types_internal(const TypeState& input, m_read_regs.emplace_back(Reg::GPR, Reg::T9); for (int i = 0; i < int(m_call_type.arg_count()) - 1; i++) { m_read_regs.emplace_back(Reg::GPR, arg_regs[i]); - m_arg_vars.push_back(Variable(VariableMode::READ, m_read_regs.back(), m_my_idx)); + m_arg_vars.push_back(RegisterAccess(AccessMode::READ, m_read_regs.back(), m_my_idx)); } return end_types; } @@ -838,7 +838,7 @@ TypeState CallOp::propagate_types_internal(const TypeState& input, m_read_regs.emplace_back(Reg::GPR, Reg::T9); for (int i = 0; i < arg_count; i++) { m_read_regs.emplace_back(Reg::GPR, arg_regs[i]); - m_arg_vars.push_back(Variable(VariableMode::READ, m_read_regs.back(), m_my_idx)); + m_arg_vars.push_back(RegisterAccess(AccessMode::READ, m_read_regs.back(), m_my_idx)); } return end_types; @@ -859,7 +859,7 @@ TypeState CallOp::propagate_types_internal(const TypeState& input, for (uint32_t i = 0; i < in_type.arg_count() - 1; i++) { m_read_regs.emplace_back(Reg::GPR, arg_regs[i]); - m_arg_vars.push_back(Variable(VariableMode::READ, m_read_regs.back(), m_my_idx)); + m_arg_vars.push_back(RegisterAccess(AccessMode::READ, m_read_regs.back(), m_my_idx)); if (i == 0 && in_tp.kind == TP_Type::Kind::VIRTUAL_METHOD) { m_read_regs.pop_back(); m_arg_vars.pop_back(); diff --git a/decompiler/IR2/Env.cpp b/decompiler/IR2/Env.cpp index 529200ab04..dbd052f036 100644 --- a/decompiler/IR2/Env.cpp +++ b/decompiler/IR2/Env.cpp @@ -71,7 +71,7 @@ const std::string& Env::remapped_name(const std::string& name) const { } } -goos::Object Env::get_variable_name(Register reg, int atomic_idx, VariableMode mode) const { +goos::Object Env::get_variable_name(Register reg, int atomic_idx, AccessMode mode) const { if (reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR) { std::string lookup_name = m_var_names.lookup(reg, atomic_idx, mode).name(); auto remapped = m_var_remap.find(lookup_name); @@ -166,22 +166,21 @@ std::vector Env::extract_visible_variables( assert(has_local_vars()); std::vector entries; if (top_level_form) { - VariableSet var_set; + RegAccessSet var_set; top_level_form->collect_vars(var_set); // we want to sort them for easier reading: - std::vector> vars; + std::vector> vars; for (auto& x : var_set) { if (x.reg().get_kind() == Reg::FPR || x.reg().get_kind() == Reg::GPR) { - vars.push_back(std::make_pair(get_ssa_var(x), x)); + vars.push_back(std::make_pair(get_program_var_id(x), x)); } } std::sort(vars.begin(), vars.end(), - [](const std::pair& a, const std::pair& b) { - return a.first < b.first; - }); + [](const std::pair& a, + const std::pair& b) { return a.first < b.first; }); RegId* prev = nullptr; for (auto& x : vars) { @@ -191,8 +190,8 @@ std::vector Env::extract_visible_variables( continue; } prev = &x.first; - auto& map = x.second.mode() == VariableMode::WRITE ? m_var_names.write_vars.at(x.second.reg()) - : m_var_names.read_vars.at(x.second.reg()); + auto& map = x.second.mode() == AccessMode::WRITE ? m_var_names.write_vars.at(x.second.reg()) + : m_var_names.read_vars.at(x.second.reg()); auto& info = map.at(x.first.id); if (info.initialized) { @@ -259,15 +258,21 @@ goos::Object Env::local_var_type_list(const Form* top_level_form, return pretty_print::build_list(elts); } -std::unordered_set Env::get_ssa_var(const VariableSet& vars) const { +std::unordered_set Env::get_ssa_var(const RegAccessSet& vars) const { std::unordered_set result; for (auto& x : vars) { - result.insert(get_ssa_var(x)); + result.insert(get_program_var_id(x)); } return result; } -RegId Env::get_ssa_var(const Variable& var) const { +RegId Env::get_program_var_id(const RegisterAccess& var) const { return m_var_names.lookup(var.reg(), var.idx(), var.mode()).reg_id; } + +const UseDefInfo& Env::get_use_def_info(const RegisterAccess& ra) const { + assert(has_local_vars()); + auto var_id = get_program_var_id(ra); + return m_var_names.use_def_info.at(var_id); +} } // namespace decompiler \ No newline at end of file diff --git a/decompiler/IR2/Env.h b/decompiler/IR2/Env.h index 4e559e0862..f29ad150aa 100644 --- a/decompiler/IR2/Env.h +++ b/decompiler/IR2/Env.h @@ -43,7 +43,7 @@ class Env { return m_reg_use; } - goos::Object get_variable_name(Register reg, int atomic_idx, VariableMode mode) const; + goos::Object get_variable_name(Register reg, int atomic_idx, AccessMode mode) const; /*! * Get the types in registers _after_ the given operation has completed. @@ -76,8 +76,8 @@ class Env { m_has_local_vars = true; } - void set_end_var(Variable var) { m_end_var = var; } - const Variable& end_var() const { return m_end_var; } + void set_end_var(RegisterAccess var) { m_end_var = var; } + const RegisterAccess& end_var() const { return m_end_var; } std::vector extract_visible_variables(const Form* top_level_form) const; std::string print_local_var_types(const Form* top_level_form) const; @@ -85,8 +85,8 @@ class Env { int nargs_to_ignore, int* count_out) const; - std::unordered_set get_ssa_var(const VariableSet& vars) const; - RegId get_ssa_var(const Variable& var) const; + std::unordered_set get_ssa_var(const RegAccessSet& vars) const; + RegId get_program_var_id(const RegisterAccess& var) const; bool allow_sloppy_pair_typing() const { return m_allow_sloppy_pair_typing; } void set_sloppy_pair_typing() { m_allow_sloppy_pair_typing = true; } @@ -113,11 +113,16 @@ class Env { m_label_types = types; } + const UseDefInfo& get_use_def_info(const RegisterAccess& ra) const; + void disable_use(const RegisterAccess& access) { m_var_names.disable_use(access); } + + void disable_def(const RegisterAccess& access) { m_var_names.disable_def(access); } + LinkedObjectFile* file = nullptr; DecompilerTypeSystem* dts = nullptr; private: - Variable m_end_var; + RegisterAccess m_end_var; bool m_has_reg_use = false; RegUsageInfo m_reg_use; diff --git a/decompiler/IR2/Form.cpp b/decompiler/IR2/Form.cpp index 7dd3eaa26f..2953611912 100644 --- a/decompiler/IR2/Form.cpp +++ b/decompiler/IR2/Form.cpp @@ -120,7 +120,7 @@ void Form::apply_form(const std::function& f) { } } -void Form::collect_vars(VariableSet& vars) const { +void Form::collect_vars(RegAccessSet& vars) const { for (auto e : m_elements) { e->collect_vars(vars); } @@ -153,7 +153,7 @@ bool SimpleExpressionElement::is_sequence_point() const { throw std::runtime_error("Should not check if a SimpleExpressionElement is a sequence point"); } -void SimpleExpressionElement::collect_vars(VariableSet& vars) const { +void SimpleExpressionElement::collect_vars(RegAccessSet& vars) const { m_expr.collect_vars(vars); } @@ -177,7 +177,7 @@ void StoreElement::apply(const std::function& f) { void StoreElement::apply_form(const std::function&) {} -void StoreElement::collect_vars(VariableSet& vars) const { +void StoreElement::collect_vars(RegAccessSet& vars) const { return m_op->collect_vars(vars); } @@ -239,7 +239,7 @@ void LoadSourceElement::apply_form(const std::function& f) { m_addr->apply_form(f); } -void LoadSourceElement::collect_vars(VariableSet& vars) const { +void LoadSourceElement::collect_vars(RegAccessSet& vars) const { m_addr->collect_vars(vars); } @@ -263,7 +263,7 @@ void SimpleAtomElement::apply(const std::function& f) { void SimpleAtomElement::apply_form(const std::function&) {} -void SimpleAtomElement::collect_vars(VariableSet& vars) const { +void SimpleAtomElement::collect_vars(RegAccessSet& vars) const { return m_atom.collect_vars(vars); } @@ -275,7 +275,7 @@ void SimpleAtomElement::get_modified_regs(RegSet& regs) const { // SetVarElement ///////////////////////////// -SetVarElement::SetVarElement(const Variable& var, +SetVarElement::SetVarElement(const RegisterAccess& var, Form* value, bool is_sequence_point, const SetVarInfo& info) @@ -301,7 +301,7 @@ bool SetVarElement::is_sequence_point() const { return m_is_sequence_point; } -void SetVarElement::collect_vars(VariableSet& vars) const { +void SetVarElement::collect_vars(RegAccessSet& vars) const { if (m_var_info.is_dead_set || m_var_info.is_dead_false) { return; } @@ -335,14 +335,14 @@ void StoreInSymbolElement::apply(const std::function& f) { void StoreInSymbolElement::apply_form(const std::function&) {} -void StoreInSymbolElement::collect_vars(VariableSet& vars) const { +void StoreInSymbolElement::collect_vars(RegAccessSet& vars) const { m_value.collect_vars(vars); } void StoreInSymbolElement::get_modified_regs(RegSet&) const {} StoreInPairElement::StoreInPairElement(bool is_car, - Variable pair, + RegisterAccess pair, SimpleExpression value, int my_idx) : m_is_car(is_car), m_pair(pair), m_value(value), m_my_idx(my_idx) {} @@ -359,7 +359,7 @@ void StoreInPairElement::apply(const std::function& f) { void StoreInPairElement::apply_form(const std::function&) {} -void StoreInPairElement::collect_vars(VariableSet& vars) const { +void StoreInPairElement::collect_vars(RegAccessSet& vars) const { m_value.collect_vars(vars); vars.insert(m_pair); } @@ -396,7 +396,7 @@ bool SetFormFormElement::is_sequence_point() const { return true; } -void SetFormFormElement::collect_vars(VariableSet& vars) const { +void SetFormFormElement::collect_vars(RegAccessSet& vars) const { m_src->collect_vars(vars); m_dst->collect_vars(vars); } @@ -421,7 +421,7 @@ void AtomicOpElement::apply(const std::function& f) { void AtomicOpElement::apply_form(const std::function&) {} -void AtomicOpElement::collect_vars(VariableSet& vars) const { +void AtomicOpElement::collect_vars(RegAccessSet& vars) const { m_op->collect_vars(vars); } @@ -451,7 +451,7 @@ void AsmOpElement::apply(const std::function& f) { void AsmOpElement::apply_form(const std::function&) {} -void AsmOpElement::collect_vars(VariableSet& vars) const { +void AsmOpElement::collect_vars(RegAccessSet& vars) const { m_op->collect_vars(vars); } @@ -510,7 +510,7 @@ void ConditionElement::invert() { m_kind = get_condition_opposite(m_kind); } -void ConditionElement::collect_vars(VariableSet& vars) const { +void ConditionElement::collect_vars(RegAccessSet& vars) const { for (auto src : m_src) { if (src.has_value() && src->is_var()) { vars.insert(src->var()); @@ -538,7 +538,7 @@ void FunctionCallElement::apply(const std::function& f) { void FunctionCallElement::apply_form(const std::function&) {} -void FunctionCallElement::collect_vars(VariableSet& vars) const { +void FunctionCallElement::collect_vars(RegAccessSet& vars) const { return m_op->collect_vars(vars); } @@ -568,7 +568,7 @@ void BranchElement::apply(const std::function& f) { void BranchElement::apply_form(const std::function&) {} -void BranchElement::collect_vars(VariableSet& vars) const { +void BranchElement::collect_vars(RegAccessSet& vars) const { return m_op->collect_vars(vars); } @@ -611,7 +611,7 @@ void ReturnElement::apply_form(const std::function& f) { } } -void ReturnElement::collect_vars(VariableSet& vars) const { +void ReturnElement::collect_vars(RegAccessSet& vars) const { return_code->collect_vars(vars); if (dead_code) { dead_code->collect_vars(vars); @@ -648,7 +648,7 @@ void BreakElement::apply_form(const std::function& f) { dead_code->apply_form(f); } -void BreakElement::collect_vars(VariableSet& vars) const { +void BreakElement::collect_vars(RegAccessSet& vars) const { return_code->collect_vars(vars); dead_code->collect_vars(vars); } @@ -708,7 +708,7 @@ void CondWithElseElement::apply_form(const std::function& f) { else_ir->apply_form(f); } -void CondWithElseElement::collect_vars(VariableSet& vars) const { +void CondWithElseElement::collect_vars(RegAccessSet& vars) const { for (auto& entry : entries) { entry.condition->collect_vars(vars); entry.body->collect_vars(vars); @@ -737,7 +737,7 @@ void EmptyElement::apply(const std::function& f) { } void EmptyElement::apply_form(const std::function&) {} -void EmptyElement::collect_vars(VariableSet&) const {} +void EmptyElement::collect_vars(RegAccessSet&) const {} void EmptyElement::get_modified_regs(RegSet&) const {} ///////////////////////////// @@ -764,7 +764,7 @@ void WhileElement::apply_form(const std::function& f) { condition->apply_form(f); } -void WhileElement::collect_vars(VariableSet& vars) const { +void WhileElement::collect_vars(RegAccessSet& vars) const { body->collect_vars(vars); condition->collect_vars(vars); } @@ -798,7 +798,7 @@ void UntilElement::apply_form(const std::function& f) { condition->apply_form(f); } -void UntilElement::collect_vars(VariableSet& vars) const { +void UntilElement::collect_vars(RegAccessSet& vars) const { body->collect_vars(vars); condition->collect_vars(vars); } @@ -854,7 +854,7 @@ goos::Object ShortCircuitElement::to_form_internal(const Env& env) const { return pretty_print::build_list(forms); } -void ShortCircuitElement::collect_vars(VariableSet& vars) const { +void ShortCircuitElement::collect_vars(RegAccessSet& vars) const { // vars.insert(final_result); // todo - this might be unused. for (auto& entry : entries) { entry.condition->collect_vars(vars); @@ -916,7 +916,7 @@ void CondNoElseElement::apply_form(const std::function& f) { } } -void CondNoElseElement::collect_vars(VariableSet& vars) const { +void CondNoElseElement::collect_vars(RegAccessSet& vars) const { for (auto& e : entries) { e.condition->collect_vars(vars); e.body->collect_vars(vars); @@ -934,7 +934,7 @@ void CondNoElseElement::get_modified_regs(RegSet& regs) const { // AbsElement ///////////////////////////// -AbsElement::AbsElement(Variable _source, RegSet _consumed) +AbsElement::AbsElement(RegisterAccess _source, RegSet _consumed) : source(_source), consumed(std::move(_consumed)) {} goos::Object AbsElement::to_form_internal(const Env& env) const { @@ -947,7 +947,7 @@ void AbsElement::apply(const std::function& f) { void AbsElement::apply_form(const std::function&) {} -void AbsElement::collect_vars(VariableSet& vars) const { +void AbsElement::collect_vars(RegAccessSet& vars) const { vars.insert(source); } @@ -957,9 +957,9 @@ void AbsElement::get_modified_regs(RegSet&) const {} // AshElement ///////////////////////////// -AshElement::AshElement(Variable _shift_amount, - Variable _value, - std::optional _clobber, +AshElement::AshElement(RegisterAccess _shift_amount, + RegisterAccess _value, + std::optional _clobber, bool _is_signed, RegSet _consumed) : shift_amount(_shift_amount), @@ -979,7 +979,7 @@ void AshElement::apply(const std::function& f) { void AshElement::apply_form(const std::function&) {} -void AshElement::collect_vars(VariableSet& vars) const { +void AshElement::collect_vars(RegAccessSet& vars) const { vars.insert(value); vars.insert(shift_amount); } @@ -990,7 +990,7 @@ void AshElement::get_modified_regs(RegSet&) const {} // TypeOfElement ///////////////////////////// -TypeOfElement::TypeOfElement(Form* _value, std::optional _clobber) +TypeOfElement::TypeOfElement(Form* _value, std::optional _clobber) : value(_value), clobber(_clobber) { value->parent_element = this; } @@ -1008,7 +1008,7 @@ void TypeOfElement::apply_form(const std::function& f) { value->apply_form(f); } -void TypeOfElement::collect_vars(VariableSet& vars) const { +void TypeOfElement::collect_vars(RegAccessSet& vars) const { value->collect_vars(vars); } @@ -1018,9 +1018,9 @@ void TypeOfElement::get_modified_regs(RegSet&) const {} // ConditionalMoveFalseElement ///////////////////////////// -ConditionalMoveFalseElement::ConditionalMoveFalseElement(Variable _dest, - Variable _old_value, - Variable _source, +ConditionalMoveFalseElement::ConditionalMoveFalseElement(RegisterAccess _dest, + RegisterAccess _old_value, + RegisterAccess _source, bool _on_zero) : dest(_dest), old_value(_old_value), source(_source), on_zero(_on_zero) {} @@ -1035,7 +1035,7 @@ void ConditionalMoveFalseElement::apply(const std::function& void ConditionalMoveFalseElement::apply_form(const std::function&) {} -void ConditionalMoveFalseElement::collect_vars(VariableSet& vars) const { +void ConditionalMoveFalseElement::collect_vars(RegAccessSet& vars) const { vars.insert(dest); vars.insert(old_value); vars.insert(source); @@ -1070,7 +1070,7 @@ GenericOperator GenericOperator::make_compare(IR2_Condition::Kind kind) { return op; } -void GenericOperator::collect_vars(VariableSet& vars) const { +void GenericOperator::collect_vars(RegAccessSet& vars) const { switch (m_kind) { case Kind::FIXED_OPERATOR: case Kind::CONDITION_OPERATOR: @@ -1303,7 +1303,7 @@ void GenericElement::apply_form(const std::function& f) { } } -void GenericElement::collect_vars(VariableSet& vars) const { +void GenericElement::collect_vars(RegAccessSet& vars) const { m_head.collect_vars(vars); for (auto x : m_elts) { x->collect_vars(vars); @@ -1340,7 +1340,7 @@ void CastElement::apply_form(const std::function& f) { m_source->apply_form(f); } -void CastElement::collect_vars(VariableSet& vars) const { +void CastElement::collect_vars(RegAccessSet& vars) const { m_source->collect_vars(vars); } @@ -1379,7 +1379,7 @@ DerefToken DerefToken::make_expr_placeholder() { return x; } -void DerefToken::collect_vars(VariableSet& vars) const { +void DerefToken::collect_vars(RegAccessSet& vars) const { switch (m_kind) { case Kind::INTEGER_CONSTANT: case Kind::FIELD_NAME: @@ -1508,7 +1508,7 @@ void DerefElement::apply_form(const std::function& f) { } } -void DerefElement::collect_vars(VariableSet& vars) const { +void DerefElement::collect_vars(RegAccessSet& vars) const { m_base->collect_vars(vars); for (auto& tok : m_tokens) { tok.collect_vars(vars); @@ -1526,7 +1526,7 @@ void DerefElement::get_modified_regs(RegSet& regs) const { // DynamicMethodAccess ///////////////////////////// -DynamicMethodAccess::DynamicMethodAccess(Variable source) : m_source(source) {} +DynamicMethodAccess::DynamicMethodAccess(RegisterAccess source) : m_source(source) {} goos::Object DynamicMethodAccess::to_form_internal(const Env& env) const { return pretty_print::build_list("dyn-method-access", m_source.to_form(env)); @@ -1538,7 +1538,7 @@ void DynamicMethodAccess::apply(const std::function& f) { void DynamicMethodAccess::apply_form(const std::function&) {} -void DynamicMethodAccess::collect_vars(VariableSet& vars) const { +void DynamicMethodAccess::collect_vars(RegAccessSet& vars) const { vars.insert(m_source); } @@ -1547,7 +1547,7 @@ void DynamicMethodAccess::get_modified_regs(RegSet&) const {} ///////////////////////////// // ArrayFieldAccess ///////////////////////////// -ArrayFieldAccess::ArrayFieldAccess(Variable source, +ArrayFieldAccess::ArrayFieldAccess(RegisterAccess source, const std::vector& deref_tokens, int expected_stride, int constant_offset) @@ -1579,7 +1579,7 @@ void ArrayFieldAccess::apply_form(const std::function& f) { } } -void ArrayFieldAccess::collect_vars(VariableSet& vars) const { +void ArrayFieldAccess::collect_vars(RegAccessSet& vars) const { vars.insert(m_source); for (auto& tok : m_deref_tokens) { tok.collect_vars(vars); @@ -1615,7 +1615,7 @@ void GetMethodElement::apply_form(const std::function& f) { m_in->apply_form(f); } -void GetMethodElement::collect_vars(VariableSet& vars) const { +void GetMethodElement::collect_vars(RegAccessSet& vars) const { m_in->collect_vars(vars); } @@ -1635,7 +1635,7 @@ goos::Object StringConstantElement::to_form_internal(const Env&) const { void StringConstantElement::apply(const std::function&) {} void StringConstantElement::apply_form(const std::function&) {} -void StringConstantElement::collect_vars(VariableSet&) const {} +void StringConstantElement::collect_vars(RegAccessSet&) const {} void StringConstantElement::get_modified_regs(RegSet&) const {} ///////////////////////////// @@ -1649,7 +1649,7 @@ goos::Object ConstantTokenElement::to_form_internal(const Env&) const { void ConstantTokenElement::apply(const std::function&) {} void ConstantTokenElement::apply_form(const std::function&) {} -void ConstantTokenElement::collect_vars(VariableSet&) const {} +void ConstantTokenElement::collect_vars(RegAccessSet&) const {} void ConstantTokenElement::get_modified_regs(RegSet&) const {} ///////////////////////////// @@ -1660,7 +1660,7 @@ ConstantFloatElement::ConstantFloatElement(float value) : m_value(value) {} void ConstantFloatElement::apply(const std::function&) {} void ConstantFloatElement::apply_form(const std::function&) {} -void ConstantFloatElement::collect_vars(VariableSet&) const {} +void ConstantFloatElement::collect_vars(RegAccessSet&) const {} void ConstantFloatElement::get_modified_regs(RegSet&) const {} goos::Object ConstantFloatElement::to_form_internal(const Env&) const { @@ -1670,7 +1670,7 @@ goos::Object ConstantFloatElement::to_form_internal(const Env&) const { StorePlainDeref::StorePlainDeref(DerefElement* dst, SimpleExpression expr, int my_idx, - Variable base_var, + RegisterAccess base_var, std::optional cast_type) : m_dst(dst), m_expr(std::move(expr)), @@ -1694,7 +1694,7 @@ void StorePlainDeref::apply(const std::function& f) { void StorePlainDeref::apply_form(const std::function&) {} -void StorePlainDeref::collect_vars(VariableSet& vars) const { +void StorePlainDeref::collect_vars(RegAccessSet& vars) const { m_expr.collect_vars(vars); m_dst->collect_vars(vars); } @@ -1706,7 +1706,7 @@ void StorePlainDeref::get_modified_regs(RegSet& regs) const { StoreArrayAccess::StoreArrayAccess(ArrayFieldAccess* dst, SimpleExpression expr, int my_idx, - Variable array_src) + RegisterAccess array_src) : m_dst(dst), m_expr(expr), m_my_idx(my_idx), m_base_var(array_src) {} goos::Object StoreArrayAccess::to_form_internal(const Env& env) const { @@ -1723,7 +1723,7 @@ void StoreArrayAccess::apply_form(const std::function& f) { m_dst->apply_form(f); } -void StoreArrayAccess::collect_vars(VariableSet& vars) const { +void StoreArrayAccess::collect_vars(RegAccessSet& vars) const { m_expr.collect_vars(vars); m_dst->collect_vars(vars); } @@ -1745,7 +1745,7 @@ void DecompiledDataElement::apply(const std::function& f) { void DecompiledDataElement::apply_form(const std::function&) {} -void DecompiledDataElement::collect_vars(VariableSet&) const {} +void DecompiledDataElement::collect_vars(RegAccessSet&) const {} void DecompiledDataElement::get_modified_regs(RegSet&) const {} diff --git a/decompiler/IR2/Form.h b/decompiler/IR2/Form.h index dbcd5314f7..adcc05265a 100644 --- a/decompiler/IR2/Form.h +++ b/decompiler/IR2/Form.h @@ -29,7 +29,7 @@ class FormElement { 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 collect_vars(RegAccessSet& vars) const = 0; virtual void get_modified_regs(RegSet& regs) const = 0; virtual bool active() const; @@ -67,7 +67,7 @@ class SimpleExpressionElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void update_from_stack(const Env& env, FormPool& pool, FormStack& stack, @@ -175,7 +175,7 @@ class StoreElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; @@ -195,7 +195,7 @@ class LoadSourceElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; int size() const { return m_size; } LoadVarOp::Kind kind() const { return m_kind; } const Form* location() const { return m_addr; } @@ -222,7 +222,7 @@ class SimpleAtomElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; const SimpleAtom& atom() const { return m_atom; } void update_from_stack(const Env& env, @@ -240,7 +240,7 @@ class SimpleAtomElement : public FormElement { */ class SetVarElement : public FormElement { public: - SetVarElement(const Variable& var, + SetVarElement(const RegisterAccess& var, Form* value, bool is_sequence_point, const SetVarInfo& info = {}); @@ -248,12 +248,12 @@ class SetVarElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; void get_modified_regs(RegSet& regs) const override; bool active() const override; - const Variable& dst() const { return m_dst; } + const RegisterAccess& dst() const { return m_dst; } const Form* src() const { return m_src; } Form* src() { return m_src; } bool is_eliminated_coloring_move() const { return m_var_info.is_eliminated_coloring_move; } @@ -268,7 +268,7 @@ class SetVarElement : public FormElement { const SetVarInfo& info() const { return m_var_info; } private: - Variable m_dst; + RegisterAccess m_dst; Form* m_src = nullptr; bool m_is_sequence_point = true; @@ -281,7 +281,7 @@ class StoreInSymbolElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; void get_modified_regs(RegSet& regs) const override; @@ -293,17 +293,17 @@ class StoreInSymbolElement : public FormElement { class StoreInPairElement : public FormElement { public: - StoreInPairElement(bool is_car, Variable pair, SimpleExpression value, int my_idx); + StoreInPairElement(bool is_car, RegisterAccess pair, SimpleExpression value, int my_idx); 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 collect_vars(RegAccessSet& vars) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; void get_modified_regs(RegSet& regs) const override; private: bool m_is_car = false; - Variable m_pair; + RegisterAccess m_pair; SimpleExpression m_value; int m_my_idx = -1; }; @@ -320,7 +320,7 @@ class SetFormFormElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; void get_modified_regs(RegSet& regs) const override; @@ -345,7 +345,7 @@ class AtomicOpElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; void get_modified_regs(RegSet& regs) const override; const AtomicOp* op() const { return m_op; } @@ -363,7 +363,7 @@ class AsmOpElement : public FormElement { 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 collect_vars(RegAccessSet& 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; } @@ -392,7 +392,7 @@ class ConditionElement : public FormElement { 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; + void collect_vars(RegAccessSet& vars) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; void update_from_stack(const Env& env, FormPool& pool, @@ -429,7 +429,7 @@ class FunctionCallElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void update_from_stack(const Env& env, FormPool& pool, FormStack& stack, @@ -452,7 +452,7 @@ class BranchElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; const BranchOp* op() const { return m_op; } @@ -475,7 +475,7 @@ class ReturnElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; void get_modified_regs(RegSet& regs) const override; }; @@ -510,7 +510,7 @@ class BreakElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; }; @@ -544,7 +544,7 @@ class CondWithElseElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; void get_modified_regs(RegSet& regs) const override; }; @@ -563,7 +563,7 @@ class EmptyElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; }; @@ -579,7 +579,7 @@ class WhileElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; void get_modified_regs(RegSet& regs) const override; Form* condition = nullptr; @@ -598,7 +598,7 @@ class UntilElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; void get_modified_regs(RegSet& regs) const override; Form* condition = nullptr; @@ -624,7 +624,7 @@ class ShortCircuitElement : public FormElement { enum Kind { UNKNOWN, AND, OR } kind = UNKNOWN; - Variable final_result; + RegisterAccess final_result; std::vector entries; std::optional used_as_value = std::nullopt; bool already_rewritten = false; @@ -633,7 +633,7 @@ class ShortCircuitElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; void update_from_stack(const Env& env, FormPool& pool, @@ -653,11 +653,11 @@ class CondNoElseElement : public FormElement { struct Entry { Form* condition = nullptr; Form* body = nullptr; - std::optional false_destination; + std::optional false_destination; FormElement* original_condition_branch = nullptr; bool cleaned = false; }; - Variable final_destination; + RegisterAccess final_destination; bool used_as_value = false; bool already_rewritten = false; std::vector entries; @@ -665,7 +665,7 @@ class CondNoElseElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; void get_modified_regs(RegSet& regs) const override; void update_from_stack(const Env& env, @@ -680,18 +680,18 @@ class CondNoElseElement : public FormElement { */ class AbsElement : public FormElement { public: - explicit AbsElement(Variable _source, RegSet _consumed); + explicit AbsElement(RegisterAccess _source, RegSet _consumed); 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 collect_vars(RegAccessSet& vars) const override; void update_from_stack(const Env& env, FormPool& pool, FormStack& stack, std::vector* result, bool allow_side_effects) override; void get_modified_regs(RegSet& regs) const override; - Variable source; + RegisterAccess source; RegSet consumed; }; @@ -702,19 +702,19 @@ class AbsElement : public FormElement { */ class AshElement : public FormElement { public: - Variable shift_amount, value; - std::optional clobber; + RegisterAccess shift_amount, value; + std::optional clobber; bool is_signed = true; RegSet consumed; - AshElement(Variable _shift_amount, - Variable _value, - std::optional _clobber, + AshElement(RegisterAccess _shift_amount, + RegisterAccess _value, + std::optional _clobber, bool _is_signed, RegSet _consumed); 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 collect_vars(RegAccessSet& vars) const override; void update_from_stack(const Env& env, FormPool& pool, FormStack& stack, @@ -730,12 +730,12 @@ class AshElement : public FormElement { class TypeOfElement : public FormElement { public: Form* value; - std::optional clobber; - TypeOfElement(Form* _value, std::optional _clobber); + std::optional clobber; + TypeOfElement(Form* _value, std::optional _clobber); 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; void update_from_stack(const Env& env, FormPool& pool, @@ -764,15 +764,18 @@ class TypeOfElement : public FormElement { */ class ConditionalMoveFalseElement : public FormElement { public: - Variable dest; - Variable old_value; - Variable source; + RegisterAccess dest; + RegisterAccess old_value; + RegisterAccess source; bool on_zero = false; - ConditionalMoveFalseElement(Variable _dest, Variable _old_value, Variable _source, bool _on_zero); + ConditionalMoveFalseElement(RegisterAccess _dest, + RegisterAccess _old_value, + RegisterAccess _source, + bool _on_zero); 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; }; @@ -790,7 +793,7 @@ class GenericOperator { static GenericOperator make_fixed(FixedOperatorKind kind); static GenericOperator make_function(Form* value); static GenericOperator make_compare(IR2_Condition::Kind kind); - void collect_vars(VariableSet& vars) const; + void collect_vars(RegAccessSet& vars) const; goos::Object to_form(const Env& env) const; void apply(const std::function& f); void apply_form(const std::function& f); @@ -836,7 +839,7 @@ class GenericElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void update_from_stack(const Env& env, FormPool& pool, FormStack& stack, @@ -859,7 +862,7 @@ class CastElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; void update_from_stack(const Env& env, FormPool& pool, @@ -890,7 +893,7 @@ class DerefToken { static DerefToken make_field_name(const std::string& name); static DerefToken make_expr_placeholder(); - void collect_vars(VariableSet& vars) const; + void collect_vars(RegAccessSet& vars) const; goos::Object to_form(const Env& env) const; void apply(const std::function& f); void apply_form(const std::function& f); @@ -923,7 +926,7 @@ class DerefElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void update_from_stack(const Env& env, FormPool& pool, FormStack& stack, @@ -947,11 +950,11 @@ class DerefElement : public FormElement { class DynamicMethodAccess : public FormElement { public: - explicit DynamicMethodAccess(Variable source); + explicit DynamicMethodAccess(RegisterAccess source); 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 collect_vars(RegAccessSet& vars) const override; void update_from_stack(const Env& env, FormPool& pool, FormStack& stack, @@ -960,19 +963,19 @@ class DynamicMethodAccess : public FormElement { void get_modified_regs(RegSet& regs) const override; private: - Variable m_source; + RegisterAccess m_source; }; class ArrayFieldAccess : public FormElement { public: - ArrayFieldAccess(Variable source, + ArrayFieldAccess(RegisterAccess source, const std::vector& deref_tokens, int expected_stride, int constant_offset); 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 collect_vars(RegAccessSet& vars) const override; void update_from_stack(const Env& env, FormPool& pool, FormStack& stack, @@ -987,7 +990,7 @@ class ArrayFieldAccess : public FormElement { bool allow_side_effects); private: - Variable m_source; + RegisterAccess m_source; std::vector m_deref_tokens; int m_expected_stride = -1; int m_constant_offset = -1; @@ -999,7 +1002,7 @@ class GetMethodElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; void update_from_stack(const Env& env, FormPool& pool, @@ -1019,7 +1022,7 @@ class StringConstantElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; void update_from_stack(const Env& env, FormPool& pool, @@ -1037,7 +1040,7 @@ class ConstantTokenElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; void update_from_stack(const Env& env, FormPool& pool, @@ -1055,7 +1058,7 @@ class ConstantFloatElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; void update_from_stack(const Env& env, FormPool& pool, @@ -1072,13 +1075,13 @@ class StorePlainDeref : public FormElement { StorePlainDeref(DerefElement* dst, SimpleExpression expr, int my_idx, - Variable base_var, + RegisterAccess base_var, std::optional cast_type); 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; @@ -1086,17 +1089,20 @@ class StorePlainDeref : public FormElement { DerefElement* m_dst = nullptr; SimpleExpression m_expr; int m_my_idx = -1; - Variable m_base_var; + RegisterAccess m_base_var; std::optional m_cast_type; }; class StoreArrayAccess : public FormElement { public: - StoreArrayAccess(ArrayFieldAccess* dst, SimpleExpression expr, int my_idx, Variable array_src); + StoreArrayAccess(ArrayFieldAccess* dst, + SimpleExpression expr, + int my_idx, + RegisterAccess array_src); 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; @@ -1104,7 +1110,7 @@ class StoreArrayAccess : public FormElement { ArrayFieldAccess* m_dst = nullptr; SimpleExpression m_expr; int m_my_idx = -1; - Variable m_base_var; + RegisterAccess m_base_var; }; class DecompiledDataElement : public FormElement { @@ -1113,7 +1119,7 @@ class DecompiledDataElement : public FormElement { 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 collect_vars(RegAccessSet& vars) const override; void get_modified_regs(RegSet& regs) const override; private: @@ -1183,7 +1189,7 @@ class Form { void inline_forms(std::vector& forms, const Env& env) const; void apply(const std::function& f); void apply_form(const std::function& f); - void collect_vars(VariableSet& vars) const; + void collect_vars(RegAccessSet& vars) const; void update_children_from_stack(const Env& env, FormPool& pool, diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index 35519f5723..75e27384cf 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -88,7 +88,7 @@ bool is_power_of_two(int in, int* out) { /*! * Create a form which represents a variable. */ -Form* var_to_form(const Variable& var, FormPool& pool) { +Form* var_to_form(const RegisterAccess& var, FormPool& pool) { return pool.alloc_single_element_form(nullptr, SimpleAtom::make_var(var)); } @@ -101,13 +101,14 @@ Form* var_to_form(const Variable& var, FormPool& pool) { * @param output : list of locations to push results. * @param consumes : if you have a different list of variables that are consumed by this operation. */ -void pop_helper(const std::vector& vars, +void pop_helper(const std::vector& vars, const Env& env, FormPool& pool, FormStack& stack, const std::vector*>& output, bool allow_side_effects, - const std::optional& consumes = std::nullopt) { + const std::optional& consumes = std::nullopt, + const std::vector& times_used = {}) { // to submit to stack to attempt popping std::vector submit_regs; // submit_reg[i] is for var submit_reg_to_var[i] @@ -126,8 +127,28 @@ void pop_helper(const std::vector& vars, if (consumes_to_use.find(var.reg()) != consumes_to_use.end()) { if (reg_counts.at(var.reg()) == 1) { // we consume the register, so it's safe to try popping. - submit_reg_to_var.push_back(var_idx); - submit_regs.push_back(var.reg()); + + int times = 1; + if (!times_used.empty()) { + times = times_used.at(var_idx); + } + + auto& use_def = env.get_use_def_info(var); + if (use_def.use_count() == times && use_def.def_count() == 1) { + submit_reg_to_var.push_back(var_idx); + submit_regs.push_back(var.reg()); + } else { + // fmt::print("Unsafe to pop {}: used {} times, def {} times, expected use {}\n", + // var.to_string(env), use_def.use_count(), use_def.def_count(), + // times); + // if (var.to_string(env) == "v1-3") { + // for (auto& use : use_def.defs) { + // if (!use.disabled) { + // fmt::print(" at instruction {}\n", use.op_id); + // } + // } + // } + } } } } @@ -185,12 +206,13 @@ void pop_helper(const std::vector& vars, * This uses the barrier register approach, but it is only effective if you put all registers * appearing at the same level. */ -std::vector pop_to_forms(const std::vector& vars, +std::vector pop_to_forms(const std::vector& vars, const Env& env, FormPool& pool, FormStack& stack, bool allow_side_effects, - const std::optional& consumes = std::nullopt) { + const std::optional& consumes = std::nullopt, + const std::vector& times_to_use = {}) { std::vector forms; std::vector> forms_out; std::vector*> form_ptrs; @@ -201,7 +223,7 @@ std::vector pop_to_forms(const std::vector& vars, form_ptrs.push_back(&x); } - pop_helper(vars, env, pool, stack, form_ptrs, allow_side_effects, consumes); + pop_helper(vars, env, pool, stack, form_ptrs, allow_side_effects, consumes, times_to_use); for (auto& x : forms_out) { forms.push_back(pool.alloc_sequence_form(nullptr, x)); @@ -215,7 +237,7 @@ std::vector pop_to_forms(const std::vector& vars, /*! * type == float (exactly)? */ -bool is_float_type(const Env& env, int my_idx, Variable var) { +bool is_float_type(const Env& env, int my_idx, RegisterAccess var) { auto type = env.get_types_before_op(my_idx).get(var.reg()).typespec(); return type == TypeSpec("float"); } @@ -223,7 +245,7 @@ bool is_float_type(const Env& env, int my_idx, Variable var) { /*! * type == int (exactly)? */ -bool is_int_type(const Env& env, int my_idx, Variable var) { +bool is_int_type(const Env& env, int my_idx, RegisterAccess var) { auto type = env.get_types_before_op(my_idx).get(var.reg()).typespec(); return type == TypeSpec("int"); } @@ -231,12 +253,12 @@ bool is_int_type(const Env& env, int my_idx, Variable var) { /*! * type == uint (exactly)? */ -bool is_uint_type(const Env& env, int my_idx, Variable var) { +bool is_uint_type(const Env& env, int my_idx, RegisterAccess var) { auto type = env.get_types_before_op(my_idx).get(var.reg()).typespec(); return type == TypeSpec("uint"); } -bool is_ptr_or_child(const Env& env, int my_idx, Variable var) { +bool is_ptr_or_child(const Env& env, int my_idx, RegisterAccess var) { auto type = env.get_types_before_op(my_idx).get(var.reg()).typespec().base_type(); return type == "pointer"; } @@ -1064,7 +1086,7 @@ void StoreInSymbolElement::push_to_stack(const Env& env, FormPool& pool, FormSta void StoreInPairElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) { auto op = m_is_car ? FixedOperatorKind::CAR : FixedOperatorKind::CDR; if (m_value.is_var()) { - auto vars = std::vector({m_value.var(), m_pair}); + auto vars = std::vector({m_value.var(), m_pair}); auto popped = pop_to_forms(vars, env, pool, stack, true); auto addr = pool.alloc_single_element_form( nullptr, GenericOperator::make_fixed(op), popped.at(1)); @@ -1088,7 +1110,7 @@ void StoreInPairElement::push_to_stack(const Env& env, FormPool& pool, FormStack void StorePlainDeref::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) { mark_popped(); if (m_expr.is_var()) { - auto vars = std::vector({m_expr.var(), m_base_var}); + auto vars = std::vector({m_expr.var(), m_base_var}); auto popped = pop_to_forms(vars, env, pool, stack, true); if (m_cast_type.has_value()) { m_dst->set_base( @@ -1104,7 +1126,7 @@ void StorePlainDeref::push_to_stack(const Env& env, FormPool& pool, FormStack& s fr->mark_popped(); stack.push_form_element(fr, true); } else { - auto vars = std::vector({m_base_var}); + auto vars = std::vector({m_base_var}); auto popped = pop_to_forms(vars, env, pool, stack, true); if (m_cast_type.has_value()) { m_dst->set_base( @@ -1127,13 +1149,13 @@ void StoreArrayAccess::push_to_stack(const Env& env, FormPool& pool, FormStack& Form* expr_form = nullptr; Form* array_form = nullptr; if (m_expr.is_var()) { - auto vars = std::vector({m_expr.var(), m_base_var}); + auto vars = std::vector({m_expr.var(), m_base_var}); auto popped = pop_to_forms(vars, env, pool, stack, true); m_dst->mark_popped(); expr_form = popped.at(0); array_form = popped.at(1); } else { - auto vars = std::vector({m_base_var}); + auto vars = std::vector({m_base_var}); auto popped = pop_to_forms(vars, env, pool, stack, true); m_dst->mark_popped(); expr_form = pool.alloc_single_element_form(nullptr, m_expr, m_my_idx); @@ -1195,23 +1217,23 @@ void FunctionCallElement::update_from_stack(const Env& env, auto nargs = m_op->arg_vars().size(); args.resize(nargs, nullptr); - std::vector all_pop_vars = {m_op->function_var()}; + std::vector all_pop_vars = {m_op->function_var()}; for (size_t i = 0; i < nargs; i++) { all_pop_vars.push_back(m_op->arg_vars().at(i)); } TypeSpec function_type; - bool is_method = false; + bool is_virtual_method = false; auto& tp_type = env.get_types_before_op(all_pop_vars.at(0).idx()).get(all_pop_vars.at(0).reg()); if (env.has_type_analysis()) { if (tp_type.kind == TP_Type::Kind::VIRTUAL_METHOD && all_pop_vars.size() >= 1) { - is_method = true; + is_virtual_method = true; } function_type = tp_type.typespec(); } // assert(is_method == m_op->is_method()); - if (is_method != m_op->is_method()) { + if (is_virtual_method != m_op->is_method()) { lg::error("Disagreement on method!"); throw std::runtime_error("Disagreement on method"); } @@ -1226,6 +1248,7 @@ void FunctionCallElement::update_from_stack(const Env& env, if (tp_type.kind == TP_Type::Kind::NON_VIRTUAL_METHOD) { std::swap(all_pop_vars.at(0), all_pop_vars.at(1)); } + auto unstacked = pop_to_forms(all_pop_vars, env, pool, stack, allow_side_effects); if (tp_type.kind == TP_Type::Kind::NON_VIRTUAL_METHOD) { std::swap(unstacked.at(0), unstacked.at(1)); @@ -1252,7 +1275,7 @@ void FunctionCallElement::update_from_stack(const Env& env, } FormElement* new_form = nullptr; - if (is_method) { + if (is_virtual_method) { // fmt::print("STACK:\n{}\n\n", stack.print(env)); auto matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::METHOD_OF_OBJECT), {Matcher::any(0), Matcher::any(1)}); @@ -1638,7 +1661,7 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac return; } // first, let's try to detect if all bodies write the same value - std::optional last_var; + std::optional last_var; bool rewrite_as_set = true; // the first condition is special @@ -1693,9 +1716,11 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac write_output_forms.push_back(else_ir); // check all to see if they write the value. + std::vector dest_sets; for (auto form : write_output_forms) { auto last_in_body = dynamic_cast(form->elts().back()); if (last_in_body) { + dest_sets.push_back(last_in_body); if (last_var.has_value()) { if (last_var->reg() != last_in_body->dst().reg()) { rewrite_as_set = false; @@ -1703,7 +1728,9 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac } } last_var = last_in_body->dst(); - } + } // For now, I am fine with letting this fail. For example, if the set is eliminated by a + // coloring move. If this makes really ugly code later on, we could use this to disable + // write as set. } if (!last_var.has_value()) { @@ -1727,6 +1754,17 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac rewrite_to_get_var(else_ir->elts(), pool, *last_var, env); } + // update register info + if (rewrite_as_set && !set_unused) { + // might not be the same if a set is eliminated by a coloring move. + // assert(dest_sets.size() == write_output_forms.size()); + for (size_t i = 0; i < dest_sets.size() - 1; i++) { + auto var = dest_sets.at(i)->dst(); + auto* env2 = const_cast(&env); + env2->disable_def(var); + } + } + if (rewrite_as_set) { if (set_unused) { stack.push_form_element(this, true); @@ -1980,7 +2018,7 @@ void ConditionElement::push_to_stack(const Env& env, FormPool& pool, FormStack& mark_popped(); std::vector source_forms, popped_forms; std::vector source_types; - std::vector vars; + std::vector vars; for (int i = 0; i < get_condition_num_args(m_kind); i++) { if (m_src[i]->is_var()) { @@ -2030,7 +2068,7 @@ void ConditionElement::update_from_stack(const Env& env, mark_popped(); std::vector source_forms, popped_forms; std::vector source_types; - std::vector vars; + std::vector vars; for (int i = 0; i < get_condition_num_args(m_kind); i++) { if (m_src[i]->is_var()) { diff --git a/decompiler/IR2/FormStack.cpp b/decompiler/IR2/FormStack.cpp index ff1674a35f..7265faf8a8 100644 --- a/decompiler/IR2/FormStack.cpp +++ b/decompiler/IR2/FormStack.cpp @@ -38,7 +38,7 @@ std::string FormStack::print(const Env& env) { return result; } -void FormStack::push_value_to_reg(Variable var, +void FormStack::push_value_to_reg(RegisterAccess var, Form* value, bool sequence_point, const SetVarInfo& info) { @@ -52,7 +52,7 @@ void FormStack::push_value_to_reg(Variable var, m_stack.push_back(entry); } -void FormStack::push_value_to_reg_dead(Variable var, +void FormStack::push_value_to_reg_dead(RegisterAccess var, Form* value, bool sequence_point, const SetVarInfo& info) { @@ -66,8 +66,8 @@ void FormStack::push_value_to_reg_dead(Variable var, m_stack.push_back(entry); } -void FormStack::push_non_seq_reg_to_reg(const Variable& dst, - const Variable& src, +void FormStack::push_non_seq_reg_to_reg(const RegisterAccess& dst, + const RegisterAccess& src, Form* src_as_form, const SetVarInfo& info) { assert(src_as_form); @@ -100,7 +100,7 @@ void FormStack::push_form_element(FormElement* elt, bool sequence_point) { m_stack.push_back(entry); } -Form* FormStack::pop_reg(const Variable& var, +Form* FormStack::pop_reg(const RegisterAccess& var, const RegSet& barrier, const Env& env, bool allow_side_effects, @@ -227,7 +227,7 @@ namespace { bool is_op_in_place(SetVarElement* elt, FixedOperatorKind op, const Env&, - Variable* base_out, + RegisterAccess* base_out, Form** val_out) { auto matcher = Matcher::op(GenericOpMatcher::fixed(op), {Matcher::any_reg(0), Matcher::any(1)}); auto result = match(matcher, elt->src()); @@ -255,7 +255,7 @@ FormElement* rewrite_set_op_in_place_for_kind(SetVarElement* in, FixedOperatorKind first_kind, FixedOperatorKind in_place_kind) { Form* val = nullptr; - Variable base; + RegisterAccess base; if (is_op_in_place(in, first_kind, env, &base, &val)) { return pool.alloc_element( @@ -334,10 +334,10 @@ std::vector FormStack::rewrite(FormPool& pool, const Env& env) { void rewrite_to_get_var(std::vector& default_result, FormPool& pool, - const Variable& var, + const RegisterAccess& var, const Env&) { bool keep_going = true; - Variable var_to_get = var; + RegisterAccess var_to_get = var; std::vector result; @@ -373,7 +373,7 @@ void rewrite_to_get_var(std::vector& default_result, std::vector rewrite_to_get_var(FormStack& stack, FormPool& pool, - const Variable& var, + const RegisterAccess& var, const Env& env) { auto default_result = stack.rewrite(pool, env); rewrite_to_get_var(default_result, pool, var, env); diff --git a/decompiler/IR2/FormStack.h b/decompiler/IR2/FormStack.h index eb82c85fdb..5adc90b393 100644 --- a/decompiler/IR2/FormStack.h +++ b/decompiler/IR2/FormStack.h @@ -13,20 +13,20 @@ class Form; class FormStack { public: explicit FormStack(bool is_root_stack) : m_is_root_stack(is_root_stack) {} - void push_value_to_reg(Variable var, + void push_value_to_reg(RegisterAccess var, Form* value, bool sequence_point, const SetVarInfo& info = {}); - void push_non_seq_reg_to_reg(const Variable& dst, - const Variable& src, + void push_non_seq_reg_to_reg(const RegisterAccess& dst, + const RegisterAccess& src, Form* src_as_form, const SetVarInfo& info = {}); - void push_value_to_reg_dead(Variable var, + void push_value_to_reg_dead(RegisterAccess var, Form* value, bool sequence_point, const SetVarInfo& info = {}); void push_form_element(FormElement* elt, bool sequence_point); - Form* pop_reg(const Variable& var, + Form* pop_reg(const RegisterAccess& var, const RegSet& barrier, const Env& env, bool allow_side_effects, @@ -45,10 +45,11 @@ class FormStack { private: struct StackEntry { - bool active = true; // should this appear in the output? - std::optional destination; // what register we are setting (or nullopt if no dest.) - std::optional non_seq_source; // source variable, if we are setting var to var. - Form* source = nullptr; // the value we are setting the register to. + bool active = true; // should this appear in the output? + std::optional + destination; // what register we are setting (or nullopt if no dest.) + std::optional non_seq_source; // source variable, if we are setting var to var. + Form* source = nullptr; // the value we are setting the register to. FormElement* elt = nullptr; bool sequence_point = false; @@ -65,10 +66,10 @@ class FormStack { void rewrite_to_get_var(std::vector& default_result, FormPool& pool, - const Variable& var, + const RegisterAccess& var, const Env& env); std::vector rewrite_to_get_var(FormStack& stack, FormPool& pool, - const Variable& var, + const RegisterAccess& var, const Env& env); } // namespace decompiler diff --git a/decompiler/IR2/GenericElementMatcher.cpp b/decompiler/IR2/GenericElementMatcher.cpp index fe7e919807..aab8f56f6b 100644 --- a/decompiler/IR2/GenericElementMatcher.cpp +++ b/decompiler/IR2/GenericElementMatcher.cpp @@ -132,7 +132,7 @@ bool Matcher::do_match(Form* input, MatchResult::Maps* maps_out) const { return true; case Kind::ANY_REG: { bool got = false; - Variable result; + RegisterAccess result; auto as_simple_atom = dynamic_cast(input->try_as_single_element()); if (as_simple_atom) { diff --git a/decompiler/IR2/GenericElementMatcher.h b/decompiler/IR2/GenericElementMatcher.h index eb2acbd2e2..21cb4af476 100644 --- a/decompiler/IR2/GenericElementMatcher.h +++ b/decompiler/IR2/GenericElementMatcher.h @@ -14,7 +14,7 @@ class GenericOpMatcher; struct MatchResult { bool matched = false; struct Maps { - std::vector> regs; + std::vector> regs; std::unordered_map strings; std::unordered_map forms; std::unordered_map label; diff --git a/decompiler/IR2/IR2_common.h b/decompiler/IR2/IR2_common.h index 250e97b224..a4b344f782 100644 --- a/decompiler/IR2/IR2_common.h +++ b/decompiler/IR2/IR2_common.h @@ -7,15 +7,17 @@ #include "third-party/fmt/core.h" namespace decompiler { -enum class VariableMode : u8 { - READ, // represents value of the variable at the beginning of the instruction - WRITE // represents value of the variable at the end of the instruction +enum class AccessMode : u8 { + READ, // represents value of the variable/register at the beginning of the instruction + WRITE // represents value of the variable/register at the end of the instruction }; /*! * A register plus an integer ID. */ struct RegId { + RegId() = default; + RegId(Register _reg, int _id) : reg(_reg), id(_id) {} Register reg; int id = -1; struct hash { @@ -33,11 +35,13 @@ struct RegId { return reg < other.reg; } } + + std::string print() const { return fmt::format("{}-{}", reg.to_charp(), id); } }; class Env; /*! - * A "Variable" represents a register at a given instruction index. + * A "RegisterAccess" represents a register at a given instruction index. * The register can either be a GOAL local variable or a GOAL register used in inline assembly. * Because OpenGOAL's registers don't one-to-one map to GOAL registers, GOAL "inline assembly * registers" will become OpenGOAL variables, and are treated similarly to variables in @@ -54,10 +58,10 @@ class Env; * Note: access to the process pointer (s6) is handled as a variable. As a result, you may always * use s6 as a variable. */ -class Variable { +class RegisterAccess { public: - Variable() = default; - Variable(VariableMode mode, Register reg, int atomic_idx, bool allow_all = false); + RegisterAccess() = default; + RegisterAccess(AccessMode mode, Register reg, int atomic_idx, bool allow_all = false); enum class Print { AS_REG, // print as a PS2 register name @@ -67,27 +71,31 @@ class Variable { }; goos::Object to_form(const Env& env, Print mode = Print::AUTOMATIC) const; + std::string to_string(const Env& env, Print mode = Print::AUTOMATIC) const; - bool operator==(const Variable& other) const; - bool operator!=(const Variable& other) const; + bool operator==(const RegisterAccess& other) const; + bool operator!=(const RegisterAccess& other) const; const Register& reg() const { return m_reg; } - VariableMode mode() const { return m_mode; } + AccessMode mode() const { return m_mode; } int idx() const { return m_atomic_idx; } struct hash { - auto operator()(const Variable& x) const { + auto operator()(const RegisterAccess& x) const { return (Register::hash()(x.m_reg) << 2) ^ (int(x.m_mode) << 1) ^ x.m_atomic_idx; } }; private: - VariableMode m_mode = VariableMode::READ; // do we represent a read or a write? - Register m_reg; // the EE register - int m_atomic_idx = -1; // the index in the function's list of AtomicOps + AccessMode m_mode = AccessMode::READ; // do we represent a read or a write? + Register m_reg; // the EE register + int m_atomic_idx = -1; // the index in the function's list of AtomicOps }; -using VariableSet = std::unordered_set; +using RegAccessSet = std::unordered_set; + +template +using RegAccessMap = std::unordered_map; enum class FixedOperatorKind { GPR_TO_FPR, @@ -131,6 +139,63 @@ enum class FixedOperatorKind { INVALID }; +// for the use of a variable +struct AccessRecord { + int op_id = -1; + int block_id = -1; + AccessMode mode = AccessMode::WRITE; + bool disabled = false; +}; + +struct UseDefInfo { + std::vector uses; + std::vector defs; + + void disable_use(int op_id) { + for (auto& x : uses) { + if (x.op_id == op_id) { + assert(!x.disabled); + x.disabled = true; + return; + } + } + assert(false); + } + + void disable_def(int op_id) { + for (auto& x : defs) { + if (x.op_id == op_id) { + assert(!x.disabled); + x.disabled = true; + return; + } + } + assert(false); + } + + int use_count() const { + int count = 0; + for (auto& x : uses) { + if (!x.disabled) { + count++; + } + } + return count; + } + + int def_count() const { + int count = 0; + for (auto& x : defs) { + if (!x.disabled) { + count++; + } + } + return count; + } + + std::unordered_set ssa_vars; +}; + struct VariableNames { struct VarInfo { VarInfo() = default; @@ -141,15 +206,32 @@ struct VariableNames { }; // todo - this is kind of gross. + // program var to info std::unordered_map, Register::hash> read_vars, write_vars; + + // access to program var. std::unordered_map, Register::hash> read_opid_to_varid, write_opid_to_varid; std::unordered_set eliminated_move_op_ids; - const VarInfo& lookup(Register reg, int op_id, VariableMode mode) const { - if (mode == VariableMode::READ) { + std::unordered_map use_def_info; + + void disable_use(const RegisterAccess& access) { + assert(access.mode() == AccessMode::READ); + auto var_id = read_opid_to_varid.at(access.reg()).at(access.idx()); + use_def_info.at(RegId(access.reg(), var_id)).disable_use(access.idx()); + } + + void disable_def(const RegisterAccess& access) { + assert(access.mode() == AccessMode::WRITE); + auto var_id = write_opid_to_varid.at(access.reg()).at(access.idx()); + use_def_info.at(RegId(access.reg(), var_id)).disable_def(access.idx()); + } + + const VarInfo& lookup(Register reg, int op_id, AccessMode mode) const { + if (mode == AccessMode::READ) { return read_vars.at(reg).at(read_opid_to_varid.at(reg).at(op_id)); } else { return write_vars.at(reg).at(write_opid_to_varid.at(reg).at(op_id)); diff --git a/decompiler/ObjectFile/ObjectFileDB_IR2.cpp b/decompiler/ObjectFile/ObjectFileDB_IR2.cpp index 5b11714964..7dd7fc61b0 100644 --- a/decompiler/ObjectFile/ObjectFileDB_IR2.cpp +++ b/decompiler/ObjectFile/ObjectFileDB_IR2.cpp @@ -36,10 +36,10 @@ void ObjectFileDB::analyze_functions_ir2(const std::string& output_dir) { ir2_type_analysis_pass(); lg::info("Register usage analysis..."); ir2_register_usage_pass(); - lg::info("Variable analysis..."); - ir2_variable_pass(); lg::info("Initial structuring.."); ir2_cfg_build_pass(); + lg::info("Variable analysis..."); + ir2_variable_pass(); if (get_config().analyze_expressions) { lg::info("Storing temporary form result..."); ir2_store_current_forms(); diff --git a/decompiler/analysis/atomic_op_builder.cpp b/decompiler/analysis/atomic_op_builder.cpp index b5920e9580..40d65612c3 100644 --- a/decompiler/analysis/atomic_op_builder.cpp +++ b/decompiler/analysis/atomic_op_builder.cpp @@ -44,15 +44,15 @@ Register rv0() { // Variable Helpers ///////////////////////// -Variable make_dst_var(Register reg, int idx) { - return Variable(VariableMode::WRITE, reg, idx); +RegisterAccess make_dst_var(Register reg, int idx) { + return RegisterAccess(AccessMode::WRITE, reg, idx); } -Variable make_src_var(Register reg, int idx) { - return Variable(VariableMode::READ, reg, idx); +RegisterAccess make_src_var(Register reg, int idx) { + return RegisterAccess(AccessMode::READ, reg, idx); } -Variable make_dst_var(const Instruction& i, int idx) { +RegisterAccess make_dst_var(const Instruction& i, int idx) { assert(i.n_dst == 1); return make_dst_var(i.get_dst(0).get_reg(), idx); } diff --git a/decompiler/analysis/cfg_builder.cpp b/decompiler/analysis/cfg_builder.cpp index 3f521b896a..40e09f2fd3 100644 --- a/decompiler/analysis/cfg_builder.cpp +++ b/decompiler/analysis/cfg_builder.cpp @@ -277,7 +277,7 @@ bool delay_slot_sets_truthy(BranchElement* branch, SetVarOp& delay) { */ bool try_clean_up_sc_as_and(FormPool& pool, Function& func, ShortCircuitElement* ir) { Register destination; - Variable ir_dest; + RegisterAccess ir_dest; for (int i = 0; i < int(ir->entries.size()) - 1; i++) { auto branch = get_condition_branch(ir->entries.at(i).condition); assert(branch.first); @@ -318,6 +318,35 @@ bool try_clean_up_sc_as_and(FormPool& pool, Function& func, ShortCircuitElement* branch_info.consumes.insert(x); } + auto delay_op = func.ir2.atomic_ops->ops.at(delay_id).get(); + auto as_set = dynamic_cast(delay_op); + assert(as_set); + if (as_set->src().is_var()) { + // must be the case where the src should have truthy in it. + // lg::warn("Disabling use of {} in or delay slot", as_set->to_string(func.ir2.env)); + func.ir2.env.disable_use(as_set->src().var()); + } + + // we also want to fix up the use/def info for the result. + // it's somewhat arbitrary, but we use the convention that the short-circuit defs + // are eliminated: + auto& ud_info = func.ir2.env.get_use_def_info(as_set->dst()); + if (i == int(ir->entries.size()) - 2) { + if (ud_info.def_count() == 1) { + // the final case of the or doesn't explicitly set the destination register. + // this can happen if the move is eliminated during coloring. + // for now, let's leave this last def here, just so it looks like _something_ sets it. + + } else { + // lg::warn("Disabling def of {} in final or delay slot", + // as_set->to_string(func.ir2.env)); + func.ir2.env.disable_def(as_set->dst()); + } + } else { + // lg::warn("Disabling def of {} in or delay slot", as_set->to_string(func.ir2.env)); + func.ir2.env.disable_def(as_set->dst()); + } + if (i == 0) { live_out_result = (branch_info.written_and_unused.find(ir_dest.reg()) == branch_info.written_and_unused.end()); @@ -346,12 +375,20 @@ bool try_clean_up_sc_as_and(FormPool& pool, Function& func, ShortCircuitElement* * Note - this will convert an and to a very strange or, so always use the try as and first. */ bool try_clean_up_sc_as_or(FormPool& pool, Function& func, ShortCircuitElement* ir) { - Register destination; - Variable ir_dest; + // all cases of an or, excluding the last one, should move the value "true" into the result reg + // in case the short circuit is taken. The final case should just write the result reg. + + Register destination; // destination register + RegisterAccess ir_dest; // destination access (the first one) + + // all but the last one (these should all do the delay slot trick) + // this first pass is where we can reject this as an or. for (int i = 0; i < int(ir->entries.size()) - 1; i++) { + // short circuit branch auto branch = get_condition_branch(ir->entries.at(i).condition); assert(branch.first); assert(ir->entries.at(i).branch_delay.has_value()); + // the branch should write true (there's two ways this can happen) if (!delay_slot_sets_truthy(branch.first, *ir->entries.at(i).branch_delay)) { return false; } @@ -367,9 +404,19 @@ bool try_clean_up_sc_as_or(FormPool& pool, Function& func, ShortCircuitElement* } } + // at this point, we know that all non-last cases write the destination, and what + // the destination is. + // so we commit to rewriting this thing as an or, and any errors from here on are fatal. + ir->kind = ShortCircuitElement::OR; ir->final_result = ir_dest; + // we also would like to know if the result of this OR is used or not. + // there's also a sanity check that: + // if the result is used - all writes to the result reg _should_ be live + // if the result is unused - all writes to the result reg should be dead. + // otherwise it means that our control flow graph is messed up, and we abort. + bool live_out_result = false; for (int i = 0; i < int(ir->entries.size()) - 1; i++) { @@ -380,12 +427,48 @@ bool try_clean_up_sc_as_or(FormPool& pool, Function& func, ShortCircuitElement* auto delay_id = ir->entries.at(i).branch_delay->dst().idx(); auto& delay_info = func.ir2.env.reg_use().op.at(delay_id); + // cheat for the old method auto branch_id = branch.first->op()->op_id(); auto& branch_info = func.ir2.env.reg_use().op.at(branch_id); for (auto x : delay_info.consumes) { branch_info.consumes.insert(x); } + // the branch may look like this: + // bnel s7, a3, L283 + // or a2, a3, r0 + // which reads a3 twice. But we want it to count as only once, as the second read + // is inserted by the GOAL compiler, not by putting a var twice in the source code. + auto delay_op = func.ir2.atomic_ops->ops.at(delay_id).get(); + auto as_set = dynamic_cast(delay_op); + assert(as_set); + if (as_set->src().is_var()) { + // must be the case where the src should have truthy in it. + // lg::warn("Disabling use of {} in or delay slot", as_set->to_string(func.ir2.env)); + func.ir2.env.disable_use(as_set->src().var()); + } + + // we also want to fix up the use/def info for the result. + // it's somewhat arbitrary, but we use the convention that the short-circuit defs + // are eliminated: + auto& ud_info = func.ir2.env.get_use_def_info(as_set->dst()); + if (i == int(ir->entries.size()) - 2) { + if (ud_info.def_count() == 1) { + // the final case of the or doesn't explicitly set the destination register. + // this can happen if the move is eliminated during coloring. + // for now, let's leave this last def here, just so it looks like _something_ sets it. + // TODO - what if this isn't a def in the last slot? Does it matter? + } else { + // lg::warn("Disabling def of {} in final or delay slot", + // as_set->to_string(func.ir2.env)); + func.ir2.env.disable_def(as_set->dst()); + } + + } else { + // lg::warn("Disabling def of {} in or delay slot", as_set->to_string(func.ir2.env)); + func.ir2.env.disable_def(as_set->dst()); + } + if (i == 0) { live_out_result = (delay_info.written_and_unused.find(ir_dest.reg()) == delay_info.written_and_unused.end()); @@ -400,7 +483,10 @@ bool try_clean_up_sc_as_or(FormPool& pool, Function& func, ShortCircuitElement* *(branch.second) = replacement; } + // TODO - check the one remaining def location? + ir->used_as_value = live_out_result; + return true; } @@ -505,7 +591,7 @@ const SimpleAtom* get_atom_src(const Form* form) { * successfully */ void convert_cond_no_else_to_compare(FormPool& pool, - const Function& f, + Function& f, FormElement** ir_loc, Form* parent_form) { CondNoElseElement* cne = dynamic_cast(*ir_loc); @@ -521,6 +607,9 @@ void convert_cond_no_else_to_compare(FormPool& pool, assert(src_atom->get_str() == "#f"); assert(cne->entries.size() == 1); + // safe to do this here because we never give up on this. + f.ir2.env.disable_def(condition.first->op()->branch_delay().var(0)); + auto condition_as_single = dynamic_cast(cne->entries.front().condition->try_as_single_element()); auto condition_replacement = condition.first->op()->get_condition_as_form(pool, f.ir2.env); @@ -556,7 +645,7 @@ void convert_cond_no_else_to_compare(FormPool& pool, } } -void clean_up_cond_no_else_final(const Function& func, CondNoElseElement* cne) { +void clean_up_cond_no_else_final(Function& func, CondNoElseElement* cne) { for (size_t idx = 0; idx < cne->entries.size(); idx++) { auto& entry = cne->entries.at(idx); if (entry.false_destination.has_value()) { @@ -589,6 +678,15 @@ void clean_up_cond_no_else_final(const Function& func, CondNoElseElement* cne) { branch_info_i.written_and_unused.end()); } } + + for (size_t i = 0; i < cne->entries.size(); i++) { + if (func.ir2.env.has_reg_use()) { + auto branch = dynamic_cast(cne->entries.at(i).original_condition_branch); + auto reg = cne->entries.at(i).false_destination; + // lg::warn("Disable def of {} at {}\n", reg->to_string(func.ir2.env), reg->idx()); + func.ir2.env.disable_def(*reg); + } + } } /*! @@ -600,10 +698,7 @@ void clean_up_cond_no_else_final(const Function& func, CondNoElseElement* cne) { * But it generally seems inconsistent. The expression propagation step will have to deal with * this. */ -void clean_up_cond_no_else(FormPool& pool, - const Function& f, - FormElement** ir_loc, - Form* parent_form) { +void clean_up_cond_no_else(FormPool& pool, Function& f, FormElement** ir_loc, Form* parent_form) { auto cne = dynamic_cast(*ir_loc); assert(cne); for (size_t idx = 0; idx < cne->entries.size(); idx++) { @@ -980,7 +1075,7 @@ Form* try_sc_as_ash(FormPool& pool, Function& f, const ShortCircuit* vtx) { return nullptr; } - std::optional clobber_ir; + std::optional clobber_ir; auto dsubu_set = dynamic_cast(dsubu_candidate); auto dsrav_set = dynamic_cast(dsrav_candidate); assert(dsubu_set && dsrav_set); @@ -988,7 +1083,7 @@ Form* try_sc_as_ash(FormPool& pool, Function& f, const ShortCircuit* vtx) { clobber_ir = dsubu_set->dst(); } - Variable dest_ir = result; + RegisterAccess dest_ir = result; SimpleAtom shift_ir = branch->op()->condition().src(0); auto value_ir = dynamic_cast(dsrav_set->src()->try_as_single_element()) @@ -1016,6 +1111,9 @@ Form* try_sc_as_ash(FormPool& pool, Function& f, const ShortCircuit* vtx) { auto set_form = pool.alloc_element(dest_ir, ash_form, true); b0_c_ptr->push_back(set_form); + // fix up reg info + f.ir2.env.disable_use(delay->src().get_arg(0).var()); + return b0_c_ptr; } @@ -1147,7 +1245,7 @@ Form* try_sc_as_type_of(FormPool& pool, Function& f, const ShortCircuit* vtx) { assert(src_reg3.var().reg() == src_reg.reg()); assert(offset.get_int() == -4); - std::optional clobber; + std::optional clobber; if (temp_reg.reg() != dst_reg.reg()) { clobber = first_branch->op()->condition().src(0).var(); } @@ -1157,12 +1255,17 @@ Form* try_sc_as_type_of(FormPool& pool, Function& f, const ShortCircuit* vtx) { // remove the shift b0_ptr->pop_back(); + // add the type-of auto obj = pool.alloc_single_element_form( nullptr, shift->expr().get_arg(0).as_expr(), set_shift->dst().idx()); auto type_op = pool.alloc_single_element_form(nullptr, obj, clobber); auto op = pool.alloc_element(else_case->dst(), type_op, true); b0_ptr->push_back(op); - // add the type-of + + // fix register info + f.ir2.env.disable_def(b0_delay_op.dst()); + f.ir2.env.disable_def(b1_delay_op.dst()); + f.ir2.env.disable_use(shift->expr().get_arg(0).var()); return b0_ptr; } diff --git a/decompiler/analysis/variable_naming.cpp b/decompiler/analysis/variable_naming.cpp index ef4a0de2e2..e08565054b 100644 --- a/decompiler/analysis/variable_naming.cpp +++ b/decompiler/analysis/variable_naming.cpp @@ -22,7 +22,7 @@ std::string reg_to_string(const T& regs) { /*! * Allocate a new SSA variable for the given register. - * This should only be used to allocate the result of a non-phi instruction. + * This should only be used to allocate the result of a non-phi instruction (a real instruction) */ VarSSA VarMapSSA::allocate(Register reg) { Entry new_entry; @@ -35,7 +35,7 @@ VarSSA VarMapSSA::allocate(Register reg) { } /*! - * Allocate a new SSA for the given register. + * Allocate a new SSA variable for the given register as a result of a phi. * This should only be used to allocate the result of a phi-function. */ VarSSA VarMapSSA::allocate_init_phi(Register reg, int block_id) { @@ -128,13 +128,16 @@ bool VarMapSSA::same(const VarSSA& var_a, const VarSSA& var_b) const { /*! * Get program variable ID from an SSA variable. */ -int VarMapSSA::var_id(const VarSSA& var) { +int VarMapSSA::var_id(const VarSSA& var) const { return m_entries.at(var.m_entry_id).var_id; } /*! * For a given register and map, remap using var_id = remap[var_id] * For variables not in the map, set ID to INT32_MIN. + * + * This allows you to do a full remapping, without worrying new/old mappings aliasing part way + * through the remapping. */ void VarMapSSA::remap_reg(Register reg, const std::unordered_map& remap) { for (auto& entry : m_entries) { @@ -337,11 +340,13 @@ SSA make_rc_ssa(const Function& function, const RegUsageInfo& rui, const Functio got_not_arg_coloring = true; auto as_set = dynamic_cast(op.get()); if (as_set) { + auto dst = as_set->dst().reg(); + if ((as_set->src().kind() == SimpleExpression::Kind::GPR_TO_FPR || as_set->src().is_identity()) && as_set->src().get_arg(0).is_var()) { auto src = as_set->src().get_arg(0).var().reg(); - auto dst = as_set->dst().reg(); + if (is_possible_coloring_move(dst, src) && rui.op.at(op_id).consumes.find(src) != rui.op.at(op_id).consumes.end()) { ssa_i.is_arg_coloring_move = true; @@ -351,6 +356,19 @@ SSA make_rc_ssa(const Function& function, const RegUsageInfo& rui, const Functio } } + auto as_set = dynamic_cast(op.get()); + if (as_set) { + auto dst = as_set->dst().reg(); + if (as_set->src().is_var()) { + auto src = as_set->src().get_arg(0).var().reg(); + auto& ri = rui.op.at(op_id); + if (ri.consumes.find(src) != ri.consumes.end() && + ri.written_and_unused.find(dst) != ri.written_and_unused.end()) { + ssa_i.is_dead_set = true; + } + } + } + // todo - verify no duplicates here? assert(op->write_regs().size() <= 1); // reads: @@ -480,6 +498,10 @@ void SSA::merge_all_phis() { } } +/*! + * Remaps all SSA variable ids to final variable IDs. + * This forces you to have all positive, consecutive IDs, with 0 being the entry value. + */ void SSA::remap() { // this keeps the order of variable assignments in the instruction order, not var_id order. struct VarIdRecord { @@ -528,6 +550,15 @@ void SSA::remap() { for (auto var_id : reg_vars.second.order) { var_remap[var_id] = i++; } + + // paranoid + assert(var_remap.size() == reg_vars.second.order.size()); + std::unordered_set check; + for (auto kv : var_remap) { + check.insert(kv.second); + } + assert(check.size() == var_remap.size()); + map.remap_reg(reg_vars.first, var_remap); program_read_vars[reg_vars.first].resize(i); program_write_vars[reg_vars.first].resize(i); @@ -554,6 +585,9 @@ void update_var_info(VariableNames::VarInfo* info, } } // namespace +/*! + * Create variable info for each variable. + */ void SSA::make_vars(const Function& function, const DecompilerTypeSystem& dts) { for (int block_id = 0; block_id < int(blocks.size()); block_id++) { const auto& block = blocks.at(block_id); @@ -595,7 +629,7 @@ void remap_color_move( old_kv->second.at(old_var.id).reg_id = new_var; } -VariableNames SSA::get_vars() { +VariableNames SSA::get_vars() const { VariableNames result; result.read_vars = program_read_vars; result.write_vars = program_write_vars; @@ -646,6 +680,71 @@ VariableNames SSA::get_vars() { return result; } +/*! + * Get a map from access to SSA variable. + */ +RegAccessMap SSA::get_ssa_mapping() { + RegAccessMap result; + + for (const auto& block : blocks) { + for (const auto& instr : block.ins) { + if (instr.dst.has_value()) { + RegisterAccess access(AccessMode::WRITE, instr.dst->reg(), instr.op_id, true); + result[access] = map.var_id(*instr.dst); + } + + for (const auto& src : instr.src) { + RegisterAccess access(AccessMode::READ, src.reg(), instr.op_id, true); + result[access] = map.var_id(src); + } + } + } + + return result; +} + +/*! + * Find Program Variables that can safely be propagated. + */ +std::unordered_map SSA::get_use_def_info( + const RegAccessMap& ssa_info) const { + std::unordered_map result; + + // now, iterate through instruction + // for (const auto& block : blocks) { + for (size_t block_id = 0; block_id < blocks.size(); block_id++) { + const auto& block = blocks[block_id]; + for (const auto& instr : block.ins) { + if (instr.is_dead_set) { + continue; + } + if (instr.dst.has_value()) { + // get the SSA var: + auto ssa_var_id = + ssa_info.at(RegisterAccess(AccessMode::WRITE, instr.dst->reg(), instr.op_id, true)); + // get the info + auto& info = result[RegId(instr.dst->reg(), map.var_id(*instr.dst))]; + // remember which SSA variable was in use here + info.defs.push_back({instr.op_id, (int)block_id, AccessMode::WRITE}); + info.ssa_vars.insert(ssa_var_id); + } + + for (const auto& src : instr.src) { + // get the SSA var: + auto ssa_var_id = + ssa_info.at(RegisterAccess(AccessMode::READ, src.reg(), instr.op_id, true)); + // get the info + auto& info = result[RegId(src.reg(), map.var_id(src))]; + // remember the variable + info.ssa_vars.insert(ssa_var_id); + info.uses.push_back({instr.op_id, (int)block_id, AccessMode::READ}); + } + } + } + + return result; +} + std::optional run_variable_renaming(const Function& function, const RegUsageInfo& rui, const FunctionAtomicOps& ops, @@ -694,14 +793,17 @@ std::optional run_variable_renaming(const Function& function, fmt::print("Basic SSA\n{}\n------------------------------------\n", ssa.print()); } - // eliminate PHIs that are stupid. + // eliminate PHIs that are not needed, still keeping us in SSA. while (ssa.simplify()) { } if (debug_prints) { fmt::print("Simplified SSA\n{}-------------------------------\n", ssa.print()); } - // Merge phis to return to executable code. + // remember what the SSA mapping was: + auto ssa_mapping = ssa.get_ssa_mapping(); + + // Merge phis to return to executable code and exit SSA. if (debug_prints) { ssa.map.debug_print_map(); } @@ -725,7 +827,10 @@ std::optional run_variable_renaming(const Function& function, if (function.ir2.env.has_type_analysis()) { // make vars ssa.make_vars(function, dts); - return ssa.get_vars(); + // + auto result = ssa.get_vars(); + result.use_def_info = ssa.get_use_def_info(ssa_mapping); + return result; } else { return std::nullopt; } diff --git a/decompiler/analysis/variable_naming.h b/decompiler/analysis/variable_naming.h index 5bdd42c4c6..1e791bc119 100644 --- a/decompiler/analysis/variable_naming.h +++ b/decompiler/analysis/variable_naming.h @@ -36,6 +36,8 @@ struct FunctionAtomicOps; * These must be created from a VarMapSSA, which can then remap and merge these. * This remapping/merging functionality is used in the initial conversion to SSA, * the simplification of the SSA, and the merging of variables. + * + * Note - these are like references to SSA or program variable. */ class VarSSA { public: @@ -72,7 +74,7 @@ class VarMapSSA { void merge_to_first(const VarSSA& var_a, const VarSSA& var_b); std::string to_string(const VarSSA& var) const; bool same(const VarSSA& var_a, const VarSSA& var_b) const; - int var_id(const VarSSA& var); + int var_id(const VarSSA& var) const; void remap_reg(Register reg, const std::unordered_map& remap); void debug_print_map() const; @@ -109,6 +111,7 @@ struct SSA { std::vector src; int op_id = -1; bool is_arg_coloring_move = false; + bool is_dead_set = false; std::string print(const VarMapSSA& var_map) const; }; @@ -134,11 +137,15 @@ struct SSA { VarSSA get_phi_dest(int block, Register dest_reg); void add_source_to_phi(int block, Register dest_reg, const VarSSA& src_var); + RegAccessMap get_ssa_mapping(); + bool simplify(); void merge_all_phis(); void remap(); void make_vars(const Function& function, const DecompilerTypeSystem& dts); - VariableNames get_vars(); + std::unordered_map get_use_def_info( + const RegAccessMap& ssa_info) const; + VariableNames get_vars() const; std::string print() const; }; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8e530a7e3c..76164522e0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -36,7 +36,7 @@ if(WIN32) target_link_libraries(goalc-test mman) endif() -gtest_discover_tests(goalc-test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +#gtest_discover_tests(goalc-test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) if(UNIX AND CMAKE_COMPILER_IS_GNUCXX AND CODE_COVERAGE) include(CodeCoverage) diff --git a/test/decompiler/FormRegressionTest.cpp b/test/decompiler/FormRegressionTest.cpp index 78a0d402ac..b71b336e62 100644 --- a/test/decompiler/FormRegressionTest.cpp +++ b/test/decompiler/FormRegressionTest.cpp @@ -128,7 +128,7 @@ std::unique_ptr FormRegressionTest::make_function( // for now, just test that this can at least be called. if (test->func.ir2.top_form) { - VariableSet vars; + RegAccessSet vars; test->func.ir2.top_form->collect_vars(vars); if (do_expressions) { diff --git a/test/decompiler/test_FormExpressionBuild.cpp b/test/decompiler/test_FormExpressionBuild.cpp index 97f868b451..1771a05a1a 100644 --- a/test/decompiler/test_FormExpressionBuild.cpp +++ b/test/decompiler/test_FormExpressionBuild.cpp @@ -266,7 +266,7 @@ TEST_F(FormRegressionTest, ExprAbs) { " jr ra\n" " daddu sp, sp, r0"; std::string type = "(function int int)"; - std::string expected = "(abs arg0)"; + std::string expected = "(begin (set! v0-0 arg0) (abs v0-0))"; test_with_expr(func, type, expected); } @@ -2053,10 +2053,10 @@ TEST_F(FormRegressionTest, ExprPrintl) { " bgtzl v1, L62\n" " lw v1, pair(s7)\n" "\n" - " lwu v1, -4(a0)\n" // want to cheat this read. + " lwu v1, -4(a0)\n" // use 1 "L62:\n" " lwu t9, 24(v1)\n" - " jalr ra, t9\n" + " jalr ra, t9\n" // use 2 " sll v0, ra, 0\n" "\n" " or v1, v0, r0\n" @@ -2076,7 +2076,8 @@ TEST_F(FormRegressionTest, ExprPrintl) { std::string expected = "(begin\n" - " ((method-of-type (rtype-of a0-1) print) arg0)\n" + " (set! a0-1 arg0)\n" + " ((method-of-type (rtype-of a0-1) print) a0-1)\n" " (format (quote #t) \"~%\")\n" " arg0\n" " )"; @@ -2535,36 +2536,40 @@ TEST_F(FormRegressionTest, ExprAssert) { std::string expected = "(begin (if (not arg0) (format (quote #t) \"A ~A\" arg1)) 0)"; test_with_expr(func, type, expected, false, "", {{"L17", "A ~A"}}); } -// -// TEST_F(FormRegressionTest, ExprTerminal2) { -// std::string func = -// "sll r0, r0, 0\n" -// "L29:\n" -// " daddiu sp, sp, -16\n" -// " sd fp, 8(sp)\n" -// " or fp, t9, r0\n" -// -// //" lwc1 f0, L71(fp)\n" -// " mtc1 f0, r0\n" -// " mtc1 f1, a0\n" -// " mul.s f0, f0, f1\n" -// " mtc1 f1, a1\n" -// " sub.s f0, f0, f1\n" -// " mtc1 f1, a2\n" -// " div.s f0, f0, f1\n" -// " sqrt.s f0, f0\n" -// " mtc1 f1, a1\n" -// " mtc1 f2, a2\n" -// " mul.s f3, f0, f0\n" -// " mul.s f2, f2, f3\n" -// " add.s f1, f1, f2\n" -// " sub.s f0, f0, f1\n" -// " mfc1 v0, f0\n" -// " ld fp, 8(sp)\n" -// " jr ra\n" -// " daddiu sp, sp, 16\n"; -// std::string type = "(function float float float float float)"; -// -// std::string expected = "(begin (if (not arg0) (format (quote #t) \"A ~A\" arg1)) 0)"; -// test_with_expr(func, type, expected, false, "", {{"L17", "A ~A"}}); -//} \ No newline at end of file + +TEST_F(FormRegressionTest, ExprTerminal2) { + std::string func = + "sll r0, r0, 0\n" + "L29:\n" + " daddiu sp, sp, -16\n" + " sd fp, 8(sp)\n" + " or fp, t9, r0\n" + + //" lwc1 f0, L71(fp)\n" + " mtc1 f0, r0\n" + " mtc1 f1, a0\n" + " mul.s f0, f0, f1\n" + " mtc1 f1, a1\n" + " sub.s f0, f0, f1\n" + " mtc1 f1, a2\n" + " div.s f0, f0, f1\n" + " sqrt.s f0, f0\n" + " mtc1 f1, a1\n" + " mtc1 f2, a2\n" + " mul.s f3, f0, f0\n" + " mul.s f2, f2, f3\n" + " add.s f1, f1, f2\n" + " sub.s f0, f0, f1\n" + " mfc1 v0, f0\n" + " ld fp, 8(sp)\n" + " jr ra\n" + " daddiu sp, sp, 16\n"; + std::string type = "(function float float float float float)"; + + std::string expected = + "(begin\n" + " (set! f0-4 (sqrt (/ (- (* 0.000000 arg0) arg1) arg2)))\n" + " (- f0-4 (+ arg1 (* arg2 (* f0-4 f0-4))))\n" + " )"; + test_with_expr(func, type, expected, false, "", {{"L17", "A ~A"}}); +} \ No newline at end of file diff --git a/test/decompiler/test_gkernel_decomp.cpp b/test/decompiler/test_gkernel_decomp.cpp index f4f6570a33..a11b144eb2 100644 --- a/test/decompiler/test_gkernel_decomp.cpp +++ b/test/decompiler/test_gkernel_decomp.cpp @@ -2242,9 +2242,9 @@ TEST_F(FormRegressionTest, ExprMethod17DeadPoolHeap) { " lwu v1, 52(s4)\n" " beq s7, v1, L161\n" - " daddiu v1, s7, 8\n" + " daddiu v1, s7, 8\n" // one - " or v1, s7, r0\n" + " or v1, s7, r0\n" // two "L161:\n" " bne s7, v1, L163\n"