diff --git a/decompiler/Disasm/Register.h b/decompiler/Disasm/Register.h index 881cfc723e..5cd544666a 100644 --- a/decompiler/Disasm/Register.h +++ b/decompiler/Disasm/Register.h @@ -144,7 +144,7 @@ class Register { bool operator==(const Register& other) const; bool operator!=(const Register& other) const; - bool operator<(const Register& other) { return id < other.id; } + bool operator<(const Register& other) const { return id < other.id; } struct hash { auto operator()(const Register& x) const { return std::hash()(x.id); } diff --git a/decompiler/Function/Function.h b/decompiler/Function/Function.h index 3cefba94c6..6d3ca4075a 100644 --- a/decompiler/Function/Function.h +++ b/decompiler/Function/Function.h @@ -166,9 +166,6 @@ class Function { bool atomic_ops_attempted = false; bool atomic_ops_succeeded = false; std::shared_ptr atomic_ops = nullptr; - bool has_reg_use = false; - RegUsageInfo reg_use; - bool has_type_info = false; Env env; FormPool form_pool; Form* top_form = nullptr; diff --git a/decompiler/IR2/AtomicOp.cpp b/decompiler/IR2/AtomicOp.cpp index 683fe4cfdd..7746800bb6 100644 --- a/decompiler/IR2/AtomicOp.cpp +++ b/decompiler/IR2/AtomicOp.cpp @@ -142,6 +142,12 @@ goos::Object SimpleAtom::to_form(const std::vector& labels, con } } +void SimpleAtom::collect_vars(VariableSet& vars) const { + if (is_var()) { + vars.insert(var()); + } +} + bool SimpleAtom::operator==(const SimpleAtom& other) const { if (other.m_kind != m_kind) { return false; @@ -353,6 +359,12 @@ void SimpleExpression::get_regs(std::vector* out) const { } } +void SimpleExpression::collect_vars(VariableSet& vars) const { + for (int i = 0; i < args(); i++) { + get_arg(i).collect_vars(vars); + } +} + ///////////////////////////// // SetVarOp ///////////////////////////// @@ -395,6 +407,11 @@ void SetVarOp::update_register_info() { m_src.get_regs(&m_read_regs); } +void SetVarOp::collect_vars(VariableSet& vars) const { + vars.insert(m_dst); + m_src.collect_vars(vars); +} + ///////////////////////////// // AsmOp ///////////////////////////// @@ -482,6 +499,17 @@ void AsmOp::update_register_info() { } } +void AsmOp::collect_vars(VariableSet& vars) const { + if (m_dst.has_value()) { + vars.insert(*m_dst); + } + + for (auto& x : m_src) { + if (x.has_value()) { + vars.insert(*x); + } + } +} ///////////////////////////// // Condition ///////////////////////////// @@ -724,6 +752,12 @@ void IR2_Condition::get_regs(std::vector* out) const { } } +void IR2_Condition::collect_vars(VariableSet& vars) const { + for (int i = 0; i < get_condition_num_args(m_kind); i++) { + m_src[i].collect_vars(vars); + } +} + ///////////////////////////// // SetVarConditionOp ///////////////////////////// @@ -761,6 +795,11 @@ void SetVarConditionOp::update_register_info() { m_condition.get_regs(&m_read_regs); } +void SetVarConditionOp::collect_vars(VariableSet& vars) const { + vars.insert(m_dst); + m_condition.collect_vars(vars); +} + ///////////////////////////// // StoreOp ///////////////////////////// @@ -824,6 +863,11 @@ void StoreOp::update_register_info() { m_value.get_regs(&m_read_regs); } +void StoreOp::collect_vars(VariableSet& vars) const { + m_addr.collect_vars(vars); + m_value.collect_vars(vars); +} + ///////////////////////////// // LoadVarOp ///////////////////////////// @@ -902,6 +946,11 @@ void LoadVarOp::update_register_info() { m_write_regs.push_back(m_dst.reg()); } +void LoadVarOp::collect_vars(VariableSet& vars) const { + vars.insert(m_dst); + m_src.collect_vars(vars); +} + ///////////////////////////// // IR2_BranchDelay ///////////////////////////// @@ -1009,6 +1058,14 @@ void IR2_BranchDelay::get_regs(std::vector* write, std::vector& labels, const Env* env) const { (void)labels; (void)env; - return pretty_print::build_list("call!"); + std::vector forms; + forms.push_back(pretty_print::to_symbol("call!")); + for (auto& x : m_arg_vars) { + forms.push_back(pretty_print::to_symbol(x.to_string(env))); + } + return pretty_print::build_list(forms); } bool CallOp::operator==(const AtomicOp& other) const { @@ -1164,6 +1236,15 @@ void CallOp::update_register_info() { clobber_temps(); } +void CallOp::collect_vars(VariableSet& vars) const { + vars.insert(m_function_var); + for (auto& e : m_arg_vars) { + vars.insert(e); + } + + vars.insert(m_return_var); +} + ///////////////////////////// // ConditionalMoveFalseOp ///////////////////////////// @@ -1200,4 +1281,9 @@ void ConditionalMoveFalseOp::update_register_info() { m_write_regs.push_back(m_dst.reg()); m_read_regs.push_back(m_src.reg()); } + +void ConditionalMoveFalseOp::collect_vars(VariableSet& vars) const { + vars.insert(m_dst); + vars.insert(m_src); +} } // namespace decompiler \ No newline at end of file diff --git a/decompiler/IR2/AtomicOp.h b/decompiler/IR2/AtomicOp.h index 0ef2174393..02b7b06642 100644 --- a/decompiler/IR2/AtomicOp.h +++ b/decompiler/IR2/AtomicOp.h @@ -16,51 +16,6 @@ class ConditionElement; class FormPool; class DecompilerTypeSystem; -/*! - * A "Variable" 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 - * decompilation. - * - * In the earlier parts of decompilation, this just behaves like a register in all cases. - * But in later parts registers can be mapped to real local variables with types. A variable can - * look itself up in an environment to determine what "local variable" it is. - * - * Note: a variable is _not_ allowed to be R0, AT, S7, K0, K1, FP, or RA by default, as these - * can never hold normal GOAL locals. Inline assembly may use these, but you must set the allow_all - * flag to true in the constructor of Variable to indicate this is what you really want. - * - * 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 { - public: - Variable() = default; - Variable(VariableMode mode, Register reg, int atomic_idx, bool allow_all = false); - - enum class Print { - AS_REG, // print as a PS2 register name - FULL, // print as a register name, plus an index, plus read or write - AS_VARIABLE, // print local variable name, error if impossible - AUTOMATIC, // print as variable, but if that's not possible print as reg. - }; - - std::string to_string(const Env* env, Print mode = Print::AUTOMATIC) const; - - bool operator==(const Variable& other) const; - bool operator!=(const Variable& other) const; - - const Register& reg() const { return m_reg; } - VariableMode mode() const { return m_mode; } - int idx() const { return 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 -}; - /*! * An atomic operation represents a single operation from the point of view of the IR2 system. * Each IR2 op is one or more instructions. @@ -114,6 +69,8 @@ class AtomicOp { // read twice. virtual void update_register_info() = 0; + virtual void collect_vars(VariableSet& vars) const = 0; + TypeState propagate_types(const TypeState& input, const Env& env, DecompilerTypeSystem& dts); int op_id() const { return m_my_idx; } @@ -171,6 +128,7 @@ class SimpleAtom { 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; bool is_var() const { return m_kind == Kind::VARIABLE; } const Variable& var() const { @@ -274,6 +232,7 @@ class SimpleExpression { TP_Type get_type_int1(const TypeState& input, const Env& env, const DecompilerTypeSystem& dts) const; + void collect_vars(VariableSet& vars) const; private: Kind m_kind = Kind::INVALID; @@ -302,6 +261,7 @@ class SetVarOp : public AtomicOp { TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; + void collect_vars(VariableSet& vars) const override; private: Variable m_dst; @@ -327,6 +287,7 @@ class AsmOp : public AtomicOp { TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; + void collect_vars(VariableSet& vars) const override; private: Instruction m_instr; @@ -392,6 +353,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; + void collect_vars(VariableSet& vars) const; private: Kind m_kind = Kind::INVALID; @@ -418,6 +380,7 @@ class SetVarConditionOp : public AtomicOp { TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; + void collect_vars(VariableSet& vars) const override; private: Variable m_dst; @@ -441,6 +404,7 @@ class StoreOp : public AtomicOp { TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; + void collect_vars(VariableSet& vars) const override; private: int m_size; @@ -467,6 +431,7 @@ class LoadVarOp : public AtomicOp { 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; private: Kind m_kind; @@ -507,6 +472,7 @@ class IR2_BranchDelay { TypeState propagate_types(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) const; + void collect_vars(VariableSet& vars) const; Kind kind() const { return m_kind; } const Variable& var(int idx) const { assert(idx < 3); @@ -539,8 +505,10 @@ class BranchOp : public AtomicOp { TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; + void collect_vars(VariableSet& 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; bool likely() const { return m_likely; } private: @@ -573,6 +541,7 @@ class SpecialOp : public AtomicOp { TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; + void collect_vars(VariableSet& vars) const override; private: Kind m_kind; @@ -594,10 +563,15 @@ class CallOp : public AtomicOp { TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; + void collect_vars(VariableSet& vars) const override; protected: TypeSpec m_call_type; bool m_call_type_set = false; + + std::vector m_arg_vars; + Variable m_function_var; + Variable m_return_var; }; /*! @@ -624,6 +598,7 @@ class ConditionalMoveFalseOp : public AtomicOp { TypeState propagate_types_internal(const TypeState& input, const Env& env, DecompilerTypeSystem& dts) override; + void collect_vars(VariableSet& vars) const override; private: Variable m_dst, m_src; diff --git a/decompiler/IR2/AtomicOpForm.cpp b/decompiler/IR2/AtomicOpForm.cpp index 83e80be877..0de72297d1 100644 --- a/decompiler/IR2/AtomicOpForm.cpp +++ b/decompiler/IR2/AtomicOpForm.cpp @@ -3,6 +3,10 @@ namespace decompiler { +ConditionElement* BranchOp::get_condition_as_form(FormPool& pool) const { + return m_condition.get_as_form(pool); +} + ConditionElement* IR2_Condition::get_as_form(FormPool& pool) const { Form* sources[2] = {nullptr, nullptr}; int n_sources = get_condition_num_args(m_kind); diff --git a/decompiler/IR2/AtomicOpTypeAnalysis.cpp b/decompiler/IR2/AtomicOpTypeAnalysis.cpp index b21a8515bc..bd1dee89ee 100644 --- a/decompiler/IR2/AtomicOpTypeAnalysis.cpp +++ b/decompiler/IR2/AtomicOpTypeAnalysis.cpp @@ -724,9 +724,11 @@ TypeState CallOp::propagate_types_internal(const TypeState& input, // we can also update register usage here. m_read_regs.clear(); + m_arg_vars.clear(); 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)); } return end_types; @@ -742,10 +744,12 @@ TypeState CallOp::propagate_types_internal(const TypeState& input, // we can also update register usage here. m_read_regs.clear(); + m_arg_vars.clear(); m_read_regs.emplace_back(Reg::GPR, Reg::T9); 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_write_regs.clear(); diff --git a/decompiler/IR2/Env.cpp b/decompiler/IR2/Env.cpp index 65f6ec2e79..a76ae31d19 100644 --- a/decompiler/IR2/Env.cpp +++ b/decompiler/IR2/Env.cpp @@ -1,6 +1,8 @@ #include #include +#include #include "Env.h" +#include "Form.h" namespace decompiler { std::string Env::get_variable_name(Register reg, int atomic_idx, VariableMode mode) const { @@ -17,32 +19,69 @@ void Env::set_types(const std::vector& block_init_types, m_has_types = true; } -std::string Env::print_local_var_types() const { +std::string Env::print_local_var_types(const Form* top_level_form) const { assert(has_local_vars()); std::vector entries; - std::unordered_map, Register::hash> printed; - for (auto& reg_info : m_var_names.read_vars) { - auto& reg_printed = printed[reg_info.first]; - for (int var_id = 0; var_id < int(reg_info.second.size()); var_id++) { - auto& info = reg_info.second.at(var_id); + if (top_level_form) { + VariableSet var_set; + top_level_form->collect_vars(var_set); + + // we want to sort them for easier reading: + std::vector> vars; + + for (auto& x : var_set) { + vars.push_back(std::make_pair(get_ssa_var(x), x)); + } + + std::sort(vars.begin(), vars.end(), + [](const std::pair& a, const std::pair& b) { + return a.first < b.first; + }); + + RegId* prev = nullptr; + for (auto& x : vars) { + // sorted by ssa var and there are likely duplicates of Variables and SSA vars, only print + // unique ssa variables. + if (prev && x.first == *prev) { + 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& info = map.at(x.first.id); + if (info.initialized) { - reg_printed.insert(var_id); entries.push_back(fmt::format("{}: {}", info.name(), info.type.typespec().print())); + } else { + assert(false); } } - } + } else { + std::unordered_map, Register::hash> printed; - for (auto& reg_info : m_var_names.write_vars) { - auto& reg_printed = printed[reg_info.first]; - for (int var_id = 0; var_id < int(reg_info.second.size()); var_id++) { - auto& info = reg_info.second.at(var_id); - if (info.initialized) { - if (reg_printed.find(var_id) == reg_printed.end()) { + for (auto& reg_info : m_var_names.read_vars) { + auto& reg_printed = printed[reg_info.first]; + for (int var_id = 0; var_id < int(reg_info.second.size()); var_id++) { + auto& info = reg_info.second.at(var_id); + if (info.initialized) { + reg_printed.insert(var_id); entries.push_back(fmt::format("{}: {}", info.name(), info.type.typespec().print())); } } } + + for (auto& reg_info : m_var_names.write_vars) { + auto& reg_printed = printed[reg_info.first]; + for (int var_id = 0; var_id < int(reg_info.second.size()); var_id++) { + auto& info = reg_info.second.at(var_id); + if (info.initialized) { + if (reg_printed.find(var_id) == reg_printed.end()) { + entries.push_back(fmt::format("{}: {}", info.name(), info.type.typespec().print())); + } + } + } + } } int max_len = 0; @@ -75,4 +114,16 @@ std::string Env::print_local_var_types() const { return result; } + +std::unordered_set Env::get_ssa_var(const VariableSet& vars) const { + std::unordered_set result; + for (auto& x : vars) { + result.insert(get_ssa_var(x)); + } + return result; +} + +RegId Env::get_ssa_var(const Variable& var) const { + return m_var_names.lookup(var.reg(), var.idx(), var.mode()).reg_id; +} } // namespace decompiler \ No newline at end of file diff --git a/decompiler/IR2/Env.h b/decompiler/IR2/Env.h index f8581658c4..42117650b3 100644 --- a/decompiler/IR2/Env.h +++ b/decompiler/IR2/Env.h @@ -6,20 +6,22 @@ #include "decompiler/util/TP_Type.h" #include "decompiler/Disasm/Register.h" #include "decompiler/IR2/IR2_common.h" +#include "decompiler/IR2/reg_usage.h" namespace decompiler { class LinkedObjectFile; +class Form; struct VariableNames { struct VarInfo { VarInfo() = default; - std::string name() const { return fmt::format("{}-{}", reg.to_charp(), id); } + std::string name() const { return fmt::format("{}-{}", reg_id.reg.to_charp(), reg_id.id); } TP_Type type; - Register reg; - int id = -1; + RegId reg_id; bool initialized = false; }; + // todo - this is kind of gross. std::unordered_map, Register::hash> read_vars, write_vars; std::unordered_map, Register::hash> read_opid_to_varid, @@ -44,6 +46,18 @@ class Env { public: bool has_local_vars() const { return m_has_local_vars; } bool has_type_analysis() const { return m_has_types; } + bool has_reg_use() const { return m_has_reg_use; } + + void set_reg_use(const RegUsageInfo& info) { + m_reg_use = info; + m_has_reg_use = true; + } + + const RegUsageInfo& reg_use() const { + assert(m_has_reg_use); + return m_reg_use; + } + std::string get_variable_name(Register reg, int atomic_idx, VariableMode mode) const; /*! @@ -71,15 +85,22 @@ class Env { m_has_local_vars = true; } - std::string print_local_var_types() const; + std::string print_local_var_types(const Form* top_level_form) const; + + std::unordered_set get_ssa_var(const VariableSet& vars) const; + RegId get_ssa_var(const Variable& var) const; LinkedObjectFile* file = nullptr; private: + bool m_has_reg_use = false; + RegUsageInfo m_reg_use; + bool m_has_local_vars = false; + VariableNames m_var_names; + bool m_has_types = false; std::vector m_block_init_types; std::vector m_op_end_types; - VariableNames m_var_names; }; } // namespace decompiler \ No newline at end of file diff --git a/decompiler/IR2/Form.cpp b/decompiler/IR2/Form.cpp index 475adb05a7..8b9406d892 100644 --- a/decompiler/IR2/Form.cpp +++ b/decompiler/IR2/Form.cpp @@ -1,4 +1,6 @@ #include "Form.h" + +#include #include "decompiler/ObjectFile/LinkedObjectFile.h" #include "common/goos/PrettyPrinter.h" @@ -55,11 +57,17 @@ void Form::apply_form(const std::function& f) { } } +void Form::collect_vars(VariableSet& vars) const { + for (auto e : m_elements) { + e->collect_vars(vars); + } +} + ///////////////////////////// // SimpleExpressionElement ///////////////////////////// -SimpleExpressionElement::SimpleExpressionElement(const SimpleExpression& expr) : m_expr(expr) {} +SimpleExpressionElement::SimpleExpressionElement(SimpleExpression expr) : m_expr(std::move(expr)) {} goos::Object SimpleExpressionElement::to_form(const Env& env) const { return m_expr.to_form(env.file->labels, &env); @@ -75,95 +83,8 @@ bool SimpleExpressionElement::is_sequence_point() const { throw std::runtime_error("Should not check if a SimpleExpressionElement is a sequence point"); } -///////////////////////////// -// SetVarElement -///////////////////////////// - -SetVarElement::SetVarElement(const Variable& var, Form* value, bool is_sequence_point) - : m_dst(var), m_src(value), m_is_sequence_point(is_sequence_point) { - value->parent_element = this; -} - -goos::Object SetVarElement::to_form(const Env& env) const { - return pretty_print::build_list("set!", m_dst.to_string(&env), m_src->to_form(env)); -} - -void SetVarElement::apply(const std::function& f) { - f(this); - m_src->apply(f); -} - -void SetVarElement::apply_form(const std::function& f) { - m_src->apply_form(f); -} - -bool SetVarElement::is_sequence_point() const { - return m_is_sequence_point; -} - -///////////////////////////// -// AtomicOpElement -///////////////////////////// - -AtomicOpElement::AtomicOpElement(const AtomicOp* op) : m_op(op) {} - -goos::Object AtomicOpElement::to_form(const Env& env) const { - return m_op->to_form(env.file->labels, &env); -} - -void AtomicOpElement::apply(const std::function& f) { - f(this); -} - -void AtomicOpElement::apply_form(const std::function&) {} - -///////////////////////////// -// ConditionElement -///////////////////////////// - -ConditionElement::ConditionElement(IR2_Condition::Kind kind, Form* src0, Form* src1) - : m_kind(kind) { - m_src[0] = src0; - m_src[1] = src1; - for (int i = 0; i < 2; i++) { - if (m_src[i]) { - m_src[i]->parent_element = this; - } - } -} - -goos::Object ConditionElement::to_form(const Env& env) const { - std::vector forms; - forms.push_back(pretty_print::to_symbol(get_condition_kind_name(m_kind))); - for (int i = 0; i < get_condition_num_args(m_kind); i++) { - forms.push_back(m_src[i]->to_form(env)); - } - if (forms.size() > 1) { - return pretty_print::build_list(forms); - } else { - return forms.front(); - } -} - -void ConditionElement::apply(const std::function& f) { - f(this); - for (int i = 0; i < 2; i++) { - if (m_src[i]) { - m_src[i]->apply(f); - } - } -} - -void ConditionElement::apply_form(const std::function& f) { - for (int i = 0; i < 2; i++) { - if (m_src[i]) { - m_src[i]->apply_form(f); - } - } -} - -void ConditionElement::invert() { - m_kind = get_condition_opposite(m_kind); +void SimpleExpressionElement::collect_vars(VariableSet& vars) const { + m_expr.collect_vars(vars); } ///////////////////////////// @@ -182,6 +103,10 @@ void StoreElement::apply(const std::function& f) { void StoreElement::apply_form(const std::function&) {} +void StoreElement::collect_vars(VariableSet& vars) const { + return m_op->collect_vars(vars); +} + ///////////////////////////// // LoadSourceElement ///////////////////////////// @@ -236,6 +161,10 @@ void LoadSourceElement::apply_form(const std::function& f) { m_addr->apply_form(f); } +void LoadSourceElement::collect_vars(VariableSet& vars) const { + m_addr->collect_vars(vars); +} + ///////////////////////////// // SimpleAtomElement ///////////////////////////// @@ -252,6 +181,118 @@ void SimpleAtomElement::apply(const std::function& f) { void SimpleAtomElement::apply_form(const std::function&) {} +void SimpleAtomElement::collect_vars(VariableSet& vars) const { + return m_atom.collect_vars(vars); +} + +///////////////////////////// +// SetVarElement +///////////////////////////// + +SetVarElement::SetVarElement(const Variable& var, Form* value, bool is_sequence_point) + : m_dst(var), m_src(value), m_is_sequence_point(is_sequence_point) { + value->parent_element = this; +} + +goos::Object SetVarElement::to_form(const Env& env) const { + return pretty_print::build_list("set!", m_dst.to_string(&env), m_src->to_form(env)); +} + +void SetVarElement::apply(const std::function& f) { + f(this); + m_src->apply(f); +} + +void SetVarElement::apply_form(const std::function& f) { + m_src->apply_form(f); +} + +bool SetVarElement::is_sequence_point() const { + return m_is_sequence_point; +} + +void SetVarElement::collect_vars(VariableSet& vars) const { + vars.insert(m_dst); + m_src->collect_vars(vars); +} + +///////////////////////////// +// AtomicOpElement +///////////////////////////// + +AtomicOpElement::AtomicOpElement(const AtomicOp* op) : m_op(op) {} + +goos::Object AtomicOpElement::to_form(const Env& env) const { + return m_op->to_form(env.file->labels, &env); +} + +void AtomicOpElement::apply(const std::function& f) { + f(this); +} + +void AtomicOpElement::apply_form(const std::function&) {} + +void AtomicOpElement::collect_vars(VariableSet& vars) const { + m_op->collect_vars(vars); +} + +///////////////////////////// +// ConditionElement +///////////////////////////// + +ConditionElement::ConditionElement(IR2_Condition::Kind kind, Form* src0, Form* src1) + : m_kind(kind) { + m_src[0] = src0; + m_src[1] = src1; + for (int i = 0; i < 2; i++) { + if (m_src[i]) { + m_src[i]->parent_element = this; + } + } +} + +goos::Object ConditionElement::to_form(const Env& env) const { + std::vector forms; + forms.push_back(pretty_print::to_symbol(get_condition_kind_name(m_kind))); + for (int i = 0; i < get_condition_num_args(m_kind); i++) { + forms.push_back(m_src[i]->to_form(env)); + } + if (forms.size() > 1) { + return pretty_print::build_list(forms); + } else { + return forms.front(); + } +} + +void ConditionElement::apply(const std::function& f) { + f(this); + for (int i = 0; i < 2; i++) { + if (m_src[i]) { + m_src[i]->apply(f); + } + } +} + +void ConditionElement::apply_form(const std::function& f) { + for (int i = 0; i < 2; i++) { + if (m_src[i]) { + m_src[i]->apply_form(f); + } + } +} + +void ConditionElement::invert() { + m_kind = get_condition_opposite(m_kind); +} + +void ConditionElement::collect_vars(VariableSet& vars) const { + for (auto src : m_src) { + if (src) { + src->collect_vars(vars); + } + } +} + ///////////////////////////// // FunctionCallElement ///////////////////////////// @@ -268,6 +309,10 @@ void FunctionCallElement::apply(const std::function& f) { void FunctionCallElement::apply_form(const std::function&) {} +void FunctionCallElement::collect_vars(VariableSet& vars) const { + return m_op->collect_vars(vars); +} + ///////////////////////////// // BranchElement ///////////////////////////// @@ -284,6 +329,10 @@ void BranchElement::apply(const std::function& f) { void BranchElement::apply_form(const std::function&) {} +void BranchElement::collect_vars(VariableSet& vars) const { + return m_op->collect_vars(vars); +} + ///////////////////////////// // ReturnElement ///////////////////////////// @@ -307,6 +356,11 @@ void ReturnElement::apply_form(const std::function& f) { dead_code->apply_form(f); } +void ReturnElement::collect_vars(VariableSet& vars) const { + return_code->collect_vars(vars); + dead_code->collect_vars(vars); +} + ///////////////////////////// // BreakElement ///////////////////////////// @@ -330,6 +384,11 @@ void BreakElement::apply_form(const std::function& f) { dead_code->apply_form(f); } +void BreakElement::collect_vars(VariableSet& vars) const { + return_code->collect_vars(vars); + dead_code->collect_vars(vars); +} + ///////////////////////////// // CondWithElseElement ///////////////////////////// @@ -379,6 +438,14 @@ void CondWithElseElement::apply_form(const std::function& f) { else_ir->apply_form(f); } +void CondWithElseElement::collect_vars(VariableSet& vars) const { + for (auto& entry : entries) { + entry.condition->collect_vars(vars); + entry.body->collect_vars(vars); + } + else_ir->collect_vars(vars); +} + ///////////////////////////// // EmptyElement ///////////////////////////// @@ -393,6 +460,8 @@ void EmptyElement::apply(const std::function& f) { void EmptyElement::apply_form(const std::function&) {} +void EmptyElement::collect_vars(VariableSet&) const {} + ///////////////////////////// // WhileElement ///////////////////////////// @@ -417,6 +486,11 @@ void WhileElement::apply_form(const std::function& f) { condition->apply_form(f); } +void WhileElement::collect_vars(VariableSet& vars) const { + body->collect_vars(vars); + condition->collect_vars(vars); +} + ///////////////////////////// // UntilElement ///////////////////////////// @@ -441,6 +515,11 @@ void UntilElement::apply_form(const std::function& f) { condition->apply_form(f); } +void UntilElement::collect_vars(VariableSet& vars) const { + body->collect_vars(vars); + condition->collect_vars(vars); +} + ///////////////////////////// // ShortCircuitElement ///////////////////////////// @@ -487,8 +566,15 @@ goos::Object ShortCircuitElement::to_form(const Env& env) const { return pretty_print::build_list(forms); } +void ShortCircuitElement::collect_vars(VariableSet& vars) const { + vars.insert(final_result); // todo - this might be unused. + for (auto& entry : entries) { + entry.condition->collect_vars(vars); + } +} + ///////////////////////////// -// ShortCircuitElement +// CondNoElseElement ///////////////////////////// goos::Object CondNoElseElement::to_form(const Env& env) const { @@ -536,6 +622,15 @@ void CondNoElseElement::apply_form(const std::function& f) { } } +void CondNoElseElement::collect_vars(VariableSet& vars) const { + for (auto& e : entries) { + e.condition->collect_vars(vars); + e.body->collect_vars(vars); + if (e.false_destination.has_value()) { + vars.insert(*e.false_destination); + } + } +} ///////////////////////////// // AbsElement ///////////////////////////// @@ -557,6 +652,10 @@ void AbsElement::apply_form(const std::function& f) { source->apply_form(f); } +void AbsElement::collect_vars(VariableSet& vars) const { + source->collect_vars(vars); +} + ///////////////////////////// // AshElement ///////////////////////////// @@ -586,6 +685,11 @@ void AshElement::apply_form(const std::function& f) { value->apply_form(f); } +void AshElement::collect_vars(VariableSet& vars) const { + shift_amount->collect_vars(vars); + value->collect_vars(vars); +} + ///////////////////////////// // TypeOfElement ///////////////////////////// @@ -608,6 +712,10 @@ void TypeOfElement::apply_form(const std::function& f) { value->apply_form(f); } +void TypeOfElement::collect_vars(VariableSet& vars) const { + value->collect_vars(vars); +} + ///////////////////////////// // ConditionalMoveFalseElement ///////////////////////////// @@ -632,4 +740,9 @@ void ConditionalMoveFalseElement::apply(const std::function& void ConditionalMoveFalseElement::apply_form(const std::function& f) { source->apply_form(f); } + +void ConditionalMoveFalseElement::collect_vars(VariableSet& vars) const { + vars.insert(dest); + source->collect_vars(vars); +} } // namespace decompiler diff --git a/decompiler/IR2/Form.h b/decompiler/IR2/Form.h index 91ebdbe84a..572bc3ac98 100644 --- a/decompiler/IR2/Form.h +++ b/decompiler/IR2/Form.h @@ -11,6 +11,7 @@ namespace decompiler { class Form; class Env; +class IR2_Stack; /*! * A "FormElement" represents a single LISP form that's not a begin. @@ -25,6 +26,15 @@ 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; + + // // push the result of this operation to the operation stack + // // this is used for the forms that aren't last in a multi-form. + // virtual void push_to_stack(const Env& env, IR2_Stack& stack) = 0; + // + // // this is used for the final of a multi-form only. + // // using the current expressions on the stack, simplify myself. + // virtual FormElement* simplify(const Env& env, FormPool& pool, IR2_Stack& stack) = 0; protected: friend class Form; @@ -36,12 +46,14 @@ class FormElement { */ class SimpleExpressionElement : public FormElement { public: - explicit SimpleExpressionElement(const SimpleExpression& expr); + explicit SimpleExpressionElement(SimpleExpression expr); goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; bool is_sequence_point() const override; + void collect_vars(VariableSet& vars) const override; + const SimpleExpression& expr() const { return m_expr; } private: @@ -60,6 +72,7 @@ class StoreElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; private: // todo - we may eventually want to use a different representation for more @@ -77,6 +90,7 @@ class LoadSourceElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; int size() const { return m_size; } LoadVarOp::Kind kind() const { return m_kind; } const Form* location() const { return m_addr; } @@ -87,12 +101,17 @@ class LoadSourceElement : public FormElement { LoadVarOp::Kind m_kind; }; +/*! + * Representing an indivisible thing, like an integer constant variable, etc. + * Just a wrapper around SimpleAtom. + */ class SimpleAtomElement : public FormElement { public: explicit SimpleAtomElement(const SimpleAtom& var); goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; private: SimpleAtom m_atom; @@ -108,6 +127,8 @@ 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; + const Variable& dst() const { return m_dst; } const Form* src() const { return m_src; } @@ -123,6 +144,7 @@ class AtomicOpElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; private: const AtomicOp* m_op; @@ -134,6 +156,7 @@ class ConditionElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; void invert(); private: @@ -147,6 +170,7 @@ class FunctionCallElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; private: const CallOp* m_op; @@ -158,6 +182,7 @@ class BranchElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; const BranchOp* op() const { return m_op; } private: @@ -173,6 +198,7 @@ class ReturnElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; }; class BreakElement : public FormElement { @@ -184,6 +210,7 @@ class BreakElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; }; class CondWithElseElement : public FormElement { @@ -200,6 +227,7 @@ class CondWithElseElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; }; class EmptyElement : public FormElement { @@ -208,6 +236,7 @@ class EmptyElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; }; class WhileElement : public FormElement { @@ -216,6 +245,7 @@ class WhileElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; Form* condition = nullptr; Form* body = nullptr; bool cleaned = false; @@ -227,6 +257,7 @@ class UntilElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; Form* condition = nullptr; Form* body = nullptr; }; @@ -237,7 +268,7 @@ class ShortCircuitElement : public FormElement { Form* condition = nullptr; // in the case where there's no else, each delay slot will write #f to the "output" register. // this can be with an or , s7, r0 - Form* output = nullptr; + // Form* output = nullptr; // todo, what? add to collect vars if we need it? bool is_output_trick = false; bool cleaned = false; }; @@ -252,6 +283,7 @@ class ShortCircuitElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; }; class CondNoElseElement : public FormElement { @@ -270,6 +302,7 @@ class CondNoElseElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; }; class AbsElement : public FormElement { @@ -278,6 +311,7 @@ class AbsElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; Form* source = nullptr; }; @@ -291,6 +325,7 @@ class AshElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; }; class TypeOfElement : public FormElement { @@ -301,6 +336,7 @@ class TypeOfElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; }; class ConditionalMoveFalseElement : public FormElement { @@ -312,6 +348,7 @@ class ConditionalMoveFalseElement : public FormElement { goos::Object to_form(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; + void collect_vars(VariableSet& vars) const override; }; /*! @@ -328,7 +365,7 @@ class Form { single_child->parent_form = this; } - Form(FormElement* parent, const std::vector sequence) + Form(FormElement* parent, const std::vector& sequence) : parent_element(parent), m_elements(sequence) { for (auto& x : sequence) { x->parent_form = this; @@ -370,6 +407,8 @@ 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; + FormElement* parent_element = nullptr; private: diff --git a/decompiler/IR2/IR2.h b/decompiler/IR2/IR2.h deleted file mode 100644 index 83d077667b..0000000000 --- a/decompiler/IR2/IR2.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -namespace decompiler { -class IR2 { - public: - private: -}; -} // namespace decompiler \ No newline at end of file diff --git a/decompiler/IR2/IR2_common.h b/decompiler/IR2/IR2_common.h index 768617c887..2604676b17 100644 --- a/decompiler/IR2/IR2_common.h +++ b/decompiler/IR2/IR2_common.h @@ -1,9 +1,88 @@ #pragma once +#include #include "common/common_types.h" +#include "decompiler/Disasm/Register.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 }; -} + +/*! + * A register plus an integer ID. + */ +struct RegId { + Register reg; + int id = -1; + struct hash { + auto operator()(const RegId& x) const { + return Register::hash()(x.reg) ^ std::hash()(x.id); + } + }; + + bool operator==(const RegId& other) const { return reg == other.reg && id == other.id; } + bool operator!=(const RegId& other) const { return !((*this) == other); } + bool operator<(const RegId& other) const { + if (reg == other.reg) { + return id < other.id; + } else { + return reg < other.reg; + } + } +}; + +class Env; +/*! + * A "Variable" 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 + * decompilation. + * + * In the earlier parts of decompilation, this just behaves like a register in all cases. + * But in later parts registers can be mapped to real local variables with types. A variable can + * look itself up in an environment to determine what "local variable" it is. + * + * Note: a variable is _not_ allowed to be R0, AT, S7, K0, K1, FP, or RA by default, as these + * can never hold normal GOAL locals. Inline assembly may use these, but you must set the allow_all + * flag to true in the constructor of Variable to indicate this is what you really want. + * + * 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 { + public: + Variable() = default; + Variable(VariableMode mode, Register reg, int atomic_idx, bool allow_all = false); + + enum class Print { + AS_REG, // print as a PS2 register name + FULL, // print as a register name, plus an index, plus read or write + AS_VARIABLE, // print local variable name, error if impossible + AUTOMATIC, // print as variable, but if that's not possible print as reg. + }; + + std::string to_string(const Env* env, Print mode = Print::AUTOMATIC) const; + + bool operator==(const Variable& other) const; + bool operator!=(const Variable& other) const; + + const Register& reg() const { return m_reg; } + VariableMode mode() const { return m_mode; } + int idx() const { return m_atomic_idx; } + + struct hash { + auto operator()(const Variable& 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 +}; + +using VariableSet = std::unordered_set; +} // namespace decompiler diff --git a/decompiler/IR2/cfg_builder.cpp b/decompiler/IR2/cfg_builder.cpp index f785ae2146..5aa25db378 100644 --- a/decompiler/IR2/cfg_builder.cpp +++ b/decompiler/IR2/cfg_builder.cpp @@ -72,7 +72,7 @@ void clean_up_cond_with_else(FormPool& pool, FormElement* ir) { assert(jump_to_next.first); assert(jump_to_next.first->op()->branch_delay().kind() == IR2_BranchDelay::Kind::NOP); // patch the branch to next with a condition. - auto replacement = jump_to_next.first->op()->condition().get_as_form(pool); + auto replacement = jump_to_next.first->op()->get_condition_as_form(pool); replacement->invert(); *(jump_to_next.second) = replacement; @@ -106,7 +106,7 @@ void clean_up_until_loop(FormPool& pool, UntilElement* ir) { auto condition_branch = get_condition_branch(ir->condition); assert(condition_branch.first); assert(condition_branch.first->op()->branch_delay().kind() == IR2_BranchDelay::Kind::NOP); - auto replacement = condition_branch.first->op()->condition().get_as_form(pool); + auto replacement = condition_branch.first->op()->get_condition_as_form(pool); replacement->invert(); *(condition_branch.second) = replacement; } @@ -244,8 +244,8 @@ bool try_clean_up_sc_as_and(FormPool& pool, const Function& func, ShortCircuitEl auto branch = get_condition_branch(ir->entries.at(i).condition); assert(branch.first); - if (func.ir2.has_reg_use) { - auto& branch_info = func.ir2.reg_use.op.at(branch.first->op()->op_id()); + if (func.ir2.env.has_reg_use()) { + auto& branch_info = func.ir2.env.reg_use().op.at(branch.first->op()->op_id()); if (i == 0) { live_out_result = (branch_info.written_and_unused.find(ir_dest.reg()) == @@ -261,7 +261,7 @@ bool try_clean_up_sc_as_and(FormPool& pool, const Function& func, ShortCircuitEl } } - auto replacement = branch.first->op()->condition().get_as_form(pool); + auto replacement = branch.first->op()->get_condition_as_form(pool); replacement->invert(); *(branch.second) = replacement; } @@ -304,8 +304,8 @@ bool try_clean_up_sc_as_or(FormPool& pool, const Function& func, ShortCircuitEle auto branch = get_condition_branch(ir->entries.at(i).condition); assert(branch.first); - if (func.ir2.has_reg_use) { - auto& branch_info = func.ir2.reg_use.op.at(branch.first->op()->op_id()); + if (func.ir2.env.has_reg_use()) { + auto& branch_info = func.ir2.env.reg_use().op.at(branch.first->op()->op_id()); if (i == 0) { live_out_result = (branch_info.written_and_unused.find(ir_dest.reg()) == @@ -317,7 +317,7 @@ bool try_clean_up_sc_as_or(FormPool& pool, const Function& func, ShortCircuitEle } } - auto replacement = branch.first->op()->condition().get_as_form(pool); + auto replacement = branch.first->op()->get_condition_as_form(pool); *(branch.second) = replacement; } @@ -442,7 +442,7 @@ void convert_cond_no_else_to_compare(FormPool& pool, auto condition_as_single = dynamic_cast(cne->entries.front().condition->try_as_single_element()); - auto condition_replacement = condition.first->op()->condition().get_as_form(pool); + auto condition_replacement = condition.first->op()->get_condition_as_form(pool); auto crf = pool.alloc_single_form(nullptr, condition_replacement); auto replacement = pool.alloc_element(dst, crf, true); replacement->parent_form = cne->parent_form; @@ -490,17 +490,17 @@ void clean_up_cond_no_else_final(const Function& func, CondNoElseElement* cne) { auto last_branch = dynamic_cast(cne->entries.back().original_condition_branch); assert(last_branch); - if (func.ir2.has_reg_use) { - auto& last_branch_info = func.ir2.reg_use.op.at(last_branch->op()->op_id()); + if (func.ir2.env.has_reg_use()) { + auto& last_branch_info = func.ir2.env.reg_use().op.at(last_branch->op()->op_id()); cne->used_as_value = last_branch_info.written_and_unused.find(cne->final_destination) == last_branch_info.written_and_unused.end(); } // check that all other delay slot writes are unused. for (size_t i = 0; i < cne->entries.size() - 1; i++) { - if (func.ir2.has_reg_use) { + if (func.ir2.env.has_reg_use()) { auto branch = dynamic_cast(cne->entries.at(i).original_condition_branch); - auto& branch_info_i = func.ir2.reg_use.op.at(branch->op()->op_id()); + auto& branch_info_i = func.ir2.env.reg_use().op.at(branch->op()->op_id()); auto reg = cne->entries.at(i).false_destination; assert(reg.has_value()); assert(branch); @@ -552,7 +552,7 @@ void clean_up_cond_no_else(FormPool& pool, e.original_condition_branch = *jump_to_next.second; - auto replacement = jump_to_next.first->op()->condition().get_as_form(pool); + auto replacement = jump_to_next.first->op()->get_condition_as_form(pool); replacement->invert(); *(jump_to_next.second) = replacement; e.cleaned = true; @@ -1181,7 +1181,7 @@ void clean_up_while_loops(FormPool& pool, Form* sequence) { assert(condition_branch.first); assert(condition_branch.first->op()->branch_delay().kind() == IR2_BranchDelay::Kind::NOP); // printf("got while condition branch %s\n", condition_branch.first->print(file).c_str()); - auto replacement = condition_branch.first->op()->condition().get_as_form(pool); + auto replacement = condition_branch.first->op()->get_condition_as_form(pool); *(condition_branch.second) = replacement; } diff --git a/decompiler/IR2/variable_naming.cpp b/decompiler/IR2/variable_naming.cpp index 2e0dbb69fa..73803197ff 100644 --- a/decompiler/IR2/variable_naming.cpp +++ b/decompiler/IR2/variable_naming.cpp @@ -465,13 +465,13 @@ void update_var_info(VariableNames::VarInfo* info, int var_id, const DecompilerTypeSystem& dts) { if (info->initialized) { - assert(info->id == var_id); - assert(info->reg == reg); + assert(info->reg_id.id == var_id); + assert(info->reg_id.reg == reg); bool changed; info->type = dts.tp_lca(info->type, ts.get(reg), &changed); } else { - info->id = var_id; - info->reg = reg; + info->reg_id.id = var_id; + info->reg_id.reg = reg; info->type = ts.get(reg); info->initialized = true; } diff --git a/decompiler/ObjectFile/ObjectFileDB_IR2.cpp b/decompiler/ObjectFile/ObjectFileDB_IR2.cpp index a9a185bc54..22d80a7790 100644 --- a/decompiler/ObjectFile/ObjectFileDB_IR2.cpp +++ b/decompiler/ObjectFile/ObjectFileDB_IR2.cpp @@ -270,7 +270,6 @@ void ObjectFileDB::ir2_type_analysis_pass() { auto hints = get_config().type_hints_by_function_by_idx[func.guessed_name.to_string()]; if (func.run_type_analysis_ir2(ts, dts, data.linked_data, hints)) { successful_functions++; - func.ir2.has_type_info = true; } else { func.warnings.append(";; Type analysis failed\n"); } @@ -295,8 +294,7 @@ void ObjectFileDB::ir2_register_usage_pass() { total_funcs++; if (!func.suspected_asm && func.ir2.atomic_ops_succeeded) { analyzed_funcs++; - func.ir2.reg_use = analyze_ir2_register_usage(func); - func.ir2.has_reg_use = true; + func.ir2.env.set_reg_use(analyze_ir2_register_usage(func)); } }); @@ -314,7 +312,8 @@ void ObjectFileDB::ir2_variable_pass() { if (!func.suspected_asm && func.ir2.atomic_ops_succeeded && func.ir2.env.has_type_analysis()) { try { attempted++; - auto result = run_variable_renaming(func, func.ir2.reg_use, *func.ir2.atomic_ops, dts); + auto result = + run_variable_renaming(func, func.ir2.env.reg_use(), *func.ir2.atomic_ops, dts); if (result.has_value()) { successful++; func.ir2.env.set_local_vars(*result); @@ -462,7 +461,7 @@ std::string ObjectFileDB::ir2_function_to_string(ObjectFileData& data, Function& } if (func.ir2.env.has_local_vars()) { - result += func.ir2.env.print_local_var_types(); + result += func.ir2.env.print_local_var_types(func.ir2.top_form); } bool print_atomics = func.ir2.atomic_ops_succeeded; @@ -544,9 +543,9 @@ std::string ObjectFileDB::ir2_function_to_string(ObjectFileData& data, Function& op.reg_type_info_as_string(*init_types, func.ir2.env.get_types_after_op(op_id)), 50); } - if (func.ir2.has_reg_use) { + if (func.ir2.env.has_reg_use()) { std::string regs; - for (auto r : func.ir2.reg_use.op.at(op_id).consumes) { + for (auto r : func.ir2.env.reg_use().op.at(op_id).consumes) { regs += r.to_charp(); regs += ' '; } diff --git a/test/decompiler/test_FormRegression.cpp b/test/decompiler/test_FormRegression.cpp index 05a3218ce0..7ab30a4c1f 100644 --- a/test/decompiler/test_FormRegression.cpp +++ b/test/decompiler/test_FormRegression.cpp @@ -107,17 +107,12 @@ class DecompilerRegressionTest : public ::testing::Test { test->func.ir2.atomic_ops = std::make_shared(std::move(ops)); test->func.ir2.atomic_ops_succeeded = true; - if (test->func.run_type_analysis_ir2(function_type, *dts, test->file, {})) { - test->func.ir2.has_type_info = true; - } else { - EXPECT_TRUE(false); - } + EXPECT_TRUE(test->func.run_type_analysis_ir2(function_type, *dts, test->file, {})); - test->func.ir2.reg_use = analyze_ir2_register_usage(test->func); - test->func.ir2.has_reg_use = true; + test->func.ir2.env.set_reg_use(analyze_ir2_register_usage(test->func)); - auto result = - run_variable_renaming(test->func, test->func.ir2.reg_use, *test->func.ir2.atomic_ops, *dts); + auto result = run_variable_renaming(test->func, test->func.ir2.env.reg_use(), + *test->func.ir2.atomic_ops, *dts); if (result.has_value()) { test->func.ir2.env.set_local_vars(*result); } else { @@ -127,6 +122,10 @@ class DecompilerRegressionTest : public ::testing::Test { build_initial_forms(test->func); EXPECT_TRUE(test->func.ir2.top_form); + // for now, just test that this can at least be called. + VariableSet vars; + test->func.ir2.top_form->collect_vars(vars); + return test; } @@ -334,7 +333,7 @@ TEST_F(DecompilerRegressionTest, FormatString) { " (set! a1-0 L343)\n" " (set! f0-0 (l.f gp-0))\n" " (set! a2-0 (fpr->gpr f0-0))\n" - " (set! v0-0 (call!))\n" + " (set! v0-0 (call! a0-1 a1-0 a2-0))\n" // #t, "~f", the float " (set! v0-1 gp-0)\n" " )"; test(func, type, expected, false, "", {{"L343", "~f"}}); @@ -702,7 +701,7 @@ TEST_F(DecompilerRegressionTest, FunctionCall) { " (set! t9-0 name=)\n" " (set! a0-2 (l.w (+ gp-0 -2)))\n" " (set! a1-1 s5-0)\n" - " (set! v0-0 (call!))\n" + " (set! v0-0 (call! a0-2 a1-1))\n" " (set! v1-1 v0-0)\n" // name match " )\n" " )\n" @@ -861,7 +860,7 @@ TEST_F(DecompilerRegressionTest, NestedAndOr) { " (set! t9-0 s5-0)\n" // func " (set! a0-1 s2-0)\n" // car " (set! a1-1 s1-0)\n" // cadr - " (set! v0-0 (call!))\n" // compare! + " (set! v0-0 (call! a0-1 a1-1))\n" // compare! " (set! v1-1 v0-0)\n" " (not v1-1)\n" // result is false (secretly sets a0-2) " )\n" @@ -982,7 +981,7 @@ TEST_F(DecompilerRegressionTest, Recursive) { " (else\n" " (set! t9-0 fact)\n" // recurse! " (set! a0-1 (+ gp-0 -1))\n" - " (set! v0-1 (call!))\n" + " (set! v0-1 (call! a0-1))\n" " (set! v0-2 (*.si gp-0 v0-1))\n" // not quite a tail call... " )\n" " )"; @@ -1019,7 +1018,7 @@ TEST_F(DecompilerRegressionTest, TypeOf) { "(begin\n" " (set! v1-1 (type-of a0-0))\n" " (set! t9-0 (l.wu (+ v1-1 24)))\n" // print method. - " (set! v0-0 (call!))\n" + " (set! v0-0 (call! a0-0))\n" " )"; test(func, type, expected, false); } \ No newline at end of file