diff --git a/decompiler/IR2/AtomicOp.cpp b/decompiler/IR2/AtomicOp.cpp index dce7877ac..37dafe390 100644 --- a/decompiler/IR2/AtomicOp.cpp +++ b/decompiler/IR2/AtomicOp.cpp @@ -140,8 +140,15 @@ goos::Object SimpleAtom::to_form(const std::vector& labels, con switch (m_kind) { case Kind::VARIABLE: return m_variable.to_form(env); - case Kind::INTEGER_CONSTANT: - return goos::Object::make_integer(m_int); + case Kind::INTEGER_CONSTANT: { + if (std::abs(m_int) > INT32_MAX) { + u64 v = m_int; + return pretty_print::to_symbol(fmt::format("#x{:x}", v)); + } else { + return goos::Object::make_integer(m_int); + } + } + case Kind::SYMBOL_PTR: if (m_string == "#t") { return pretty_print::to_symbol("#t"); diff --git a/decompiler/IR2/AtomicOpForm.cpp b/decompiler/IR2/AtomicOpForm.cpp index 9aa14b400..4f66afebe 100644 --- a/decompiler/IR2/AtomicOpForm.cpp +++ b/decompiler/IR2/AtomicOpForm.cpp @@ -609,8 +609,8 @@ Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const { u64 value; memcpy(&value, &word0.data, 4); memcpy(((u8*)&value) + 4, &word1.data, 4); - return pool.alloc_single_element_form(nullptr, - fmt::format("#x{:x}", value)); + return pool.alloc_single_element_form( + nullptr, SimpleAtom::make_int_constant(value)); } // is it a constant bitfield? diff --git a/decompiler/IR2/Env.cpp b/decompiler/IR2/Env.cpp index 7d1357658..dfca92a0a 100644 --- a/decompiler/IR2/Env.cpp +++ b/decompiler/IR2/Env.cpp @@ -90,6 +90,10 @@ const std::string& Env::remapped_name(const std::string& name) const { } } +goos::Object Env::get_variable_name_with_cast(const RegisterAccess& access) const { + return get_variable_name_with_cast(access.reg(), access.idx(), access.mode()); +} + goos::Object Env::get_variable_name_with_cast(Register reg, int atomic_idx, AccessMode mode) const { if (reg.get_kind() == Reg::FPR || reg.get_kind() == Reg::GPR) { auto& var_info = m_var_names.lookup(reg, atomic_idx, mode); diff --git a/decompiler/IR2/Env.h b/decompiler/IR2/Env.h index 8fe819161..9d5fe88c8 100644 --- a/decompiler/IR2/Env.h +++ b/decompiler/IR2/Env.h @@ -52,6 +52,7 @@ class Env { // TODO - remove this. goos::Object get_variable_name_with_cast(Register reg, int atomic_idx, AccessMode mode) const; + goos::Object get_variable_name_with_cast(const RegisterAccess& access) const; std::string get_variable_name(const RegisterAccess& access) const; std::optional get_user_cast_for_access(const RegisterAccess& access) const; TypeSpec get_variable_type(const RegisterAccess& access, bool using_user_var_types) const; diff --git a/decompiler/IR2/Form.cpp b/decompiler/IR2/Form.cpp index 6c6a192e7..101a6ddd5 100644 --- a/decompiler/IR2/Form.cpp +++ b/decompiler/IR2/Form.cpp @@ -309,7 +309,7 @@ goos::Object SetVarElement::to_form_internal(const Env& env) const { auto expected_type = env.get_variable_type(m_dst, true); if (!env.dts->ts.tc(expected_type, m_src_type)) { return pretty_print::build_list( - "set!", m_dst.to_form(env), + "set!", m_dst.to_form(env, RegisterAccess::Print::AS_VARIABLE_NO_CAST), pretty_print::build_list("the-as", expected_type.print(), m_src->to_form(env))); } } diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index a7186ee97..c580e6507 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -712,10 +712,19 @@ void SimpleExpressionElement::update_from_stack_add_i(const Env& env, GenericOperator::make_fixed(FixedOperatorKind::ADDITION_PTR), args.at(0), args.at(1)); result->push_back(new_form); } else { - auto cast = pool.alloc_single_element_form( + auto casted0 = args.at(0); + + auto arg0_type = env.get_types_before_op(m_my_idx).get(m_expr.get_arg(0).var().reg()); + if (!arg0_i && !arg0_u && arg0_type.typespec() != TypeSpec("binteger")) { + casted0 = pool.alloc_single_element_form( + nullptr, TypeSpec(arg0_i ? "int" : "uint"), args.at(0)); + } + + auto casted1 = pool.alloc_single_element_form( nullptr, TypeSpec(arg0_i ? "int" : "uint"), args.at(1)); + auto new_form = pool.alloc_element( - GenericOperator::make_fixed(FixedOperatorKind::ADDITION), args.at(0), cast); + GenericOperator::make_fixed(FixedOperatorKind::ADDITION), casted0, casted1); result->push_back(new_form); } } @@ -877,6 +886,16 @@ void SimpleExpressionElement::update_from_stack_copy_first_int_2(const Env& env, } } +namespace { +Form* strip_int_or_uint_cast(Form* in) { + auto as_cast = in->try_as_element(); + if (as_cast && (as_cast->type() == TypeSpec("int") || as_cast->type() == TypeSpec("uint"))) { + return as_cast->source(); + } + return in; +} +} // namespace + void SimpleExpressionElement::update_from_stack_logor_or_logand(const Env& env, FixedOperatorKind kind, FormPool& pool, @@ -884,24 +903,26 @@ void SimpleExpressionElement::update_from_stack_logor_or_logand(const Env& env, std::vector* result, bool allow_side_effects) { auto arg0_type = env.get_variable_type(m_expr.get_arg(0).var(), true); - BitfieldManip::Kind manip_kind; - if (kind == FixedOperatorKind::LOGAND) { - manip_kind = BitfieldManip::Kind::LOGAND; - } else if (kind == FixedOperatorKind::LOGIOR) { - manip_kind = BitfieldManip::Kind::LOGIOR_WITH_CONSTANT_INT; - } else { - assert(false); - } auto type_info = env.dts->ts.lookup_type(arg0_type); auto bitfield_info = dynamic_cast(type_info); if (bitfield_info && m_expr.get_arg(1).is_int()) { - // andi, bitfield + // andi, ori with bitfield. auto base = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0); - auto read_elt = dynamic_cast(base->try_as_single_element()); + auto read_elt = dynamic_cast(base->try_as_single_element()); if (!read_elt) { - read_elt = pool.alloc_element(base, arg0_type); + read_elt = pool.alloc_element(base, arg0_type); } + + BitfieldManip::Kind manip_kind; + if (kind == FixedOperatorKind::LOGAND) { + manip_kind = BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT; + } else if (kind == FixedOperatorKind::LOGIOR) { + manip_kind = BitfieldManip::Kind::LOGIOR_WITH_CONSTANT_INT; + } else { + assert(false); + } + BitfieldManip step(manip_kind, m_expr.get_arg(1).get_int()); auto other = read_elt->push_step(step, env.dts->ts, pool); if (other) { @@ -925,18 +946,45 @@ void SimpleExpressionElement::update_from_stack_logor_or_logand(const Env& env, allow_side_effects); if (bitfield_info) { - // mask didn't fit in imm - auto arg1_atom = form_as_atom(args.at(1)); + // either the immediate didn't fit in the 16-bit imm or it's with a variable + auto read_elt = dynamic_cast(args.at(0)->try_as_single_element()); + if (!read_elt) { + read_elt = pool.alloc_element(args.at(0), arg0_type); + } + + auto stripped_arg1 = strip_int_or_uint_cast(args.at(1)); + auto arg1_atom = form_as_atom(strip_int_or_uint_cast(args.at(1))); if (arg1_atom && arg1_atom->is_int()) { - auto read_elt = dynamic_cast(args.at(0)->try_as_single_element()); - if (!read_elt) { - read_elt = pool.alloc_element(args.at(0), arg0_type); + BitfieldManip::Kind manip_kind; + if (kind == FixedOperatorKind::LOGAND) { + manip_kind = BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT; + } else if (kind == FixedOperatorKind::LOGIOR) { + manip_kind = BitfieldManip::Kind::LOGIOR_WITH_CONSTANT_INT; + } else { + assert(false); } BitfieldManip step(manip_kind, arg1_atom->get_int()); auto other = read_elt->push_step(step, env.dts->ts, pool); assert(!other); // shouldn't be complete. result->push_back(read_elt); return; + } else { + BitfieldManip::Kind manip_kind; + if (kind == FixedOperatorKind::LOGAND) { + manip_kind = BitfieldManip::Kind::LOGAND_WITH_FORM; + } else if (kind == FixedOperatorKind::LOGIOR) { + manip_kind = BitfieldManip::Kind::LOGIOR_WITH_FORM; + } else { + assert(false); + } + auto step = BitfieldManip::from_form(manip_kind, stripped_arg1); + auto other = read_elt->push_step(step, env.dts->ts, pool); + if (other) { + result->push_back(other); + } else { + result->push_back(read_elt); + } + return; } } @@ -967,7 +1015,7 @@ void SimpleExpressionElement::update_from_stack_left_shift(const Env& env, auto bitfield_info = dynamic_cast(type_info); if (bitfield_info && m_expr.get_arg(1).is_int()) { auto base = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0); - auto read_elt = pool.alloc_element(base, arg0_type); + auto read_elt = pool.alloc_element(base, arg0_type); BitfieldManip step(BitfieldManip::Kind::LEFT_SHIFT, m_expr.get_arg(1).get_int()); auto other = read_elt->push_step(step, env.dts->ts, pool); assert(!other); // shouldn't be complete. @@ -988,7 +1036,7 @@ void SimpleExpressionElement::update_from_stack_right_shift_logic(const Env& env auto bitfield_info = dynamic_cast(type_info); if (bitfield_info && m_expr.get_arg(1).is_int()) { auto base = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0); - auto read_elt = pool.alloc_element(base, arg0_type); + auto read_elt = pool.alloc_element(base, arg0_type); BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_LOGICAL, m_expr.get_arg(1).get_int()); auto other = read_elt->push_step(step, env.dts->ts, pool); assert(other); // should be a high field. @@ -999,7 +1047,7 @@ void SimpleExpressionElement::update_from_stack_right_shift_logic(const Env& env if (m_expr.get_arg(1).is_int()) { auto arg = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0); - auto as_bitfield_access = dynamic_cast(arg->try_as_single_element()); + auto as_bitfield_access = dynamic_cast(arg->try_as_single_element()); if (as_bitfield_access) { BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_LOGICAL, m_expr.get_arg(1).get_int()); @@ -1040,7 +1088,7 @@ void SimpleExpressionElement::update_from_stack_right_shift_arith(const Env& env auto bitfield_info = dynamic_cast(type_info); if (bitfield_info && m_expr.get_arg(1).is_int()) { auto base = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0); - auto read_elt = pool.alloc_element(base, arg0_type); + auto read_elt = pool.alloc_element(base, arg0_type); BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_ARITH, m_expr.get_arg(1).get_int()); auto other = read_elt->push_step(step, env.dts->ts, pool); assert(other); // should be a high field. @@ -1325,12 +1373,14 @@ void SetFormFormElement::push_to_stack(const Env& env, FormPool& pool, FormStack if (dst_form == src_form) { // success! auto value = src_as_bf_set->mods().at(0).value; + value->parent_element = this; // make the (-> thing bitfield) auto field_token = DerefToken::make_field_name(src_as_bf_set->mods().at(0).field_name); auto loc_elt = pool.alloc_element(m_dst, false, field_token); loc_elt->inline_nested(); auto loc = pool.alloc_single_form(nullptr, loc_elt); + loc->parent_element = this; m_dst = loc; m_src = value; @@ -2218,7 +2268,7 @@ FormElement* ConditionElement::make_nonzero_check_generic(const Env& env, FormElement* bitfield_compare = nullptr; assert(source_forms.size() == 1); auto as_bitfield_op = - dynamic_cast(source_forms.at(0)->try_as_single_element()); + dynamic_cast(source_forms.at(0)->try_as_single_element()); if (as_bitfield_op) { bitfield_compare = as_bitfield_op->push_step( BitfieldManip(BitfieldManip::Kind::NONZERO_COMPARE, 0), env.dts->ts, pool); @@ -2587,7 +2637,7 @@ void push_asm_srl_to_stack(const AsmOp* op, auto bitfield_info = dynamic_cast(type_info); if (bitfield_info) { auto base = pop_to_forms({*var}, env, pool, stack, true).at(0); - auto read_elt = pool.alloc_element(base, arg0_type); + auto read_elt = pool.alloc_element(base, arg0_type); BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_LOGICAL_32BIT, integer); auto other = read_elt->push_step(step, env.dts->ts, pool); assert(other); // should be a high field. @@ -2843,7 +2893,7 @@ void ArrayFieldAccess::update_with_val(Form* new_val, new_val->to_string(env)); } - auto base = mr.maps.forms.at(1); + auto base = strip_int_or_uint_cast(mr.maps.forms.at(1)); auto idx = mr.maps.forms.at(0); assert(idx && base); diff --git a/decompiler/IR2/FormStack.cpp b/decompiler/IR2/FormStack.cpp index 3840534ed..cd43e1394 100644 --- a/decompiler/IR2/FormStack.cpp +++ b/decompiler/IR2/FormStack.cpp @@ -257,6 +257,13 @@ bool is_op_in_place(SetVarElement* elt, return false; } + // also check with casts. This avoid something like + // (set! x (+ (the x) 1)) + auto src_var_cast = env.get_variable_name_with_cast(*first); + if (src_var_cast.print() != dst_var) { + return false; + } + *val_out = result.maps.forms.at(1); *base_out = first.value(); return true; diff --git a/decompiler/IR2/bitfields.cpp b/decompiler/IR2/bitfields.cpp index 2c5738a62..8e728ed9c 100644 --- a/decompiler/IR2/bitfields.cpp +++ b/decompiler/IR2/bitfields.cpp @@ -62,9 +62,14 @@ void BitfieldStaticDefElement::get_modified_regs(RegSet&) const {} ModifiedCopyBitfieldElement::ModifiedCopyBitfieldElement( const TypeSpec& type, Form* base, - const std::vector& field_modifications) + const std::vector& field_modifications) : m_type(type), m_base(base), m_field_modifications(field_modifications) { m_base->parent_element = this; + for (auto& mod : m_field_modifications) { + if (mod.value) { + mod.value->parent_element = this; + } + } } goos::Object ModifiedCopyBitfieldElement::to_form_internal(const Env& env) const { @@ -112,29 +117,29 @@ void ModifiedCopyBitfieldElement::get_modified_regs(RegSet& regs) const { // BitfieldReadElement //////////////////////////////// -BitfieldReadElement::BitfieldReadElement(Form* base_value, const TypeSpec& ts) +BitfieldAccessElement::BitfieldAccessElement(Form* base_value, const TypeSpec& ts) : m_base(base_value), m_type(ts) { m_base->parent_element = this; } -goos::Object BitfieldReadElement::to_form_internal(const Env& env) const { +goos::Object BitfieldAccessElement::to_form_internal(const Env& env) const { return pretty_print::build_list("incomplete-bitfield-access", m_base->to_form(env)); } -void BitfieldReadElement::apply(const std::function& f) { +void BitfieldAccessElement::apply(const std::function& f) { f(this); m_base->apply(f); } -void BitfieldReadElement::apply_form(const std::function& f) { +void BitfieldAccessElement::apply_form(const std::function& f) { m_base->apply_form(f); } -void BitfieldReadElement::collect_vars(RegAccessSet& vars, bool recursive) const { +void BitfieldAccessElement::collect_vars(RegAccessSet& vars, bool recursive) const { m_base->collect_vars(vars, recursive); } -void BitfieldReadElement::get_modified_regs(RegSet& regs) const { +void BitfieldAccessElement::get_modified_regs(RegSet& regs) const { m_base->get_modified_regs(regs); } @@ -180,15 +185,49 @@ std::optional find_field_from_mask(const TypeSystem& ts, return find_field(ts, type, mask_range->first(), mask_range->size(), {}); } +Form* strip_int_or_uint_cast(Form* in) { + auto as_cast = in->try_as_element(); + if (as_cast && (as_cast->type() == TypeSpec("int") || as_cast->type() == TypeSpec("uint"))) { + return as_cast->source(); + } + return in; +} + +std::optional get_bitfield_initial_set(Form* form, + const BitFieldType* type, + const TypeSystem& ts) { + // (shr (shl arg1 59) 44) for example + auto matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::SHR), + {Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::SHL), + {Matcher::any(0), Matcher::any_integer(1)}), + Matcher::any_integer(2)}); + auto mr = match(matcher, strip_int_or_uint_cast(form)); + if (mr.matched) { + auto value = mr.maps.forms.at(0); + int left = mr.maps.ints.at(1); + int right = mr.maps.ints.at(2); + int size = 64 - left; + int offset = left - right; + auto& f = find_field(ts, type, offset, size, {}); + BitFieldDef def; + def.value = value; + def.field_name = f.name(); + def.is_signed = false; // we don't know. + return def; + } + + return {}; +} + } // namespace /*! * Add a step to the bitfield access. If this completes the access, returns a form representing the * access */ -FormElement* BitfieldReadElement::push_step(const BitfieldManip step, - const TypeSystem& ts, - FormPool& pool) { +FormElement* BitfieldAccessElement::push_step(const BitfieldManip step, + const TypeSystem& ts, + FormPool& pool) { if (m_steps.empty() && step.kind == BitfieldManip::Kind::LEFT_SHIFT) { // for left/right shift combo to get a field. m_steps.push_back(step); @@ -235,13 +274,13 @@ FormElement* BitfieldReadElement::push_step(const BitfieldManip step, return result; } - if (m_steps.empty() && step.kind == BitfieldManip::Kind::LOGAND) { + if (m_steps.empty() && step.kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT) { // and with mask m_steps.push_back(step); return nullptr; } - if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND && + if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT && step.kind == BitfieldManip::Kind::NONZERO_COMPARE) { auto type = ts.lookup_type(m_type); auto as_bitfield = dynamic_cast(type); @@ -257,9 +296,9 @@ FormElement* BitfieldReadElement::push_step(const BitfieldManip step, } } - if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND && + if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT && step.kind == BitfieldManip::Kind::LOGIOR_WITH_CONSTANT_INT) { - // this is setting a bitfield. + // this is setting a bitfield to a constant. // first, let's check that the mask is used properly: u64 mask = m_steps.at(0).amount; u64 value = step.amount; @@ -284,12 +323,36 @@ FormElement* BitfieldReadElement::push_step(const BitfieldManip step, set_value = extract_bitfield(value, field->offset(), field->size()); } - BitfieldFormDef def; + BitFieldDef def; def.field_name = field->name(); def.value = pool.alloc_single_element_form( nullptr, SimpleAtom::make_int_constant(set_value)); return pool.alloc_element(m_type, m_base, - std::vector{def}); + std::vector{def}); + } + + if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT && + step.kind == BitfieldManip::Kind::LOGIOR_WITH_FORM) { + // this is setting a bitfield to a variable + u64 mask = m_steps.at(0).amount; + + auto type = ts.lookup_type(m_type); + auto as_bitfield = dynamic_cast(type); + assert(as_bitfield); + // use the mask to figure out the field. + auto field = find_field_from_mask(ts, as_bitfield, ~mask); + assert(field); + + auto val = get_bitfield_initial_set(step.value, as_bitfield, ts); + assert(val); + if (val->field_name != field->name()) { + throw std::runtime_error("Incompatible bitfield set"); + } + + return pool.alloc_element(m_type, m_base, + std::vector{*val}); + + // todo check that the mask and the set are compatible with eachother } throw std::runtime_error("Unknown state in BitfieldReadElement"); @@ -342,41 +405,6 @@ std::optional get_goal_integer_constant(Form* in, const Env&) { return {}; } -Form* strip_int_or_uint_cast(Form* in) { - auto as_cast = in->try_as_element(); - if (as_cast && (as_cast->type() == TypeSpec("int") || as_cast->type() == TypeSpec("uint"))) { - return as_cast->source(); - } - return in; -} - -std::optional get_bitfield_initial_set(Form* form, - const BitFieldType* type, - const TypeSystem& ts, - const Env&) { - // (shr (shl arg1 59) 44) for example - auto matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::SHR), - {Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::SHL), - {Matcher::any(0), Matcher::any_integer(1)}), - Matcher::any_integer(2)}); - auto mr = match(matcher, strip_int_or_uint_cast(form)); - if (mr.matched) { - auto value = mr.maps.forms.at(0); - int left = mr.maps.ints.at(1); - int right = mr.maps.ints.at(2); - int size = 64 - left; - int offset = left - right; - auto& f = find_field(ts, type, offset, size, {}); - BitFieldDef def; - def.value = value; - def.field_name = f.name(); - def.is_signed = false; // we don't know. - return def; - } - - return {}; -} - } // namespace BitFieldDef BitFieldDef::from_constant(const BitFieldConstantDef& constant, FormPool& pool) { @@ -432,7 +460,7 @@ Form* cast_to_bitfield(const BitFieldType* type_info, // now variables for (auto& arg : args) { - auto maybe_field = get_bitfield_initial_set(arg, type_info, env.dts->ts, env); + auto maybe_field = get_bitfield_initial_set(arg, type_info, env.dts->ts); if (!maybe_field) { // failed, just return cast. return pool.alloc_single_element_form(nullptr, typespec, in); diff --git a/decompiler/IR2/bitfields.h b/decompiler/IR2/bitfields.h index 58574c8f1..f13c41550 100644 --- a/decompiler/IR2/bitfields.h +++ b/decompiler/IR2/bitfields.h @@ -13,12 +13,15 @@ struct BitfieldManip { RIGHT_SHIFT_LOGICAL, RIGHT_SHIFT_LOGICAL_32BIT, RIGHT_SHIFT_ARITH, - LOGAND, + LOGAND_WITH_CONSTANT_INT, LOGIOR_WITH_CONSTANT_INT, + LOGIOR_WITH_FORM, + LOGAND_WITH_FORM, NONZERO_COMPARE, INVALID } kind = Kind::INVALID; s64 amount = -1; + Form* value = nullptr; bool is_right_shift() const { return kind == Kind::RIGHT_SHIFT_ARITH || kind == Kind::RIGHT_SHIFT_LOGICAL || @@ -44,11 +47,20 @@ struct BitfieldManip { } BitfieldManip(Kind k, s64 imm) : kind(k), amount(imm) {} + static BitfieldManip from_form(Kind k, Form* val) { + BitfieldManip result; + result.kind = k; + result.value = val; + return result; + } + + private: + BitfieldManip() = default; }; -class BitfieldReadElement : public FormElement { +class BitfieldAccessElement : public FormElement { public: - BitfieldReadElement(Form* base_value, const TypeSpec& ts); + BitfieldAccessElement(Form* base_value, const TypeSpec& ts); 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; @@ -95,11 +107,6 @@ class BitfieldStaticDefElement : public FormElement { std::vector m_field_defs; }; -struct BitfieldFormDef { - Form* value; - std::string field_name; -}; - /*! * This represents copying a bitfield object, then modifying the type. * It's an intermediate step to modifying a bitfield in place and it's not expected to appear @@ -109,7 +116,7 @@ class ModifiedCopyBitfieldElement : public FormElement { public: ModifiedCopyBitfieldElement(const TypeSpec& type, Form* base, - const std::vector& field_modifications); + const std::vector& field_modifications); 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; @@ -117,12 +124,12 @@ class ModifiedCopyBitfieldElement : public FormElement { void get_modified_regs(RegSet& regs) const override; Form* base() const { return m_base; } - const std::vector mods() const { return m_field_modifications; } + const std::vector mods() const { return m_field_modifications; } private: TypeSpec m_type; Form* m_base = nullptr; - std::vector m_field_modifications; + std::vector m_field_modifications; }; Form* cast_to_bitfield(const BitFieldType* type_info, diff --git a/decompiler/config/all-types.gc b/decompiler/config/all-types.gc index 1fe487f4b..57e78a195 100644 --- a/decompiler/config/all-types.gc +++ b/decompiler/config/all-types.gc @@ -2655,9 +2655,9 @@ ;;(define-extern dma-buffer object) ;; unknown type (define-extern dma-buffer-send-chain (function dma-bank-source dma-buffer none)) (define-extern dma-buffer-send (function dma-bank dma-buffer none)) -(define-extern dma-bucket-insert-tag (function dma-bucket int dma-bucket (pointer uint64) dma-bucket)) +(define-extern dma-bucket-insert-tag (function dma-bucket int pointer (pointer dma-tag) pointer)) (define-extern dma-buffer-add-buckets (function dma-buffer int none)) -(define-extern dma-buffer-patch-buckets (function dma-bucket int int)) +(define-extern dma-buffer-patch-buckets (function dma-bucket int dma-bucket)) ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc b/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc index cc2538bcd..859b84b46 100644 --- a/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc +++ b/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc @@ -160,14 +160,17 @@ ], "dma-buffer-patch-buckets": [ - [3, "a0", "dma-bucket"], - [[3, 5], "a2", "dma-bucket"], [7, "a0", "pointer"], - [11, "a0", "dma-bucket"], - //[12, "a2", "uint"], - [13, "a0", "dma-bucket"], [14, "a0", "pointer"], - [[15, 17], "a0", "dma-bucket"] + [3, "a0", "dma-bucket"], + [11, "a0", "dma-bucket"], + [13, "a0", "dma-bucket"], + [19, "a0", "dma-bucket"] + ], + + "dma-bucket-insert-tag": [ + [[2, 6], "v1", "dma-bucket"], + [3, "a0", "dma-bucket"] ], // LEVEL diff --git a/decompiler/config/jak1_ntsc_black_label/var_names.jsonc b/decompiler/config/jak1_ntsc_black_label/var_names.jsonc index 6a6bf4fb2..4f3ab40df 100644 --- a/decompiler/config/jak1_ntsc_black_label/var_names.jsonc +++ b/decompiler/config/jak1_ntsc_black_label/var_names.jsonc @@ -678,7 +678,8 @@ }, "dma-bucket-insert-tag": { - "args": ["base", "idx", "tag-start", "tag-end"] + "args": ["base", "idx", "tag-start", "tag-end"], + "vars": { "v1-1": "bucket" } }, "disasm-vif-details": { diff --git a/goal_src/engine/dma/dma-bucket.gc b/goal_src/engine/dma/dma-bucket.gc index 23dbf61e9..e29e96dd5 100644 --- a/goal_src/engine/dma/dma-bucket.gc +++ b/goal_src/engine/dma/dma-bucket.gc @@ -52,35 +52,31 @@ (defun dma-buffer-patch-buckets ((bucket dma-bucket) (count int)) - "Patch last pointers in a sequence of buckets. - Call this after you have added everything to buckets." - (local-vars (i int)) + "After adding all data to buckets, call this to stitch together the chains for + count consecutive buckets" (when (nonzero? bucket) - (set! i 0) - (while (< i count) - ;; make last tag of this bucket point to the next bucket. - (set! (-> bucket last 0) - (logior (logand (-> bucket last 0) #x80000000ffffffff) - (shr (shl (+ (the-as uint bucket) 16) 33) 1) - ) - ) - ;; clear last, and move on to the next bucket. - (set! (-> bucket last) (the (pointer dma-tag) 0)) + (dotimes (i count) + ;; set last tag's address to the next bucket. + (set! (-> bucket last 0 addr) (the-as int (&+ (the-as pointer bucket) 16))) + ;; clear last pointer. + (set! (-> bucket last) (the-as (pointer dma-tag) 0)) + ;; next bucket (set! bucket (&+ bucket 16)) - (set! i (+ i 1)) ) ) - (the-as int bucket) + bucket ) -(defun dma-bucket-insert-tag ((base dma-bucket) (idx int) (tag-start dma-bucket) (tag-end (pointer uint64))) - "Splice in a tag." - (local-vars (bucket dma-bucket)) - ;; seek to bucket we want to splice before. - (set! bucket (&+ base (shl idx 4))) - ;; append to last tag (kind of a hack here with the types) - (set! (-> (the dma-bucket (-> bucket last)) next) (the uint tag-start)) - ;; make prev = end of tag so we can add more. - (set! (-> bucket last) (the (pointer dma-tag) tag-end)) +(defun dma-bucket-insert-tag ((base dma-bucket) (idx int) (tag-start pointer) (tag-end (pointer dma-tag))) + "Add a dma chain to the idx bucket" + + ;; find the bucket + (let ((bucket (the-as dma-bucket (&+ base (the-as uint (shl idx 4)))))) + ;; update our last bucket to point to this one. + ;; this is abusing the dma-bucket type to set the "addr" field of the dma-tag. + (set! (-> (the-as dma-bucket (-> bucket last)) next) (the-as uint tag-start)) + ;; remember this as the last tag in the bucket. + (set! (-> bucket last) tag-end) + ) tag-start ) diff --git a/test/decompiler/reference/dma-bucket_REF.gc b/test/decompiler/reference/dma-bucket_REF.gc new file mode 100644 index 000000000..27c5bfc06 --- /dev/null +++ b/test/decompiler/reference/dma-bucket_REF.gc @@ -0,0 +1,60 @@ +;;-*-Lisp-*- +(in-package goal) + +;; definition for function dma-buffer-add-buckets +;; INFO: Return type mismatch pointer vs none. +(defun dma-buffer-add-buckets ((dma-buf dma-buffer) (count int)) + (let ((current-bucket (the-as dma-bucket (-> dma-buf base)))) + (dotimes (i count) + (set! + (-> current-bucket tag) + (new 'static 'dma-tag + :id #x2 + :addr (the-as int (&+ (the-as pointer current-bucket) 16)) + ) + ) + (set! (-> current-bucket last) (the-as (pointer dma-tag) current-bucket)) + (set! + current-bucket + (the-as dma-bucket (&+ (the-as pointer current-bucket) 16)) + ) + ) + (set! (-> dma-buf base) (the-as pointer current-bucket)) + ) + (none) + ) + +;; definition for function dma-buffer-patch-buckets +(defun dma-buffer-patch-buckets ((bucket dma-bucket) (count int)) + (when (nonzero? bucket) + (dotimes (i count) + (set! (-> bucket last 0 addr) (the-as int (&+ (the-as pointer bucket) 16))) + (set! (-> bucket last) (the-as (pointer dma-tag) 0)) + (set! bucket (the-as dma-bucket (&+ (the-as pointer bucket) 16))) + ) + ) + bucket + ) + +;; definition for function dma-bucket-insert-tag +(defun + dma-bucket-insert-tag + ((base dma-bucket) (idx int) (tag-start pointer) (tag-end (pointer dma-tag))) + (let + ((bucket + (the-as dma-bucket (+ (the-as uint base) (the-as uint (shl idx 4)))) + ) + ) + (set! (-> (the-as dma-bucket (-> bucket last)) next) (the-as uint tag-start)) + (set! (-> bucket last) tag-end) + ) + tag-start + ) + +;; failed to figure out what this is: +(let ((v0-0 0)) + ) + +;; failed to figure out what this is: +(none) + diff --git a/test/decompiler/test_FormExpressionBuild2.cpp b/test/decompiler/test_FormExpressionBuild2.cpp index 3a47e5271..eecac1f8e 100644 --- a/test/decompiler/test_FormExpressionBuild2.cpp +++ b/test/decompiler/test_FormExpressionBuild2.cpp @@ -997,3 +997,30 @@ TEST_F(FormRegressionTest, DmaBufferAddVuFunction) { " )"; test_with_expr(func, type, expected, false, "", {}, "[[[9, 33], \"t2\", \"dma-packet\"]]"); } + +TEST_F(FormRegressionTest, DmaBucketInsertTag) { + std::string func = + "sll r0, r0, 0\n" + " dsll v1, a1, 4\n" + " daddu v1, a0, v1\n" + " lwu a0, 8(v1)\n" + " sw a2, 4(a0)\n" + " sw a3, 8(v1)\n" + " or v0, a2, r0\n" + " jr ra\n" + " daddu sp, sp, r0"; + std::string type = "(function dma-bucket int pointer (pointer dma-tag) pointer)"; + std::string expected = + "(begin\n" + " (let ((v1-1 (the-as dma-bucket (+ (the-as uint arg0) (the-as uint (shl arg1 4))))))\n" + " (set! (-> (the-as dma-bucket (-> v1-1 last)) next) (the-as uint arg2))\n" + " (set! (-> v1-1 last) arg3)\n" + " )\n" + " arg2\n" + " )"; + test_with_expr(func, type, expected, false, "", {}, + "[\n" + " [[2, 6], \"v1\", \"dma-bucket\"],\n" + " [3, \"a0\", \"dma-bucket\"]\n" + " ]"); +} diff --git a/test/offline/offline_test_main.cpp b/test/offline/offline_test_main.cpp index 6e00647f5..5dbbae046 100644 --- a/test/offline/offline_test_main.cpp +++ b/test/offline/offline_test_main.cpp @@ -17,6 +17,7 @@ const std::unordered_set g_object_files_to_decompile = { "trigonometry-h", /* transformq-h */ "matrix", "transform", "quaternion", "euler", /* geometry, trigonometry, */ "gsound-h", "timer-h", "timer", "vif-h", "dma-h", "video-h", "vu1-user-h", "dma", "dma-buffer", + "dma-bucket", /* gap */ "bounding-box", /* gap */ @@ -31,7 +32,7 @@ const std::vector g_object_files_to_check_against_reference = { /* transformq-h, */ "matrix", "transform", "quaternion", "euler", /* geometry, trigonometry */ "gsound-h", "timer-h", /* timer, */ "vif-h", "dma-h", "video-h", "vu1-user-h", "dma", - "dma-buffer", + "dma-buffer", "dma-bucket", /* gap */ "bounding-box", /* gap */ "sync-info-h", "sync-info"};