From 06814108a4e221178ed935e4edd52ce4926754d4 Mon Sep 17 00:00:00 2001 From: water111 Date: Mon, 13 Apr 2026 15:26:20 -0400 Subject: [PATCH] add stack var support --- decompiler/IR2/AtomicOpForm.cpp | 6 +- decompiler/IR2/Env.cpp | 75 +++++++++++++ decompiler/IR2/Env.h | 20 ++++ decompiler/IR2/Form.cpp | 22 +++- decompiler/IR2/Form.h | 6 +- decompiler/IR2/FormExpressionAnalysis.cpp | 130 +++++++++++++++++----- decompiler/IR2/FormStack.cpp | 4 +- decompiler/analysis/insert_lets.cpp | 24 ++++ 8 files changed, 252 insertions(+), 35 deletions(-) diff --git a/decompiler/IR2/AtomicOpForm.cpp b/decompiler/IR2/AtomicOpForm.cpp index e4c860d639..837b0b135d 100644 --- a/decompiler/IR2/AtomicOpForm.cpp +++ b/decompiler/IR2/AtomicOpForm.cpp @@ -843,9 +843,13 @@ FormElement* StackSpillLoadOp::get_as_form(FormPool& pool, const Env& env) const if (kv != env.stack_slot_entries.end()) { type = kv->second.typespec; } + std::optional read_type; + if (env.has_type_analysis()) { + read_type = env.get_types_before_op(m_my_idx).get_slot(m_offset).typespec(); + } return pool.alloc_element(m_dst, pool.alloc_single_element_form( - nullptr, m_size, m_offset, m_is_signed), + nullptr, m_size, m_offset, m_is_signed, read_type), true, type); } diff --git a/decompiler/IR2/Env.cpp b/decompiler/IR2/Env.cpp index 75fe361c05..a355113b42 100644 --- a/decompiler/IR2/Env.cpp +++ b/decompiler/IR2/Env.cpp @@ -162,6 +162,10 @@ std::string Env::get_variable_name(const RegisterAccess& access) const { } std::string Env::get_variable_name_name_only(const RegisterAccess& access) const { + if (is_stack_slot_access(access)) { + return get_spill_slot_var_name(get_stack_slot_offset_from_access(access)); + } + if (access.reg().get_kind() == Reg::FPR || access.reg().get_kind() == Reg::GPR) { auto& var_info = m_var_names.lookup(access.reg(), access.idx(), access.mode()); return var_info.name(); @@ -172,6 +176,14 @@ std::string Env::get_variable_name_name_only(const RegisterAccess& access) const } VariableWithCast Env::get_variable_and_cast(const RegisterAccess& access) const { + if (is_stack_slot_access(access)) { + VariableWithCast result; + auto original_name = get_spill_slot_var_name(get_stack_slot_offset_from_access(access)); + auto remapped = m_var_remap.find(original_name); + result.name = remapped != m_var_remap.end() ? remapped->second : original_name; + return result; + } + if (access.reg().get_kind() == Reg::FPR || access.reg().get_kind() == Reg::GPR) { auto& var_info = m_var_names.lookup(access.reg(), access.idx(), access.mode()); // this is a bit of a confusing process. The first step is to grab the auto-generated name: @@ -272,6 +284,10 @@ goos::Object Env::get_variable_name_with_cast(Register reg, int atomic_idx, Acce } std::optional Env::get_user_cast_for_access(const RegisterAccess& access) const { + if (is_stack_slot_access(access)) { + return {}; + } + if (access.reg().get_kind() == Reg::FPR || access.reg().get_kind() == Reg::GPR) { auto& var_info = m_var_names.lookup(access.reg(), access.idx(), access.mode()); std::string original_name = var_info.name(); @@ -310,6 +326,22 @@ std::optional Env::get_user_cast_for_access(const RegisterAccess& acce * of the variable. */ TypeSpec Env::get_variable_type(const RegisterAccess& access, bool using_user_var_types) const { + if (is_stack_slot_access(access)) { + auto offset = get_stack_slot_offset_from_access(access); + auto it = stack_slot_entries.find(offset); + if (it != stack_slot_entries.end()) { + auto type_of_var = it->second.typespec; + if (using_user_var_types) { + auto retype_kv = m_var_retype.find(it->second.name()); + if (retype_kv != m_var_retype.end()) { + type_of_var = retype_kv->second; + } + } + return type_of_var; + } + return TypeSpec("object"); + } + if (access.reg().get_kind() == Reg::FPR || access.reg().get_kind() == Reg::GPR) { auto& var_info = m_var_names.lookup(access.reg(), access.idx(), access.mode()); std::string original_name = var_info.name(); @@ -328,6 +360,37 @@ TypeSpec Env::get_variable_type(const RegisterAccess& access, bool using_user_va } } +TP_Type Env::get_variable_tp_type(const RegisterAccess& access, bool using_user_var_types) const { + if (is_stack_slot_access(access)) { + auto offset = get_stack_slot_offset_from_access(access); + auto it = stack_slot_entries.find(offset); + if (it != stack_slot_entries.end()) { + auto type_of_var = it->second.tp_type; + if (using_user_var_types) { + auto retype_kv = m_var_retype.find(it->second.name()); + if (retype_kv != m_var_retype.end()) { + type_of_var = TP_Type::make_from_ts(retype_kv->second); + } + } + return type_of_var; + } + return TP_Type::make_from_ts(TypeSpec("object")); + } + + if (access.reg().get_kind() == Reg::FPR || access.reg().get_kind() == Reg::GPR) { + auto& var_info = m_var_names.lookup(access.reg(), access.idx(), access.mode()); + if (using_user_var_types) { + auto retype_kv = m_var_retype.find(var_info.name()); + if (retype_kv != m_var_retype.end()) { + return TP_Type::make_from_ts(retype_kv->second); + } + } + return var_info.type; + } + + throw std::runtime_error("TP types are not supported for this kind of register"); +} + /*! * Update the Env with the result of the type analysis pass. */ @@ -420,6 +483,9 @@ std::vector Env::extract_visible_variables( std::vector> vars; for (auto& x : var_set) { + if (is_stack_slot_access(x)) { + continue; + } if (x.reg().get_kind() == Reg::FPR || x.reg().get_kind() == Reg::GPR) { vars.push_back(std::make_pair(get_program_var_id(x), x)); } @@ -519,6 +585,9 @@ FunctionVariableDefinitions Env::local_var_type_list(const Form* top_level_form, std::sort(spills.begin(), spills.end(), [](const StackSpillEntry& a, const StackSpillEntry& b) { return a.offset < b.offset; }); for (auto& x : spills) { + if (m_vars_defined_in_let.find(x.name()) != m_vars_defined_in_let.end()) { + continue; + } elts.push_back(pretty_print::build_list(x.name(), x.typespec.print())); count++; } @@ -549,12 +618,18 @@ const UseDefInfo& Env::get_use_def_info(const RegisterAccess& ra) const { } void Env::disable_def(const RegisterAccess& access, DecompWarnings& warnings) { + if (is_stack_slot_access(access)) { + return; + } if (has_local_vars()) { m_var_names.disable_def(access, warnings); } } void Env::disable_use(const RegisterAccess& access) { + if (is_stack_slot_access(access)) { + return; + } if (has_local_vars()) { m_var_names.disable_use(access); } diff --git a/decompiler/IR2/Env.h b/decompiler/IR2/Env.h index 889c67b0a5..167f02bb99 100644 --- a/decompiler/IR2/Env.h +++ b/decompiler/IR2/Env.h @@ -44,6 +44,26 @@ struct StackSpillEntry { } }; +inline bool is_stack_slot_access(const RegisterAccess& access) { + return access.reg() == Register(Reg::GPR, Reg::SP) && access.idx() < 0; +} + +inline int get_stack_slot_offset_from_access(const RegisterAccess& access) { + ASSERT(is_stack_slot_access(access)); + return -access.idx() - 1; +} + +inline RegisterAccess make_stack_slot_access(int offset) { + return RegisterAccess(AccessMode::READ, Register(Reg::GPR, Reg::SP), -offset - 1, true); +} + +inline bool same_expression_var(const RegisterAccess& a, const RegisterAccess& b) { + if (is_stack_slot_access(a) || is_stack_slot_access(b)) { + return a == b; + } + return a.reg() == b.reg(); +} + struct FunctionVariableDefinitions { std::optional local_vars; bool had_pp = false; diff --git a/decompiler/IR2/Form.cpp b/decompiler/IR2/Form.cpp index a7e9847a36..cb879ed3cc 100644 --- a/decompiler/IR2/Form.cpp +++ b/decompiler/IR2/Form.cpp @@ -7,6 +7,7 @@ #include "common/type_system/TypeSystem.h" #include "common/util/print_float.h" +#include "decompiler/IR2/Env.h" #include "decompiler/ObjectFile/LinkedObjectFile.h" #include "decompiler/util/DecompilerTypeSystem.h" #include "decompiler/util/data_decompile.h" @@ -2801,11 +2802,22 @@ void StackSpillStoreElement::get_modified_regs(RegSet&) const {} // StackSpillValueElement //////////////////////////////// -StackSpillValueElement::StackSpillValueElement(int size, int stack_offset, bool is_signed) - : m_size(size), m_stack_offset(stack_offset), m_is_signed(is_signed) {} +StackSpillValueElement::StackSpillValueElement(int size, + int stack_offset, + bool is_signed, + std::optional read_type) + : m_size(size), + m_stack_offset(stack_offset), + m_is_signed(is_signed), + m_read_type(std::move(read_type)) {} goos::Object StackSpillValueElement::to_form_internal(const Env& env) const { - return pretty_print::to_symbol(env.get_spill_slot_var_name(m_stack_offset)); + auto var = make_stack_slot_access(m_stack_offset); + auto base = pretty_print::to_symbol(env.get_spill_slot_var_name(m_stack_offset)); + if (m_read_type && env.get_variable_type(var, true) != *m_read_type) { + return pretty_print::build_list("the-as", m_read_type->print(), base); + } + return base; } void StackSpillValueElement::apply(const std::function& f) { @@ -2813,7 +2825,9 @@ void StackSpillValueElement::apply(const std::function& f) { } void StackSpillValueElement::apply_form(const std::function&) {} -void StackSpillValueElement::collect_vars(RegAccessSet&, bool) const {} +void StackSpillValueElement::collect_vars(RegAccessSet& vars, bool) const { + vars.insert(make_stack_slot_access(m_stack_offset)); +} void StackSpillValueElement::get_modified_regs(RegSet&) const {} //////////////////////////////// diff --git a/decompiler/IR2/Form.h b/decompiler/IR2/Form.h index f627939fe4..2cfdd8d640 100644 --- a/decompiler/IR2/Form.h +++ b/decompiler/IR2/Form.h @@ -1564,7 +1564,10 @@ class StackSpillStoreElement : public FormElement { // the value from a stack load. class StackSpillValueElement : public FormElement { public: - StackSpillValueElement(int size, int stack_offset, bool is_signed); + StackSpillValueElement(int size, + int stack_offset, + bool is_signed, + std::optional read_type = std::nullopt); goos::Object to_form_internal(const Env& env) const override; void apply(const std::function& f) override; void apply_form(const std::function& f) override; @@ -1580,6 +1583,7 @@ class StackSpillValueElement : public FormElement { int m_size = -1; int m_stack_offset = -1; bool m_is_signed = false; + std::optional m_read_type; }; class MethodOfTypeElement : public FormElement { diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index 153e560505..4adeca1d71 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -12,6 +12,7 @@ #include "common/util/BitUtils.h" #include "common/util/print_float.h" +#include "decompiler/IR2/Env.h" #include "decompiler/IR2/ExpressionHelpers.h" #include "decompiler/IR2/bitfields.h" #include "decompiler/ObjectFile/LinkedObjectFile.h" @@ -65,6 +66,27 @@ namespace decompiler { namespace { +Form* cast_form(Form* in, const TypeSpec& new_type, FormPool& pool, const Env& env, bool tc_pass); + +TypeSpec get_input_type_for_access(const Env& env, const RegisterAccess& access) { + if (is_stack_slot_access(access)) { + return env.get_variable_type(access, true); + } + return env.get_types_before_op(access.idx()).get(access.reg()).typespec(); +} + +Form* cast_stack_slot_var_if_needed(Form* in, + const TypeSpec& desired_type, + FormPool& pool, + const Env& env) { + auto atom = form_as_atom(in); + if (atom && atom->is_var() && is_stack_slot_access(atom->var()) && + env.get_variable_type(atom->var(), true) != desired_type) { + return cast_form(in, desired_type, pool, env, false); + } + return in; +} + Form* strip_pcypld_64(Form* in) { auto m = match(Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::PCPYLD), {Matcher::integer(0), Matcher::any(0)}), @@ -347,6 +369,9 @@ Form* repop_passthrough_arg(Form* in, auto as_atom = form_as_atom(in); if (as_atom && as_atom->is_var()) { + if (is_stack_slot_access(as_atom->var())) { + return in; + } return stack.pop_reg(as_atom->var().reg(), {}, env, true, -1, orig_out, found_orig_out); } return in; @@ -389,6 +414,9 @@ void pop_helper(const std::vector& vars, for (size_t var_idx = 0; var_idx < vars.size(); var_idx++) { const auto& var = vars.at(var_idx); + if (is_stack_slot_access(var)) { + continue; + } auto& ri = env.reg_use().op.at(var.idx()); RegSet consumes_to_use = consumes.value_or(ri.consumes); if (consumes_to_use.find(var.reg()) != consumes_to_use.end()) { @@ -555,6 +583,14 @@ std::vector pop_to_forms(const std::vector& vars, for (size_t i = 0; i < vars.size(); i++) { auto atom = form_as_atom(forms[i]); bool is_var = atom && atom->is_var(); + if (is_var && is_stack_slot_access(atom->var())) { + auto access_type = get_input_type_for_access(env, vars[i]); + if (env.get_variable_type(atom->var(), true) != access_type) { + forms[i] = cast_form(forms[i], access_type, pool, env); + atom = form_as_atom(forms[i]); + is_var = atom && atom->is_var(); + } + } auto cast = env.get_user_cast_for_access(vars[i]); // only cast if we didn't get a var (compacting expressions). // there is a separate system for casting variables that will do a better job. @@ -1146,7 +1182,9 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env, } } ASSERT(used_index); - result->push_back(pool.alloc_element(args.at(1), out.addr_of, tokens)); + result->push_back(pool.alloc_element( + cast_stack_slot_var_if_needed(args.at(1), arg1_type.typespec(), pool, env), + out.addr_of, tokens)); return; } else { throw std::runtime_error( @@ -1186,7 +1224,9 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env, } } ASSERT(used_index); - result->push_back(pool.alloc_element(args.at(1), out.addr_of, tokens)); + result->push_back(pool.alloc_element( + cast_stack_slot_var_if_needed(args.at(1), arg1_type.typespec(), pool, env), + out.addr_of, tokens)); return; } else { throw std::runtime_error( @@ -1215,7 +1255,9 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env, } } ASSERT(used_index); - result->push_back(pool.alloc_element(args.at(1), out.addr_of, tokens)); + result->push_back(pool.alloc_element( + cast_stack_slot_var_if_needed(args.at(1), arg1_type.typespec(), pool, env), + out.addr_of, tokens)); return; } else { throw std::runtime_error( @@ -1267,7 +1309,9 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env, } } ASSERT(used_index); - result->push_back(pool.alloc_element(args.at(0), rd_ok.addr_of, tokens)); + result->push_back(pool.alloc_element( + cast_stack_slot_var_if_needed(args.at(0), arg0_type.typespec(), pool, env), + rd_ok.addr_of, tokens)); return; } else { throw std::runtime_error(fmt::format( @@ -1318,7 +1362,9 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env, } } ASSERT(used_index); - result->push_back(pool.alloc_element(args.at(1), rd_ok.addr_of, tokens)); + result->push_back(pool.alloc_element( + cast_stack_slot_var_if_needed(args.at(1), arg1_type.typespec(), pool, env), + rd_ok.addr_of, tokens)); return; } else { // TODO - output error to IR @@ -1341,7 +1387,9 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env, tokens.push_back(to_token(tok)); } - result->push_back(pool.alloc_element(args.at(1), out.addr_of, tokens)); + result->push_back(pool.alloc_element( + cast_stack_slot_var_if_needed(args.at(1), arg1_type.typespec(), pool, env), out.addr_of, + tokens)); return; } } @@ -2357,7 +2405,7 @@ void SimpleExpressionElement::update_from_stack_int_to_float(const Env& env, // the gpr->fpr operation beacuse it doesn't matter. auto fpr_convert_matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::GPR_TO_FPR), {Matcher::any(0)}); - auto type = env.get_types_before_op(var.idx()).get(var.reg()).typespec(); + auto type = get_input_type_for_access(env, var); // want to allow any child of integer so integer enums can also be converted to floats. if (env.dts->ts.tc(TypeSpec("integer"), type) || type == TypeSpec("seconds")) { auto mr = match(fpr_convert_matcher, arg); @@ -2379,7 +2427,7 @@ void SimpleExpressionElement::update_from_stack_float_to_int(const Env& env, bool allow_side_effects) { auto var = m_expr.get_arg(0).var(); auto arg = pop_to_forms({var}, env, pool, stack, allow_side_effects).at(0); - auto type = env.get_types_before_op(var.idx()).get(var.reg()).typespec(); + auto type = get_input_type_for_access(env, var); auto fpr_convert_matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::GPR_TO_FPR), {Matcher::any(0)}); auto mr = match(fpr_convert_matcher, arg); @@ -2412,7 +2460,7 @@ void SimpleExpressionElement::update_from_stack_subu_l32_s7(const Env& env, bool allow_side_effects) { auto var = m_expr.get_arg(0).var(); auto arg = pop_to_forms({var}, env, pool, stack, allow_side_effects).at(0); - auto type = env.get_types_before_op(var.idx()).get(var.reg()).typespec(); + auto type = get_input_type_for_access(env, var); if (type != TypeSpec("handle")) { env.func->warnings.warning( ".subu (32-bit) used on a {} at idx {}. This probably should be a handle.", type.print(), @@ -2634,9 +2682,12 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta } auto var = src_as_se->expr().get_arg(0).var(); - auto& info = env.reg_use().op.at(var.idx()); - if (var.reg() == Register(Reg::GPR, Reg::S6) || - info.consumes.find(var.reg()) != info.consumes.end()) { + bool is_consumed_reg_move = false; + if (!is_stack_slot_access(var)) { + auto& info = env.reg_use().op.at(var.idx()); + is_consumed_reg_move = info.consumes.find(var.reg()) != info.consumes.end(); + } + if (var.reg() == Register(Reg::GPR, Reg::S6) || is_consumed_reg_move) { stack.push_non_seq_reg_to_reg(m_dst, src_as_se->expr().get_arg(0).var(), m_src, m_src_type, m_var_info); return; @@ -3495,8 +3546,14 @@ void FunctionCallElement::update_from_stack(const Env& env, } TypeSpec function_type; - auto& in_type_state = env.get_types_before_op(all_pop_vars.at(0).idx()); - auto& tp_type = in_type_state.get(all_pop_vars.at(0).reg()); + const TypeState* in_type_state = nullptr; + TP_Type tp_type; + if (!is_stack_slot_access(all_pop_vars.at(0))) { + in_type_state = &env.get_types_before_op(all_pop_vars.at(0).idx()); + tp_type = in_type_state->get(all_pop_vars.at(0).reg()); + } else { + tp_type = env.get_variable_tp_type(all_pop_vars.at(0), true); + } if (env.has_type_analysis()) { function_type = tp_type.typespec(); } @@ -3504,7 +3561,8 @@ void FunctionCallElement::update_from_stack(const Env& env, // if we're actually a go: Form* go_next_state = nullptr; if (tp_type.kind == TP_Type::Kind::ENTER_STATE_FUNCTION) { - auto& next_state_type = in_type_state.next_state_type; + ASSERT(in_type_state); + auto& next_state_type = in_type_state->next_state_type; if (next_state_type.typespec().base_type() != "state") { throw std::runtime_error("Bad state type in expressions (not state): " + next_state_type.print()); @@ -3591,7 +3649,7 @@ void FunctionCallElement::update_from_stack(const Env& env, auto val = unstacked.at(arg_id + 1); // first is the function itself. auto& var = all_pop_vars.at(arg_id + 1); if (has_good_types) { - auto actual_arg_type = env.get_types_before_op(var.idx()).get(var.reg()).typespec(); + auto actual_arg_type = get_input_type_for_access(env, var); if (arg_id == 0) { first_arg_type = actual_arg_type; @@ -3997,7 +4055,7 @@ void FunctionCallElement::update_from_stack(const Env& env, ASSERT(new_args.size() >= 3); for (size_t i = 0; i < 3; i++) { auto& var = all_pop_vars.at(i + 1); // 0 is the function itself. - auto arg_type = env.get_types_before_op(var.idx()).get(var.reg()).typespec(); + auto arg_type = get_input_type_for_access(env, var); if (!env.dts->ts.tc(expected_arg_types.at(i), arg_type)) { new_args.at(i) = pool.form(expected_arg_types.at(i), new_args.at(i)); } @@ -5031,9 +5089,11 @@ void CondWithElseElement::push_to_stack(const Env& env, FormPool& pool, FormStac // determine if set destination is used bool set_unused = false; if (rewrite_as_set) { - auto& info = env.reg_use().op.at(last_var->idx()); - if (info.written_and_unused.find(last_var->reg()) != info.written_and_unused.end()) { - set_unused = true; + if (!is_stack_slot_access(*last_var)) { + auto& info = env.reg_use().op.at(last_var->idx()); + if (info.written_and_unused.find(last_var->reg()) != info.written_and_unused.end()) { + set_unused = true; + } } } @@ -6019,7 +6079,7 @@ void ConditionElement::push_to_stack(const Env& env, FormPool& pool, FormStack& if (m_src[i]->is_var()) { auto& var = m_src[i]->var(); vars.push_back(var); - source_types.push_back(env.get_types_before_op(var.idx()).get(var.reg()).typespec()); + source_types.push_back(get_input_type_for_access(env, var)); } else if (m_src[i]->is_int()) { if (m_src[i]->get_int() == 0 && condition_uses_float(m_kind)) { // if we're doing a floating point comparison, and one of our arguments is a constant @@ -6072,7 +6132,7 @@ void ConditionElement::update_from_stack(const Env& env, if (m_src[i]->is_var()) { auto& var = m_src[i]->var(); vars.push_back(var); - source_types.push_back(env.get_types_before_op(var.idx()).get(var.reg()).typespec()); + source_types.push_back(get_input_type_for_access(env, var)); } else if (m_src[i]->is_int()) { if (m_src[i]->get_int() == 0 && condition_uses_float(m_kind)) { // if we're doing a floating point comparison, and one of our arguments is a constant @@ -6915,11 +6975,19 @@ void StackSpillStoreElement::push_to_stack(const Env& env, FormPool& pool, FormS src = pool.form(m_value); } - auto dst = pool.form(env.get_spill_slot_var_name(m_stack_offset)); if (m_cast_type) { src = cast_form(src, *m_cast_type, pool, env); } - stack.push_form_element(pool.alloc_element(dst, src), true); + + TypeSpec stack_type("object"); + auto it = env.stack_slot_entries.find(m_stack_offset); + if (it != env.stack_slot_entries.end()) { + stack_type = it->second.typespec; + } else if (m_cast_type) { + stack_type = *m_cast_type; + } + + stack.push_value_to_reg(make_stack_slot_access(m_stack_offset), src, true, stack_type); } namespace { @@ -7173,13 +7241,21 @@ void StackStructureDefElement::update_from_stack(const Env&, result->push_back(this); } -void StackSpillValueElement::update_from_stack(const Env&, - FormPool&, +void StackSpillValueElement::update_from_stack(const Env& env, + FormPool& pool, FormStack&, std::vector* result, bool) { mark_popped(); - result->push_back(this); + auto var = make_stack_slot_access(m_stack_offset); + Form* form = + pool.alloc_single_element_form(nullptr, SimpleAtom::make_var(var)); + if (m_read_type && env.get_variable_type(var, true) != *m_read_type) { + form = cast_form(form, *m_read_type, pool, env); + } + for (auto elt : form->elts()) { + result->push_back(elt); + } } void GetSymbolStringPointer::update_from_stack(const Env&, diff --git a/decompiler/IR2/FormStack.cpp b/decompiler/IR2/FormStack.cpp index 672e315c9e..97c97ce84c 100644 --- a/decompiler/IR2/FormStack.cpp +++ b/decompiler/IR2/FormStack.cpp @@ -325,7 +325,7 @@ std::vector FormStack::rewrite(FormPool& pool, const Env& env) con 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()) { + if (last_op_as_set && same_expression_var(last_op_as_set->dst(), var_to_get)) { result.pop_back(); auto as_one = dynamic_cast( last_op_as_set->src()->try_as_single_element()); @@ -384,7 +384,7 @@ std::optional rewrite_to_get_var(std::vector& defa while (keep_going && !default_result.empty()) { 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() && + if (last_op_as_set && same_expression_var(last_op_as_set->dst(), var_to_get) && (first || last_op_as_set->info().is_compactable)) { default_result.pop_back(); auto as_one = diff --git a/decompiler/analysis/insert_lets.cpp b/decompiler/analysis/insert_lets.cpp index 12d93bedc5..136d4fe475 100644 --- a/decompiler/analysis/insert_lets.cpp +++ b/decompiler/analysis/insert_lets.cpp @@ -45,6 +45,7 @@ If the previous let variables appear in the definition of new one, make the let namespace { FormElement* rewrite_let(LetElement* in, const Env& env, FormPool& pool, LetRewriteStats& stats); +bool let_uses_stack_slot_access(const LetElement* in); std::vector path_up_tree(Form* in, const Env&) { std::vector path; @@ -2409,6 +2410,10 @@ FormElement* rewrite_set_font_single(LetElement* in, FormElement* rewrite_let(LetElement* in, const Env& env, FormPool& pool, LetRewriteStats& stats) { // ordered based on frequency. for best performance, you check the most likely rewrites first! + if (let_uses_stack_slot_access(in)) { + return nullptr; + } + auto as_unused = rewrite_empty_let(in, env, pool); if (as_unused) { stats.unused++; @@ -3073,6 +3078,10 @@ FormElement* rewrite_multi_let(LetElement* in, const Env& env, FormPool& pool, LetRewriteStats& stats) { + if (let_uses_stack_slot_access(in)) { + return in; + } + if (in->entries().size() >= 2) { auto as_with_dma_buf_add_bucket = rewrite_with_dma_buf_add_bucket(in, env, pool); if (as_with_dma_buf_add_bucket) { @@ -3569,6 +3578,12 @@ FormElement* rewrite_let_sequence(const std::vector& in, const Env& env, FormPool& pool, LetRewriteStats& stats) { + for (const auto* let : in) { + if (let_uses_stack_slot_access(let)) { + return nullptr; + } + } + if (in.size() == 3) { auto as_dma_buffer_add_gs_set = rewrite_dma_buffer_add_gs_set(in, env, pool); if (as_dma_buffer_add_gs_set) { @@ -3609,6 +3624,15 @@ Form* insert_cast_for_let(RegisterAccess dst, return src; } +bool let_uses_stack_slot_access(const LetElement* in) { + for (const auto& entry : in->entries()) { + if (is_stack_slot_access(entry.dest)) { + return true; + } + } + return false; +} + bool register_can_hold_var(const Register& reg) { return reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR; }