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