[Decompiler] Decompile let (#309)

* test

* fix bug

* fix tests for let

* missing formatting fix
This commit is contained in:
water111 2021-03-05 18:48:01 -05:00 committed by GitHub
parent 9168f03289
commit 65ffe83468
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 2312 additions and 1655 deletions

View file

@ -6,6 +6,7 @@ add_library(
analysis/cfg_builder.cpp
analysis/expression_build.cpp
analysis/final_output.cpp
analysis/insert_lets.cpp
analysis/reg_usage.cpp
analysis/variable_naming.cpp

View file

@ -82,6 +82,7 @@ goos::Object Env::get_variable_name(Register reg, int atomic_idx, AccessMode mod
if (type_kv != m_typehints.end()) {
for (auto& x : type_kv->second) {
if (x.reg == reg) {
// TODO - redo this!
return pretty_print::build_list("the-as", x.type_name, lookup_name);
}
}
@ -92,6 +93,19 @@ goos::Object Env::get_variable_name(Register reg, int atomic_idx, AccessMode mod
}
}
std::string Env::get_variable_name(const RegisterAccess& access) const {
if (access.reg().get_kind() == Reg::FPR || access.reg().get_kind() == Reg::GPR) {
std::string lookup_name = m_var_names.lookup(access.reg(), access.idx(), access.mode()).name();
auto remapped = m_var_remap.find(lookup_name);
if (remapped != m_var_remap.end()) {
lookup_name = remapped->second;
}
return lookup_name;
} else {
throw std::runtime_error("Cannot store a variable in this reg");
}
}
/*!
* Update the Env with the result of the type analysis pass.
*/
@ -167,7 +181,7 @@ std::vector<VariableNames::VarInfo> Env::extract_visible_variables(
std::vector<VariableNames::VarInfo> entries;
if (top_level_form) {
RegAccessSet var_set;
top_level_form->collect_vars(var_set);
top_level_form->collect_vars(var_set, true);
// we want to sort them for easier reading:
std::vector<std::pair<RegId, RegisterAccess>> vars;
@ -243,13 +257,19 @@ goos::Object Env::local_var_type_list(const Form* top_level_form,
x.reg_id.reg.get_gpr() >= Reg::A0 && x.reg_id.id == 0) {
continue;
}
count++;
std::string lookup_name = x.name();
auto remapped = m_var_remap.find(lookup_name);
if (remapped != m_var_remap.end()) {
lookup_name = remapped->second;
}
if (m_vars_defined_in_let.find(lookup_name) != m_vars_defined_in_let.end()) {
continue;
}
count++;
elts.push_back(pretty_print::build_list(lookup_name, x.type.typespec().print()));
}
if (count_out) {

View file

@ -43,7 +43,9 @@ class Env {
return m_reg_use;
}
// TODO - remove this.
goos::Object get_variable_name(Register reg, int atomic_idx, AccessMode mode) const;
std::string get_variable_name(const RegisterAccess& access) const;
/*!
* Get the types in registers _after_ the given operation has completed.
@ -126,6 +128,8 @@ class Env {
}
}
void set_defined_in_let(const std::string& var) { m_vars_defined_in_let.insert(var); }
LinkedObjectFile* file = nullptr;
DecompilerTypeSystem* dts = nullptr;
@ -148,5 +152,7 @@ class Env {
std::unordered_map<int, std::vector<TypeHint>> m_typehints;
std::unordered_map<std::string, std::string> m_var_remap;
std::unordered_map<std::string, LabelType> m_label_types;
std::unordered_set<std::string> m_vars_defined_in_let;
};
} // namespace decompiler

View file

@ -120,9 +120,9 @@ void Form::apply_form(const std::function<void(Form*)>& f) {
}
}
void Form::collect_vars(RegAccessSet& vars) const {
void Form::collect_vars(RegAccessSet& vars, bool recursive) const {
for (auto e : m_elements) {
e->collect_vars(vars);
e->collect_vars(vars, recursive);
}
}
@ -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(RegAccessSet& vars) const {
void SimpleExpressionElement::collect_vars(RegAccessSet& vars, bool) 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(RegAccessSet& vars) const {
void StoreElement::collect_vars(RegAccessSet& vars, bool) const {
return m_op->collect_vars(vars);
}
@ -239,8 +239,10 @@ void LoadSourceElement::apply_form(const std::function<void(Form*)>& f) {
m_addr->apply_form(f);
}
void LoadSourceElement::collect_vars(RegAccessSet& vars) const {
m_addr->collect_vars(vars);
void LoadSourceElement::collect_vars(RegAccessSet& vars, bool recursive) const {
if (recursive) {
m_addr->collect_vars(vars, recursive);
}
}
void LoadSourceElement::get_modified_regs(RegSet& regs) const {
@ -263,7 +265,7 @@ void SimpleAtomElement::apply(const std::function<void(FormElement*)>& f) {
void SimpleAtomElement::apply_form(const std::function<void(Form*)>&) {}
void SimpleAtomElement::collect_vars(RegAccessSet& vars) const {
void SimpleAtomElement::collect_vars(RegAccessSet& vars, bool) const {
return m_atom.collect_vars(vars);
}
@ -301,12 +303,14 @@ bool SetVarElement::is_sequence_point() const {
return m_is_sequence_point;
}
void SetVarElement::collect_vars(RegAccessSet& vars) const {
void SetVarElement::collect_vars(RegAccessSet& vars, bool recursive) const {
if (m_var_info.is_dead_set || m_var_info.is_dead_false) {
return;
}
vars.insert(m_dst);
m_src->collect_vars(vars);
if (recursive) {
m_src->collect_vars(vars, recursive);
}
}
void SetVarElement::get_modified_regs(RegSet& regs) const {
@ -350,7 +354,7 @@ void StoreInSymbolElement::apply(const std::function<void(FormElement*)>& f) {
void StoreInSymbolElement::apply_form(const std::function<void(Form*)>&) {}
void StoreInSymbolElement::collect_vars(RegAccessSet& vars) const {
void StoreInSymbolElement::collect_vars(RegAccessSet& vars, bool) const {
m_value.collect_vars(vars);
}
@ -374,7 +378,7 @@ void StoreInPairElement::apply(const std::function<void(FormElement*)>& f) {
void StoreInPairElement::apply_form(const std::function<void(Form*)>&) {}
void StoreInPairElement::collect_vars(RegAccessSet& vars) const {
void StoreInPairElement::collect_vars(RegAccessSet& vars, bool) const {
m_value.collect_vars(vars);
vars.insert(m_pair);
}
@ -438,9 +442,11 @@ bool SetFormFormElement::is_sequence_point() const {
return true;
}
void SetFormFormElement::collect_vars(RegAccessSet& vars) const {
m_src->collect_vars(vars);
m_dst->collect_vars(vars);
void SetFormFormElement::collect_vars(RegAccessSet& vars, bool recursive) const {
if (recursive) {
m_src->collect_vars(vars, recursive);
m_dst->collect_vars(vars, recursive);
}
}
void SetFormFormElement::get_modified_regs(RegSet& regs) const {
@ -463,7 +469,7 @@ void AtomicOpElement::apply(const std::function<void(FormElement*)>& f) {
void AtomicOpElement::apply_form(const std::function<void(Form*)>&) {}
void AtomicOpElement::collect_vars(RegAccessSet& vars) const {
void AtomicOpElement::collect_vars(RegAccessSet& vars, bool) const {
m_op->collect_vars(vars);
}
@ -493,7 +499,7 @@ void AsmOpElement::apply(const std::function<void(FormElement*)>& f) {
void AsmOpElement::apply_form(const std::function<void(Form*)>&) {}
void AsmOpElement::collect_vars(RegAccessSet& vars) const {
void AsmOpElement::collect_vars(RegAccessSet& vars, bool) const {
m_op->collect_vars(vars);
}
@ -552,7 +558,7 @@ void ConditionElement::invert() {
m_kind = get_condition_opposite(m_kind);
}
void ConditionElement::collect_vars(RegAccessSet& vars) const {
void ConditionElement::collect_vars(RegAccessSet& vars, bool) const {
for (auto src : m_src) {
if (src.has_value() && src->is_var()) {
vars.insert(src->var());
@ -580,7 +586,7 @@ void FunctionCallElement::apply(const std::function<void(FormElement*)>& f) {
void FunctionCallElement::apply_form(const std::function<void(Form*)>&) {}
void FunctionCallElement::collect_vars(RegAccessSet& vars) const {
void FunctionCallElement::collect_vars(RegAccessSet& vars, bool) const {
return m_op->collect_vars(vars);
}
@ -610,7 +616,7 @@ void BranchElement::apply(const std::function<void(FormElement*)>& f) {
void BranchElement::apply_form(const std::function<void(Form*)>&) {}
void BranchElement::collect_vars(RegAccessSet& vars) const {
void BranchElement::collect_vars(RegAccessSet& vars, bool) const {
return m_op->collect_vars(vars);
}
@ -628,6 +634,14 @@ void BranchElement::get_modified_regs(RegSet& regs) const {
// ReturnElement
/////////////////////////////
ReturnElement::ReturnElement(Form* _return_code, Form* _dead_code)
: return_code(_return_code), dead_code(_dead_code) {
return_code->parent_element = this;
if (dead_code) {
dead_code->parent_element = this;
}
}
goos::Object ReturnElement::to_form_internal(const Env& env) const {
std::vector<goos::Object> forms;
forms.push_back(pretty_print::to_symbol("return"));
@ -653,10 +667,12 @@ void ReturnElement::apply_form(const std::function<void(Form*)>& f) {
}
}
void ReturnElement::collect_vars(RegAccessSet& vars) const {
return_code->collect_vars(vars);
if (dead_code) {
dead_code->collect_vars(vars);
void ReturnElement::collect_vars(RegAccessSet& vars, bool recursive) const {
if (recursive) {
return_code->collect_vars(vars, recursive);
if (dead_code) {
dead_code->collect_vars(vars, recursive);
}
}
}
@ -671,6 +687,12 @@ void ReturnElement::get_modified_regs(RegSet& regs) const {
// BreakElement
/////////////////////////////
BreakElement::BreakElement(Form* _return_code, Form* _dead_code)
: return_code(_return_code), dead_code(_dead_code) {
return_code->parent_element = this;
dead_code->parent_element = this;
}
goos::Object BreakElement::to_form_internal(const Env& env) const {
std::vector<goos::Object> forms;
forms.push_back(pretty_print::to_symbol("break"));
@ -690,9 +712,11 @@ void BreakElement::apply_form(const std::function<void(Form*)>& f) {
dead_code->apply_form(f);
}
void BreakElement::collect_vars(RegAccessSet& vars) const {
return_code->collect_vars(vars);
dead_code->collect_vars(vars);
void BreakElement::collect_vars(RegAccessSet& vars, bool recursive) const {
if (recursive) {
return_code->collect_vars(vars, recursive);
dead_code->collect_vars(vars, recursive);
}
}
void BreakElement::get_modified_regs(RegSet& regs) const {
@ -705,6 +729,15 @@ void BreakElement::get_modified_regs(RegSet& regs) const {
// CondWithElseElement
/////////////////////////////
CondWithElseElement::CondWithElseElement(std::vector<Entry> _entries, Form* _else_ir)
: entries(std::move(_entries)), else_ir(_else_ir) {
for (auto& e : entries) {
e.condition->parent_element = this;
e.body->parent_element = this;
}
else_ir->parent_element = this;
}
goos::Object CondWithElseElement::to_form_internal(const Env& env) const {
// for now we only turn it into an if statement if both cases won't require a begin at the top
// level. I think it is more common to write these as a two-case cond instead of an if with begin.
@ -750,12 +783,14 @@ void CondWithElseElement::apply_form(const std::function<void(Form*)>& f) {
else_ir->apply_form(f);
}
void CondWithElseElement::collect_vars(RegAccessSet& vars) const {
for (auto& entry : entries) {
entry.condition->collect_vars(vars);
entry.body->collect_vars(vars);
void CondWithElseElement::collect_vars(RegAccessSet& vars, bool recursive) const {
if (recursive) {
for (auto& entry : entries) {
entry.condition->collect_vars(vars, recursive);
entry.body->collect_vars(vars, recursive);
}
else_ir->collect_vars(vars, recursive);
}
else_ir->collect_vars(vars);
}
void CondWithElseElement::get_modified_regs(RegSet& regs) const {
@ -779,13 +814,18 @@ void EmptyElement::apply(const std::function<void(FormElement*)>& f) {
}
void EmptyElement::apply_form(const std::function<void(Form*)>&) {}
void EmptyElement::collect_vars(RegAccessSet&) const {}
void EmptyElement::collect_vars(RegAccessSet&, bool) const {}
void EmptyElement::get_modified_regs(RegSet&) const {}
/////////////////////////////
// WhileElement
/////////////////////////////
WhileElement::WhileElement(Form* _condition, Form* _body) : condition(_condition), body(_body) {
condition->parent_element = this;
body->parent_element = this;
}
void WhileElement::apply(const std::function<void(FormElement*)>& f) {
// note - this is done in program order, rather than print order. Not sure if this makes sense.
f(this);
@ -806,9 +846,11 @@ void WhileElement::apply_form(const std::function<void(Form*)>& f) {
condition->apply_form(f);
}
void WhileElement::collect_vars(RegAccessSet& vars) const {
body->collect_vars(vars);
condition->collect_vars(vars);
void WhileElement::collect_vars(RegAccessSet& vars, bool recursive) const {
if (recursive) {
body->collect_vars(vars, recursive);
condition->collect_vars(vars, recursive);
}
}
void WhileElement::get_modified_regs(RegSet& regs) const {
@ -820,6 +862,11 @@ void WhileElement::get_modified_regs(RegSet& regs) const {
// UntilElement
/////////////////////////////
UntilElement::UntilElement(Form* _condition, Form* _body) : condition(_condition), body(_body) {
condition->parent_element = this;
body->parent_element = this;
}
void UntilElement::apply(const std::function<void(FormElement*)>& f) {
// note - this is done in program order, rather than print order. Not sure if this makes sense.
f(this);
@ -840,9 +887,11 @@ void UntilElement::apply_form(const std::function<void(Form*)>& f) {
condition->apply_form(f);
}
void UntilElement::collect_vars(RegAccessSet& vars) const {
body->collect_vars(vars);
condition->collect_vars(vars);
void UntilElement::collect_vars(RegAccessSet& vars, bool recursive) const {
if (recursive) {
body->collect_vars(vars, recursive);
condition->collect_vars(vars, recursive);
}
}
void UntilElement::get_modified_regs(RegSet& regs) const {
@ -854,6 +903,13 @@ void UntilElement::get_modified_regs(RegSet& regs) const {
// ShortCircuitElement
/////////////////////////////
ShortCircuitElement::ShortCircuitElement(std::vector<Entry> _entries)
: entries(std::move(_entries)) {
for (auto& entry : entries) {
entry.condition->parent_element = this;
}
}
void ShortCircuitElement::apply(const std::function<void(FormElement*)>& f) {
f(this);
for (auto& x : entries) {
@ -896,10 +952,12 @@ goos::Object ShortCircuitElement::to_form_internal(const Env& env) const {
return pretty_print::build_list(forms);
}
void ShortCircuitElement::collect_vars(RegAccessSet& vars) const {
void ShortCircuitElement::collect_vars(RegAccessSet& vars, bool recursive) const {
// vars.insert(final_result); // todo - this might be unused.
for (auto& entry : entries) {
entry.condition->collect_vars(vars);
if (recursive) {
for (auto& entry : entries) {
entry.condition->collect_vars(vars, recursive);
}
}
}
@ -913,6 +971,13 @@ void ShortCircuitElement::get_modified_regs(RegSet& regs) const {
// CondNoElseElement
/////////////////////////////
CondNoElseElement::CondNoElseElement(std::vector<Entry> _entries) : entries(std::move(_entries)) {
for (auto& entry : entries) {
entry.condition->parent_element = this;
entry.body->parent_element = this;
}
}
goos::Object CondNoElseElement::to_form_internal(const Env& env) const {
if (entries.size() == 1 && entries.front().body->is_single_element()) {
// print as an if statement if we can put the body in a single form.
@ -958,10 +1023,12 @@ void CondNoElseElement::apply_form(const std::function<void(Form*)>& f) {
}
}
void CondNoElseElement::collect_vars(RegAccessSet& vars) const {
for (auto& e : entries) {
e.condition->collect_vars(vars);
e.body->collect_vars(vars);
void CondNoElseElement::collect_vars(RegAccessSet& vars, bool recursive) const {
if (recursive) {
for (auto& e : entries) {
e.condition->collect_vars(vars, recursive);
e.body->collect_vars(vars, recursive);
}
}
}
@ -989,7 +1056,7 @@ void AbsElement::apply(const std::function<void(FormElement*)>& f) {
void AbsElement::apply_form(const std::function<void(Form*)>&) {}
void AbsElement::collect_vars(RegAccessSet& vars) const {
void AbsElement::collect_vars(RegAccessSet& vars, bool) const {
vars.insert(source);
}
@ -1021,7 +1088,7 @@ void AshElement::apply(const std::function<void(FormElement*)>& f) {
void AshElement::apply_form(const std::function<void(Form*)>&) {}
void AshElement::collect_vars(RegAccessSet& vars) const {
void AshElement::collect_vars(RegAccessSet& vars, bool) const {
vars.insert(value);
vars.insert(shift_amount);
}
@ -1050,8 +1117,10 @@ void TypeOfElement::apply_form(const std::function<void(Form*)>& f) {
value->apply_form(f);
}
void TypeOfElement::collect_vars(RegAccessSet& vars) const {
value->collect_vars(vars);
void TypeOfElement::collect_vars(RegAccessSet& vars, bool recursive) const {
if (recursive) {
value->collect_vars(vars, recursive);
}
}
void TypeOfElement::get_modified_regs(RegSet&) const {}
@ -1077,7 +1146,7 @@ void ConditionalMoveFalseElement::apply(const std::function<void(FormElement*)>&
void ConditionalMoveFalseElement::apply_form(const std::function<void(Form*)>&) {}
void ConditionalMoveFalseElement::collect_vars(RegAccessSet& vars) const {
void ConditionalMoveFalseElement::collect_vars(RegAccessSet& vars, bool) const {
vars.insert(dest);
vars.insert(old_value);
vars.insert(source);
@ -1112,13 +1181,15 @@ GenericOperator GenericOperator::make_compare(IR2_Condition::Kind kind) {
return op;
}
void GenericOperator::collect_vars(RegAccessSet& vars) const {
void GenericOperator::collect_vars(RegAccessSet& vars, bool recursive) const {
switch (m_kind) {
case Kind::FIXED_OPERATOR:
case Kind::CONDITION_OPERATOR:
return;
case Kind::FUNCTION_EXPR:
m_function->collect_vars(vars);
if (recursive) {
m_function->collect_vars(vars, recursive);
}
return;
default:
assert(false);
@ -1324,6 +1395,7 @@ goos::Object GenericElement::to_form_internal(const Env& env) const {
std::vector<goos::Object> result;
result.push_back(m_head.to_form(env));
for (auto x : m_elts) {
assert(x->parent_element);
result.push_back(x->to_form(env));
}
return pretty_print::build_list(result);
@ -1345,10 +1417,12 @@ void GenericElement::apply_form(const std::function<void(Form*)>& f) {
}
}
void GenericElement::collect_vars(RegAccessSet& vars) const {
m_head.collect_vars(vars);
for (auto x : m_elts) {
x->collect_vars(vars);
void GenericElement::collect_vars(RegAccessSet& vars, bool recursive) const {
if (recursive) {
m_head.collect_vars(vars, recursive);
for (auto x : m_elts) {
x->collect_vars(vars, recursive);
}
}
}
@ -1382,8 +1456,10 @@ void CastElement::apply_form(const std::function<void(Form*)>& f) {
m_source->apply_form(f);
}
void CastElement::collect_vars(RegAccessSet& vars) const {
m_source->collect_vars(vars);
void CastElement::collect_vars(RegAccessSet& vars, bool recursive) const {
if (recursive) {
m_source->collect_vars(vars, recursive);
}
}
void CastElement::get_modified_regs(RegSet& regs) const {
@ -1421,14 +1497,16 @@ DerefToken DerefToken::make_expr_placeholder() {
return x;
}
void DerefToken::collect_vars(RegAccessSet& vars) const {
void DerefToken::collect_vars(RegAccessSet& vars, bool recursive) const {
switch (m_kind) {
case Kind::INTEGER_CONSTANT:
case Kind::FIELD_NAME:
case Kind::EXPRESSION_PLACEHOLDER:
break;
case Kind::INTEGER_EXPRESSION:
m_expr->collect_vars(vars);
if (recursive) {
m_expr->collect_vars(vars, recursive);
}
break;
default:
assert(false);
@ -1527,6 +1605,7 @@ DerefElement::DerefElement(Form* base, bool is_addr_of, std::vector<DerefToken>
}
goos::Object DerefElement::to_form_internal(const Env& env) const {
assert(m_base->parent_element);
std::vector<goos::Object> forms = {pretty_print::to_symbol(m_is_addr_of ? "&->" : "->"),
m_base->to_form(env)};
for (auto& tok : m_tokens) {
@ -1550,10 +1629,12 @@ void DerefElement::apply_form(const std::function<void(Form*)>& f) {
}
}
void DerefElement::collect_vars(RegAccessSet& vars) const {
m_base->collect_vars(vars);
for (auto& tok : m_tokens) {
tok.collect_vars(vars);
void DerefElement::collect_vars(RegAccessSet& vars, bool recursive) const {
if (recursive) {
m_base->collect_vars(vars, recursive);
for (auto& tok : m_tokens) {
tok.collect_vars(vars, recursive);
}
}
}
@ -1564,6 +1645,11 @@ void DerefElement::get_modified_regs(RegSet& regs) const {
}
}
void DerefElement::set_base(Form* new_base) {
m_base = new_base;
m_base->parent_element = this;
}
/////////////////////////////
// DynamicMethodAccess
/////////////////////////////
@ -1580,7 +1666,7 @@ void DynamicMethodAccess::apply(const std::function<void(FormElement*)>& f) {
void DynamicMethodAccess::apply_form(const std::function<void(Form*)>&) {}
void DynamicMethodAccess::collect_vars(RegAccessSet& vars) const {
void DynamicMethodAccess::collect_vars(RegAccessSet& vars, bool) const {
vars.insert(m_source);
}
@ -1596,7 +1682,13 @@ ArrayFieldAccess::ArrayFieldAccess(RegisterAccess source,
: m_source(source),
m_deref_tokens(deref_tokens),
m_expected_stride(expected_stride),
m_constant_offset(constant_offset) {}
m_constant_offset(constant_offset) {
for (auto& token : m_deref_tokens) {
if (token.kind() == DerefToken::Kind::INTEGER_EXPRESSION) {
token.expr()->parent_element = this;
}
}
}
goos::Object ArrayFieldAccess::to_form_internal(const Env& env) const {
std::vector<goos::Object> elts;
@ -1621,10 +1713,12 @@ void ArrayFieldAccess::apply_form(const std::function<void(Form*)>& f) {
}
}
void ArrayFieldAccess::collect_vars(RegAccessSet& vars) const {
void ArrayFieldAccess::collect_vars(RegAccessSet& vars, bool recursive) const {
vars.insert(m_source);
for (auto& tok : m_deref_tokens) {
tok.collect_vars(vars);
if (recursive) {
for (auto& tok : m_deref_tokens) {
tok.collect_vars(vars, recursive);
}
}
}
@ -1657,8 +1751,10 @@ void GetMethodElement::apply_form(const std::function<void(Form*)>& f) {
m_in->apply_form(f);
}
void GetMethodElement::collect_vars(RegAccessSet& vars) const {
m_in->collect_vars(vars);
void GetMethodElement::collect_vars(RegAccessSet& vars, bool recursive) const {
if (recursive) {
m_in->collect_vars(vars, recursive);
}
}
void GetMethodElement::get_modified_regs(RegSet& regs) const {
@ -1677,7 +1773,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(RegAccessSet&) const {}
void StringConstantElement::collect_vars(RegAccessSet&, bool) const {}
void StringConstantElement::get_modified_regs(RegSet&) const {}
/////////////////////////////
@ -1691,7 +1787,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(RegAccessSet&) const {}
void ConstantTokenElement::collect_vars(RegAccessSet&, bool) const {}
void ConstantTokenElement::get_modified_regs(RegSet&) const {}
/////////////////////////////
@ -1702,13 +1798,17 @@ 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(RegAccessSet&) const {}
void ConstantFloatElement::collect_vars(RegAccessSet&, bool) const {}
void ConstantFloatElement::get_modified_regs(RegSet&) const {}
goos::Object ConstantFloatElement::to_form_internal(const Env&) const {
return pretty_print::float_representation(m_value);
}
/////////////////////////////
// StorePlainDeref
/////////////////////////////
StorePlainDeref::StorePlainDeref(DerefElement* dst,
SimpleExpression expr,
int my_idx,
@ -1736,15 +1836,19 @@ void StorePlainDeref::apply(const std::function<void(FormElement*)>& f) {
void StorePlainDeref::apply_form(const std::function<void(Form*)>&) {}
void StorePlainDeref::collect_vars(RegAccessSet& vars) const {
void StorePlainDeref::collect_vars(RegAccessSet& vars, bool recursive) const {
m_expr.collect_vars(vars);
m_dst->collect_vars(vars);
m_dst->collect_vars(vars, recursive);
}
void StorePlainDeref::get_modified_regs(RegSet& regs) const {
m_dst->get_modified_regs(regs);
}
/////////////////////////////
// StoreArrayAccess
/////////////////////////////
StoreArrayAccess::StoreArrayAccess(ArrayFieldAccess* dst,
SimpleExpression expr,
int my_idx,
@ -1765,15 +1869,19 @@ void StoreArrayAccess::apply_form(const std::function<void(Form*)>& f) {
m_dst->apply_form(f);
}
void StoreArrayAccess::collect_vars(RegAccessSet& vars) const {
void StoreArrayAccess::collect_vars(RegAccessSet& vars, bool recursive) const {
m_expr.collect_vars(vars);
m_dst->collect_vars(vars);
m_dst->collect_vars(vars, recursive);
}
void StoreArrayAccess::get_modified_regs(RegSet& regs) const {
m_dst->get_modified_regs(regs);
}
/////////////////////////////
// DecompiledDataElement
/////////////////////////////
DecompiledDataElement::DecompiledDataElement(goos::Object description)
: m_description(std::move(description)) {}
@ -1787,8 +1895,78 @@ void DecompiledDataElement::apply(const std::function<void(FormElement*)>& f) {
void DecompiledDataElement::apply_form(const std::function<void(Form*)>&) {}
void DecompiledDataElement::collect_vars(RegAccessSet&) const {}
void DecompiledDataElement::collect_vars(RegAccessSet&, bool) const {}
void DecompiledDataElement::get_modified_regs(RegSet&) const {}
/////////////////////////////
// LetElement
/////////////////////////////
LetElement::LetElement(Form* body, bool star) : m_body(body), m_star(star) {
m_body->parent_element = this;
}
void LetElement::add_def(RegisterAccess dst, Form* value) {
value->parent_element = this;
m_entries.push_back({dst, value});
}
void LetElement::make_let_star() {
m_star = true;
}
goos::Object LetElement::to_form_internal(const Env& env) const {
std::vector<goos::Object> outer = {pretty_print::to_symbol(m_star ? "let*" : "let")};
std::vector<goos::Object> def_list;
for (auto& entry : m_entries) {
def_list.push_back(pretty_print::build_list(entry.dest.to_form(env), entry.src->to_form(env)));
}
outer.push_back(pretty_print::build_list(def_list));
m_body->inline_forms(outer, env);
return pretty_print::build_list(outer);
}
void LetElement::apply(const std::function<void(FormElement*)>& f) {
f(this);
for (auto& entry : m_entries) {
entry.src->apply(f);
}
m_body->apply(f);
}
void LetElement::apply_form(const std::function<void(Form*)>& f) {
for (auto& entry : m_entries) {
entry.src->apply_form(f);
}
m_body->apply_form(f);
}
void LetElement::collect_vars(RegAccessSet& vars, bool recursive) const {
for (auto& entry : m_entries) {
vars.insert(entry.dest);
}
m_body->collect_vars(vars, recursive);
}
void LetElement::get_modified_regs(RegSet& regs) const {
for (auto& entry : m_entries) {
regs.insert(entry.dest.reg());
}
m_body->get_modified_regs(regs);
}
void LetElement::add_entry(const Entry& e) {
e.src->parent_element = this;
m_entries.push_back(e);
}
void LetElement::set_body(Form* new_body) {
m_body = new_body;
m_body->parent_element = this;
}
} // namespace decompiler

View file

@ -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(RegAccessSet& vars) const = 0;
virtual void collect_vars(RegAccessSet& vars, bool recursive) 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
const SimpleAtom& atom() const { return m_atom; }
void update_from_stack(const Env& env,
@ -248,7 +248,7 @@ 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) 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;
@ -285,7 +285,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
void get_modified_regs(RegSet& regs) const override;
@ -303,7 +303,7 @@ class StoreInPairElement : 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
void get_modified_regs(RegSet& regs) const override;
@ -330,7 +330,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
void get_modified_regs(RegSet& regs) const override;
@ -356,7 +356,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) 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; }
@ -374,7 +374,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) 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; }
@ -403,7 +403,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
void update_from_stack(const Env& env,
FormPool& pool,
@ -440,7 +440,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void update_from_stack(const Env& env,
FormPool& pool,
FormStack& stack,
@ -463,7 +463,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
const BranchOp* op() const { return m_op; }
@ -481,12 +481,11 @@ class ReturnElement : public FormElement {
public:
Form* return_code = nullptr;
Form* dead_code = nullptr;
ReturnElement(Form* _return_code, Form* _dead_code)
: return_code(_return_code), dead_code(_dead_code) {}
ReturnElement(Form* _return_code, Form* _dead_code);
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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
void get_modified_regs(RegSet& regs) const override;
};
@ -516,12 +515,11 @@ class BreakElement : public FormElement {
public:
Form* return_code = nullptr;
Form* dead_code = nullptr;
BreakElement(Form* _return_code, Form* _dead_code)
: return_code(_return_code), dead_code(_dead_code) {}
BreakElement(Form* _return_code, Form* _dead_code);
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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
};
@ -550,12 +548,11 @@ class CondWithElseElement : public FormElement {
std::vector<Entry> entries;
Form* else_ir = nullptr;
bool already_rewritten = false;
CondWithElseElement(std::vector<Entry> _entries, Form* _else_ir)
: entries(std::move(_entries)), else_ir(_else_ir) {}
CondWithElseElement(std::vector<Entry> _entries, Form* _else_ir);
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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
void get_modified_regs(RegSet& regs) const override;
};
@ -574,7 +571,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
};
@ -586,11 +583,11 @@ class EmptyElement : public FormElement {
*/
class WhileElement : public FormElement {
public:
WhileElement(Form* _condition, Form* _body) : condition(_condition), body(_body) {}
WhileElement(Form* _condition, Form* _body);
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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) 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;
@ -605,11 +602,11 @@ class WhileElement : public FormElement {
*/
class UntilElement : public FormElement {
public:
UntilElement(Form* _condition, Form* _body) : condition(_condition), body(_body) {}
UntilElement(Form* _condition, Form* _body);
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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) 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;
@ -640,11 +637,11 @@ class ShortCircuitElement : public FormElement {
std::optional<bool> used_as_value = std::nullopt;
bool already_rewritten = false;
explicit ShortCircuitElement(std::vector<Entry> _entries) : entries(std::move(_entries)) {}
explicit ShortCircuitElement(std::vector<Entry> _entries);
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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
void update_from_stack(const Env& env,
FormPool& pool,
@ -672,11 +669,11 @@ class CondNoElseElement : public FormElement {
bool used_as_value = false;
bool already_rewritten = false;
std::vector<Entry> entries;
explicit CondNoElseElement(std::vector<Entry> _entries) : entries(std::move(_entries)) {}
explicit CondNoElseElement(std::vector<Entry> _entries);
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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) 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,
@ -695,7 +692,7 @@ class AbsElement : 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void update_from_stack(const Env& env,
FormPool& pool,
FormStack& stack,
@ -725,7 +722,7 @@ class AshElement : 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void update_from_stack(const Env& env,
FormPool& pool,
FormStack& stack,
@ -746,7 +743,7 @@ class TypeOfElement : 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
void update_from_stack(const Env& env,
FormPool& pool,
@ -786,7 +783,7 @@ class ConditionalMoveFalseElement : 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
};
@ -804,7 +801,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(RegAccessSet& vars) const;
void collect_vars(RegAccessSet& vars, bool recursive) 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);
@ -850,7 +847,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void update_from_stack(const Env& env,
FormPool& pool,
FormStack& stack,
@ -873,7 +870,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
void update_from_stack(const Env& env,
FormPool& pool,
@ -904,7 +901,7 @@ class DerefToken {
static DerefToken make_field_name(const std::string& name);
static DerefToken make_expr_placeholder();
void collect_vars(RegAccessSet& vars) const;
void collect_vars(RegAccessSet& vars, bool recursive) 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);
@ -937,7 +934,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void update_from_stack(const Env& env,
FormPool& pool,
FormStack& stack,
@ -951,7 +948,7 @@ class DerefElement : public FormElement {
const Form* base() const { return m_base; }
Form* base() { return m_base; }
const std::vector<DerefToken>& tokens() const { return m_tokens; }
void set_base(Form* new_base) { m_base = new_base; }
void set_base(Form* new_base);
private:
Form* m_base = nullptr;
@ -965,7 +962,7 @@ class DynamicMethodAccess : 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void update_from_stack(const Env& env,
FormPool& pool,
FormStack& stack,
@ -986,7 +983,7 @@ class ArrayFieldAccess : 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void update_from_stack(const Env& env,
FormPool& pool,
FormStack& stack,
@ -1013,7 +1010,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
void update_from_stack(const Env& env,
FormPool& pool,
@ -1033,7 +1030,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
void update_from_stack(const Env& env,
FormPool& pool,
@ -1051,7 +1048,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
void update_from_stack(const Env& env,
FormPool& pool,
@ -1069,7 +1066,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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
void update_from_stack(const Env& env,
FormPool& pool,
@ -1092,7 +1089,7 @@ class StorePlainDeref : 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
@ -1113,7 +1110,7 @@ class StoreArrayAccess : 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override;
@ -1130,13 +1127,41 @@ 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(RegAccessSet& vars) const override;
void collect_vars(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
private:
goos::Object m_description;
};
class LetElement : public FormElement {
public:
LetElement(Form* body, bool star = false);
void add_def(RegisterAccess dst, Form* value);
void make_let_star();
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(RegAccessSet& vars, bool recursive) const override;
void get_modified_regs(RegSet& regs) const override;
Form* body() { return m_body; }
void set_body(Form* new_body);
struct Entry {
RegisterAccess dest;
Form* src = nullptr;
};
std::vector<Entry> entries() { return m_entries; }
void add_entry(const Entry& e);
bool is_star() const { return m_star; }
private:
Form* m_body = nullptr;
std::vector<Entry> m_entries;
bool m_star = false;
};
/*!
* A Form is a wrapper around one or more FormElements.
* This is done for two reasons:
@ -1186,6 +1211,11 @@ class Form {
const std::vector<FormElement*>& elts() const { return m_elements; }
std::vector<FormElement*>& elts() { return m_elements; }
void claim_all_children() {
for (auto elt : elts()) {
elt->parent_form = this;
}
}
void push_back(FormElement* elt) {
elt->parent_form = this;
@ -1200,7 +1230,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(RegAccessSet& vars) const;
void collect_vars(RegAccessSet& vars, bool recursive) const;
void update_children_from_stack(const Env& env,
FormPool& pool,

View file

@ -1348,7 +1348,8 @@ void FunctionCallElement::update_from_stack(const Env& env,
throw std::runtime_error("Failed to resolve.");
}
arg_forms.insert(arg_forms.begin(), unsafe);
arg_forms.insert(arg_forms.begin(), mr.maps.forms.at(0));
new_form = pool.alloc_element<GenericElement>(
GenericOperator::make_function(mr.maps.forms.at(1)), arg_forms);
@ -1750,8 +1751,10 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac
if (rewrite_as_set && !set_unused) {
for (auto& entry : entries) {
rewrite_to_get_var(entry.body->elts(), pool, *last_var, env);
entry.body->claim_all_children();
}
rewrite_to_get_var(else_ir->elts(), pool, *last_var, env);
else_ir->claim_all_children();
}
// update register info

View file

@ -75,6 +75,7 @@ class ObjectFileDB {
void ir2_cfg_build_pass();
void ir2_store_current_forms();
void ir2_build_expressions();
void ir2_insert_lets();
void ir2_write_results(const std::string& output_dir);
std::string ir2_to_file(ObjectFileData& data);
std::string ir2_function_to_string(ObjectFileData& data, Function& function, int seg);

View file

@ -10,6 +10,7 @@
#include "common/util/FileUtil.h"
#include "decompiler/Function/TypeInspector.h"
#include "decompiler/analysis/reg_usage.h"
#include "decompiler/analysis/insert_lets.h"
#include "decompiler/analysis/variable_naming.h"
#include "decompiler/analysis/cfg_builder.h"
#include "decompiler/analysis/final_output.h"
@ -45,6 +46,11 @@ void ObjectFileDB::analyze_functions_ir2(const std::string& output_dir) {
ir2_store_current_forms();
lg::info("Expression building...");
ir2_build_expressions();
if (get_config().insert_lets) {
lg::info("Inserting lets...");
ir2_insert_lets();
}
}
if (!output_dir.empty()) {
@ -422,6 +428,22 @@ void ObjectFileDB::ir2_build_expressions() {
lg::info("{}/{}/{} expression build in {:.2f} ms\n", successful, attempted, total, timer.getMs());
}
void ObjectFileDB::ir2_insert_lets() {
Timer timer;
LetStats combined_stats;
int attempted = 0;
for_each_function_def_order([&](Function& func, int, ObjectFileData&) {
if (func.ir2.expressions_succeeded) {
attempted++;
combined_stats += insert_lets(func, func.ir2.env, *func.ir2.form_pool, func.ir2.top_form);
}
});
lg::info("Let pass on {} functions ({}/{} vars in lets) in {:.2f} ms\n", attempted,
combined_stats.vars_in_lets, combined_stats.total_vars, timer.getMs());
}
void ObjectFileDB::ir2_write_results(const std::string& output_dir) {
Timer timer;
lg::info("Writing IR2 results to file...");

View file

@ -0,0 +1,342 @@
#include <algorithm>
#include "insert_lets.h"
namespace decompiler {
/*
Part 1:
Create a std::unordered_map<ProgVar, std::vector<FormElement*>> which maps a program variable to the
collection of FormElement* which reference it.
Part 2:
For each ProgVar, find the lowest common ancestor Form* of the FormElement*'s in the above map.
Part 3:
For each Form*, find the smallest range of FormElement*s which include all uses of the ProgVar
Part 4:
Sort these from the largest to smaller range.
This makes sure that at a single level (in the original tree), we insert larger lets first, leaving
us with only one nesting case to worry about in the next step.
Check the first FormElement* which uses the ProgVar.
If it is a (set! var xxx), then we can insert a let.
If we are inserting directly inside of another let, at the beginning of that let's body, add to that
let. This makes the scope larger than it needs to be, but this seems like it will lead to more
readable code.
If the previous let variables appear in the definition of new one, make the let into a let*
*/
namespace {
std::vector<Form*> path_up_tree(Form* in) {
std::vector<Form*> path;
while (in) {
path.push_back(in);
// lg::warn("In: {}", in->to_string(env));
if (in->parent_element) {
// lg::warn(" {}", in->parent_element->to_string(env));
in = in->parent_element->parent_form;
} else {
in = nullptr;
}
}
// lg::warn("DONE\n");
return path;
}
Form* lca_form(Form* a, Form* b, const Env& env) {
(void)env;
if (!a) {
return b;
}
// fmt::print("lca {} ({}) and {} ({})\n", a->to_string(env), (void*)a, b->to_string(env),
// (void*)b);
auto a_up = path_up_tree(a);
auto b_up = path_up_tree(b);
int ai = a_up.size() - 1;
int bi = b_up.size() - 1;
Form* result = nullptr;
while (ai >= 0 && bi >= 0) {
if (a_up.at(ai) == b_up.at(bi)) {
result = a_up.at(ai);
} else {
break;
}
ai--;
bi--;
}
assert(result);
// fmt::print("{}\n\n", result->to_string(env));
return result;
}
} // namespace
LetStats insert_lets(const Function& func, Env& env, FormPool& pool, Form* top_level_form) {
(void)func;
// if (func.guessed_name.to_string() != "(method 4 pair)") {
// return {};
// }
LetStats stats;
// Stored per variable.
struct PerVarInfo {
std::string var_name; // name used to uniquely identify
RegisterAccess access;
std::unordered_set<FormElement*> elts_using_var; // all FormElements using var
Form* lca_form = nullptr; // the lowest common form that contains all the above elts
int start_idx = -1; // in the above form, first FormElement using var's index
int end_idx = -1; // in the above form, 1 + last FormElement using var's index
};
std::unordered_map<std::string, PerVarInfo> var_info;
// Part 1, figure out which forms reference each var
top_level_form->apply([&](FormElement* elt) {
// for each element, figure out what vars we reference:
RegAccessSet reg_accesses;
elt->collect_vars(reg_accesses, false);
// and add it.
for (auto& access : reg_accesses) {
if (access.reg().get_kind() == Reg::FPR || access.reg().get_kind() == Reg::GPR) {
auto name = env.get_variable_name(access);
var_info[name].elts_using_var.insert(elt);
var_info[name].var_name = name;
var_info[name].access = access;
}
}
});
stats.total_vars = var_info.size();
// Part 2, figure out the lca form which contains all uses of a var
for (auto& kv : var_info) {
// fmt::print("--------------------- {}\n", kv.first);
Form* lca = nullptr;
for (auto fe : kv.second.elts_using_var) {
lca = lca_form(lca, fe->parent_form, env);
}
assert(lca);
var_info[kv.first].lca_form = lca;
}
// Part 3, find the minimum range of FormElement's within the lca form that contain
// all uses. This is the minimum possible range for a set!
for (auto& kv : var_info) {
// fmt::print("Setting range for let {}\n", kv.first);
kv.second.start_idx = std::numeric_limits<int>::max();
kv.second.end_idx = std::numeric_limits<int>::min();
bool got_one = false;
for (int i = 0; i < kv.second.lca_form->size(); i++) {
RegAccessSet ras;
kv.second.lca_form->at(i)->collect_vars(ras, true);
bool uses = false;
for (auto& ra : ras) {
if (env.get_variable_name(ra) == kv.second.var_name) {
uses = true;
}
}
if (uses) {
// if (kv.second.elts_using_var.find(kv.second.lca_form->at(i)) !=
// kv.second.elts_using_var.end()) {
got_one = true;
kv.second.start_idx = std::min(kv.second.start_idx, i);
kv.second.end_idx = std::max(kv.second.end_idx, i + 1);
// fmt::print("update range {} to {} because of {}\n", kv.second.start_idx,
// kv.second.end_idx, kv.second.lca_form->at(i)->to_string(env));
}
}
assert(got_one);
}
// fmt::print("\n");
// Part 4, sort the var infos in descending size.
// this simplifies future passes.
std::vector<PerVarInfo> sorted_info;
for (auto& kv : var_info) {
sorted_info.push_back(kv.second);
}
std::sort(sorted_info.begin(), sorted_info.end(), [](const PerVarInfo& a, const PerVarInfo& b) {
return (a.end_idx - a.start_idx) > (b.end_idx - b.start_idx);
});
// Part 5, find where we want to insert lets. But don't actually do any insertions.
// Only variables that begin with a set! var value can be used in a let, so we may discard
// some variables here. Though I suspect most reasonable functions will not discard any.
struct LetInsertion {
Form* form = nullptr;
int start_elt = -1; // this is the set!
SetVarElement* set_form = nullptr;
int end_elt = -1;
std::string name;
};
// stored per containing form.
std::unordered_map<Form*, std::vector<LetInsertion>> possible_insertions;
for (auto& info : sorted_info) {
auto first_form = info.lca_form->at(info.start_idx);
auto first_form_as_set = dynamic_cast<SetVarElement*>(first_form);
if (first_form_as_set &&
env.get_variable_name(first_form_as_set->dst()) == env.get_variable_name(info.access) &&
!first_form_as_set->info().is_eliminated_coloring_move) {
// success!
// fmt::print("Want let for {} range {} to {}\n",
// env.get_variable_name(first_form_as_set->dst()), info.start_idx, info.end_idx);
LetInsertion li;
li.form = info.lca_form;
li.start_elt = info.start_idx;
li.end_elt = info.end_idx;
li.set_form = first_form_as_set;
li.name = info.var_name;
possible_insertions[li.form].push_back(li);
stats.vars_in_lets++;
} else {
// fmt::print("fail for {} : {}\n", info.var_name, first_form->to_string(env));
}
}
// Part 6, expand ends of intervals to prevent "tangled lets"
for (auto& group : possible_insertions) {
// Note : this algorithm is not efficient.
bool changed = true;
while (changed) {
changed = false;
for (auto& let_a : group.second) {
for (auto& let_b : group.second) {
// If b starts within a and ends after a, expand a.
if (let_b.start_elt > let_a.start_elt && let_b.start_elt < let_a.end_elt &&
let_b.end_elt > let_a.end_elt) {
changed = true;
// fmt::print("Resized {}'s end to {}\n", let_a.set_form->dst().to_string(env),
// let_b.end_elt);
let_a.end_elt = let_b.end_elt;
}
}
}
}
}
// Part 7: insert lets!
for (auto& group : possible_insertions) {
// sort decreasing size.
std::sort(group.second.begin(), group.second.end(),
[](const LetInsertion& a, const LetInsertion& b) {
return (a.end_elt - a.start_elt) > (b.end_elt - b.start_elt);
});
// ownership[elt_idx] = the let which actually has this.
std::vector<int> ownership;
ownership.resize(group.first->size(), -1);
for (int let_idx = 0; let_idx < int(group.second.size()); let_idx++) {
for (int elt_idx = group.second.at(let_idx).start_elt;
elt_idx < group.second.at(let_idx).end_elt; elt_idx++) {
ownership.at(elt_idx) = let_idx;
}
}
// build lets
std::vector<LetElement*> lets;
lets.resize(group.first->size(), nullptr);
// start at the smallest.
for (size_t let_idx = group.second.size(); let_idx-- > 0;) {
auto& let_desc = group.second.at(let_idx);
std::vector<FormElement*> body;
int elt_idx = let_desc.start_elt + 1; // plus one to skip the variable def.
while (elt_idx < let_desc.end_elt) {
if (ownership.at(elt_idx) == int(let_idx)) {
body.push_back(let_desc.form->at(elt_idx));
elt_idx++;
} else {
auto existing_let = lets.at(ownership[elt_idx]);
assert(existing_let);
auto& existing_let_info = group.second.at(ownership[elt_idx]);
assert(existing_let_info.start_elt == elt_idx);
body.push_back(existing_let);
elt_idx = existing_let_info.end_elt;
}
}
assert(elt_idx == let_desc.end_elt);
auto new_let = pool.alloc_element<LetElement>(pool.alloc_sequence_form(nullptr, body));
new_let->add_def(let_desc.set_form->dst(), let_desc.set_form->src());
env.set_defined_in_let(let_desc.name);
lets.at(let_idx) = new_let;
}
// now rebuild form
int elt_idx = 0;
std::vector<FormElement*> new_body;
while (elt_idx < group.first->size()) {
if (ownership.at(elt_idx) == -1) {
new_body.push_back(group.first->at(elt_idx));
elt_idx++;
} else {
auto existing_let = lets.at(ownership[elt_idx]);
assert(existing_let);
auto& existing_let_info = group.second.at(ownership[elt_idx]);
assert(existing_let_info.start_elt == elt_idx);
new_body.push_back(existing_let);
elt_idx = existing_let_info.end_elt;
}
}
assert(elt_idx == group.first->size());
group.first->elts() = new_body;
group.first->claim_all_children();
}
// Part 8: (todo) recognize loops and stuff.
// Part 9: compact recursive lets:
bool changed = true;
while (changed) {
changed = false;
top_level_form->apply([&](FormElement* f) {
auto as_let = dynamic_cast<LetElement*>(f);
if (!as_let) {
return;
}
auto inner_let = dynamic_cast<LetElement*>(as_let->body()->try_as_single_element());
if (!inner_let) {
return;
}
for (auto& e : inner_let->entries()) {
if (!as_let->is_star()) {
RegAccessSet used;
e.src->collect_vars(used, true);
std::unordered_set<std::string> used_by_name;
for (auto used_var : used) {
used_by_name.insert(env.get_variable_name(used_var));
}
for (auto& old_entry : as_let->entries()) {
if (used_by_name.find(env.get_variable_name(old_entry.dest)) != used_by_name.end()) {
as_let->make_let_star();
break;
}
}
}
as_let->add_entry(e);
}
as_let->set_body(inner_let->body());
changed = true;
});
}
return stats;
}
} // namespace decompiler

View file

@ -0,0 +1,21 @@
#pragma once
#include "decompiler/IR2/Env.h"
#include "decompiler/Function/Function.h"
#include "decompiler/IR2/Form.h"
namespace decompiler {
struct LetStats {
int total_vars = 0;
int vars_in_lets = 0;
void operator+=(const LetStats& other) {
total_vars += other.total_vars;
vars_in_lets += other.vars_in_lets;
}
};
LetStats insert_lets(const Function& func, Env& env, FormPool& pool, Form* top_level_form);
} // namespace decompiler

View file

@ -53,6 +53,7 @@ void set_config(const std::string& path_to_config_file) {
gConfig.function_type_prop = cfg.at("function_type_prop").get<bool>();
gConfig.analyze_expressions = cfg.at("analyze_expressions").get<bool>();
gConfig.run_ir2 = cfg.at("run_ir2").get<bool>();
gConfig.insert_lets = cfg.at("insert_lets").get<bool>();
std::vector<std::string> asm_functions_by_name =
cfg.at("asm_functions_by_name").get<std::vector<std::string>>();

View file

@ -40,6 +40,7 @@ struct Config {
bool write_func_json = false;
bool function_type_prop = false;
bool analyze_expressions = false;
bool insert_lets = false;
std::unordered_set<std::string> asm_functions_by_name;
std::unordered_set<std::string> pair_functions_by_name;
std::unordered_set<std::string> no_type_analysis_functions_by_name;

View file

@ -61,6 +61,7 @@
"analyze_functions":true,
"analyze_expressions":true,
"function_type_prop":true,
"insert_lets":true,
"write_disassembly":true,
"write_hex_near_instructions":false,

View file

@ -5,6 +5,7 @@
#include "decompiler/analysis/cfg_builder.h"
#include "decompiler/analysis/expression_build.h"
#include "decompiler/analysis/final_output.h"
#include "decompiler/analysis/insert_lets.h"
#include "common/goos/PrettyPrinter.h"
#include "decompiler/IR2/Form.h"
#include "third-party/json.hpp"
@ -129,7 +130,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) {
RegAccessSet vars;
test->func.ir2.top_form->collect_vars(vars);
test->func.ir2.top_form->collect_vars(vars, true);
if (do_expressions) {
bool success = convert_to_expressions(test->func.ir2.top_form, *test->func.ir2.form_pool,
@ -139,6 +140,8 @@ std::unique_ptr<FormRegressionTest::TestData> FormRegressionTest::make_function(
if (!success) {
return nullptr;
}
insert_lets(test->func, test->func.ir2.env, *test->func.ir2.form_pool,
test->func.ir2.top_form);
}
}

File diff suppressed because it is too large Load diff

View file

@ -266,7 +266,7 @@ TEST_F(FormRegressionTest, ExprAbs) {
" jr ra\n"
" daddu sp, sp, r0";
std::string type = "(function int int)";
std::string expected = "(begin (set! v0-0 arg0) (abs v0-0))";
std::string expected = "(let ((v0-0 arg0)) (abs v0-0))";
test_with_expr(func, type, expected);
}
@ -454,14 +454,11 @@ TEST_F(FormRegressionTest, ExprBasicTypeP) {
std::string type = "(function basic type symbol)";
std::string expected =
"(begin\n"
" (set! v1-0 (-> arg0 type))\n"
" (set! a0-1 object)\n"
" (until\n"
" (begin (set! v1-0 (-> v1-0 parent)) (= v1-0 a0-1))\n" // likely using set! as value. we
// don't plan on supporting this.
" (if\n"
" (= v1-0 arg1)\n"
" (return #t)\n"
" (let\n"
" ((v1-0 (-> arg0 type)) (a0-1 object))\n"
" (until\n"
" (begin (set! v1-0 (-> v1-0 parent)) (= v1-0 a0-1))\n"
" (if (= v1-0 arg1) (return #t))\n"
" )\n"
" )\n"
" #f\n"
@ -497,17 +494,14 @@ TEST_F(FormRegressionTest, FinalBasicTypeP) {
std::string type = "(function basic type symbol)";
std::string expected =
"(defun test-function ((arg0 basic) (arg1 type))\n"
" (local-vars\n"
" (v1-0 type)\n"
" (a0-1 type)\n"
" )\n"
" (set! v1-0 (-> arg0 type))\n"
" (set! a0-1 object)\n"
" (let\n"
" ((v1-0 (-> arg0 type)) (a0-1 object))\n"
" (until\n"
" (begin (set! v1-0 (-> v1-0 parent)) (= v1-0 a0-1))\n"
" (if (= v1-0 arg1) (return #t))\n"
" )\n"
" #f\n"
" )\n"
" #f\n"
" )";
test_final_function(func, type, expected);
}
@ -553,13 +547,12 @@ TEST_F(FormRegressionTest, ExprTypeTypep) {
std::string expected =
"(begin\n"
" (set! v1-0 object)\n"
" (until\n"
" (begin\n"
" (set! arg0 (-> arg0 parent))\n"
" (or (= arg0 v1-0) (zero? arg0))\n"
" (let\n"
" ((v1-0 object))\n"
" (until\n"
" (begin (set! arg0 (-> arg0 parent)) (or (= arg0 v1-0) (zero? arg0)))\n"
" (if (= arg0 arg1) (return #t))\n"
" )\n"
" (if (= arg0 arg1) (return #t))\n"
" )\n"
" #f\n"
" )";
@ -615,13 +608,15 @@ TEST_F(FormRegressionTest, ExprFindParentMethod) {
std::string expected =
"(begin\n"
" (set! v1-2 (-> arg0 method-table arg1))\n"
" (until\n"
" (!= v0-0 v1-2)\n"
" (if (= arg0 object) (return nothing))\n"
" (set! arg0 (-> arg0 parent))\n"
" (set! v0-0 (-> arg0 method-table arg1))\n"
" (if (zero? v0-0) (return nothing))\n"
" (let\n"
" ((v1-2 (-> arg0 method-table arg1)))\n"
" (until\n"
" (!= v0-0 v1-2)\n"
" (if (= arg0 object) (return nothing))\n"
" (set! arg0 (-> arg0 parent))\n"
" (set! v0-0 (-> arg0 method-table arg1))\n"
" (if (zero? v0-0) (return nothing))\n"
" )\n"
" )\n"
" v0-0\n"
" )";
@ -656,13 +651,9 @@ TEST_F(FormRegressionTest, ExprRef) {
std::string expected =
"(begin\n"
" (set! v1-0 0)\n"
" (while\n"
" (< v1-0 arg1)\n"
" (nop!)\n"
" (nop!)\n"
" (set! arg0 (cdr arg0))\n"
" (+! v1-0 1)\n"
" (let\n"
" ((v1-0 0))\n"
" (while (< v1-0 arg1) (nop!) (nop!) (set! arg0 (cdr arg0)) (+! v1-0 1))\n"
" )\n"
" (car arg0)\n"
" )";
@ -715,19 +706,20 @@ TEST_F(FormRegressionTest, ExprPairMethod4) {
" daddu sp, sp, r0\n";
std::string type = "(function pair int)";
// multiple return paths merged variable issue.
std::string expected =
"(begin\n"
" (cond\n"
" ((= arg0 '()) (set! v0-0 0))\n"
" ((= arg0 (quote ())) (set! v0-0 0))\n"
" (else\n"
" (set! v1-1 (cdr arg0))\n"
" (set! v0-0 1)\n"
" (while\n"
" (and (!= v1-1 '()) "
" (< (shl (the-as int v1-1) 62) 0)\n"
" (let\n"
" ((v1-1 (cdr arg0)))\n"
" (set! v0-0 1)\n"
" (while\n"
" (and (!= v1-1 (quote ())) (< (shl (the-as int v1-1) 62) 0))\n"
" (+! v0-0 1)\n"
" (set! v1-1 (cdr v1-1))\n"
" )\n"
" (+! v0-0 1)\n"
" (set! v1-1 (cdr v1-1))\n"
" )\n"
" )\n"
" )\n"
@ -774,12 +766,9 @@ TEST_F(FormRegressionTest, ExprLast) {
std::string type = "(function object object)";
std::string expected =
"(begin\n"
" (set! v0-0 arg0)\n"
" (while (!= (cdr v0-0) '())"
" (nop!)\n"
" (nop!)\n"
" (set! v0-0 (cdr v0-0)))\n"
"(let\n"
" ((v0-0 arg0))\n"
" (while (!= (cdr v0-0) (quote ())) (nop!) (nop!) (set! v0-0 (cdr v0-0)))\n"
" v0-0\n"
" )";
test_with_expr(func, type, expected, true, "");
@ -826,13 +815,13 @@ TEST_F(FormRegressionTest, ExprMember) {
std::string type = "(function object object object)";
std::string expected =
"(begin\n"
" (set! v1-0 arg1)\n"
"(let\n"
" ((v1-0 arg1))\n"
" (while\n"
" (not (or (= v1-0 '()) (= (car v1-0) arg0)))\n"
" (not (or (= v1-0 (quote ())) (= (car v1-0) arg0)))\n"
" (set! v1-0 (cdr v1-0))\n"
" )\n"
" (if (!= v1-0 '()) v1-0)\n"
" (if (!= v1-0 (quote ())) v1-0)\n"
" )";
test_with_expr(func, type, expected, true, "");
}
@ -939,13 +928,13 @@ TEST_F(FormRegressionTest, ExprAssoc) {
std::string type = "(function object object object)";
std::string expected =
"(begin\n"
" (set! v1-0 arg1)\n"
"(let\n"
" ((v1-0 arg1))\n"
" (while\n"
" (not (or (= v1-0 '()) (= (car (car v1-0)) arg0)))\n"
" (not (or (= v1-0 (quote ())) (= (car (car v1-0)) arg0)))\n"
" (set! v1-0 (cdr v1-0))\n"
" )\n"
" (if (!= v1-0 '()) (car v1-0))\n"
" (if (!= v1-0 (quote ())) (car v1-0))\n"
" )";
test_with_expr(func, type, expected, true, "");
}
@ -1001,19 +990,19 @@ TEST_F(FormRegressionTest, ExprAssoce) {
std::string type = "(function object object object)";
std::string expected =
"(begin\n"
" (set! v1-0 arg1)\n"
"(let\n"
" ((v1-0 arg1))\n"
" (while\n"
" (not\n"
" (or\n"
" (= v1-0 '())\n"
" (= v1-0 (quote ()))\n"
" (= (car (car v1-0)) arg0)\n"
" (= (car (car v1-0)) 'else)\n"
" (= (car (car v1-0)) (quote else))\n"
" )\n"
" )\n"
" (set! v1-0 (cdr v1-0))\n"
" )\n"
" (if (!= v1-0 '()) (car v1-0))\n"
" (if (!= v1-0 (quote ())) (car v1-0))\n"
" )";
test_with_expr(func, type, expected, true, "");
}
@ -1092,13 +1081,13 @@ TEST_F(FormRegressionTest, ExprNassoc) {
" (not\n"
" (or\n"
" (= arg1 (quote ()))\n"
" (begin\n"
" (set! a1-1 (car (car arg1)))\n"
" (if "
" (let\n"
" ((a1-1 (car (car arg1))))\n"
" (if\n"
" (pair? a1-1)\n"
" (nmember (the-as basic arg0) a1-1)\n"
" (name= (the-as basic a1-1) (the-as basic arg0))"
" )\n"
" (name= (the-as basic a1-1) (the-as basic arg0))\n"
" )\n"
" )\n"
" )\n"
" )\n"
@ -1194,8 +1183,8 @@ TEST_F(FormRegressionTest, ExprNassoce) {
" (not\n"
" (or\n"
" (= arg1 (quote ()))\n"
" (begin\n"
" (set! s4-0 (car (car arg1)))\n"
" (let\n"
" ((s4-0 (car (car arg1))))\n"
" (if\n"
" (pair? s4-0)\n"
" (nmember (the-as basic arg0) s4-0)\n"
@ -1260,11 +1249,13 @@ TEST_F(FormRegressionTest, ExprAppend) {
std::string expected =
"(cond\n"
" ((= arg0 '()) arg1)\n"
" ((= arg0 (quote ())) arg1)\n"
" (else\n"
" (set! v1-1 arg0)\n"
" (while (!= (cdr v1-1) '()) (nop!) (nop!) (set! v1-1 (cdr v1-1)))\n"
" (if (!= v1-1 '()) (set! (cdr v1-1) arg1))\n"
" (let\n"
" ((v1-1 arg0))\n"
" (while (!= (cdr v1-1) (quote ())) (nop!) (nop!) (set! v1-1 (cdr v1-1)))\n"
" (if (!= v1-1 (quote ())) (set! (cdr v1-1) arg1))\n"
" )\n"
" arg0\n"
" )\n"
" )";
@ -1332,14 +1323,15 @@ TEST_F(FormRegressionTest, ExprDelete) {
" (cond\n"
" ((= arg0 (car arg1)) (cdr arg1))\n"
" (else\n"
" (set! v1-1 arg1)\n"
" (set! a2-0 (cdr arg1))\n"
" (while\n"
" (not (or (= a2-0 (quote ())) (= (car a2-0) arg0)))\n"
" (set! v1-1 a2-0)\n"
" (set! a2-0 (cdr a2-0))\n"
" (let\n"
" ((v1-1 arg1) (a2-0 (cdr arg1)))\n"
" (while\n"
" (not (or (= a2-0 (quote ())) (= (car a2-0) arg0)))\n"
" (set! v1-1 a2-0)\n"
" (set! a2-0 (cdr a2-0))\n"
" )\n"
" (if (!= a2-0 (quote ())) (set! (cdr v1-1) (cdr a2-0)))\n"
" )\n"
" (if (!= a2-0 (quote ())) (set! (cdr v1-1) (cdr a2-0)))\n"
" arg1\n"
" )\n"
" )\n"
@ -1410,14 +1402,15 @@ TEST_F(FormRegressionTest, ExprDeleteCar) {
" (cond\n"
" ((= arg0 (car (car arg1))) (cdr arg1))\n"
" (else\n"
" (set! v1-2 arg1)\n"
" (set! a2-0 (cdr arg1))\n"
" (while\n"
" (not (or (= a2-0 (quote ())) (= (car (car a2-0)) arg0)))\n"
" (set! v1-2 a2-0)\n"
" (set! a2-0 (cdr a2-0))\n"
" (let\n"
" ((v1-2 arg1) (a2-0 (cdr arg1)))\n"
" (while\n"
" (not (or (= a2-0 (quote ())) (= (car (car a2-0)) arg0)))\n"
" (set! v1-2 a2-0)\n"
" (set! a2-0 (cdr a2-0))\n"
" )\n"
" (if (!= a2-0 (quote ())) (set! (cdr v1-2) (cdr a2-0)))\n"
" )\n"
" (if (!= a2-0 (quote ())) (set! (cdr v1-2) (cdr a2-0)))\n"
" arg1\n"
" )\n"
" )\n"
@ -1454,11 +1447,7 @@ TEST_F(FormRegressionTest, ExprInsertCons) {
std::string type = "(function object object pair)";
// NOTE - this appears to _not_ be a nested call.
std::string expected =
"(begin\n"
" (set! a3-0 (delete-car! (car arg0) arg1))\n"
" (cons arg0 a3-0)\n"
" )";
std::string expected = "(let ((a3-0 (delete-car! (car arg0) arg1))) (cons arg0 a3-0))";
test_with_expr(func, type, expected, true, "");
}
@ -1568,25 +1557,29 @@ TEST_F(FormRegressionTest, ExprSort) {
// TODO - this should probably be tested.
std::string expected =
"(begin\n"
" (set! s4-0 -1)\n"
" (while\n"
" (nonzero? s4-0)\n"
" (set! s4-0 0)\n"
" (set! s3-0 arg0)\n"
" (let\n"
" ((s4-0 -1))\n"
" (while\n"
" (not\n"
" (or (= (cdr s3-0) (quote ())) (>= (shl (the-as int (cdr s3-0)) 62) 0))\n"
" (nonzero? s4-0)\n"
" (set! s4-0 0)\n"
" (let\n"
" ((s3-0 arg0))\n"
" (while\n"
" (not\n"
" (or (= (cdr s3-0) (quote ())) (>= (shl (the-as int (cdr s3-0)) 62) 0))\n"
" )\n"
" (let*\n"
" ((s2-0 (car s3-0)) (s1-0 (car (cdr s3-0))) (v1-1 (arg1 s2-0 s1-0)))\n"
" (when\n"
" (and (or (not v1-1) (> (the-as int v1-1) 0)) (!= v1-1 #t))\n"
" (+! s4-0 1)\n"
" (set! (car s3-0) s1-0)\n"
" (set! (car (cdr s3-0)) s2-0)\n"
" )\n"
" )\n"
" (set! s3-0 (cdr s3-0))\n"
" )\n"
" )\n"
" (set! s2-0 (car s3-0))\n"
" (set! s1-0 (car (cdr s3-0)))\n"
" (set! v1-1 (arg1 s2-0 s1-0))\n"
" (when\n"
" (and (or (not v1-1) (> (the-as int v1-1) 0)) (!= v1-1 #t))\n"
" (+! s4-0 1)\n"
" (set! (car s3-0) s1-0)\n"
" (set! (car (cdr s3-0)) s2-0)\n"
" )\n"
" (set! s3-0 (cdr s3-0))\n"
" )\n"
" )\n"
" arg0\n"
@ -1627,13 +1620,13 @@ TEST_F(FormRegressionTest, ExprInlineArrayMethod0) {
std::string type = "(function symbol type int inline-array-class)";
std::string expected =
"(begin\n"
" (set!\n"
" v0-0\n"
" (object-new\n"
" arg0\n"
" arg1\n"
" (the-as int (+ (-> arg1 size) (* (the-as uint arg2) (-> arg1 heap-base))))\n"
"(let\n"
" ((v0-0\n"
" (object-new\n"
" arg0\n"
" arg1\n"
" (the-as int (+ (-> arg1 size) (* (the-as uint arg2) (-> arg1 heap-base))))\n"
" )\n"
" )\n"
" )\n"
" (when\n"
@ -1743,17 +1736,17 @@ TEST_F(FormRegressionTest, ExprArrayMethod0) {
std::string type = "(function symbol type type int array)";
std::string expected =
"(begin\n"
" (set!\n"
" v0-1\n"
" (object-new\n"
" arg0\n"
" arg1\n"
" (the-as\n"
" int\n"
" (+\n"
" (-> arg1 size)\n"
" (the-as uint (* arg3 (if (type-type? arg2 number) (-> arg2 size) 4)))\n"
"(let\n"
" ((v0-1\n"
" (object-new\n"
" arg0\n"
" arg1\n"
" (the-as\n"
" int\n"
" (+\n"
" (-> arg1 size)\n"
" (the-as uint (* arg3 (if (type-type? arg2 number) (-> arg2 size) 4)))\n"
" )\n"
" )\n"
" )\n"
" )\n"
@ -1867,15 +1860,17 @@ TEST_F(FormRegressionTest, ExprMemCopy) {
std::string type = "(function pointer pointer int pointer)";
std::string expected =
"(begin\n"
" (set! v0-0 arg0)\n"
" (set! v1-0 0)\n"
" (while\n"
" (< v1-0 arg2)\n"
" (set! (-> (the-as (pointer int8) arg0)) (-> (the-as (pointer uint8) arg1)))\n"
" (&+! arg0 1)\n"
" (&+! arg1 1)\n"
" (+! v1-0 1)\n"
"(let\n"
" ((v0-0 arg0))\n"
" (let\n"
" ((v1-0 0))\n"
" (while\n"
" (< v1-0 arg2)\n"
" (set! (-> (the-as (pointer int8) arg0)) (-> (the-as (pointer uint8) arg1)))\n"
" (&+! arg0 1)\n"
" (&+! arg1 1)\n"
" (+! v1-0 1)\n"
" )\n"
" )\n"
" v0-0\n"
" )";
@ -1908,15 +1903,17 @@ TEST_F(FormRegressionTest, ExprMemSet32) {
std::string type = "(function pointer int int pointer)";
std::string expected =
"(begin\n"
" (set! v0-0 arg0)\n"
" (set! v1-0 0)\n"
" (while\n"
" (< v1-0 arg1)\n"
" (set! (-> (the-as (pointer int32) arg0)) arg2)\n"
" (&+! arg0 4)\n"
" (nop!)\n"
" (+! v1-0 1)\n"
"(let\n"
" ((v0-0 arg0))\n"
" (let\n"
" ((v1-0 0))\n"
" (while\n"
" (< v1-0 arg1)\n"
" (set! (-> (the-as (pointer int32) arg0)) arg2)\n"
" (&+! arg0 4)\n"
" (nop!)\n"
" (+! v1-0 1)\n"
" )\n"
" )\n"
" v0-0\n"
" )";
@ -1952,21 +1949,23 @@ TEST_F(FormRegressionTest, ExprMemOr) {
std::string type = "(function pointer pointer int pointer)";
std::string expected =
"(begin\n"
" (set! v0-0 arg0)\n"
" (set! v1-0 0)\n"
" (while\n"
" (< v1-0 arg2)\n"
" (set!\n"
" (-> (the-as (pointer int8) arg0))\n"
" (logior\n"
" (-> (the-as (pointer uint8) arg0))\n"
" (-> (the-as (pointer uint8) arg1))\n"
"(let\n"
" ((v0-0 arg0))\n"
" (let\n"
" ((v1-0 0))\n"
" (while\n"
" (< v1-0 arg2)\n"
" (set!\n"
" (-> (the-as (pointer int8) arg0))\n"
" (logior\n"
" (-> (the-as (pointer uint8) arg0))\n"
" (-> (the-as (pointer uint8) arg1))\n"
" )\n"
" )\n"
" (&+! arg0 1)\n"
" (&+! arg1 1)\n"
" (+! v1-0 1)\n"
" )\n"
" (&+! arg0 1)\n"
" (&+! arg1 1)\n"
" (+! v1-0 1)\n"
" )\n"
" v0-0\n"
" )";
@ -2076,8 +2075,7 @@ TEST_F(FormRegressionTest, ExprPrintl) {
std::string expected =
"(begin\n"
" (set! a0-1 arg0)\n"
" ((method-of-type (rtype-of a0-1) print) a0-1)\n"
" (let ((a0-1 arg0)) ((method-of-type (rtype-of a0-1) print) a0-1))\n"
" (format #t \"~%\")\n"
" arg0\n"
" )";
@ -2173,16 +2171,14 @@ TEST_F(FormRegressionTest, ExprPrintTreeBitmask) {
std::string expected =
"(begin\n"
" (set! s4-0 0)\n"
" (while\n"
" (< s4-0 arg1)\n"
" (if\n"
" (zero? (logand arg0 1))\n"
" (format #t \" \")\n"
" (format #t \"| \")\n"
" (let\n"
" ((s4-0 0))\n"
" (while\n"
" (< s4-0 arg1)\n"
" (if (zero? (logand arg0 1)) (format #t \" \") (format #t \"| \"))\n"
" (set! arg0 (shr arg0 1))\n"
" (+! s4-0 1)\n"
" )\n"
" (set! arg0 (shr arg0 1))\n"
" (+! s4-0 1)\n"
" )\n"
" #f\n"
" )";
@ -2351,7 +2347,7 @@ TEST_F(FormRegressionTest, ExprStopwatchElapsedSeconds) {
" daddiu sp, sp, 16";
std::string type = "(function int float)";
std::string expected = "(begin (set! v1-0 (abs arg0)) (* (l.f L20) (the float v1-0)))";
std::string expected = "(let ((v1-0 (abs arg0))) (* (l.f L20) (the float v1-0)))";
test_with_expr(func, type, expected, false, "");
}
@ -2381,17 +2377,23 @@ TEST_F(FormRegressionTest, ExprCopyStringString) {
" jr ra\n"
" daddu sp, sp, r0";
std::string type = "(function string string string)";
// this is correct, but an example of where let's might look better if nested,
// even if it means making the scope a little bit larger...
std::string expected =
"(begin\n"
" (set! v1-0 (-> arg0 data))\n"
" (set! a1-1 (-> arg1 data))\n"
" (while\n"
" (nonzero? (-> a1-1 0))\n"
" (set! (-> v1-0 0) (-> a1-1 0))\n"
" (set! v1-0 (&-> v1-0 1))\n"
" (set! a1-1 (&-> a1-1 1))\n"
" (let\n"
" ((v1-0 (-> arg0 data)))\n"
" (let\n"
" ((a1-1 (-> arg1 data)))\n"
" (while\n"
" (nonzero? (-> a1-1 0))\n"
" (set! (-> v1-0 0) (-> a1-1 0))\n"
" (set! v1-0 (&-> v1-0 1))\n"
" (set! a1-1 (&-> a1-1 1))\n"
" )\n"
" )\n"
" (set! (-> v1-0 0) 0)\n"
" )\n"
" (set! (-> v1-0 0) 0)\n"
" arg0\n"
" )";
test_with_expr(func, type, expected, false, "");
@ -2485,21 +2487,23 @@ TEST_F(FormRegressionTest, StringLt) {
std::string type = "(function string string symbol)";
std::string expected =
"(begin\n"
" (set!\n"
" s4-1\n"
" (min\n"
" ((method-of-type string length) arg0)\n"
" ((method-of-type string length) arg1)\n"
" (let\n"
" ((s4-1\n"
" (min\n"
" ((method-of-type string length) arg0)\n"
" ((method-of-type string length) arg1)\n"
" )\n"
" )\n"
" (v1-4 0)\n"
" )\n"
" )\n"
" (set! v1-4 0)\n"
" (while\n"
" (< v1-4 s4-1)\n"
" (cond\n"
" ((< (-> arg0 data v1-4) (-> arg1 data v1-4)) (return #t))\n"
" ((< (-> arg1 data v1-4) (-> arg0 data v1-4)) (return #f))\n"
" (while\n"
" (< v1-4 s4-1)\n"
" (cond\n"
" ((< (-> arg0 data v1-4) (-> arg1 data v1-4)) (return #t))\n"
" ((< (-> arg1 data v1-4) (-> arg0 data v1-4)) (return #f))\n"
" )\n"
" (+! v1-4 1)\n"
" )\n"
" (+! v1-4 1)\n"
" )\n"
" #f\n"
" )";
@ -2567,8 +2571,8 @@ TEST_F(FormRegressionTest, ExprTerminal2) {
std::string type = "(function float float float float float)";
std::string expected =
"(begin\n"
" (set! f0-4 (sqrt (/ (- (* 0.000000 arg0) arg1) arg2)))\n"
"(let\n"
" ((f0-4 (sqrt (/ (- (* 0.0 arg0) arg1) arg2))))\n"
" (- f0-4 (+ arg1 (* arg2 (* f0-4 f0-4))))\n"
" )";
test_with_expr(func, type, expected, false, "", {{"L17", "A ~A"}});

File diff suppressed because it is too large Load diff

View file

@ -62,10 +62,12 @@ TEST_F(FormRegressionTest, ExprLoadPackage) {
std::string expected =
"(when\n"
" (not (nmember arg0 *kernel-packages*))\n"
" (dgo-load arg0 arg1 15 2097152)\n"
" (set! v0-1 (cons arg0 *kernel-packages*))\n"
" (set! *kernel-packages* v0-1)\n"
" v0-1\n"
" (dgo-load arg0 arg1 15 #x200000)\n"
" (let\n"
" ((v0-1 (cons arg0 *kernel-packages*)))\n"
" (set! *kernel-packages* v0-1)\n"
" v0-1\n"
" )\n"
" )";
test_with_expr(func, type, expected);
}
@ -100,8 +102,10 @@ TEST_F(FormRegressionTest, ExprUnloadPackage) {
std::string type = "(function string pair)";
std::string expected =
"(begin\n"
" (set! v1-0 (nmember arg0 *kernel-packages*))\n"
" (if v1-0 (set! *kernel-packages* (delete! (car v1-0) *kernel-packages*)))\n"
" (let\n"
" ((v1-0 (nmember arg0 *kernel-packages*)))\n"
" (if v1-0 (set! *kernel-packages* (delete! (car v1-0) *kernel-packages*)))\n"
" )\n"
" *kernel-packages*\n"
" )";
test_with_expr(func, type, expected, true);
@ -128,7 +132,7 @@ TEST_F(FormRegressionTest, ExprMethod1Thread) {
std::string type = "(function thread none)";
std::string expected =
"(begin\n"
" (when (= arg0 (-> arg0 process main-thread)) (break!) (set! v1-3 0))\n"
" (when (= arg0 (-> arg0 process main-thread)) (break!) (let ((v1-3 0))))\n"
" (set! (-> arg0 process top-thread) (-> arg0 previous))\n"
" )";
test_with_expr(func, type, expected, false);
@ -253,26 +257,28 @@ TEST_F(FormRegressionTest, ExprMethod9Thread) {
std::string type = "(function thread int none)";
std::string expected =
"(begin\n"
" (set! a2-0 (-> arg0 process))\n"
" (cond\n"
" ((!= arg0 (-> a2-0 main-thread)) (format 0 \"1 ~A ~%\" a2-0))\n"
" ((= (-> arg0 stack-size) arg1))\n"
" ((=\n"
" (-> a2-0 heap-cur)\n"
" (+\n"
" (+ (+ (-> arg0 stack-size) -4) (the-as int (-> arg0 type size)))\n"
" (the-as int arg0)\n"
" (let\n"
" ((a2-0 (-> arg0 process)))\n"
" (cond\n"
" ((!= arg0 (-> a2-0 main-thread)) (format 0 \"1 ~A ~%\" a2-0))\n"
" ((= (-> arg0 stack-size) arg1))\n"
" ((=\n"
" (-> a2-0 heap-cur)\n"
" (+\n"
" (+ (+ (-> arg0 stack-size) -4) (the-as int (-> arg0 type size)))\n"
" (the-as int arg0)\n"
" )\n"
" )\n"
" (set!\n"
" (-> a2-0 heap-cur)\n"
" (+ (+ (+ arg1 -4) (the-as int (-> arg0 type size))) (the-as int arg0))\n"
" )\n"
" (set! (-> arg0 stack-size) arg1)\n"
" )\n"
" (set!\n"
" (-> a2-0 heap-cur)\n"
" (+ (+ (+ arg1 -4) (the-as int (-> arg0 type size))) (the-as int arg0))\n"
" )\n"
" (set! (-> arg0 stack-size) arg1)\n"
" (else (format 0 \"2 ~A ~%\" a2-0))\n"
" )\n"
" (else (format 0 \"2 ~A ~%\" a2-0))\n"
" )\n"
" (set! v0-2 0)\n"
" (let ((v0-2 0)))\n"
" )";
test_with_expr(func, type, expected, false, "", {{"L342", "1 ~A ~%"}, {"L341", "2 ~A ~%"}});
}
@ -319,18 +325,16 @@ TEST_F(FormRegressionTest, ExprMethod0Thread) {
" daddu sp, sp, r0";
std::string type = "(function symbol type process symbol int pointer thread)";
std::string expected =
"(begin\n"
" (set!\n"
" v0-0\n"
" (cond\n"
" ((-> arg2 top-thread) (&+ arg5 -7164))\n"
" (else\n"
" (set!\n"
" v1-2\n"
" (logand -16 (the-as int (&+ (-> arg2 heap-cur) 15)))\n"
"(let\n"
" ((v0-0\n"
" (if\n"
" (-> arg2 top-thread)\n"
" (&+ arg5 -7164)\n"
" (let\n"
" ((v1-2 (logand -16 (the-as int (&+ (-> arg2 heap-cur) 15)))))\n"
" (set! (-> arg2 heap-cur) (+ (+ v1-2 (the-as int (-> arg1 size))) arg4))\n"
" (+ v1-2 4)\n"
" )\n"
" (set! (-> arg2 heap-cur) (+ (+ v1-2 (the-as int (-> arg1 size))) arg4))\n"
" (+ v1-2 4)\n"
" )\n"
" )\n"
" )\n"
@ -341,7 +345,6 @@ TEST_F(FormRegressionTest, ExprMethod0Thread) {
" (set! (-> v0-0 stack-top) arg5)\n"
" (set! (-> v0-0 previous) (-> arg2 top-thread))\n"
" (set! (-> arg2 top-thread) v0-0)\n"
// TODO - we could make this method access nicer.
" (set! (-> v0-0 suspend-hook) (method-of-object v0-0 thread-suspend))\n"
" (set! (-> v0-0 resume-hook) (method-of-object v0-0 thread-resume))\n"
" (set! (-> v0-0 stack-size) arg4)\n"
@ -383,11 +386,13 @@ TEST_F(FormRegressionTest, RemoveExit) {
" daddu sp, sp, r0";
std::string type = "(function stack-frame)";
std::string expected =
"(when\n"
"(if\n"
" (-> pp stack-frame-top)\n"
" (set! v0-0 (-> pp stack-frame-top next))\n"
" (set! (-> pp stack-frame-top) v0-0)\n"
" v0-0\n"
" (let\n"
" ((v0-0 (-> pp stack-frame-top next)))\n"
" (set! (-> pp stack-frame-top) v0-0)\n"
" v0-0\n"
" )\n"
" )";
test_with_expr(func, type, expected, false);
}
@ -423,8 +428,8 @@ TEST_F(FormRegressionTest, RemoveMethod0ProcessTree) {
" daddiu sp, sp, 32";
std::string type = "(function symbol type basic process-tree)";
std::string expected =
"(begin\n"
" (set! v0-0 (object-new arg0 arg1 (the-as int (-> arg1 size))))\n"
"(let\n"
" ((v0-0 (object-new arg0 arg1 (the-as int (-> arg1 size)))))\n"
" (set! (-> v0-0 name) arg2)\n"
" (set! (-> v0-0 mask) 256)\n"
" (set! (-> v0-0 parent) #f)\n"
@ -525,21 +530,18 @@ TEST_F(FormRegressionTest, RemoveMethod3ProcessTree) {
" (format #t \"[~8x] ~A~%\" arg0 (-> arg0 type))\n"
" (format #t \"~Tname: ~S~%\" (-> arg0 name))\n"
" (format #t \"~Tmask: #x~X~%\" (-> arg0 mask))\n"
" (set! t9-3 format)\n"
" (set! a0-4 #t)\n"
" (set! a1-3 \"~Tparent: ~A~%\")\n"
" (set! v1-0 (-> arg0 parent))\n"
" (t9-3 a0-4 a1-3 (if v1-0 (-> v1-0 0 self)))\n"
" (set! t9-4 format)\n"
" (set! a0-5 #t)\n"
" (set! a1-4 \"~Tbrother: ~A~%\")\n"
" (set! v1-2 (-> arg0 brother))\n"
" (t9-4 a0-5 a1-4 (if v1-2 (-> v1-2 0 self)))\n"
" (set! t9-5 format)\n"
" (set! a0-6 #t)\n"
" (set! a1-5 \"~Tchild: ~A~%\")\n"
" (set! v1-4 (-> arg0 child))\n"
" (t9-5 a0-6 a1-5 (if v1-4 (-> v1-4 0 self)))\n"
" (let\n"
" ((t9-3 format) (a0-4 #t) (a1-3 \"~Tparent: ~A~%\") (v1-0 (-> arg0 parent)))\n"
" (t9-3 a0-4 a1-3 (if v1-0 (-> v1-0 0 self)))\n"
" )\n"
" (let\n"
" ((t9-4 format) (a0-5 #t) (a1-4 \"~Tbrother: ~A~%\") (v1-2 (-> arg0 brother)))\n"
" (t9-4 a0-5 a1-4 (if v1-2 (-> v1-2 0 self)))\n"
" )\n"
" (let\n"
" ((t9-5 format) (a0-6 #t) (a1-5 \"~Tchild: ~A~%\") (v1-4 (-> arg0 child)))\n"
" (t9-5 a0-6 a1-5 (if v1-4 (-> v1-4 0 self)))\n"
" )\n"
" arg0\n"
" )";
test_with_expr(func, type, expected, false, "process-tree",
@ -619,13 +621,17 @@ TEST_F(FormRegressionTest, ExprMethod0Process) {
" daddiu sp, sp, 48";
std::string type = "(function symbol type basic int process)";
std::string expected =
"(begin\n"
" (set!\n"
" v0-0\n"
" (if\n"
" (= (-> arg0 type) symbol)\n"
" (object-new arg0 arg1 (the-as int (+ (-> process size) (the-as uint arg3))))\n"
" (+ (the-as int arg0) 4)\n"
"(let\n"
" ((v0-0\n"
" (if\n"
" (= (-> arg0 type) symbol)\n"
" (object-new\n"
" arg0\n"
" arg1\n"
" (the-as int (+ (-> process size) (the-as uint arg3)))\n"
" )\n"
" (+ (the-as int arg0) 4)\n"
" )\n"
" )\n"
" )\n"
" (set! (-> (the-as process v0-0) name) arg2)\n"
@ -635,9 +641,11 @@ TEST_F(FormRegressionTest, ExprMethod0Process) {
" (set! (-> v0-0 allocated-length) arg3)\n"
" (set! (-> v0-0 top-thread) #f)\n"
" (set! (-> v0-0 main-thread) #f)\n"
" (set! v1-5 (-> v0-0 stack))\n"
" (set! (-> v0-0 heap-cur) v1-5)\n"
" (set! (-> v0-0 heap-base) v1-5)\n"
" (let\n"
" ((v1-5 (-> v0-0 stack)))\n"
" (set! (-> v0-0 heap-cur) v1-5)\n"
" (set! (-> v0-0 heap-base) v1-5)\n"
" )\n"
" (set! (-> v0-0 heap-top) (&-> v0-0 stack (-> v0-0 allocated-length)))\n"
" (set! (-> v0-0 stack-frame-top) (-> v0-0 heap-top))\n"
" (set! (-> v0-0 stack-frame-top) #f)\n"
@ -711,15 +719,16 @@ TEST_F(FormRegressionTest, ExprInspectProcessHeap) {
std::string type = "(function process symbol)";
std::string expected =
"(begin\n"
" (set! s5-0 (&+ (-> arg0 heap-base) 4))\n"
" (while\n"
" (< (the-as int s5-0) (the-as int (-> arg0 heap-cur)))\n"
" (inspect (the-as basic s5-0))\n"
" (+! (the-as int s5-0) (logand -16 (+ (asize-of s5-0) 15)))\n"
" (let\n"
" ((s5-0 (&+ (-> arg0 heap-base) 4)))\n"
" (while\n"
" (< (the-as int s5-0) (the-as int (-> arg0 heap-cur)))\n"
" (inspect (the-as basic s5-0))\n"
" (+! (the-as int s5-0) (logand -16 (+ (asize-of s5-0) 15)))\n"
" )\n"
" )\n"
" #f\n"
" )\n"
"";
" )";
test_with_expr(func, type, expected, false, "", {},
parse_hint_json("[\t\t[4, [\"s5\", \"basic\"]],\n"
"\t\t[17, [\"s5\", \"int\"]]]"));
@ -914,8 +923,8 @@ TEST_F(FormRegressionTest, ExprMethod0DeadPool) {
" daddiu sp, sp, 112";
std::string type = "(function symbol type int int basic dead-pool)";
std::string expected =
"(begin\n"
" (set! s3-0 (object-new arg0 arg1 (the-as int (-> arg1 size))))\n"
"(let\n"
" ((s3-0 (object-new arg0 arg1 (the-as int (-> arg1 size)))))\n"
" (set! (-> s3-0 name) arg4)\n"
" (set! (-> s3-0 mask) 256)\n"
" (set! (-> s3-0 parent) #f)\n"
@ -923,18 +932,21 @@ TEST_F(FormRegressionTest, ExprMethod0DeadPool) {
" (set! (-> s3-0 child) #f)\n"
" (set! (-> s3-0 self) s3-0)\n"
" (set! (-> s3-0 ppointer) (&-> s3-0 self))\n"
" (set! s2-1 0)\n"
" (while\n"
" (< s2-1 arg2)\n"
" (set! s1-0 (-> s3-0 child))\n"
" (set! v1-5 ((method-of-type process new) arg0 process (quote dead) arg3))\n"
" (set! a0-3 v1-5)\n"
" (set! (-> s3-0 child) (if a0-3 (-> a0-3 ppointer)))\n"
" (set! a0-4 s3-0)\n"
" (set! (-> v1-5 parent) (if a0-4 (-> a0-4 ppointer)))\n"
" (set! (-> v1-5 pool) s3-0)\n"
" (set! (-> v1-5 brother) s1-0)\n"
" (+! s2-1 1)\n"
" (let\n"
" ((s2-1 0))\n"
" (while\n"
" (< s2-1 arg2)\n"
" (let\n"
" ((s1-0 (-> s3-0 child))\n"
" (v1-5 ((method-of-type process new) arg0 process (quote dead) arg3))\n"
" )\n"
" (let ((a0-3 v1-5)) (set! (-> s3-0 child) (if a0-3 (-> a0-3 ppointer))))\n"
" (let ((a0-4 s3-0)) (set! (-> v1-5 parent) (if a0-4 (-> a0-4 ppointer))))\n"
" (set! (-> v1-5 pool) s3-0)\n"
" (set! (-> v1-5 brother) s1-0)\n"
" )\n"
" (+! s2-1 1)\n"
" )\n"
" )\n"
" s3-0\n"
" )";
@ -1039,28 +1051,31 @@ TEST_F(FormRegressionTest, ExprMethod14DeadPool) {
" jr ra\n"
" daddiu sp, sp, 64";
std::string type = "(function dead-pool type int process)";
// todo - why does one work but not the other??
std::string expected =
"(begin\n"
" (set! s4-0 (-> arg0 child))\n"
"(let\n"
" ((s4-0 (-> arg0 child)))\n"
" (when\n"
" (and (not s4-0) *debug-segment* (!= arg0 *debug-dead-pool*))\n"
" (set! s4-0 (get-process *debug-dead-pool* arg1 arg2))\n"
" (when\n"
" (if\n"
" s4-0\n"
" (set! t9-1 format)\n"
" (set! a0-2 0)\n"
" (set!\n"
" a1-2\n"
" \"WARNING: ~A ~A had to be allocated from the debug pool, because ~A was empty.~%\"\n"
" )\n"
" (set! a2-1 arg1)\n"
" (set! v1-6 s4-0)\n"
" (t9-1\n"
" a0-2\n"
" a1-2\n"
" a2-1\n"
" (if v1-6 (-> (the-as (pointer process-tree) v1-6) 0 self))\n"
" (-> arg0 name)\n"
" (let\n"
" ((t9-1 format)\n"
" (a0-2 0)\n"
" (a1-2\n"
" \"WARNING: ~A ~A had to be allocated from the debug pool, because ~A was empty.~%\"\n"
" )\n"
" (a2-1 arg1)\n"
" (v1-6 s4-0)\n"
" )\n"
" (t9-1\n"
" a0-2\n"
" a1-2\n"
" a2-1\n"
" (if v1-6 (-> (the-as (pointer process-tree) v1-6) 0 self))\n"
" (-> arg0 name)\n"
" )\n"
" )\n"
" )\n"
" )\n"
@ -1214,17 +1229,17 @@ TEST_F(FormRegressionTest, ExprMethod0DeadPoolHeap) {
" daddiu sp, sp, 64";
std::string type = "(function symbol type basic int int dead-pool-heap)";
std::string expected =
"(begin\n"
" (set!\n"
" v0-0\n"
" (object-new\n"
" arg0\n"
" arg1\n"
" (the-as\n"
" int\n"
" (+\n"
" (+ (-> arg1 size) (the-as uint (logand -16 (+ (* 12 arg3) 15))))\n"
" (the-as uint arg4)\n"
"(let\n"
" ((v0-0\n"
" (object-new\n"
" arg0\n"
" arg1\n"
" (the-as\n"
" int\n"
" (+\n"
" (+ (-> arg1 size) (the-as uint (logand -16 (+ (* 12 arg3) 15))))\n"
" (the-as uint arg4)\n"
" )\n"
" )\n"
" )\n"
" )\n"
@ -1237,13 +1252,17 @@ TEST_F(FormRegressionTest, ExprMethod0DeadPoolHeap) {
" (set! (-> v0-0 child) #f)\n"
" (set! (-> v0-0 self) v0-0)\n"
" (set! (-> v0-0 ppointer) (&-> v0-0 self))\n"
" (set! v1-4 arg3)\n"
" (while\n"
" (nonzero? v1-4)\n"
" (+! v1-4 -1)\n"
" (set! a0-4 (-> v0-0 process-list v1-4))\n"
" (set! (-> a0-4 process) *null-process*)\n"
" (set! (-> a0-4 next) (-> v0-0 process-list (+ v1-4 1)))\n"
" (let\n"
" ((v1-4 arg3))\n"
" (while\n"
" (nonzero? v1-4)\n"
" (+! v1-4 -1)\n"
" (let\n"
" ((a0-4 (-> v0-0 process-list v1-4)))\n"
" (set! (-> a0-4 process) *null-process*)\n"
" (set! (-> a0-4 next) (-> v0-0 process-list (+ v1-4 1)))\n"
" )\n"
" )\n"
" )\n"
" (set! (-> v0-0 dead-list next) (-> v0-0 process-list))\n"
" (set! (-> v0-0 alive-list process) #f)\n"
@ -1255,10 +1274,7 @@ TEST_F(FormRegressionTest, ExprMethod0DeadPoolHeap) {
" (set! (-> v0-0 first-shrink) #f)\n"
" (set!\n"
" (-> v0-0 heap base)\n"
" (logand\n"
" -16\n"
" (the-as int (&+ (+ (the-as int v0-0) 115) (* 12 arg3)))\n"
" )\n"
" (logand -16 (the-as int (&+ (+ (the-as int v0-0) 115) (* 12 arg3))))\n"
" )\n"
" (set! (-> v0-0 heap current) (-> v0-0 heap base))\n"
" (set! (-> v0-0 heap top) (&+ (-> v0-0 heap base) arg4))\n"
@ -1364,13 +1380,14 @@ TEST_F(FormRegressionTest, ExprMethod21DeadPoolHeap) {
" daddu sp, sp, r0";
std::string type = "(function dead-pool-heap dead-pool-heap-rec int)";
std::string expected =
"(cond\n"
" ((-> arg1 process)\n"
" (set!\n"
" v1-3\n"
" (&+\n"
" (&+ (-> arg1 process) (-> process size))\n"
" (-> arg1 process allocated-length)\n"
"(if\n"
" (-> arg1 process)\n"
" (let\n"
" ((v1-3\n"
" (&+\n"
" (&+ (-> arg1 process) (-> process size))\n"
" (-> arg1 process allocated-length)\n"
" )\n"
" )\n"
" )\n"
" (if\n"
@ -1379,15 +1396,10 @@ TEST_F(FormRegressionTest, ExprMethod21DeadPoolHeap) {
" (- (-> arg0 heap top) (the-as uint (&+ v1-3 4)))\n"
" )\n"
" )\n"
" (else\n"
" (if\n"
" (-> arg1 next)\n"
" (-\n"
" (-> arg1 next process)\n"
" (the-as uint (&+ (-> arg0 heap base) 4))\n"
" )\n"
" (- (-> arg0 heap top) (the-as uint (-> arg0 heap base)))\n"
" )\n"
" (if\n"
" (-> arg1 next)\n"
" (- (-> arg1 next process) (the-as uint (&+ (-> arg0 heap base) 4)))\n"
" (- (-> arg0 heap top) (the-as uint (-> arg0 heap base)))\n"
" )\n"
" )";
test_with_expr(func, type, expected, false, "", {},
@ -1524,44 +1536,48 @@ TEST_F(FormRegressionTest, ExprMethod3DeadPoolHeap) {
std::string type = "(function dead-pool-heap dead-pool-heap)";
std::string expected =
"(begin\n"
" (set! s5-0 (- (-> arg0 heap top) (the-as uint (-> arg0 heap base))))\n"
" (set!\n"
" v1-3\n"
" (if (-> arg0 alive-list prev) (gap-size arg0 (-> arg0 alive-list prev)) s5-0)\n"
" (let*\n"
" ((s5-0 (- (-> arg0 heap top) (the-as uint (-> arg0 heap base))))\n"
" (v1-3\n"
" (if\n"
" (-> arg0 alive-list prev)\n"
" (gap-size arg0 (-> arg0 alive-list prev))\n"
" s5-0\n"
" )\n"
" )\n"
" )\n"
" (format\n"
" #t\n"
" \"~Tprocess-list[0] @ #x~X ~D/~D bytes used~%\"\n"
" (-> arg0 process-list)\n"
" (- s5-0 v1-3)\n"
" s5-0\n"
" )\n"
" )\n"
" (format\n"
" #t\n"
" \"~Tprocess-list[0] @ #x~X ~D/~D bytes used~%\"\n"
" (-> arg0 process-list)\n"
" (- s5-0 v1-3)\n"
" s5-0\n"
" )\n"
" (set! s5-1 (-> arg0 alive-list))\n"
" (set! s4-0 0)\n"
" (while\n"
" s5-1\n"
" (if\n"
" (-> s5-1 process)\n"
" (format\n"
" #t\n"
" \"~T [~3D] #<dead-pool-heap-rec @ #x~X> ~A~%\"\n"
" s4-0\n"
" s5-1\n"
" (let\n"
" ((s5-1 (-> arg0 alive-list)) (s4-0 0))\n"
" (while\n"
" s5-1\n"
" (if\n"
" (-> s5-1 process)\n"
" (format\n"
" #t\n"
" \"~T [~3D] #<dead-pool-heap-rec @ #x~X> ~A~%\"\n"
" s4-0\n"
" s5-1\n"
" (-> s5-1 process)\n"
" )\n"
" )\n"
" )\n"
" (set! s3-0 (gap-size arg0 s5-1))\n"
" (if\n"
" (nonzero? s3-0)\n"
" (format\n"
" #t\n"
" \"~T gap: ~D bytes @ #x~X~%\"\n"
" s3-0\n"
" (gap-location arg0 s5-1)\n"
" (let\n"
" ((s3-0 (gap-size arg0 s5-1)))\n"
" (if\n"
" (nonzero? s3-0)\n"
" (format #t \"~T gap: ~D bytes @ #x~X~%\" s3-0 (gap-location arg0 s5-1))\n"
" )\n"
" )\n"
" (set! s5-1 (-> s5-1 next))\n"
" (+! s4-0 1)\n"
" )\n"
" (set! s5-1 (-> s5-1 next))\n"
" (+! s4-0 1)\n"
" )\n"
" arg0\n"
" )";
@ -1683,8 +1699,8 @@ TEST_F(FormRegressionTest, ExprMethod25DeadPoolHeap) {
" daddiu sp, sp, 16";
std::string type = "(function dead-pool-heap int)";
std::string expected =
"(begin\n"
" (set! v1-0 (-> arg0 heap top))\n"
"(let\n"
" ((v1-0 (-> arg0 heap top)))\n"
" (if\n"
" (-> arg0 alive-list prev)\n"
" (gap-size arg0 (-> arg0 alive-list prev))\n"
@ -1754,8 +1770,8 @@ TEST_F(FormRegressionTest, ExprMethod24DeadPoolHeap) {
" daddiu sp, sp, 64";
std::string type = "(function dead-pool-heap int dead-pool-heap-rec)";
std::string expected =
"(begin\n"
" (set! gp-0 (-> arg0 first-gap))\n"
"(let\n"
" ((gp-0 (-> arg0 first-gap)))\n"
" (while (and gp-0 (< (gap-size arg0 gp-0) arg1)) (set! gp-0 (-> gp-0 next)))\n"
" gp-0\n"
" )";
@ -1966,63 +1982,69 @@ TEST_F(FormRegressionTest, ExprMethod14DeadPoolHeap) {
" daddiu sp, sp, 112";
std::string type = "(function dead-pool-heap type int process)";
std::string expected =
"(begin\n"
" (set! s4-0 (-> arg0 dead-list next))\n"
" (set! s3-0 #f)\n"
" (set! s1-0 (find-gap-by-size arg0 (+ (-> process size) (the-as uint arg2))))\n"
" (cond\n"
" ((and s4-0 s1-0)\n"
" (set! (-> arg0 dead-list next) (-> s4-0 next))\n"
" (set! v1-5 (-> s1-0 next))\n"
" (set! (-> s1-0 next) s4-0)\n"
" (set! (-> s4-0 next) v1-5)\n"
" (when v1-5 (set! (-> v1-5 prev) s4-0) (set! v1-6 s4-0))\n"
" (set! (-> s4-0 prev) s1-0)\n"
" (when\n"
" (= s1-0 (-> arg0 alive-list prev))\n"
" (set! (-> arg0 alive-list prev) s4-0)\n"
" (set! v1-9 s4-0)\n"
" )\n"
" (set! a0-4 (gap-location arg0 s1-0))\n"
" (set!\n"
" s3-0\n"
" ((method-of-type process new)\n"
" (the-as symbol a0-4)\n"
" process\n"
" (quote process)\n"
" arg2\n"
"(let\n"
" ((s4-0 (-> arg0 dead-list next)) (s3-0 #f))\n"
" (let\n"
" ((s1-0 (find-gap-by-size arg0 (+ (-> process size) (the-as uint arg2)))))\n"
" (cond\n"
" ((and s4-0 s1-0)\n"
" (set! (-> arg0 dead-list next) (-> s4-0 next))\n"
" (let\n"
" ((v1-5 (-> s1-0 next)))\n"
" (set! (-> s1-0 next) s4-0)\n"
" (set! (-> s4-0 next) v1-5)\n"
" (when v1-5 (set! (-> v1-5 prev) s4-0) (let ((v1-6 s4-0))))\n"
" )\n"
" )\n"
" (set! (-> s4-0 process) s3-0)\n"
" (set! (-> s3-0 ppointer) (&-> s4-0 process))\n"
" (if\n"
" (= (-> arg0 first-gap) s1-0)\n"
" (set! (-> arg0 first-gap) (find-gap arg0 s4-0))\n"
" )\n"
" (when\n"
" (or\n"
" (not (-> arg0 first-shrink))\n"
" (< (the-as int s3-0) (the-as int (-> arg0 first-shrink process)))\n"
" (set! (-> s4-0 prev) s1-0)\n"
" (when\n"
" (= s1-0 (-> arg0 alive-list prev))\n"
" (set! (-> arg0 alive-list prev) s4-0)\n"
" (let ((v1-9 s4-0)))\n"
" )\n"
" (set! (-> arg0 first-shrink) s4-0)\n"
" (set! v1-22 s4-0)\n"
" )\n"
" (set! (-> s3-0 parent) (-> arg0 ppointer))\n"
" (set! (-> s3-0 pool) arg0)\n"
" (set! (-> arg0 child) (&-> s4-0 process))\n"
" )\n"
" (else\n"
" (when\n"
" (and *debug-segment* (!= arg0 *debug-dead-pool*))\n"
" (set! s3-0 (get-process *debug-dead-pool* arg1 arg2))\n"
" (if\n"
" (and s3-0 *vis-boot*)\n"
" (format\n"
" 0\n"
" \"WARNING: ~A ~A had to be allocated from the debug pool, because ~A was empty.~%\"\n"
" arg1\n"
" (let\n"
" ((a0-4 (gap-location arg0 s1-0)))\n"
" (set!\n"
" s3-0\n"
" (-> arg0 name)\n"
" ((method-of-type process new)\n"
" (the-as symbol a0-4)\n"
" process\n"
" (quote process)\n"
" arg2\n"
" )\n"
" )\n"
" )\n"
" (set! (-> s4-0 process) s3-0)\n"
" (set! (-> s3-0 ppointer) (&-> s4-0 process))\n"
" (if\n"
" (= (-> arg0 first-gap) s1-0)\n"
" (set! (-> arg0 first-gap) (find-gap arg0 s4-0))\n"
" )\n"
" (when\n"
" (or\n"
" (not (-> arg0 first-shrink))\n"
" (< (the-as int s3-0) (the-as int (-> arg0 first-shrink process)))\n"
" )\n"
" (set! (-> arg0 first-shrink) s4-0)\n"
" (let ((v1-22 s4-0)))\n"
" )\n"
" (set! (-> s3-0 parent) (-> arg0 ppointer))\n"
" (set! (-> s3-0 pool) arg0)\n"
" (set! (-> arg0 child) (&-> s4-0 process))\n"
" )\n"
" (else\n"
" (when\n"
" (and *debug-segment* (!= arg0 *debug-dead-pool*))\n"
" (set! s3-0 (get-process *debug-dead-pool* arg1 arg2))\n"
" (if\n"
" (and s3-0 *vis-boot*)\n"
" (format\n"
" 0\n"
" \"WARNING: ~A ~A had to be allocated from the debug pool, because ~A was "
"empty.~%\"\n"
" arg1\n"
" s3-0\n"
" (-> arg0 name)\n"
" )\n"
" )\n"
" )\n"
" )\n"
@ -2180,35 +2202,34 @@ TEST_F(FormRegressionTest, ExprMethod15DeadPoolHeap) {
" )\n"
" (change-parent arg1 arg0)\n"
" (set! (-> arg0 child) #f)\n"
" (set! s5-1 (-> arg1 ppointer))\n"
" (if\n"
" (or\n"
" (= (-> arg0 first-gap) s5-1)\n"
" (<\n"
" (the-as int (gap-location arg0 s5-1))\n"
" (the-as int (gap-location arg0 (-> arg0 first-gap)))\n"
" (let\n"
" ((s5-1 (-> arg1 ppointer)))\n"
" (if\n"
" (or\n"
" (= (-> arg0 first-gap) s5-1)\n"
" (<\n"
" (the-as int (gap-location arg0 s5-1))\n"
" (the-as int (gap-location arg0 (-> arg0 first-gap)))\n"
" )\n"
" )\n"
" (set! (-> arg0 first-gap) (-> s5-1 1))\n"
" )\n"
" (set! (-> arg0 first-gap) (-> s5-1 1))\n"
" )\n"
" (when\n"
" (= (-> arg0 first-shrink) s5-1)\n"
" (set! (-> arg0 first-shrink) (-> s5-1 1))\n"
" (when\n"
" (not (-> arg0 first-shrink process))\n"
" (set! (-> arg0 first-shrink) #f)\n"
" (= (-> arg0 first-shrink) s5-1)\n"
" (set! (-> arg0 first-shrink) (-> s5-1 1))\n"
" (when (not (-> arg0 first-shrink process)) (set! (-> arg0 first-shrink) #f))\n"
" )\n"
" (set! (-> s5-1 1 parent) (-> s5-1 2))\n"
" (if\n"
" (-> s5-1 2)\n"
" (set! (-> s5-1 2 mask) (-> s5-1 1))\n"
" (set! (-> arg0 alive-list prev) (-> s5-1 1))\n"
" )\n"
" (set! (-> s5-1 2) (-> arg0 dead-list next))\n"
" (set! (-> arg0 dead-list next) s5-1)\n"
" (set! (-> s5-1 0) *null-process*)\n"
" )\n"
" (set! (-> s5-1 1 parent) (-> s5-1 2))\n"
" (if\n"
" (-> s5-1 2)\n"
" (set! (-> s5-1 2 mask) (-> s5-1 1))\n"
" (set! (-> arg0 alive-list prev) (-> s5-1 1))\n"
" )\n"
" (set! (-> s5-1 2) (-> arg0 dead-list next))\n"
" (set! (-> arg0 dead-list next) s5-1)\n"
" (set! (-> s5-1 0) *null-process*)\n"
" (set! v0-4 0)\n"
" (let ((v0-4 0)))\n"
" )";
test_with_expr(func, type, expected, false, "",
{{"L297", "ERROR: process ~A does not belong to dead-pool-heap ~A.~%"}});
@ -2305,30 +2326,32 @@ TEST_F(FormRegressionTest, ExprMethod17DeadPoolHeap) {
std::string expected =
"(begin\n"
" (when\n"
" (if\n"
" arg1\n"
" (set! s5-0 (-> arg1 ppointer))\n"
" (when\n"
" (not\n"
" (or\n"
" (nonzero? (logand (-> arg1 mask) 512))\n"
" (and (not (-> arg1 next-state)) (not (-> arg1 state)))\n"
" (let\n"
" ((s5-0 (-> arg1 ppointer)))\n"
" (when\n"
" (not\n"
" (or\n"
" (nonzero? (logand (-> arg1 mask) 512))\n"
" (and (not (-> arg1 next-state)) (not (-> arg1 state)))\n"
" )\n"
" )\n"
" (set!\n"
" (-> arg1 allocated-length)\n"
" (- (-> arg1 heap-cur) (the-as uint (-> arg1 stack)))\n"
" )\n"
" (set! (-> arg1 heap-top) (&-> arg1 stack (-> arg1 allocated-length)))\n"
" (if\n"
" (< (the-as int arg1) (the-as int (gap-location arg0 (-> arg0 first-gap))))\n"
" (set! (-> arg0 first-gap) (find-gap arg0 s5-0))\n"
" )\n"
" (set! (-> arg1 mask) (logior (-> arg1 mask) 512))\n"
" )\n"
" (set!\n"
" (-> arg1 allocated-length)\n"
" (- (-> arg1 heap-cur) (the-as uint (-> arg1 stack)))\n"
" )\n"
" (set! (-> arg1 heap-top) (&-> arg1 stack (-> arg1 allocated-length)))\n"
" (if\n"
" (< (the-as int arg1) (the-as int (gap-location arg0 (-> arg0 first-gap))))\n"
" (set! (-> arg0 first-gap) (find-gap arg0 s5-0))\n"
" (= (-> arg0 first-shrink) s5-0)\n"
" (set! (-> arg0 first-shrink) (-> s5-0 2))\n"
" )\n"
" (set! (-> arg1 mask) (logior (-> arg1 mask) 512))\n"
" )\n"
" (if\n"
" (= (-> arg0 first-shrink) s5-0)\n"
" (set! (-> arg0 first-shrink) (-> s5-0 2))\n"
" )\n"
" )\n"
" arg0\n"
@ -2528,49 +2551,57 @@ TEST_F(FormRegressionTest, ExprMethod16DeadPoolHeap) {
std::string expected =
"(begin\n"
" (set! s4-0 (memory-free arg0))\n"
" (set! v1-2 (memory-total arg0))\n"
" (set! f0-2 (/ (the float s4-0) (the float v1-2)))"
" (cond\n"
" ((< f0-2 (l.f L346))\n"
" (set! arg1 1000)\n"
" (if\n"
" (and *debug-segment* (-> *kernel-context* low-memory-message))\n"
" (format *stdcon* \"~3LLow Actor Memory~%~0L\" a2-0)\n" // ~3L tricks it.
" )\n"
" (let*\n"
" ((s4-0 (memory-free arg0))\n"
" (v1-2 (memory-total arg0))\n"
" (f0-2 (/ (the float s4-0) (the float v1-2)))\n"
" )\n"
" (cond\n"
" ((< f0-2 (l.f L346))\n"
" (set! arg1 1000)\n"
" (if\n"
" (and *debug-segment* (-> *kernel-context* low-memory-message))\n"
" (format *stdcon* \"~3LLow Actor Memory~%~0L\" a2-0)\n"
" )\n"
" )\n"
" ((< f0-2 (l.f L347)) (set! arg1 (shl arg1 2)) (let ((v1-10 arg1))))\n"
" ((< f0-2 (l.f L348)) (set! arg1 (shl arg1 1)) (let ((v1-12 arg1))))\n"
" )\n"
" ((< f0-2 (l.f L347)) (set! arg1 (shl arg1 2)) (set! v1-10 arg1))\n"
" ((< f0-2 (l.f L348)) (set! arg1 (shl arg1 1)) (set! v1-12 arg1))\n"
" )\n"
" (set! (-> arg0 compact-count-targ) arg1)\n"
" (set! (-> arg0 compact-count) 0)\n"
" (while\n"
" (nonzero? arg1)\n"
" (+! arg1 -1)\n"
" (set! v1-13 (-> arg0 first-shrink))\n"
" (when\n"
" (not v1-13)\n"
" (set! v1-13 (-> arg0 alive-list next))\n"
" (set! (-> arg0 first-shrink) v1-13)\n"
" (set! a0-5 v1-13)\n"
" )\n"
" (if v1-13 (shrink-heap arg0 (-> v1-13 process)))\n"
" (set! s4-1 (-> arg0 first-gap))\n"
" (when\n"
" (-> s4-1 next)\n"
" (set! s3-0 (-> s4-1 next process))\n"
" (set! s2-0 (gap-size arg0 s4-1))\n"
" (let\n"
" ((v1-13 (-> arg0 first-shrink)))\n"
" (when\n"
" (nonzero? s2-0)\n"
" (when (< s2-0 0) (break!) (set! v1-20 0))\n"
" (shrink-heap arg0 s3-0)\n"
" (relocate s3-0 (- s2-0))\n"
" (set! (-> arg0 first-gap) (find-gap arg0 s4-1))\n"
" (set! (-> arg0 compact-count) (+ (-> arg0 compact-count) 1))\n"
" (not v1-13)\n"
" (set! v1-13 (-> arg0 alive-list next))\n"
" (set! (-> arg0 first-shrink) v1-13)\n"
" (let ((a0-5 v1-13)))\n"
" )\n"
" (if v1-13 (shrink-heap arg0 (-> v1-13 process)))\n"
" )\n"
" (let\n"
" ((s4-1 (-> arg0 first-gap)))\n"
" (if\n"
" (-> s4-1 next)\n"
" (let\n"
" ((s3-0 (-> s4-1 next process)) (s2-0 (gap-size arg0 s4-1)))\n"
" (when\n"
" (nonzero? s2-0)\n"
" (when (< s2-0 0) (break!) (let ((v1-20 0))))\n"
" (shrink-heap arg0 s3-0)\n"
" (relocate s3-0 (- s2-0))\n"
" (set! (-> arg0 first-gap) (find-gap arg0 s4-1))\n"
" (set! (-> arg0 compact-count) (+ (-> arg0 compact-count) 1))\n"
" )\n"
" )\n"
" )\n"
" )\n"
" )\n"
" (set! v0-8 0)\n"
" (let ((v0-8 0)))\n"
" )";
test_with_expr(func, type, expected, false, "", {{"L296", "~3LLow Actor Memory~%~0L"}});
}
@ -2723,50 +2754,56 @@ TEST_F(FormRegressionTest, ExprMethod18DeadPoolHeap) {
" (while\n"
" (nonzero? arg1)\n"
" (+! arg1 -1)\n"
" (set! s4-0 (-> arg0 alive-list next))\n"
" (when\n"
" s4-0\n"
" (if\n"
" (or\n"
" (= (-> arg0 first-gap) s4-0)\n"
" (<\n"
" (the-as int (gap-location arg0 s4-0))\n"
" (the-as int (gap-location arg0 (-> arg0 first-gap)))\n"
" (let\n"
" ((s4-0 (-> arg0 alive-list next)))\n"
" (when\n"
" s4-0\n"
" (if\n"
" (or\n"
" (= (-> arg0 first-gap) s4-0)\n"
" (<\n"
" (the-as int (gap-location arg0 s4-0))\n"
" (the-as int (gap-location arg0 (-> arg0 first-gap)))\n"
" )\n"
" )\n"
" (set! (-> arg0 first-gap) (-> s4-0 prev))\n"
" )\n"
" (when\n"
" (= (-> arg0 first-shrink) s4-0)\n"
" (set! (-> arg0 first-shrink) (-> s4-0 prev))\n"
" (when\n"
" (not (-> arg0 first-shrink process))\n"
" (set! (-> arg0 first-shrink) #f)\n"
" )\n"
" )\n"
" (set! (-> arg0 first-gap) (-> s4-0 prev))\n"
" )\n"
" (when\n"
" (= (-> arg0 first-shrink) s4-0)\n"
" (set! (-> arg0 first-shrink) (-> s4-0 prev))\n"
" (when\n"
" (not (-> arg0 first-shrink process))\n"
" (set! (-> arg0 first-shrink) #f)\n"
" (set! (-> s4-0 prev next) (-> s4-0 next))\n"
" (if\n"
" (-> s4-0 next)\n"
" (set! (-> s4-0 next prev) (-> s4-0 prev))\n"
" (set! (-> arg0 alive-list prev) (-> s4-0 prev))\n"
" )\n"
" )\n"
" (set! (-> s4-0 prev next) (-> s4-0 next))\n"
" (if\n"
" (-> s4-0 next)\n"
" (set! (-> s4-0 next prev) (-> s4-0 prev))\n"
" (set! (-> arg0 alive-list prev) (-> s4-0 prev))\n"
" )\n"
" (set! a1-3 (-> arg0 alive-list prev))\n"
" (set! v1-19 (-> a1-3 next))\n"
" (set! (-> a1-3 next) s4-0)\n"
" (set! (-> s4-0 next) v1-19)\n"
" (when v1-19 (set! (-> v1-19 prev) s4-0) (set! v1-20 s4-0))\n"
" (set! (-> s4-0 prev) a1-3)\n"
" (set! (-> arg0 alive-list prev) s4-0)\n"
" (set!\n"
" (-> s4-0 process)\n"
" (relocate\n"
" (-> s4-0 process)\n"
" (- (gap-location arg0 a1-3) (the-as uint (&-> (-> s4-0 process) type)))\n"
" (let\n"
" ((a1-3 (-> arg0 alive-list prev)))\n"
" (let\n"
" ((v1-19 (-> a1-3 next)))\n"
" (set! (-> a1-3 next) s4-0)\n"
" (set! (-> s4-0 next) v1-19)\n"
" (when v1-19 (set! (-> v1-19 prev) s4-0) (let ((v1-20 s4-0))))\n"
" )\n"
" (set! (-> s4-0 prev) a1-3)\n"
" (set! (-> arg0 alive-list prev) s4-0)\n"
" (set!\n"
" (-> s4-0 process)\n"
" (relocate\n"
" (-> s4-0 process)\n"
" (- (gap-location arg0 a1-3) (the-as uint (&-> (-> s4-0 process) type)))\n"
" )\n"
" )\n"
" )\n"
" )\n"
" )\n"
" )\n"
" (set! v0-4 0)\n"
" (let ((v0-4 0)))\n"
" )";
test_with_expr(func, type, expected);
}

View file

@ -94,11 +94,11 @@ TEST_F(FormRegressionTest, ExprSeek) {
" daddu sp, sp, r0";
std::string type = "(function float float float float)";
std::string expected =
"(begin\n"
" (set! f2-0 (- arg1 arg0))\n"
"(let\n"
" ((f2-0 (- arg1 arg0)))\n"
" (cond\n"
" ((>= arg2 (fabs f2-0)) arg1)\n"
" ((>= f2-0 0.000000) (+ arg0 arg2))\n"
" ((>= f2-0 0.0) (+ arg0 arg2))\n"
" (else (- arg0 arg2))\n"
" )\n"
" )";

View file

@ -40,7 +40,20 @@ const std::unordered_set<std::string> expected_skip_in_decompiler = {
const std::unordered_set<std::string> skip_in_compiling = {
// these functions are not implemented by the compiler in OpenGOAL, but are in GOAL.
"abs", "ash", "min", "max", "lognor", "(method 3 vec4s)", "(method 2 vec4s)"};
"abs", "ash", "min", "max", "lognor",
// these require 128-bit integers. We want these eventually, but disabling for now to focus
// on more important issues.
"(method 3 vec4s)", "(method 2 vec4s)",
// these should pass eventually
"(method 2 array)", "(method 3 array)", "valid?", "mem-copy!", "qmem-copy<-!", "qmem-copy->!",
"mem-or!", "breakpoint-range-set!", "print", "printl", "inspect"};
// The decompiler does not attempt to insert forward definitions, as this would be part of an
// unimplemented full-program type analysis pass. For now, we manually specify all functions
// that should have a forward definition here.
const std::string g_forward_type_defs =
"(define-extern name= (function basic basic symbol))\n"
"(define-extern fact (function int int))";
// default location for the data. It can be changed with a command line argument.
std::string g_iso_data_path = "";
@ -321,6 +334,8 @@ TEST_F(OfflineDecompilation, Reference) {
TEST_F(OfflineDecompilation, Compile) {
Compiler compiler;
compiler.run_front_end_on_string(g_forward_type_defs);
for (auto& file : g_object_files_to_check_against_reference) {
auto& obj_l = db->obj_files_by_name.at(file);
ASSERT_EQ(obj_l.size(), 1);