diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index 369b843632..f99b229a4f 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -959,29 +959,17 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta } assert(m_src->parent_element == this); + // hack for method stuff if (is_dead_set()) { stack.push_value_to_reg_dead(m_dst, m_src, true, m_var_info); return; } - m_src->update_children_from_stack(env, pool, stack, true); - - for (auto x : m_src->elts()) { - assert(x->parent_form == m_src); - } - + // if we are a reg-reg move that consumes the original, push it without popping from stack. + // it is the Stack's responsibility to untangle these later on. if (m_src->is_single_element()) { auto src_as_se = dynamic_cast(m_src->back()); if (src_as_se) { - if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY && - m_dst.reg().get_kind() == Reg::FPR && src_as_se->expr().get_arg(0).is_int() && - src_as_se->expr().get_arg(0).get_int() == 0) { - stack.push_value_to_reg(m_dst, - pool.alloc_single_element_form(nullptr, 0.0), - true, m_var_info); - return; - } - if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY && src_as_se->expr().get_arg(0).is_var()) { // this can happen late in the case of coloring moves which are also gpr -> fpr's @@ -1001,6 +989,37 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta } } + // we aren't a reg-reg move, so update our source + m_src->update_children_from_stack(env, pool, stack, true); + + for (auto x : m_src->elts()) { + assert(x->parent_form == m_src); + } + + if (m_src->is_single_element()) { + auto src_as_se = dynamic_cast(m_src->back()); + if (src_as_se) { + if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY && + m_dst.reg().get_kind() == Reg::FPR && src_as_se->expr().get_arg(0).is_int() && + src_as_se->expr().get_arg(0).get_int() == 0) { + // not sure this is the best place for this. + stack.push_value_to_reg(m_dst, + pool.alloc_single_element_form(nullptr, 0.0), + true, m_var_info); + return; + } + + // this might get skipped earlier because gpr->fpr gets wrapped in an operation that's + // stripped off by update_children_from_stack. + if (src_as_se->expr().kind() == SimpleExpression::Kind::IDENTITY && + src_as_se->expr().get_arg(0).is_var()) { + if (env.op_id_is_eliminated_coloring_move(src_as_se->expr().get_arg(0).var().idx())) { + m_var_info.is_eliminated_coloring_move = true; + } + } + } + } + stack.push_value_to_reg(m_dst, m_src, true, m_var_info); for (auto x : m_src->elts()) { assert(x->parent_form == m_src); diff --git a/decompiler/IR2/FormStack.cpp b/decompiler/IR2/FormStack.cpp index 5bcf42956c..1ed498f052 100644 --- a/decompiler/IR2/FormStack.cpp +++ b/decompiler/IR2/FormStack.cpp @@ -7,13 +7,25 @@ namespace decompiler { std::string FormStack::StackEntry::print(const Env& env) const { if (destination.has_value()) { assert(source && !elt); - return fmt::format("d: {} s: {} | {} <- {} f: {}", active, sequence_point, - destination.value().reg().to_charp(), source->to_string(env), - non_seq_source.has_value()); + if (active) { + return fmt::format("d: {} s: {} | {} <- {} f: {} w: {}", active, sequence_point, + destination.value().reg().to_charp(), source->to_string(env), + non_seq_source.has_value(), is_compactable); + } else { + return fmt::format(" d: {} s: {} | {} <- {} f: {} w: {}", active, sequence_point, + destination.value().reg().to_charp(), source->to_string(env), + non_seq_source.has_value(), is_compactable); + } + } else { assert(elt && !source); - return fmt::format("d: {} s: {} | {} f: {}", active, sequence_point, elt->to_string(env), - non_seq_source.has_value()); + if (active) { + return fmt::format("d: {} s: {} | {} f: {}", active, sequence_point, elt->to_string(env), + non_seq_source.has_value()); + } else { + return fmt::format(" d: {} s: {} | {} f: {}", active, sequence_point, elt->to_string(env), + non_seq_source.has_value()); + } } } @@ -66,6 +78,7 @@ void FormStack::push_non_seq_reg_to_reg(const Variable& dst, entry.non_seq_source = src; entry.source = src_as_form; entry.set_info = info; + entry.is_compactable = true; m_stack.push_back(entry); } @@ -109,6 +122,7 @@ Form* FormStack::pop_reg(Register reg, const Env& env, bool allow_side_effects, int begin_idx) { + assert(allow_side_effects); (void)env; // keep this for easy debugging. RegSet modified; size_t begin = m_stack.size(); @@ -279,8 +293,32 @@ std::vector FormStack::rewrite(FormPool& pool, const Env& env) { if (e.destination.has_value()) { // (set! x (+ x y)) -> (+! x y) - auto elt = - pool.alloc_element(*e.destination, e.source, e.sequence_point, e.set_info); + // we want to untangle coloring moves here + auto simplified_source = e.source; + auto src_as_var = dynamic_cast(e.source->try_as_single_element()); + if (src_as_var && src_as_var->expr().is_var() && e.is_compactable) { + bool keep_going = true; + auto var_to_get = src_as_var->expr().var(); + while (keep_going && !result.empty()) { + keep_going = false; + auto last_op_as_set = dynamic_cast(result.back()); + if (last_op_as_set && last_op_as_set->dst().reg() == var_to_get.reg()) { + result.pop_back(); + auto as_one = dynamic_cast( + last_op_as_set->src()->try_as_single_element()); + if (as_one && as_one->expr().is_identity() && as_one->expr().is_var() && + !result.empty()) { + keep_going = true; + var_to_get = as_one->expr().var(); + } + simplified_source = last_op_as_set->src(); + // result = last_op_as_set->src()->elts(); + } + } + } + + auto elt = pool.alloc_element(*e.destination, simplified_source, + e.sequence_point, e.set_info); e.source->parent_element = elt; auto final_elt = try_rewrites_in_place(elt, env, pool); @@ -296,15 +334,38 @@ void rewrite_to_get_var(std::vector& default_result, FormPool& pool, const Variable& var, const Env&) { - auto last_op_as_set = dynamic_cast(default_result.back()); - if (last_op_as_set && last_op_as_set->dst().reg() == var.reg()) { - default_result.pop_back(); - for (auto form : last_op_as_set->src()->elts()) { - form->parent_form = nullptr; // will get set later, this makes it obvious if I forget. - default_result.push_back(form); + bool keep_going = true; + Variable var_to_get = var; + + std::vector result; + + bool first = true; + while (keep_going) { + keep_going = false; + auto last_op_as_set = dynamic_cast(default_result.back()); + if (last_op_as_set && last_op_as_set->dst().reg() == var_to_get.reg() && + (first || last_op_as_set->info().is_compactable)) { + default_result.pop_back(); + auto as_one = + dynamic_cast(last_op_as_set->src()->try_as_single_element()); + if (as_one && as_one->expr().is_identity() && as_one->expr().is_var() && + !default_result.empty()) { + keep_going = true; + var_to_get = as_one->expr().var(); + } + + result = last_op_as_set->src()->elts(); } - } else { + first = false; + } + + if (result.empty()) { default_result.push_back(pool.alloc_element(SimpleAtom::make_var(var))); + } else { + for (auto x : result) { + x->parent_form = nullptr; + default_result.push_back(x); + } } } diff --git a/decompiler/IR2/FormStack.h b/decompiler/IR2/FormStack.h index c30590006f..eb82c85fdb 100644 --- a/decompiler/IR2/FormStack.h +++ b/decompiler/IR2/FormStack.h @@ -53,6 +53,7 @@ class FormStack { FormElement* elt = nullptr; bool sequence_point = false; TP_Type type; + bool is_compactable = false; SetVarInfo set_info; diff --git a/decompiler/IR2/IR2_common.h b/decompiler/IR2/IR2_common.h index 014a40da4b..250e97b224 100644 --- a/decompiler/IR2/IR2_common.h +++ b/decompiler/IR2/IR2_common.h @@ -166,6 +166,9 @@ struct SetVarInfo { bool is_dead_set = false; // is this a (set! var #f) where the value of #f isn't used? bool is_dead_false = false; + // it this a move that can be compacted? For now, this is always false, but we may want to + // allow more advanced compaction later. + bool is_compactable = false; }; } // namespace decompiler diff --git a/test/decompiler/test_FormExpressionBuild.cpp b/test/decompiler/test_FormExpressionBuild.cpp index 708b0f7e3f..730a6813d4 100644 --- a/test/decompiler/test_FormExpressionBuild.cpp +++ b/test/decompiler/test_FormExpressionBuild.cpp @@ -2503,4 +2503,35 @@ TEST_F(FormRegressionTest, StringLt) { " #f\n" " )"; test_with_expr(func, type, expected, false, ""); +} + +TEST_F(FormRegressionTest, ExprAssert) { + std::string func = + " sll r0, r0, 0\n" + " daddiu sp, sp, -16\n" + " sd ra, 0(sp)\n" + " sd fp, 8(sp)\n" + " or fp, t9, r0\n" + + " bne s7, a0, L12\n" + " or v1, s7, r0\n" + + " lw t9, format(s7)\n" + " daddiu a0, s7, #t\n" + " daddiu v1, fp, L17\n" + " or a2, a1, r0\n" + " or a1, v1, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + + "L12:\n" + " or v0, r0, r0\n" + " ld ra, 0(sp)\n" + " ld fp, 8(sp)\n" + " jr ra\n" + " daddiu sp, sp, 16"; + std::string type = "(function symbol string int)"; + + std::string expected = "(begin (if (not arg0) (format (quote #t) \"A ~A\" arg1)) 0)"; + test_with_expr(func, type, expected, false, "", {{"L17", "A ~A"}}); } \ No newline at end of file