[Decompiler] Get used variables, handle function calls better, and minor cleanup (#205)

* small fixes, implement getting used variables

* improve printing of variables

* missing include
This commit is contained in:
water111 2021-01-19 21:45:17 -05:00 committed by GitHub
parent af328ffa29
commit 679b17ae6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 572 additions and 213 deletions

View File

@ -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<uint16_t>()(x.id); }

View File

@ -166,9 +166,6 @@ class Function {
bool atomic_ops_attempted = false;
bool atomic_ops_succeeded = false;
std::shared_ptr<FunctionAtomicOps> 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;

View File

@ -142,6 +142,12 @@ goos::Object SimpleAtom::to_form(const std::vector<DecompilerLabel>& 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<Register>* 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<Register>* 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<Register>* write, std::vector<Registe
}
}
void IR2_BranchDelay::collect_vars(VariableSet& vars) const {
for (auto& x : m_var) {
if (x.has_value()) {
vars.insert(*x);
}
}
}
/////////////////////////////
// BranchOp
/////////////////////////////
@ -1064,6 +1121,11 @@ void BranchOp::update_register_info() {
m_branch_delay.get_regs(&m_write_regs, &m_read_regs);
}
void BranchOp::collect_vars(VariableSet& vars) const {
m_condition.collect_vars(vars);
m_branch_delay.collect_vars(vars);
}
/////////////////////////////
// SpecialOp
/////////////////////////////
@ -1124,16 +1186,26 @@ void SpecialOp::update_register_info() {
}
}
void SpecialOp::collect_vars(VariableSet&) const {}
/////////////////////////////
// CallOp
/////////////////////////////
CallOp::CallOp(int my_idx) : AtomicOp(my_idx) {}
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) {}
goos::Object CallOp::to_form(const std::vector<DecompilerLabel>& labels, const Env* env) const {
(void)labels;
(void)env;
return pretty_print::build_list("call!");
std::vector<goos::Object> 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

View File

@ -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<DecompilerLabel>& 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<Variable> 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;

View File

@ -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);

View File

@ -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();

View File

@ -1,6 +1,8 @@
#include <stdexcept>
#include <unordered_set>
#include <algorithm>
#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<TypeState>& 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<std::string> entries;
std::unordered_map<Register, std::unordered_set<int>, 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<std::pair<RegId, Variable>> 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<RegId, Variable>& a, const std::pair<RegId, Variable>& 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, std::unordered_set<int>, 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<RegId, RegId::hash> Env::get_ssa_var(const VariableSet& vars) const {
std::unordered_set<RegId, RegId::hash> 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

View File

@ -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, std::vector<VariableNames::VarInfo>, Register::hash> read_vars,
write_vars;
std::unordered_map<Register, std::vector<int>, 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<RegId, RegId::hash> 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<TypeState> m_block_init_types;
std::vector<TypeState> m_op_end_types;
VariableNames m_var_names;
};
} // namespace decompiler

View File

@ -1,4 +1,6 @@
#include "Form.h"
#include <utility>
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "common/goos/PrettyPrinter.h"
@ -55,11 +57,17 @@ void Form::apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) {
f(this);
m_src->apply(f);
}
void SetVarElement::apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) {
f(this);
}
void AtomicOpElement::apply_form(const std::function<void(Form*)>&) {}
/////////////////////////////
// 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<goos::Object> 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<void(FormElement*)>& 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<void(Form*)>& 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<void(FormElement*)>& f) {
void StoreElement::apply_form(const std::function<void(Form*)>&) {}
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<void(Form*)>& 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<void(FormElement*)>& f) {
void SimpleAtomElement::apply_form(const std::function<void(Form*)>&) {}
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<void(FormElement*)>& f) {
f(this);
m_src->apply(f);
}
void SetVarElement::apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) {
f(this);
}
void AtomicOpElement::apply_form(const std::function<void(Form*)>&) {}
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<goos::Object> 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<void(FormElement*)>& 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<void(Form*)>& 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<void(FormElement*)>& f) {
void FunctionCallElement::apply_form(const std::function<void(Form*)>&) {}
void FunctionCallElement::collect_vars(VariableSet& vars) const {
return m_op->collect_vars(vars);
}
/////////////////////////////
// BranchElement
/////////////////////////////
@ -284,6 +329,10 @@ void BranchElement::apply(const std::function<void(FormElement*)>& f) {
void BranchElement::apply_form(const std::function<void(Form*)>&) {}
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<void(Form*)>& 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<void(Form*)>& 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<void(Form*)>& 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<void(FormElement*)>& f) {
void EmptyElement::apply_form(const std::function<void(Form*)>&) {}
void EmptyElement::collect_vars(VariableSet&) const {}
/////////////////////////////
// WhileElement
/////////////////////////////
@ -417,6 +486,11 @@ void WhileElement::apply_form(const std::function<void(Form*)>& 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<void(Form*)>& 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<void(Form*)>& 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<void(Form*)>& 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<void(Form*)>& 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<void(Form*)>& 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(FormElement*)>&
void ConditionalMoveFalseElement::apply_form(const std::function<void(Form*)>& f) {
source->apply_form(f);
}
void ConditionalMoveFalseElement::collect_vars(VariableSet& vars) const {
vars.insert(dest);
source->collect_vars(vars);
}
} // namespace decompiler

View File

@ -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<void(FormElement*)>& f) = 0;
virtual void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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 <output>, 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<void(FormElement*)>& f) override;
void apply_form(const std::function<void(Form*)>& 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<FormElement*> sequence)
Form(FormElement* parent, const std::vector<FormElement*>& 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<goos::Object>& forms, const Env& env) const;
void apply(const std::function<void(FormElement*)>& f);
void apply_form(const std::function<void(Form*)>& f);
void collect_vars(VariableSet& vars) const;
FormElement* parent_element = nullptr;
private:

View File

@ -1,8 +0,0 @@
#pragma once
namespace decompiler {
class IR2 {
public:
private:
};
} // namespace decompiler

View File

@ -1,9 +1,88 @@
#pragma once
#include <unordered_set>
#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<int>()(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<Variable, Variable::hash>;
} // namespace decompiler

View File

@ -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<BranchElement*>(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<SetVarElement>(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<BranchElement*>(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<BranchElement*>(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;
}

View File

@ -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;
}

View File

@ -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 += ' ';
}

View File

@ -107,17 +107,12 @@ class DecompilerRegressionTest : public ::testing::Test {
test->func.ir2.atomic_ops = std::make_shared<FunctionAtomicOps>(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);
}