From af1691a0bc5e555d30c89b7fad992a12fe818825 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Mon, 15 Feb 2021 11:36:59 -0500 Subject: [PATCH] [Decompiler] More progress on method stuff (#264) * more progress on method * fix method issues --- decompiler/IR2/Form.h | 6 + decompiler/IR2/FormExpressionAnalysis.cpp | 163 ++- decompiler/IR2/FormStack.cpp | 21 +- decompiler/IR2/FormStack.h | 6 +- decompiler/analysis/cfg_builder.cpp | 3 +- decompiler/util/DecompilerTypeSystem.cpp | 2 +- .../decompiler/test_FormBeforeExpressions.cpp | 1 + test/decompiler/test_gkernel_decomp.cpp | 1154 ++++++++++++++++- 8 files changed, 1281 insertions(+), 75 deletions(-) diff --git a/decompiler/IR2/Form.h b/decompiler/IR2/Form.h index 6c776016b2..6dcc2374b0 100644 --- a/decompiler/IR2/Form.h +++ b/decompiler/IR2/Form.h @@ -101,6 +101,12 @@ class SimpleExpressionElement : public FormElement { FormStack& stack, std::vector* result, bool allow_side_effects); + void update_from_stack_si_1(const Env& env, + FixedOperatorKind kind, + FormPool& pool, + FormStack& stack, + std::vector* result, + bool allow_side_effects); void update_from_stack_add_i(const Env& env, FormPool& pool, FormStack& stack, diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index a4d06e901e..62c60f5e31 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -257,6 +257,27 @@ void FormElement::update_from_stack(const Env& env, throw std::runtime_error(fmt::format("update_from_stack NYI for {}", to_string(env))); } +namespace { +Form* make_cast(Form* in, const TypeSpec& in_type, const TypeSpec& out_type, FormPool& pool) { + if (in_type == out_type) { + return in; + } + return pool.alloc_single_element_form(nullptr, out_type, in); +} + +std::vector make_cast(const std::vector& in, + const std::vector& in_types, + const TypeSpec& out_type, + FormPool& pool) { + std::vector out; + assert(in.size() == in_types.size()); + for (size_t i = 0; i < in_types.size(); i++) { + out.push_back(make_cast(in.at(i), in_types.at(i), out_type, pool)); + } + return out; +} +} // namespace + /*! * Update a LoadSourceElement from the stack. */ @@ -401,6 +422,18 @@ void SimpleExpressionElement::update_from_stack_float_1(const Env& env, } } +void SimpleExpressionElement::update_from_stack_si_1(const Env& env, + FixedOperatorKind kind, + FormPool& pool, + FormStack& stack, + std::vector* result, + bool allow_side_effects) { + auto in_type = env.get_types_before_op(m_my_idx).get(m_expr.get_arg(0).var().reg()).typespec(); + auto arg = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0); + result->push_back(pool.alloc_element( + GenericOperator::make_fixed(kind), make_cast(arg, in_type, TypeSpec("int"), pool))); +} + void SimpleExpressionElement::update_from_stack_add_i(const Env& env, FormPool& pool, FormStack& stack, @@ -688,7 +721,8 @@ void SimpleExpressionElement::update_from_stack_int_to_float(const Env& env, } result->push_back(pool.alloc_element(TypeSpec("float"), arg, true)); } else { - throw std::runtime_error("Used int to float on a " + type.print()); + throw std::runtime_error(fmt::format("Used int to float on a {} from {}: {}", type.print(), + var.to_form(env).print(), arg->to_string(env))); } } @@ -753,6 +787,10 @@ void SimpleExpressionElement::update_from_stack(const Env& env, update_from_stack_float_1(env, FixedOperatorKind::FABS, pool, stack, result, allow_side_effects); break; + case SimpleExpression::Kind::NEG: + update_from_stack_si_1(env, FixedOperatorKind::SUBTRACTION, pool, stack, result, + allow_side_effects); + break; case SimpleExpression::Kind::NEG_S: update_from_stack_float_1(env, FixedOperatorKind::SUBTRACTION, pool, stack, result, allow_side_effects); @@ -839,6 +877,12 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta assert(x->parent_form == m_src); } assert(m_src->parent_element == this); + + 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); @@ -980,6 +1024,7 @@ void FunctionCallElement::update_from_stack(const Env& env, FormElement* new_form = nullptr; if (is_method) { + // fmt::print("STACK:\n{}\n\n", stack.print(env)); auto matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::METHOD_OF_OBJECT), {Matcher::any(0), Matcher::any(1)}); auto mr = match(matcher, unstacked.at(0)); @@ -988,7 +1033,7 @@ void FunctionCallElement::update_from_stack(const Env& env, unstacked.at(0)->to_string(env)); } - auto unsafe = stack.unsafe_peek(Register(Reg::GPR, Reg::A0)); + auto unsafe = stack.unsafe_peek(Register(Reg::GPR, Reg::A0), env); if (!unsafe) { if (!stack.is_root()) { fmt::print("STACK:\n{}\n\n", stack.print(env)); @@ -996,21 +1041,59 @@ void FunctionCallElement::update_from_stack(const Env& env, } // failed to peek by reaching the end AND root stack, means we just take the function // argument. - assert(false); // want to test this before enabling. unsafe = mr.maps.forms.at(0); } + + bool resolved = false; if (unsafe) { if (!unsafe->try_as_single_element()) { throw std::runtime_error( fmt::format("Peek got something weird: {}\n", unsafe->to_string(env))); } - if (unsafe->try_as_single_element() != mr.maps.forms.at(0)->try_as_single_element()) { - throw std::runtime_error(fmt::format("Invalid method call. {} vs {}.", - unsafe->to_string(env), - mr.maps.forms.at(0)->to_string(env))); + + if (unsafe->try_as_single_element() == mr.maps.forms.at(0)->try_as_single_element()) { + resolved = true; } - } else { - throw std::runtime_error("Failed to peek for arg0"); + + if (!resolved) { + if (unsafe->try_as_single_element()->to_form(env) == + mr.maps.forms.at(0)->try_as_single_element()->to_form(env)) { + resolved = true; + lg::warn( + fmt::format("Rare method call (type 1). {} vs {}. Not an error, but check carefully", + unsafe->to_string(env), mr.maps.forms.at(0)->to_string(env))); + } + } + + if (!resolved) { + lg::warn( + fmt::format("Rare method call (type 2). {} vs {}. Not an error, but check carefully", + unsafe->to_string(env), mr.maps.forms.at(0)->to_string(env))); + + auto unsafe_as_se = dynamic_cast(unsafe->try_as_single_element()); + if (unsafe_as_se && unsafe_as_se->expr().is_identity() && + unsafe_as_se->expr().get_arg(0).is_var()) { + auto var = unsafe_as_se->expr().get_arg(0).var(); + auto unsafe_2 = stack.unsafe_peek(var.reg(), env); + if (unsafe_2) { + if (unsafe_2->try_as_single_element() == mr.maps.forms.at(0)->try_as_single_element()) { + resolved = true; + unsafe = unsafe_2; + } else { + if (unsafe_2->try_as_single_element()->to_form(env) == + mr.maps.forms.at(0)->try_as_single_element()->to_form(env)) { + lg::warn("Check even more carefully"); + resolved = true; + unsafe = unsafe_2; + } + } + } + } + } + } + + if (!resolved) { + throw std::runtime_error("Failed to resolve."); } arg_forms.insert(arg_forms.begin(), unsafe); @@ -1178,6 +1261,34 @@ void DerefElement::update_from_stack(const Env& env, } } + if (m_tokens.size() >= 3) { + auto& method_name = m_tokens.at(m_tokens.size() - 1); + auto& mbn = m_tokens.at(m_tokens.size() - 2); + auto& type = m_tokens.at(m_tokens.size() - 3); + if (method_name.kind() == DerefToken::Kind::FIELD_NAME && + mbn.kind() == DerefToken::Kind::FIELD_NAME && mbn.field_name() == "methods-by-name" && + type.kind() == DerefToken::Kind::FIELD_NAME && type.field_name() == "type") { + std::string name = method_name.field_name(); + m_tokens.pop_back(); + m_tokens.pop_back(); + m_tokens.pop_back(); + + if (m_tokens.empty()) { + auto method_op = pool.alloc_element( + GenericOperator::make_fixed(FixedOperatorKind::METHOD_OF_OBJECT), m_base, + pool.alloc_single_element_form(nullptr, name)); + result->push_back(method_op); + } else { + auto method_op = pool.alloc_element( + GenericOperator::make_fixed(FixedOperatorKind::METHOD_OF_OBJECT), + pool.alloc_single_form(nullptr, this), + pool.alloc_single_element_form(nullptr, name)); + result->push_back(method_op); + } + return; + } + } + // rewrite access to the method table to use method-of-object // (-> type methods-by-name ) // (method-of-object ) @@ -1219,8 +1330,10 @@ void UntilElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stac } void WhileElement::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) { - for (auto form : {condition, body}) { - FormStack temp_stack(false); + bool first = true; + for (auto form : {body, condition}) { + FormStack temp_stack(first && stack.is_root()); + first = false; for (auto& entry : form->elts()) { entry->push_to_stack(env, pool, temp_stack); } @@ -1475,27 +1588,6 @@ void ShortCircuitElement::update_from_stack(const Env& env, // ConditionElement /////////////////// -namespace { -Form* make_cast(Form* in, const TypeSpec& in_type, const TypeSpec& out_type, FormPool& pool) { - if (in_type == out_type) { - return in; - } - return pool.alloc_single_element_form(nullptr, out_type, in); -} - -std::vector make_cast(const std::vector& in, - const std::vector& in_types, - const TypeSpec& out_type, - FormPool& pool) { - std::vector out; - assert(in.size() == in_types.size()); - for (size_t i = 0; i < in_types.size(); i++) { - out.push_back(make_cast(in.at(i), in_types.at(i), out_type, pool)); - } - return out; -} -} // namespace - FormElement* ConditionElement::make_generic(const Env&, FormPool& pool, const std::vector& source_forms, @@ -1721,12 +1813,11 @@ void GenericElement::update_from_stack(const Env& env, FormStack& stack, std::vector* result, bool) { - for (auto it = m_elts.rbegin(); it != m_elts.rend(); it++) { - (*it)->update_children_from_stack(env, pool, stack, false); - } - + // TODO improve. if (m_head.m_kind == GenericOperator::Kind::FUNCTION_EXPR) { m_head.m_function->update_children_from_stack(env, pool, stack, false); + } else { + m_elts.back()->update_children_from_stack(env, pool, stack, false); } result->push_back(this); } diff --git a/decompiler/IR2/FormStack.cpp b/decompiler/IR2/FormStack.cpp index dab8a3d4fa..6033e10ac6 100644 --- a/decompiler/IR2/FormStack.cpp +++ b/decompiler/IR2/FormStack.cpp @@ -39,6 +39,20 @@ void FormStack::push_value_to_reg(Variable var, m_stack.push_back(entry); } +void FormStack::push_value_to_reg_dead(Variable var, + Form* value, + bool sequence_point, + const SetVarInfo& info) { + assert(value); + StackEntry entry; + entry.active = false; + entry.sequence_point = sequence_point; + entry.destination = var; + entry.source = value; + entry.set_info = info; + m_stack.push_back(entry); +} + void FormStack::push_non_seq_reg_to_reg(const Variable& dst, const Variable& src, Form* src_as_form, @@ -146,17 +160,22 @@ Form* FormStack::pop_reg(Register reg, } } } + } else { + if (entry.destination.has_value() && entry.destination->reg() == reg) { + return nullptr; + } } } // we didn't have it... return nullptr; } -Form* FormStack::unsafe_peek(Register reg) { +Form* FormStack::unsafe_peek(Register reg, const Env& env) { RegSet modified; for (size_t i = m_stack.size(); i-- > 0;) { auto& entry = m_stack.at(i); if (entry.active) { + fmt::print("PEEK ERROR {}:\n{}\n", reg.to_string(), print(env)); throw std::runtime_error("Failed to unsafe peek 1"); } diff --git a/decompiler/IR2/FormStack.h b/decompiler/IR2/FormStack.h index b00134f8fe..64256454a9 100644 --- a/decompiler/IR2/FormStack.h +++ b/decompiler/IR2/FormStack.h @@ -21,6 +21,10 @@ class FormStack { const Variable& src, Form* src_as_form, const SetVarInfo& info = {}); + void push_value_to_reg_dead(Variable var, + Form* value, + bool sequence_point, + const SetVarInfo& info = {}); void push_form_element(FormElement* elt, bool sequence_point); Form* pop_reg(const Variable& var, const RegSet& barrier, @@ -32,7 +36,7 @@ class FormStack { const Env& env, bool allow_side_effects, int begin_idx = -1); - Form* unsafe_peek(Register reg); + Form* unsafe_peek(Register reg, const Env& env); bool is_single_expression(); std::vector rewrite(FormPool& pool); std::string print(const Env& env); diff --git a/decompiler/analysis/cfg_builder.cpp b/decompiler/analysis/cfg_builder.cpp index db361b0911..e941032162 100644 --- a/decompiler/analysis/cfg_builder.cpp +++ b/decompiler/analysis/cfg_builder.cpp @@ -1232,7 +1232,8 @@ void convert_and_inline(FormPool& pool, Function& f, const BlockVtx* as_block, T // check for deadness if (op_as_set->is_dead_set()) { // we want to eliminate, but we should we fix up the register info. - add = false; + // now adding. + // add = false; auto consumed_expr = dynamic_cast(op_as_set->src()->try_as_single_element()); assert(consumed_expr); diff --git a/decompiler/util/DecompilerTypeSystem.cpp b/decompiler/util/DecompilerTypeSystem.cpp index 36bc3815f5..30a331bd4b 100644 --- a/decompiler/util/DecompilerTypeSystem.cpp +++ b/decompiler/util/DecompilerTypeSystem.cpp @@ -361,7 +361,7 @@ int DecompilerTypeSystem::get_format_arg_count(const std::string& str) const { for (size_t i = 0; i < str.length(); i++) { if (str.at(i) == '~') { i++; // also eat the next character. - if (i < str.length() && (str.at(i) == '%' || str.at(i) == 'T')) { + if (i < str.length() && (str.at(i) == '%' || str.at(i) == 'T' || str.at(i) == '0')) { // newline (~%) or tab (~T) don't take an argument. continue; } diff --git a/test/decompiler/test_FormBeforeExpressions.cpp b/test/decompiler/test_FormBeforeExpressions.cpp index 23b786c471..d3bf68a78b 100644 --- a/test/decompiler/test_FormBeforeExpressions.cpp +++ b/test/decompiler/test_FormBeforeExpressions.cpp @@ -734,6 +734,7 @@ TEST_F(FormRegressionTest, NestedAndOr) { " (set! (car s3-0) s1-0)\n" // set iter's car to cadr " (set! v1-4 (cdr s3-0))\n" // current cdr " (set! (car v1-4) s2-0)\n" // set cadr + " (set! v1-5 s2-0)\n" " )\n" " (set! s3-0 (cdr s3-0))\n" // increment! " )\n" diff --git a/test/decompiler/test_gkernel_decomp.cpp b/test/decompiler/test_gkernel_decomp.cpp index 1a4408b9dc..07cec3a028 100644 --- a/test/decompiler/test_gkernel_decomp.cpp +++ b/test/decompiler/test_gkernel_decomp.cpp @@ -1654,38 +1654,1122 @@ TEST_F(FormRegressionTest, ExprMethod20DeadPoolHeap) { std::string expected = "(- (-> arg0 heap top) (the-as uint (-> arg0 heap base)))"; test_with_expr(func, type, expected); } -// -// TEST_F(FormRegressionTest, ExprMethod25DeadPoolHeap) { -// std::string func = -// " sll r0, r0, 0\n" -// " daddiu sp, sp, -16\n" -// " sd ra, 0(sp)\n" -// -// " lwu v1, 64(a0)\n" -// " lwu a1, 80(a0)\n" -// " beq s7, a1, L191\n" -// " sll r0, r0, 0\n" -// -// " or v1, a0, r0\n" -// " lwu a1, -4(v1)\n" -// " lwu t9, 100(a1)\n" -// " lwu a1, 80(a0)\n" -// " or a0, v1, r0\n" - is eliminated falsely because of the virtual method thing. -// " jalr ra, t9\n" -// " sll v0, ra, 0\n" -// -// " beq r0, r0, L192\n" -// " sll r0, r0, 0\n" -// -// "L191:\n" -// " lwu a0, 60(a0)\n" -// " dsubu v0, v1, a0\n" -// -// "L192:\n" -// " ld ra, 0(sp)\n" -// " jr ra\n" -// " daddiu sp, sp, 16"; -// std::string type = "(function dead-pool-heap int)"; -// std::string expected = "blah"; -// test_with_expr(func, type, expected); -//} \ No newline at end of file + +TEST_F(FormRegressionTest, ExprMethod25DeadPoolHeap) { + std::string func = + " sll r0, r0, 0\n" + " daddiu sp, sp, -16\n" + " sd ra, 0(sp)\n" + + " lwu v1, 64(a0)\n" + " lwu a1, 80(a0)\n" + " beq s7, a1, L191\n" + " sll r0, r0, 0\n" + + " or v1, a0, r0\n" + " lwu a1, -4(v1)\n" + " lwu t9, 100(a1)\n" + " lwu a1, 80(a0)\n" + " or a0, v1, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + + " beq r0, r0, L192\n" + " sll r0, r0, 0\n" + + "L191:\n" + " lwu a0, 60(a0)\n" + " dsubu v0, v1, a0\n" + + "L192:\n" + " ld ra, 0(sp)\n" + " jr ra\n" + " daddiu sp, sp, 16"; + std::string type = "(function dead-pool-heap int)"; + std::string expected = + "(begin\n" + " (set! v1-0 (-> arg0 heap top))\n" + " (if\n" + " (-> arg0 alive-list prev)\n" + " (gap-size arg0 (-> arg0 alive-list prev))\n" + " (- v1-0 (the-as uint (-> arg0 heap base)))\n" + " )\n" + " )"; + test_with_expr(func, type, expected); +} + +TEST_F(FormRegressionTest, ExprMethod26DeadPoolHeap) { + std::string func = + " sll r0, r0, 0\n" + " lwu v0, 32(a0)\n" + " jr ra\n" + " daddu sp, sp, r0"; + std::string type = "(function dead-pool-heap uint)"; + std::string expected = "(-> arg0 compact-time)"; + test_with_expr(func, type, expected); +} + +TEST_F(FormRegressionTest, ExprMethod24DeadPoolHeap) { + std::string func = + " sll r0, r0, 0\n" + " daddiu sp, sp, -64\n" + " sd ra, 0(sp)\n" + " sq s4, 16(sp)\n" + " sq s5, 32(sp)\n" + " sq gp, 48(sp)\n" + + " or s5, a0, r0\n" + " or s4, a1, r0\n" + " lwu gp, 48(s5)\n" + " beq r0, r0, L187\n" + " sll r0, r0, 0\n" + + "L186:\n" + " lwu gp, 8(gp)\n" + + "L187:\n" + " beql s7, gp, L188\n" + + " or v1, gp, r0\n" + + " or a0, s5, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 100(v1)\n" + " or a1, gp, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + + " or v1, v0, r0\n" + " slt a0, v1, s4\n" + " daddiu v1, s7, 8\n" + " movz v1, s7, a0\n" + + "L188:\n" + " bne s7, v1, L186\n" + " sll r0, r0, 0\n" + + " or v1, s7, r0\n" + " or v0, gp, r0\n" + " ld ra, 0(sp)\n" + " lq gp, 48(sp)\n" + " lq s5, 32(sp)\n" + " lq s4, 16(sp)\n" + " jr ra\n" + " 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" + " (while (and gp-0 (< (gap-size arg0 gp-0) arg1)) (set! gp-0 (-> gp-0 next)))\n" + " gp-0\n" + " )"; + test_with_expr(func, type, expected); +} + +TEST_F(FormRegressionTest, ExprMethod14DeadPoolHeap) { + std::string func = + " sll r0, r0, 0\n" + " daddiu sp, sp, -112\n" + " sd ra, 0(sp)\n" + " sd fp, 8(sp)\n" + " or fp, t9, r0\n" + " sq s1, 16(sp)\n" + " sq s2, 32(sp)\n" + " sq s3, 48(sp)\n" + " sq s4, 64(sp)\n" + " sq s5, 80(sp)\n" + " sq gp, 96(sp)\n" + + " or gp, a0, r0\n" + " or s5, a1, r0\n" + " or s2, a2, r0\n" + " lwu s4, 96(gp)\n" + " or s3, s7, r0\n" + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 112(v1)\n" + " lw v1, process(s7)\n" + " lhu v1, 8(v1)\n" + " daddu a1, v1, s2\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + + " or s1, v0, r0\n" + " beql s7, s4, L173\n" + + " or v1, s4, r0\n" + + " or v1, s1, r0\n" + + "L173:\n" + " beq s7, v1, L179\n" + " sll r0, r0, 0\n" + + " lwu v1, 8(s4)\n" + " sw v1, 96(gp)\n" + " lwu v1, 8(s1)\n" + " sw s4, 8(s1)\n" + " sw v1, 8(s4)\n" + " beq s7, v1, L174\n" + " or a0, s7, r0\n" + + " sw s4, 4(v1)\n" + " or v1, s4, r0\n" + + "L174:\n" + " sw s1, 4(s4)\n" + " lwu v1, 80(gp)\n" + " bne s1, v1, L175\n" + " or v1, s7, r0\n" + + " sw s4, 80(gp)\n" + " or v1, s4, r0\n" + + "L175:\n" + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 104(v1)\n" + " or a1, s1, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or a0, v0, r0\n" + " lw v1, process(s7)\n" + " lwu t9, 16(v1)\n" + " lw a1, process(s7)\n" + " daddiu a2, s7, process\n" + " or a3, s2, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or s3, v0, r0\n" + " sw s3, 0(s4)\n" + " daddu v1, r0, s4\n" + " sw v1, 20(s3)\n" + " lwu v1, 48(gp)\n" + " bne v1, s1, L176\n" + " or v1, s7, r0\n" + "\n" + + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 108(v1)\n" + " or a1, s4, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or v1, v0, r0\n" + " sw v1, 48(gp)\n" + + "L176:\n" + " lwu v1, 52(gp)\n" + " beql s7, v1, L177\n" + + " daddiu v1, s7, 8\n" + + " lwu v1, 52(gp)\n" + " lwu v1, 0(v1)\n" + " slt a0, s3, v1\n" + " daddiu v1, s7, 8\n" + " movz v1, s7, a0\n" + + "L177:\n" + " beq s7, v1, L178\n" + " or v1, s7, r0\n" + + " sw s4, 52(gp)\n" + " or v1, s4, r0\n" + + "L178:\n" + " lwu v1, 20(gp)\n" + " sw v1, 8(s3)\n" + " sw gp, 28(s3)\n" + " daddu v1, r0, s4\n" + " sw v1, 16(gp)\n" + " beq r0, r0, L182\n" + " sll r0, r0, 0\n" + + "L179:\n" + " lw v1, *debug-segment*(s7)\n" + " beql s7, v1, L180\n" + + " or v1, v1, r0\n" + + " lw v1, *debug-dead-pool*(s7)\n" + " dsubu a0, gp, v1\n" + " daddiu v1, s7, 8\n" + " movz v1, s7, a0\n" + + "L180:\n" + " beq s7, v1, L182\n" + " or v1, s7, r0\n" + "\n" + + " lw a0, *debug-dead-pool*(s7)\n" + " lwu v1, -4(a0)\n" + " lwu t9, 72(v1)\n" + " or a1, s5, r0\n" + " or a2, s2, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or s3, v0, r0\n" + " beql s7, s3, L181\n" + + " or v1, s3, r0\n" + "\n" + + " lw v1, *vis-boot*(s7)\n" + + "L181:\n" + " beq s7, v1, L182\n" + " or v1, s7, r0\n" + + " lw t9, format(s7)\n" + " addiu a0, r0, 0\n" + " daddiu a1, fp, L315\n" + " or a2, s5, r0\n" + " or a3, s3, r0\n" + " lwu t0, 0(gp)\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or v1, v0, r0\n" + + "L182:\n" + " beq s7, s3, L183\n" + " sll r0, r0, 0\n" + + " sw s5, -4(s3)\n" + " beq r0, r0, L184\n" + " sll r0, r0, 0\n" + + "L183:\n" + " lw t9, format(s7)\n" + " addiu a0, r0, 0\n" + " daddiu a1, fp, L314\n" + " or a2, s5, r0\n" + " or a3, s3, r0\n" + " lwu t0, 0(gp)\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or v1, v0, r0\n" + + "L184:\n" + " or v0, s3, r0\n" + " ld ra, 0(sp)\n" + " ld fp, 8(sp)\n" + " lq gp, 96(sp)\n" + " lq s5, 80(sp)\n" + " lq s4, 64(sp)\n" + " lq s3, 48(sp)\n" + " lq s2, 32(sp)\n" + " lq s1, 16(sp)\n" + " jr ra\n" + " 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 (quote #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" + " )\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" + " (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" + " s3-0\n" + " (-> arg0 name)\n" + " )\n" + " )\n" + " )\n" + " )\n" + " )\n" + " (if\n" + " s3-0\n" + " (set! (-> s3-0 type) arg1)\n" + " (format\n" + " 0\n" + " \"WARNING: ~A ~A could not be allocated, because ~A was empty.~%\"\n" + " arg1\n" + " s3-0\n" + " (-> arg0 name)\n" + " )\n" + " )\n" + " s3-0\n" + " )"; + test_with_expr( + func, type, expected, false, "", + {{"L315", "WARNING: ~A ~A had to be allocated from the debug pool, because ~A was empty.~%"}, + {"L314", "WARNING: ~A ~A could not be allocated, because ~A was empty.~%"}}); +} + +TEST_F(FormRegressionTest, ExprMethod15DeadPoolHeap) { + std::string func = + " sll r0, r0, 0\n" + " daddiu sp, sp, -64\n" + " sd ra, 0(sp)\n" + " sd fp, 8(sp)\n" + " or fp, t9, r0\n" + " sq s4, 16(sp)\n" + " sq s5, 32(sp)\n" + " sq gp, 48(sp)\n" + + " or gp, a0, r0\n" + " or s5, a1, r0\n" + " lwu v1, 28(s5)\n" + " beq gp, v1, L166\n" + " or v1, s7, r0\n" + + " lw t9, format(s7)\n" + " addiu a0, r0, 0\n" + " daddiu a1, fp, L297\n" + " or a2, s5, r0\n" + " or a3, gp, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or v1, v0, r0\n" + + "L166:\n" + " lw t9, change-parent(s7)\n" + " or a0, s5, r0\n" + " or a1, gp, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " sw s7, 16(gp)\n" + " lwu s5, 20(s5)\n" + " lwu v1, 48(gp)\n" + " dsubu v1, v1, s5\n" + " daddiu a0, s7, 8\n" + " movn a0, s7, v1\n" + " bnel s7, a0, L167\n" + + " or v1, a0, r0\n" + + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 104(v1)\n" + " or a1, s5, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or s4, v0, r0\n" + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 104(v1)\n" + " lwu a1, 48(gp)\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or v1, v0, r0\n" + " slt a0, s4, v1\n" + " daddiu v1, s7, 8\n" + " movz v1, s7, a0\n" + + "L167:\n" + " beq s7, v1, L168\n" + " or v1, s7, r0\n" + + " lwu v1, 4(s5)\n" + " sw v1, 48(gp)\n" + + "L168:\n" + " lwu v1, 52(gp)\n" + " bne v1, s5, L169\n" + " or v1, s7, r0\n" + + " lwu v1, 4(s5)\n" + " sw v1, 52(gp)\n" + " lwu v1, 52(gp)\n" + " lwu v1, 0(v1)\n" + " bne s7, v1, L169\n" + " or v1, s7, r0\n" + + " sw s7, 52(gp)\n" + " or v1, s7, r0\n" + + "L169:\n" + " lwu v1, 8(s5)\n" + " lwu a0, 4(s5)\n" + " sw v1, 8(a0)\n" + " lwu v1, 8(s5)\n" + " beq s7, v1, L170\n" + " sll r0, r0, 0\n" + + " lwu a0, 4(s5)\n" + " lwu v1, 8(s5)\n" + " sw a0, 4(v1)\n" + " beq r0, r0, L171\n" + " sll r0, r0, 0\n" + + "L170:\n" + " lwu v1, 4(s5)\n" + " sw v1, 80(gp)\n" + + "L171:\n" + " lwu v1, 96(gp)\n" + " sw v1, 8(s5)\n" + " sw s5, 96(gp)\n" + " lw v1, *null-process*(s7)\n" + " sw v1, 0(s5)\n" + " or v0, r0, r0\n" + " ld ra, 0(sp)\n" + " ld fp, 8(sp)\n" + " lq gp, 48(sp)\n" + " lq s5, 32(sp)\n" + " lq s4, 16(sp)\n" + " jr ra\n" + " daddiu sp, sp, 64"; + std::string type = "(function dead-pool-heap process none)"; + // NOTE: has wrong types for s5-1, but it's okay + std::string expected = + "(begin\n" + " (if\n" + " (!= arg0 (-> arg1 pool))\n" + " (format\n" + " 0\n" + " \"ERROR: process ~A does not belong to dead-pool-heap ~A.~%\"\n" + " arg1\n" + " arg0\n" + " )\n" + " )\n" + " (change-parent arg1 arg0)\n" + " (set! (-> arg0 child) (quote #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" + " )\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) (quote #f))\n" + " )\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" + " )"; + test_with_expr(func, type, expected, false, "", + {{"L297", "ERROR: process ~A does not belong to dead-pool-heap ~A.~%"}}); +} + +TEST_F(FormRegressionTest, ExprMethod17DeadPoolHeap) { + std::string func = + " sll r0, r0, 0\n" + " daddiu sp, sp, -64\n" + " sd ra, 0(sp)\n" + " sq s4, 16(sp)\n" + " sq s5, 32(sp)\n" + " sq gp, 48(sp)\n" + + " or gp, a0, r0\n" + " or s4, a1, r0\n" + " beq s7, s4, L164\n" + " or v1, s7, r0\n" + + " lwu s5, 20(s4)\n" + " lwu v1, 4(s4)\n" + " andi v1, v1, 512\n" + " bnel v1, r0, L161\n" + + " daddiu v1, s7, 8\n" + + " lwu v1, 72(s4)\n" + " bnel s7, v1, L161\n" + + " or v1, s7, r0\n" + + " lwu v1, 52(s4)\n" + " beq s7, v1, L161\n" + " daddiu v1, s7, 8\n" + + " or v1, s7, r0\n" + + "L161:\n" + " bne s7, v1, L163\n" + " or v1, s7, r0\n" + + " lwu v1, 84(s4)\n" + " daddiu a0, s4, 108\n" + " dsubu v1, v1, a0\n" + " sw v1, 68(s4)\n" + " lw v1, 68(s4)\n" + " daddiu v1, v1, 108\n" + " daddu v1, v1, s4\n" + " sw v1, 80(s4)\n" + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 104(v1)\n" + " lwu a1, 48(gp)\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or v1, v0, r0\n" + " slt v1, s4, v1\n" + " beq v1, r0, L162\n" + " or v1, s7, r0\n" + + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 108(v1)\n" + " or a1, s5, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or v1, v0, r0\n" + " sw v1, 48(gp)\n" + + "L162:\n" + " lwu v1, 4(s4)\n" + " ori v1, v1, 512\n" + " sw v1, 4(s4)\n" + + "L163:\n" + " lwu v1, 52(gp)\n" + " bne v1, s5, L164\n" + " or v1, s7, r0\n" + + " lwu v1, 8(s5)\n" + " sw v1, 52(gp)\n" + + "L164:\n" + " or v0, gp, r0\n" + " ld ra, 0(sp)\n" + " lq gp, 48(sp)\n" + " lq s5, 32(sp)\n" + " lq s4, 16(sp)\n" + " jr ra\n" + " daddiu sp, sp, 64"; + std::string type = "(function dead-pool-heap process dead-pool-heap)"; + + std::string expected = + "(begin\n" + " (when\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" + " )\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" + " (if\n" + " (= (-> arg0 first-shrink) s5-0)\n" + " (set! (-> arg0 first-shrink) (-> s5-0 2))\n" + " )\n" + " )\n" + " arg0\n" + " )"; + test_with_expr(func, type, expected); +} + +TEST_F(FormRegressionTest, ExprMethod16DeadPoolHeap) { + std::string func = + " sll r0, r0, 0\n" + "L346:\n" + "L347:\n" + "L348:\n" + " daddiu sp, sp, -96\n" + " sd ra, 0(sp)\n" + " sd fp, 8(sp)\n" + " or fp, t9, r0\n" + " sq s2, 16(sp)\n" + " sq s3, 32(sp)\n" + " sq s4, 48(sp)\n" + " sq s5, 64(sp)\n" + " sq gp, 80(sp)\n" + + " or gp, a0, r0\n" + " or s5, a1, r0\n" + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 116(v1)\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or s4, v0, r0\n" + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 96(v1)\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or v1, v0, r0\n" + " mtc1 f0, s4\n" + " cvt.s.w f0, f0\n" + " mtc1 f1, v1\n" + " cvt.s.w f1, f1\n" + " div.s f0, f0, f1\n" + " lwc1 f1, L346(fp)\n" + " c.lt.s f0, f1\n" + " bc1f L152\n" + " or v1, s7, r0\n" + + " addiu s5, r0, 1000\n" + " lw v1, *debug-segment*(s7)\n" + " beql s7, v1, L150\n" + + " or v1, v1, r0\n" + + " lw v1, *kernel-context*(s7)\n" + " lwu v1, 40(v1)\n" + + "L150:\n" + " beq s7, v1, L151\n" + " or v1, s7, r0\n" + + " lw t9, format(s7)\n" + " lw a0, *stdcon*(s7)\n" + " daddiu a1, fp, L296\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or v1, v0, r0\n" + + "L151:\n" + " beq r0, r0, L154\n" + " sll r0, r0, 0\n" + + "L152:\n" + " lwc1 f1, L347(fp)\n" + " c.lt.s f0, f1\n" + " bc1f L153\n" + " or v1, s7, r0\n" + + " dsll s5, s5, 2\n" + " or v1, s5, r0\n" + " beq r0, r0, L154\n" + " sll r0, r0, 0\n" + + "L153:\n" + " lwc1 f1, L348(fp)\n" + " c.lt.s f0, f1\n" + " bc1f L154\n" + " or v1, s7, r0\n" + + " dsll s5, s5, 1\n" + " or v1, s5, r0\n" + + "L154:\n" + " sw s5, 36(gp)\n" + " sw r0, 40(gp)\n" + " beq r0, r0, L159\n" + " sll r0, r0, 0\n" + + "L155:\n" + " daddiu s5, s5, -1\n" + " lwu v1, 52(gp)\n" + " bne s7, v1, L156\n" + " or a0, s7, r0\n" + + " lwu v1, 84(gp)\n" + " sw v1, 52(gp)\n" + " or a0, v1, r0\n" + + "L156:\n" + " beq s7, v1, L157\n" + " or a0, s7, r0\n" + + " or a0, gp, r0\n" + " lwu a1, -4(a0)\n" + " lwu t9, 84(a1)\n" + " lwu a1, 0(v1)\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or a0, v0, r0\n" + + "L157:\n" + " lwu s4, 48(gp)\n" + " lwu v1, 8(s4)\n" + " beq s7, v1, L159\n" + " or v1, s7, r0\n" + + " lwu v1, 8(s4)\n" + " lwu s3, 0(v1)\n" + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 100(v1)\n" + " or a1, s4, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or s2, v0, r0\n" + " beq s2, r0, L159\n" + " or v1, s7, r0\n" + + " slt v1, s2, r0\n" + " beq v1, r0, L158\n" + " or v1, s7, r0\n" + + " lw r0, 2(r0)\n" + " addiu v1, r0, 0\n" + + "L158:\n" + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 84(v1)\n" + " or a1, s3, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or v1, v0, r0\n" + " lwu v1, -4(s3)\n" + " lwu t9, 44(v1)\n" + " dsubu a1, r0, s2\n" + " or a0, s3, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or v1, v0, r0\n" + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 108(v1)\n" + " or a1, s4, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or v1, v0, r0\n" + " sw v1, 48(gp)\n" + " lwu v1, 40(gp)\n" + " daddiu v1, v1, 1\n" + " sw v1, 40(gp)\n" + + "L159:\n" + " bne s5, r0, L155\n" + " sll r0, r0, 0\n" + + " or v1, s7, r0\n" + " or v1, s7, r0\n" + " or v0, r0, r0\n" + " ld ra, 0(sp)\n" + " ld fp, 8(sp)\n" + " lq gp, 80(sp)\n" + " lq s5, 64(sp)\n" + " lq s4, 48(sp)\n" + " lq s3, 32(sp)\n" + " lq s2, 16(sp)\n" + " jr ra\n" + " daddiu sp, sp, 96"; + std::string type = "(function dead-pool-heap int none)"; + + std::string expected = + "(begin\n" + " (set! s4-0 (memory-free arg0))\n" + " (set! f0-2 (/ (the float s4-0) (the float (memory-total arg0))))\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" // ~3L tricks it. + " )\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" + " (set! arg1 (+ 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" + " (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" + " )\n" + " )\n" + " )\n" + " (set! v0-8 0)\n" + " )"; + test_with_expr(func, type, expected, false, "", {{"L296", "~3LLow Actor Memory~%~0L"}}); +} + +// nested method calls +TEST_F(FormRegressionTest, ExprMethod18DeadPoolHeap) { + std::string func = + " sll r0, r0, 0\n" + "L140:\n" + " daddiu sp, sp, -96\n" + " sd ra, 0(sp)\n" + " sq s2, 16(sp)\n" + " sq s3, 32(sp)\n" + " sq s4, 48(sp)\n" + " sq s5, 64(sp)\n" + " sq gp, 80(sp)\n" + + " or gp, a0, r0\n" + " or s5, a1, r0\n" + " beq r0, r0, L148\n" + " sll r0, r0, 0\n" + + "L141:\n" + " daddiu s5, s5, -1\n" + " lwu s4, 84(gp)\n" + " beq s7, s4, L148\n" + " or v1, s7, r0\n" + + " lwu v1, 48(gp)\n" + " dsubu v1, v1, s4\n" + " daddiu a0, s7, 8\n" + " movn a0, s7, v1\n" + " bnel s7, a0, L142\n" + + " or v1, a0, r0\n" + + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 104(v1)\n" + " or a1, s4, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + + " or s3, v0, r0\n" + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 104(v1)\n" + " lwu a1, 48(gp)\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + + " or v1, v0, r0\n" + " slt a0, s3, v1\n" + " daddiu v1, s7, 8\n" + " movz v1, s7, a0\n" + + "L142:\n" + " beq s7, v1, L143\n" + " or v1, s7, r0\n" + + " lwu v1, 4(s4)\n" + " sw v1, 48(gp)\n" + + "L143:\n" + " lwu v1, 52(gp)\n" + " bne v1, s4, L144\n" + " or v1, s7, r0\n" + + " lwu v1, 4(s4)\n" + " sw v1, 52(gp)\n" + " lwu v1, 52(gp)\n" + " lwu v1, 0(v1)\n" + " bne s7, v1, L144\n" + " or v1, s7, r0\n" + + " sw s7, 52(gp)\n" + " or v1, s7, r0\n" + + "L144:\n" + " lwu v1, 8(s4)\n" + " lwu a0, 4(s4)\n" + " sw v1, 8(a0)\n" + " lwu v1, 8(s4)\n" + " beq s7, v1, L145\n" + " sll r0, r0, 0\n" + + " lwu a0, 4(s4)\n" + " lwu v1, 8(s4)\n" + " sw a0, 4(v1)\n" + " beq r0, r0, L146\n" + " sll r0, r0, 0\n" + + "L145:\n" + " lwu v1, 4(s4)\n" + " sw v1, 80(gp)\n" + + "L146:\n" + " lwu a1, 80(gp)\n" + " lwu v1, 8(a1)\n" + " sw s4, 8(a1)\n" + " sw v1, 8(s4)\n" + " beq s7, v1, L147\n" + " or a0, s7, r0\n" + + " sw s4, 4(v1)\n" + " or v1, s4, r0\n" + + "L147:\n" + " sw a1, 4(s4)\n" + " sw s4, 80(gp)\n" + " lwu s3, 0(s4)\n" + " lwu v1, -4(s3)\n" + " lwu s2, 44(v1)\n" + " or a0, gp, r0\n" + " lwu v1, -4(a0)\n" + " lwu t9, 104(v1)\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or v1, v0, r0\n" + " lwu a0, 0(s4)\n" + " daddiu a0, a0, -4\n" + " dsubu a1, v1, a0\n" + " or t9, s2, r0\n" + " or a0, s3, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + "\n" + " or v1, v0, r0\n" + " sw v1, 0(s4)\n" + + "L148:\n" + " bne s5, r0, L141\n" + " sll r0, r0, 0\n" + + " or v1, s7, r0\n" + " or v1, s7, r0\n" + " or v0, r0, r0\n" + " ld ra, 0(sp)\n" + " lq gp, 80(sp)\n" + " lq s5, 64(sp)\n" + " lq s4, 48(sp)\n" + " lq s3, 32(sp)\n" + " lq s2, 16(sp)\n" + " jr ra\n" + " daddiu sp, sp, 96"; + std::string type = "(function dead-pool-heap int none)"; + std::string expected = + "(begin\n" + " (while\n" + " (nonzero? arg1)\n" + " (set! arg1 (+ 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" + " )\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) (quote #f))\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" + " )\n" + " )\n" + " )\n" + " )\n" + " (set! v0-4 0)\n" + " )"; + test_with_expr(func, type, expected); +} \ No newline at end of file