diff --git a/decompiler/Disasm/InstructionParser.cpp b/decompiler/Disasm/InstructionParser.cpp index b3843495b9..4540d20237 100644 --- a/decompiler/Disasm/InstructionParser.cpp +++ b/decompiler/Disasm/InstructionParser.cpp @@ -43,7 +43,8 @@ InstructionParser::InstructionParser() { InstructionKind::BGTZ, InstructionKind::BLTZL, InstructionKind::BGTZL, InstructionKind::BGEZL, InstructionKind::MTC1, InstructionKind::MFC1, InstructionKind::MFLO, InstructionKind::MFHI, InstructionKind::MTLO1, - InstructionKind::MFLO1, InstructionKind::SYNCL, InstructionKind::PCPYUD}) { + InstructionKind::MFLO1, InstructionKind::SYNCL, InstructionKind::PCPYUD, + InstructionKind::PEXTUW, InstructionKind::POR}) { auto& info = gOpcodeInfo[int(i)]; if (info.defined) { m_opcode_name_lookup[info.name] = int(i); diff --git a/decompiler/IR2/AtomicOpTypeAnalysis.cpp b/decompiler/IR2/AtomicOpTypeAnalysis.cpp index b3824ebf1b..c9db63123b 100644 --- a/decompiler/IR2/AtomicOpTypeAnalysis.cpp +++ b/decompiler/IR2/AtomicOpTypeAnalysis.cpp @@ -289,7 +289,11 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input, // this could be a bitfield access or a multiply. // we pick bitfield access if the parent is a bitfield. if (dynamic_cast(dts.ts.lookup_type(arg0_type.typespec()))) { - return TP_Type::make_from_left_shift_bitfield(arg0_type.typespec(), m_args[1].get_int()); + return TP_Type::make_from_left_shift_bitfield(arg0_type.typespec(), m_args[1].get_int(), + false); + } else if (arg0_type.kind == TP_Type::Kind::PCPYUD_BITFIELD) { + return TP_Type::make_from_left_shift_bitfield(arg0_type.get_bitfield_type(), + m_args[1].get_int(), true); } else { return TP_Type::make_from_product(1ull << m_args[1].get_int(), is_signed(dts, arg0_type)); } @@ -307,6 +311,9 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input, if (arg0_type.kind == TP_Type::Kind::LEFT_SHIFTED_BITFIELD && m_args[1].is_int()) { // second op in left/right shift combo int end_bit = 64 - arg0_type.get_left_shift(); + if (arg0_type.pcpyud()) { + end_bit += 64; + } int size = 64 - m_args[1].get_int(); int start_bit = end_bit - size; @@ -592,6 +599,35 @@ TypeState AsmOp::propagate_types_internal(const TypeState& input, return result; } + if (m_instr.kind == InstructionKind::PCPYUD) { + if (m_src[1] && m_src[1]->reg() == Register(Reg::GPR, Reg::R0)) { + assert(m_src[0]); + auto& in_type = result.get(m_src[0]->reg()); + auto bf = dynamic_cast(dts.ts.lookup_type(in_type.typespec())); + if (bf) { + assert(m_dst); + result.get(m_dst->reg()) = TP_Type::make_from_pcpyud_bitfield(in_type.typespec()); + return result; + } + } + } + + // pextuw t0, r0, gp + if (m_instr.kind == InstructionKind::PEXTUW) { + if (m_src[0] && m_src[0]->reg() == Register(Reg::GPR, Reg::R0)) { + // sponge + assert(m_src[1]); + auto type = dts.ts.lookup_type(result.get(m_src[1]->reg()).typespec()); + auto as_bitfield = dynamic_cast(type); + if (as_bitfield) { + auto field = find_field(dts.ts, as_bitfield, 64, 32, true); + assert(m_dst); + result.get(m_dst->reg()) = TP_Type::make_from_ts(field.type()); + return result; + } + } + } + if (m_dst.has_value()) { auto kind = m_dst->reg().get_kind(); if (kind == Reg::FPR) { diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index 9fd7ce4392..d3c260e2e1 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -1121,12 +1121,22 @@ void SimpleExpressionElement::update_from_stack_left_shift(const Env& env, } else { // try to turn this into a multiplication, if possible if (m_expr.get_arg(1).is_int()) { + auto args = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects); int sa = m_expr.get_arg(1).get_int(); + + auto as_ba = args.at(0)->try_as_element(); + if (as_ba) { + BitfieldManip step(BitfieldManip::Kind::LEFT_SHIFT, m_expr.get_arg(1).get_int()); + auto other = as_ba->push_step(step, env.dts->ts, pool, env); + assert(!other); // shouldn't be complete. + result->push_back(as_ba); + return; + } + // somewhat arbitrary threshold to switch from multiplications to shift. if (sa < 10) { s64 multiplier = (s64(1) << sa); - auto args = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects); auto new_form = pool.alloc_element( GenericOperator::make_fixed(FixedOperatorKind::MULTIPLICATION), args.at(0), pool.alloc_single_element_form( @@ -1134,6 +1144,33 @@ void SimpleExpressionElement::update_from_stack_left_shift(const Env& env, result->push_back(new_form); return; } + + auto arg0_i = is_int_type(env, m_my_idx, m_expr.get_arg(0).var()); + auto arg0_u = is_uint_type(env, m_my_idx, m_expr.get_arg(0).var()); + if (!arg0_i && !arg0_u) { + auto bti = dynamic_cast(env.dts->ts.lookup_type(arg0_type)); + if (bti) { + auto new_form = pool.alloc_element( + GenericOperator::make_fixed(FixedOperatorKind::SHL), args.at(0), + cast_form( + pool.alloc_single_element_form(nullptr, m_expr.get_arg(1)), + arg0_type, pool, env)); + result->push_back(new_form); + } else { + auto new_form = pool.alloc_element( + GenericOperator::make_fixed(FixedOperatorKind::SHL), + pool.alloc_single_element_form(nullptr, TypeSpec("int"), args.at(0)), + pool.alloc_single_element_form(nullptr, m_expr.get_arg(1))); + result->push_back(new_form); + } + } else { + auto new_form = pool.alloc_element( + GenericOperator::make_fixed(FixedOperatorKind::SHL), args.at(0), + pool.alloc_single_element_form(nullptr, m_expr.get_arg(1))); + result->push_back(new_form); + } + + return; } update_from_stack_copy_first_int_2(env, FixedOperatorKind::SHL, pool, stack, result, @@ -1226,22 +1263,52 @@ void SimpleExpressionElement::update_from_stack_right_shift_arith(const Env& env assert(other); // should be a high field. result->push_back(other); } else { - if (m_expr.get_arg(1).is_int() && m_expr.get_arg(1).get_int() < 10) { - auto arg0_i = is_int_type(env, m_my_idx, m_expr.get_arg(0).var()); - auto arg = - pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0); - int sa = m_expr.get_arg(1).get_int(); + if (m_expr.get_arg(1).is_int()) { + if (m_expr.get_arg(1).get_int() < 10) { + auto arg0_i = is_int_type(env, m_my_idx, m_expr.get_arg(0).var()); + auto arg = + pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects).at(0); + int sa = m_expr.get_arg(1).get_int(); - if (!arg0_i) { - arg = cast_form(arg, TypeSpec("int"), pool, env); + if (!arg0_i) { + arg = cast_form(arg, TypeSpec("int"), pool, env); + } + s64 multiplier = (s64(1) << sa); + + auto new_form = pool.alloc_element( + GenericOperator::make_fixed(FixedOperatorKind::DIVISION), arg, + pool.alloc_single_element_form( + nullptr, SimpleAtom::make_int_constant(multiplier))); + result->push_back(new_form); + return; + } + + auto arg0_i = is_int_type(env, m_my_idx, m_expr.get_arg(0).var()); + auto arg0_u = is_uint_type(env, m_my_idx, m_expr.get_arg(0).var()); + auto args = pop_to_forms({m_expr.get_arg(0).var()}, env, pool, stack, allow_side_effects); + + auto as_ba = args.at(0)->try_as_element(); + if (as_ba) { + BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_ARITH, m_expr.get_arg(1).get_int()); + auto other = as_ba->push_step(step, env.dts->ts, pool, env); + assert(other); // should be a high field. + result->push_back(other); + return; + } + + if (!arg0_i && !arg0_u) { + auto new_form = pool.alloc_element( + GenericOperator::make_fixed(FixedOperatorKind::SAR), + pool.alloc_single_element_form(nullptr, TypeSpec("int"), args.at(0)), + pool.alloc_single_element_form(nullptr, m_expr.get_arg(1))); + result->push_back(new_form); + } else { + auto new_form = pool.alloc_element( + GenericOperator::make_fixed(FixedOperatorKind::SAR), args.at(0), + pool.alloc_single_element_form(nullptr, m_expr.get_arg(1))); + result->push_back(new_form); } - s64 multiplier = (s64(1) << sa); - auto new_form = pool.alloc_element( - GenericOperator::make_fixed(FixedOperatorKind::DIVISION), arg, - pool.alloc_single_element_form( - nullptr, SimpleAtom::make_int_constant(multiplier))); - result->push_back(new_form); return; } @@ -2728,7 +2795,7 @@ void ReturnElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta namespace { void push_asm_srl_to_stack(const AsmOp* op, - FormElement* form_elt, + FormElement* /*form_elt*/, const Env& env, FormPool& pool, FormStack& stack) { @@ -2754,6 +2821,123 @@ void push_asm_srl_to_stack(const AsmOp* op, assert(other); // should be a high field. stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, other), true, env.get_variable_type(*dst, true)); + } else { + // stack.push_form_element(form_elt, true); + auto src_var = pop_to_forms({*var}, env, pool, stack, true).at(0); + auto as_ba = src_var->try_as_element(); + if (as_ba) { + BitfieldManip step(BitfieldManip::Kind::RIGHT_SHIFT_LOGICAL_32BIT, integer); + auto other = as_ba->push_step(step, env.dts->ts, pool, env); + assert(other); // should immediately get a field. + stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, other), true, + env.get_variable_type(*dst, true)); + } else { + throw std::runtime_error("Got invalid bitfield manip for srl"); + } + } +} + +void push_asm_sllv_to_stack(const AsmOp* op, + FormElement* form_elt, + const Env& env, + FormPool& pool, + FormStack& stack) { + auto var = op->src(0); + assert(var.has_value()); + + auto dst = op->dst(); + assert(dst.has_value()); + + auto sav = op->src(1); + assert(sav.has_value()); + + auto arg0_type = env.get_variable_type(*var, true); + auto type_info = env.dts->ts.lookup_type(arg0_type); + auto bitfield_info = dynamic_cast(type_info); + if (sav->reg() == Register(Reg::GPR, Reg::R0)) { + if (bitfield_info) { + auto base = pop_to_forms({*var}, env, pool, stack, true).at(0); + auto read_elt = pool.alloc_element(base, arg0_type); + BitfieldManip step(BitfieldManip::Kind::SLLV_SEXT, 0); + auto other = read_elt->push_step(step, env.dts->ts, pool, env); + assert(other); // should immediately get a field. + stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, other), true, + env.get_variable_type(*dst, true)); + } else { + auto src_var = pop_to_forms({*var}, env, pool, stack, true).at(0); + auto as_ba = src_var->try_as_element(); + if (as_ba) { + BitfieldManip step(BitfieldManip::Kind::SLLV_SEXT, 0); + auto other = as_ba->push_step(step, env.dts->ts, pool, env); + assert(other); // should immediately get a field. + stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, other), true, + env.get_variable_type(*dst, true)); + } else { + throw std::runtime_error("Got invalid bitfield manip for sllv"); + } + } + } else { + stack.push_form_element(form_elt, true); + } +} + +void push_asm_pcpyud_to_stack(const AsmOp* op, + FormElement* form_elt, + const Env& env, + FormPool& pool, + FormStack& stack) { + // pcpyud v1, gp, r0 for example. + + auto var = op->src(0); + assert(var.has_value()); + + auto dst = op->dst(); + assert(dst.has_value()); + + auto possible_r0 = op->src(1); + assert(possible_r0.has_value()); + + auto arg0_type = env.get_variable_type(*var, true); + auto type_info = env.dts->ts.lookup_type(arg0_type); + auto bitfield_info = dynamic_cast(type_info); + if (bitfield_info && possible_r0->reg() == Register(Reg::GPR, Reg::R0)) { + auto base = pop_to_forms({*var}, env, pool, stack, true).at(0); + auto read_elt = pool.alloc_element(base, arg0_type); + read_elt->push_pcpyud(); + stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, read_elt), true, + env.get_variable_type(*dst, true)); + } else { + stack.push_form_element(form_elt, true); + } +} + +void push_asm_pextuw_to_stack(const AsmOp* op, + FormElement* form_elt, + const Env& env, + FormPool& pool, + FormStack& stack) { + // (.pextuw t0-0 r0-0 obj) + + auto var = op->src(1); + assert(var.has_value()); + + auto dst = op->dst(); + assert(dst.has_value()); + + auto possible_r0 = op->src(0); + assert(possible_r0.has_value()); + + auto arg0_type = env.get_variable_type(*var, true); + auto type_info = env.dts->ts.lookup_type(arg0_type); + auto bitfield_info = dynamic_cast(type_info); + if (bitfield_info && possible_r0->reg() == Register(Reg::GPR, Reg::R0)) { + auto base = pop_to_forms({*var}, env, pool, stack, true).at(0); + auto read_elt = pool.alloc_element(base, arg0_type); + BitfieldManip step(BitfieldManip::Kind::PEXTUW, 0); + auto other = read_elt->push_step(step, env.dts->ts, pool, env); + assert(other); // should immediately get a field. + stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, other), true, + env.get_variable_type(*dst, true)); } else { stack.push_form_element(form_elt, true); } @@ -2768,6 +2952,15 @@ void push_asm_to_stack(const AsmOp* op, case InstructionKind::SRL: push_asm_srl_to_stack(op, form_elt, env, pool, stack); break; + case InstructionKind::SLLV: + push_asm_sllv_to_stack(op, form_elt, env, pool, stack); + break; + case InstructionKind::PCPYUD: + push_asm_pcpyud_to_stack(op, form_elt, env, pool, stack); + break; + case InstructionKind::PEXTUW: + push_asm_pextuw_to_stack(op, form_elt, env, pool, stack); + break; default: stack.push_form_element(form_elt, true); break; diff --git a/decompiler/IR2/bitfields.cpp b/decompiler/IR2/bitfields.cpp index 4d1e45126b..26c744c337 100644 --- a/decompiler/IR2/bitfields.cpp +++ b/decompiler/IR2/bitfields.cpp @@ -171,7 +171,9 @@ const BitField& find_field(const TypeSystem& ts, std::optional looking_for_unsigned) { for (auto& field : type->fields()) { if (field.size() == size && field.offset() == start_bit) { - bool is_int = ts.tc(TypeSpec("integer"), field.type()); + // the GOAL compiler sign extended floats. + bool is_int = + ts.tc(TypeSpec("integer"), field.type()) || ts.tc(TypeSpec("float"), field.type()); bool is_uint = ts.tc(TypeSpec("uinteger"), field.type()); // allow sign match, or unsigned+not a number. @@ -182,6 +184,12 @@ const BitField& find_field(const TypeSystem& ts, // matched! return field; } + + // this is a hack to work around bad code where extracting the (pointer process) + // would sign extend the address. + if (!is_int && !is_uint && type->get_name() == "handle") { + return field; + } } else { return field; } @@ -286,6 +294,13 @@ std::optional get_bitfield_initial_set(Form* form, } // namespace +void BitfieldAccessElement::push_pcpyud() { + if (!m_steps.empty()) { + throw std::runtime_error("Unexpected pcpyud!"); + } + m_got_pcpyud = true; +} + /*! * Add a step to the bitfield access. If this completes the access, returns a form representing the * access @@ -294,6 +309,8 @@ FormElement* BitfieldAccessElement::push_step(const BitfieldManip step, const TypeSystem& ts, FormPool& pool, const Env& env) { + int pcpyud_offset = m_got_pcpyud ? 64 : 0; + if (m_steps.empty() && step.kind == BitfieldManip::Kind::LEFT_SHIFT) { // for left/right shift combo to get a field. m_steps.push_back(step); @@ -305,7 +322,38 @@ FormElement* BitfieldAccessElement::push_step(const BitfieldManip step, bool is_unsigned = step.right_shift_unsigned(); int shift_size = step.get_shift_start_bit(); int size = shift_size - step.amount; - int start_bit = shift_size - size; + int start_bit = pcpyud_offset + shift_size - size; + auto type = ts.lookup_type(m_type); + auto as_bitfield = dynamic_cast(type); + assert(as_bitfield); + auto field = find_field(ts, as_bitfield, start_bit, size, is_unsigned); + auto result = + pool.alloc_element(m_base, false, DerefToken::make_field_name(field.name())); + result->inline_nested(); + return result; + } + + if (m_steps.empty() && step.kind == BitfieldManip::Kind::SLLV_SEXT) { + bool is_unsigned = false; + int size = 32; + int start_bit = 0 + pcpyud_offset; + auto type = ts.lookup_type(m_type); + auto as_bitfield = dynamic_cast(type); + assert(as_bitfield); + auto field = find_field(ts, as_bitfield, start_bit, size, is_unsigned); + auto result = + pool.alloc_element(m_base, false, DerefToken::make_field_name(field.name())); + result->inline_nested(); + return result; + } + + if (m_steps.empty() && step.kind == BitfieldManip::Kind::PEXTUW) { + if (m_got_pcpyud) { + throw std::runtime_error("unknown pcpyud PEXTUW sequence in bitfield"); + } + bool is_unsigned = true; + int size = 32; + int start_bit = 64; auto type = ts.lookup_type(m_type); auto as_bitfield = dynamic_cast(type); assert(as_bitfield); @@ -318,7 +366,7 @@ FormElement* BitfieldAccessElement::push_step(const BitfieldManip step, if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LEFT_SHIFT) { // second op in left/right shift combo - int end_bit = 64 - m_steps.at(0).amount; + int end_bit = pcpyud_offset + 64 - m_steps.at(0).amount; if (!step.is_right_shift()) { throw std::runtime_error("Unexpected step 1"); } @@ -342,12 +390,18 @@ FormElement* BitfieldAccessElement::push_step(const BitfieldManip step, if (m_steps.empty() && step.kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT) { // and with mask + if (m_got_pcpyud) { + throw std::runtime_error("unknown pcpyud LOGAND_WITH_CONSTANT_INT sequence in bitfield"); + } m_steps.push_back(step); return nullptr; } if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT && step.kind == BitfieldManip::Kind::NONZERO_COMPARE) { + if (m_got_pcpyud) { + throw std::runtime_error("unknown pcpyud NONZERO_COMPARE sequence in bitfield"); + } auto type = ts.lookup_type(m_type); auto as_bitfield = dynamic_cast(type); assert(as_bitfield); @@ -364,6 +418,9 @@ FormElement* BitfieldAccessElement::push_step(const BitfieldManip step, if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT && step.kind == BitfieldManip::Kind::LOGIOR_WITH_CONSTANT_INT) { + if (m_got_pcpyud) { + throw std::runtime_error("unknown pcpyud LOGIOR_WITH_CONSTANT_INT sequence in 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; @@ -399,6 +456,9 @@ FormElement* BitfieldAccessElement::push_step(const BitfieldManip step, if (m_steps.size() == 1 && m_steps.at(0).kind == BitfieldManip::Kind::LOGAND_WITH_CONSTANT_INT && step.kind == BitfieldManip::Kind::LOGIOR_WITH_FORM) { + if (m_got_pcpyud) { + throw std::runtime_error("unknown pcpyud LOGIOR_WITH_FORM sequence in bitfield"); + } // this is setting a bitfield to a variable u64 mask = m_steps.at(0).amount; diff --git a/decompiler/IR2/bitfields.h b/decompiler/IR2/bitfields.h index efb28993ff..7031db95aa 100644 --- a/decompiler/IR2/bitfields.h +++ b/decompiler/IR2/bitfields.h @@ -18,6 +18,8 @@ struct BitfieldManip { LOGIOR_WITH_FORM, LOGAND_WITH_FORM, NONZERO_COMPARE, + SLLV_SEXT, // sllv x, y, r0 + PEXTUW, INVALID } kind = Kind::INVALID; s64 amount = -1; @@ -72,6 +74,10 @@ struct BitfieldManip { return "logand-form"; case Kind::NONZERO_COMPARE: return "nonzero-compare"; + case Kind::SLLV_SEXT: + return "sllv-sext"; + case Kind::PEXTUW: + return "pextuw"; case Kind::INVALID: default: assert(false); @@ -94,8 +100,10 @@ class BitfieldAccessElement : public FormElement { const TypeSystem& ts, FormPool& pool, const Env& env); + void push_pcpyud(); private: + bool m_got_pcpyud = false; Form* m_base = nullptr; TypeSpec m_type; std::vector m_steps; diff --git a/decompiler/analysis/atomic_op_builder.cpp b/decompiler/analysis/atomic_op_builder.cpp index 4c8250743a..94d0cbae95 100644 --- a/decompiler/analysis/atomic_op_builder.cpp +++ b/decompiler/analysis/atomic_op_builder.cpp @@ -215,7 +215,6 @@ std::unique_ptr make_standard_store(const Instruction& i0, std::unique_ptr make_asm_op(const Instruction& i0, int idx) { switch (i0.kind) { - case InstructionKind::POR: case InstructionKind::SLLV: // goal will use dsllv case InstructionKind::SLL: // goal will use dsll case InstructionKind::PCPYUD: @@ -442,6 +441,14 @@ std::unique_ptr convert_or_1(const Instruction& i0, int idx) { return std::make_unique(dest, src, idx); } +std::unique_ptr convert_por_1(const Instruction& i0, int idx) { + if (is_gpr_3(i0, InstructionKind::POR, {}, {}, rr0())) { + return std::make_unique(make_dst_var(i0, idx), + make_src_atom(i0.get_src(0).get_reg(), idx).as_expr(), idx); + } + return std::make_unique(i0, idx); +} + std::unique_ptr convert_ori_1(const Instruction& i0, int idx) { auto dest = make_dst_var(i0, idx); SimpleExpression src; @@ -713,6 +720,8 @@ std::unique_ptr convert_1(const Instruction& i0, int idx, bool hint_in switch (i0.kind) { case InstructionKind::OR: return convert_or_1(i0, idx); + case InstructionKind::POR: + return convert_por_1(i0, idx); case InstructionKind::ORI: return convert_ori_1(i0, idx); case InstructionKind::AND: diff --git a/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc b/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc index bfef2aee4f..49a1e3ebe3 100644 --- a/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc +++ b/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc @@ -381,15 +381,12 @@ ], // RES - "(method 4 res-tag)": [[12, "a0", "type"]], "(method 19 res-lump)": [ [46, "t2", "(pointer uint64)"], [100, "t3", "(pointer uint64)"], [184, "t5", "(pointer uint64)"], [64, "t6", "(pointer uint64)"] ], - "(method 13 res-lump)": [[6, "a0", "res-tag"]], - "(method 14 res-lump)": [[2, "a0", "res-tag"]], // SHADOW-CPU-H "(method 10 shadow-control)": [[1, "v1", "int"]], diff --git a/decompiler/util/TP_Type.cpp b/decompiler/util/TP_Type.cpp index 38c330eedf..6b9c176f07 100644 --- a/decompiler/util/TP_Type.cpp +++ b/decompiler/util/TP_Type.cpp @@ -60,7 +60,13 @@ std::string TP_Type::print() const { case Kind::NON_VIRTUAL_METHOD: return fmt::format("", m_ts.print()); case Kind::LEFT_SHIFTED_BITFIELD: - return fmt::format("(<{}> << {})", m_ts.print(), m_int); + if (m_pcpyud) { + return fmt::format("(<{}> << {}) :u", m_ts.print(), m_int); + } else { + return fmt::format("(<{}> << {})", m_ts.print(), m_int); + } + case Kind::PCPYUD_BITFIELD: + return fmt::format("", m_ts.print()); case Kind::INVALID: default: assert(false); @@ -108,7 +114,9 @@ bool TP_Type::operator==(const TP_Type& other) const { return m_int == other.m_int && m_ts == other.m_ts && m_extra_multiplier == other.m_extra_multiplier; case Kind::LEFT_SHIFTED_BITFIELD: - return m_int == other.m_int && m_ts == other.m_ts; + return m_int == other.m_int && m_ts == other.m_ts && m_pcpyud == other.m_pcpyud; + case Kind::PCPYUD_BITFIELD: + return m_pcpyud == other.m_pcpyud && m_ts == other.m_ts; case Kind::INVALID: default: assert(false); @@ -163,6 +171,8 @@ TypeSpec TP_Type::typespec() const { return m_ts; case Kind::LEFT_SHIFTED_BITFIELD: return TypeSpec("int"); // ideally this is never used. + case Kind::PCPYUD_BITFIELD: + return TypeSpec("int"); case Kind::INVALID: default: assert(false); diff --git a/decompiler/util/TP_Type.h b/decompiler/util/TP_Type.h index 7c893fbf1b..5306a2b132 100644 --- a/decompiler/util/TP_Type.h +++ b/decompiler/util/TP_Type.h @@ -32,6 +32,7 @@ class TP_Type { DYNAMIC_METHOD_ACCESS, // partial access into a VIRTUAL_METHOD, NON_VIRTUAL_METHOD, + PCPYUD_BITFIELD, LEFT_SHIFTED_BITFIELD, // (bitfield << some-constant) INVALID } kind = Kind::UNINITIALIZED; @@ -59,6 +60,7 @@ class TP_Type { case Kind::VIRTUAL_METHOD: case Kind::NON_VIRTUAL_METHOD: case Kind::LEFT_SHIFTED_BITFIELD: + case Kind::PCPYUD_BITFIELD: return false; case Kind::UNINITIALIZED: case Kind::OBJECT_NEW_METHOD: @@ -211,11 +213,20 @@ class TP_Type { return result; } - static TP_Type make_from_left_shift_bitfield(const TypeSpec& ts, int amount) { + static TP_Type make_from_left_shift_bitfield(const TypeSpec& ts, int amount, bool pcpyud) { TP_Type result; result.kind = Kind::LEFT_SHIFTED_BITFIELD; result.m_ts = ts; result.m_int = amount; + result.m_pcpyud = pcpyud; + return result; + } + + static TP_Type make_from_pcpyud_bitfield(const TypeSpec& ts) { + TP_Type result; + result.kind = Kind::PCPYUD_BITFIELD; + result.m_ts = ts; + result.m_pcpyud = true; return result; } @@ -265,14 +276,23 @@ class TP_Type { } const TypeSpec& get_bitfield_type() const { - assert(kind == Kind::LEFT_SHIFTED_BITFIELD); + assert(kind == Kind::LEFT_SHIFTED_BITFIELD || kind == Kind::PCPYUD_BITFIELD); return m_ts; } + bool pcpyud() const { + if (kind == Kind::LEFT_SHIFTED_BITFIELD) { + return m_pcpyud; + } + assert(false); + return false; + } + private: TypeSpec m_ts; std::string m_str; int64_t m_int = 0; + bool m_pcpyud = false; // have we extracted the top doubleword of a bitfield? int64_t m_extra_multiplier = 0; }; diff --git a/test/decompiler/reference/engine/gfx/hw/gs_REF.gc b/test/decompiler/reference/engine/gfx/hw/gs_REF.gc index 7a8f5405a4..e8985cc708 100644 --- a/test/decompiler/reference/engine/gfx/hw/gs_REF.gc +++ b/test/decompiler/reference/engine/gfx/hw/gs_REF.gc @@ -767,160 +767,31 @@ ;; definition for method 3 of type gif-tag ;; INFO: Return type mismatch object vs gif-tag. -;; WARN: Unsupported inline assembly instruction kind - [por gp, a0, r0] -;; WARN: Unsupported inline assembly instruction kind - [por a2, gp, r0] -;; WARN: Unsupported inline assembly instruction kind - [srl a2, v1, 28] (defmethod inspect gif-tag ((obj gif-tag)) - (local-vars - (r0-0 none) - (v1-6 uint128) - (v1-8 uint128) - (v1-10 uint128) - (v1-12 uint128) - (v1-14 uint128) - (v1-16 uint128) - (v1-18 uint128) - (v1-20 uint128) - (v1-21 uint128) - (v1-23 uint128) - (v1-25 uint128) - (v1-27 uint128) - (v1-29 uint128) - (v1-31 uint128) - (v1-33 uint128) - (v1-35 uint128) - (a2-0 uint128) - (a2-15 uint128) - (gp-0 uint128) - ) - (.por gp-0 obj r0-0) - (let ((t9-0 format) - (a0-1 #t) - (a1-0 "[~8x] gif-tag~%") - ) - (.por a2-0 gp-0 r0-0) - (t9-0 a0-1 a1-0 a2-0) - ) - (format #t "~Tnloop: ~4d~%" (shr (shl (the-as int gp-0) 49) 49)) - (format #t "~Teop : ~4d~%" (shr (shl (the-as int gp-0) 48) 63)) - (format #t "~Tid : ~4d~%" (shr (shl (the-as int gp-0) 18) 50)) - (format #t "~Tpre : ~4d~%" (shr (shl (the-as int gp-0) 17) 63)) - (format #t "~Tprim : ~4d~%" (shr (* gp-0 64) 53)) - (format #t "~Tflg : ~4d~%" (shr (* gp-0 16) 62)) - (format #t "~Tnreg : ~4d~%" (shr (the-as int gp-0) 60)) - (let ((t9-8 format) - (a0-9 #t) - (a1-8 "~Tregs0 : ~4d~%") - ) - (.pcpyud v1-6 gp-0 r0-0) - (t9-8 a0-9 a1-8 (shr (shl (the-as int v1-6) 60) 60)) - ) - (let ((t9-9 format) - (a0-10 #t) - (a1-9 "~Tregs1 : ~4d~%") - ) - (.pcpyud v1-8 gp-0 r0-0) - (t9-9 a0-10 a1-9 (shr (shl (the-as int v1-8) 56) 60)) - ) - (let ((t9-10 format) - (a0-11 #t) - (a1-10 "~Tregs2 : ~4d~%") - ) - (.pcpyud v1-10 gp-0 r0-0) - (t9-10 a0-11 a1-10 (shr (shl (the-as int v1-10) 52) 60)) - ) - (let ((t9-11 format) - (a0-12 #t) - (a1-11 "~Tregs3 : ~4d~%") - ) - (.pcpyud v1-12 gp-0 r0-0) - (t9-11 a0-12 a1-11 (shr (shl (the-as int v1-12) 48) 60)) - ) - (let ((t9-12 format) - (a0-13 #t) - (a1-12 "~Tregs4 : ~4d~%") - ) - (.pcpyud v1-14 gp-0 r0-0) - (t9-12 a0-13 a1-12 (shr (shl (the-as int v1-14) 44) 60)) - ) - (let ((t9-13 format) - (a0-14 #t) - (a1-13 "~Tregs5 : ~4d~%") - ) - (.pcpyud v1-16 gp-0 r0-0) - (t9-13 a0-14 a1-13 (shr (shl (the-as int v1-16) 40) 60)) - ) - (let ((t9-14 format) - (a0-15 #t) - (a1-14 "~Tregs6 : ~4d~%") - ) - (.pcpyud v1-18 gp-0 r0-0) - (t9-14 a0-15 a1-14 (shr (shl (the-as int v1-18) 36) 60)) - ) - (let ((t9-15 format) - (a0-16 #t) - (a1-15 "~Tregs7 : ~4d~%") - ) - (.pcpyud v1-20 gp-0 r0-0) - (.srl a2-15 v1-20 28) - (t9-15 a0-16 a1-15 a2-15) - ) - (let ((t9-16 format) - (a0-17 #t) - (a1-16 "~Tregs8 : ~4d~%") - ) - (.pcpyud v1-21 gp-0 r0-0) - (t9-16 a0-17 a1-16 (shr (shl (the-as int v1-21) 28) 60)) - ) - (let ((t9-17 format) - (a0-18 #t) - (a1-17 "~Tregs9 : ~4d~%") - ) - (.pcpyud v1-23 gp-0 r0-0) - (t9-17 a0-18 a1-17 (shr (shl (the-as int v1-23) 24) 60)) - ) - (let ((t9-18 format) - (a0-19 #t) - (a1-18 "~Tregs10: ~4d~%") - ) - (.pcpyud v1-25 gp-0 r0-0) - (t9-18 a0-19 a1-18 (shr (shl (the-as int v1-25) 20) 60)) - ) - (let ((t9-19 format) - (a0-20 #t) - (a1-19 "~Tregs11: ~4d~%") - ) - (.pcpyud v1-27 gp-0 r0-0) - (t9-19 a0-20 a1-19 (shr (shl (the-as int v1-27) 16) 60)) - ) - (let ((t9-20 format) - (a0-21 #t) - (a1-20 "~Tregs12: ~4d~%") - ) - (.pcpyud v1-29 gp-0 r0-0) - (t9-20 a0-21 a1-20 (shr (shl (the-as int v1-29) 12) 60)) - ) - (let ((t9-21 format) - (a0-22 #t) - (a1-21 "~Tregs13: ~4d~%") - ) - (.pcpyud v1-31 gp-0 r0-0) - (t9-21 a0-22 a1-21 (shr (* v1-31 256) 60)) - ) - (let ((t9-22 format) - (a0-23 #t) - (a1-22 "~Tregs14: ~4d~%") - ) - (.pcpyud v1-33 gp-0 r0-0) - (t9-22 a0-23 a1-22 (shr (* v1-33 16) 60)) - ) - (let ((t9-23 format) - (a0-24 #t) - (a1-23 "~Tregs15: ~4d~%") - ) - (.pcpyud v1-35 gp-0 r0-0) - (the-as gif-tag (t9-23 a0-24 a1-23 (shr (the-as int v1-35) 60))) - ) + (format #t "[~8x] gif-tag~%" obj) + (format #t "~Tnloop: ~4d~%" (-> obj nloop)) + (format #t "~Teop : ~4d~%" (-> obj eop)) + (format #t "~Tid : ~4d~%" (-> obj id)) + (format #t "~Tpre : ~4d~%" (-> obj pre)) + (format #t "~Tprim : ~4d~%" (-> obj prim)) + (format #t "~Tflg : ~4d~%" (-> obj flg)) + (format #t "~Tnreg : ~4d~%" (-> obj nreg)) + (format #t "~Tregs0 : ~4d~%" (-> obj regs0)) + (format #t "~Tregs1 : ~4d~%" (-> obj regs1)) + (format #t "~Tregs2 : ~4d~%" (-> obj regs2)) + (format #t "~Tregs3 : ~4d~%" (-> obj regs3)) + (format #t "~Tregs4 : ~4d~%" (-> obj regs4)) + (format #t "~Tregs5 : ~4d~%" (-> obj regs5)) + (format #t "~Tregs6 : ~4d~%" (-> obj regs6)) + (format #t "~Tregs7 : ~4d~%" (-> obj regs7)) + (format #t "~Tregs8 : ~4d~%" (-> obj regs8)) + (format #t "~Tregs9 : ~4d~%" (-> obj regs9)) + (format #t "~Tregs10: ~4d~%" (-> obj regs10)) + (format #t "~Tregs11: ~4d~%" (-> obj regs11)) + (format #t "~Tregs12: ~4d~%" (-> obj regs12)) + (format #t "~Tregs13: ~4d~%" (-> obj regs13)) + (format #t "~Tregs14: ~4d~%" (-> obj regs14)) + (the-as gif-tag (format #t "~Tregs15: ~4d~%" (-> obj regs15))) ) ;; definition for symbol *fog-color*, type int diff --git a/test/decompiler/reference/kernel/gcommon_REF.gc b/test/decompiler/reference/kernel/gcommon_REF.gc index fc72de591d..11e72ee0f8 100644 --- a/test/decompiler/reference/kernel/gcommon_REF.gc +++ b/test/decompiler/reference/kernel/gcommon_REF.gc @@ -112,94 +112,27 @@ ) ;; definition for method 3 of type vec4s -;; INFO: Return type mismatch uint128 vs vec4s. -;; WARN: Unsupported inline assembly instruction kind - [por gp, a0, r0] -;; WARN: Unsupported inline assembly instruction kind - [por a2, gp, r0] -;; WARN: Unsupported inline assembly instruction kind - [sllv a2, gp, r0] -;; WARN: Unsupported inline assembly instruction kind - [sllv a2, v1, r0] -;; WARN: Unsupported inline assembly instruction kind - [por v0, gp, r0] (defmethod inspect vec4s ((obj vec4s)) - (local-vars - (r0-0 none) - (v0-5 uint128) - (v1-0 uint128) - (v1-1 uint128) - (a2-0 uint128) - (a2-1 uint128) - (a2-3 uint128) - (gp-0 uint128) - ) - (.por gp-0 obj r0-0) - (let ((t9-0 format) - (a0-1 #t) - (a1-0 "[~8x] ~A~%") - ) - (.por a2-0 gp-0 r0-0) - (t9-0 a0-1 a1-0 a2-0 'vec4s) - ) - (let ((t9-1 format) - (a0-2 #t) - (a1-1 "~Tx: ~f~%") - ) - (.sllv a2-1 gp-0 r0-0) - (t9-1 a0-2 a1-1 a2-1) - ) - (format #t "~Ty: ~f~%" (sar (the-as int gp-0) 32)) - (let ((t9-3 format) - (a0-4 #t) - (a1-3 "~Tz: ~f~%") - ) - (.pcpyud v1-0 gp-0 r0-0) - (.sllv a2-3 v1-0 r0-0) - (t9-3 a0-4 a1-3 a2-3) - ) - (let ((t9-4 format) - (a0-5 #t) - (a1-4 "~Tw: ~f~%") - ) - (.pcpyud v1-1 gp-0 r0-0) - (t9-4 a0-5 a1-4 (sar (the-as int v1-1) 32)) - ) - (.por v0-5 gp-0 r0-0) - (the-as vec4s v0-5) + (format #t "[~8x] ~A~%" obj 'vec4s) + (format #t "~Tx: ~f~%" (-> obj x)) + (format #t "~Ty: ~f~%" (-> obj y)) + (format #t "~Tz: ~f~%" (-> obj z)) + (format #t "~Tw: ~f~%" (-> obj w)) + obj ) ;; definition for method 2 of type vec4s -;; INFO: Return type mismatch uint128 vs vec4s. -;; WARN: Unsupported inline assembly instruction kind - [por gp, a0, r0] -;; WARN: Unsupported inline assembly instruction kind - [sllv a2, gp, r0] -;; WARN: Unsupported inline assembly instruction kind - [sllv t0, v1, r0] -;; WARN: Unsupported inline assembly instruction kind - [por t2, gp, r0] -;; WARN: Unsupported inline assembly instruction kind - [por v0, gp, r0] (defmethod print vec4s ((obj vec4s)) - (local-vars - (r0-0 none) - (v0-1 uint128) - (v1-0 uint128) - (v1-1 uint128) - (a2-0 uint128) - (t0-0 uint128) - (t2-0 uint128) - (gp-0 uint128) + (format + #t + "#" + (-> obj x) + (-> obj y) + (-> obj z) + (-> obj w) + obj ) - (.por gp-0 obj r0-0) - (let ((t9-0 format) - (a0-1 #t) - (a1-0 "#") - ) - (.sllv a2-0 gp-0 r0-0) - (let ((a3-0 (sar (the-as int gp-0) 32))) - (.pcpyud v1-0 gp-0 r0-0) - (.sllv t0-0 v1-0 r0-0) - (.pcpyud v1-1 gp-0 r0-0) - (let ((t1-0 (sar (the-as int v1-1) 32))) - (.por t2-0 gp-0 r0-0) - (t9-0 a0-1 a1-0 a2-0 a3-0 t0-0 t1-0 t2-0) - ) - ) - ) - (.por v0-1 gp-0 r0-0) - (the-as vec4s v0-1) + obj ) ;; definition of type bfloat diff --git a/test/decompiler/reference/kernel/gkernel-h_REF.gc b/test/decompiler/reference/kernel/gkernel-h_REF.gc index 8e200812f7..738493844d 100644 --- a/test/decompiler/reference/kernel/gkernel-h_REF.gc +++ b/test/decompiler/reference/kernel/gkernel-h_REF.gc @@ -286,9 +286,8 @@ ;; definition for method 2 of type handle ;; WARN: Unsupported inline assembly instruction kind - [subu a2, v1, s7] -;; WARN: Unsupported inline assembly instruction kind - [sllv a2, v1, r0] (defmethod print handle ((obj handle)) - (local-vars (r0-0 none) (a2-0 int) (a2-2 (pointer process)) (s7-0 none)) + (local-vars (a2-0 int) (s7-0 none)) (cond ((nonzero? obj) (let ((t9-0 format) @@ -297,15 +296,17 @@ (v1-0 obj) ) (.subu a2-0 v1-0 s7-0) - (t9-0 a0-1 a1-0 (and (nonzero? a2-0) (begin - (.sllv a2-2 v1-0 r0-0) - (let ((a3-0 (-> a2-2 0))) - (if (= (-> v1-0 pid) (-> a3-0 pid)) - a3-0 - ) - ) - ) - ) + (t9-0 + a0-1 + a1-0 + (and + (nonzero? a2-0) + (let ((a3-0 (-> (the-as (pointer process) (-> v1-0 process)) 0))) + (if (= (-> v1-0 pid) (-> a3-0 pid)) + a3-0 + ) + ) + ) (-> obj pid) ) ) diff --git a/test/decompiler/test_FormExpressionBuild2.cpp b/test/decompiler/test_FormExpressionBuild2.cpp index 5c79cb6214..7ea1d8ee01 100644 --- a/test/decompiler/test_FormExpressionBuild2.cpp +++ b/test/decompiler/test_FormExpressionBuild2.cpp @@ -1142,4 +1142,88 @@ TEST_F(FormRegressionTest, Method11FontContext) { " arg0\n" " )"; test_with_expr(func, type, expected); +} + +// 128-bit bitfields, also type for pextuw +TEST_F(FormRegressionTest, Method4ResTag) { + std::string func = + "sll r0, r0, 0\n" + "L135:\n" + " pcpyud v1, a0, r0\n" + " dsrl32 v1, v1, 31\n" + " bne v1, r0, L136\n" + " sll r0, r0, 0\n" + + " pcpyud v1, a0, r0\n" + " dsll v1, v1, 1\n" + " dsrl32 v1, v1, 17\n" + " dsll v0, v1, 2\n" + " beq r0, r0, L137\n" + " sll r0, r0, 0\n" + + "L136:\n" + " pcpyud v1, a0, r0\n" + " dsll v1, v1, 1\n" + " dsrl32 v1, v1, 17\n" + " pextuw a0, r0, a0\n" + " lhu a0, 8(a0)\n" + " multu3 v0, v1, a0\n" + + "L137:\n" + " jr ra\n" + " daddu sp, sp, r0"; + std::string type = "(function res-tag int)"; + std::string expected = + "(the-as int (if (zero? (-> arg0 inlined?))\n" + " (* (-> arg0 elt-count) 4)\n" + " (* (the-as uint (-> arg0 elt-count)) (-> arg0 elt-type size))\n" + " )\n" + " )"; + test_with_expr(func, type, expected); +} + +TEST_F(FormRegressionTest, Method2Vec4s) { + std::string func = + "sll r0, r0, 0\n" + " daddiu sp, sp, -32\n" + " sd ra, 0(sp)\n" + " sd fp, 8(sp)\n" + " or fp, t9, r0\n" + " sq gp, 16(sp)\n" + + " por gp, a0, r0\n" + " lw t9, format(s7)\n" + " daddiu a0, s7, #t\n" + " daddiu a1, fp, L344\n" + " sllv a2, gp, r0\n" + " dsra32 a3, gp, 0\n" + " pcpyud v1, gp, r0\n" + " sllv t0, v1, r0\n" + " pcpyud v1, gp, r0\n" + " dsra32 t1, v1, 0\n" + " por t2, gp, r0\n" + " jalr ra, t9\n" + " sll v0, ra, 0\n" + + " por v0, gp, r0\n" + " ld ra, 0(sp)\n" + " ld fp, 8(sp)\n" + " lq gp, 16(sp)\n" + " jr ra\n" + " daddiu sp, sp, 32"; + std::string type = "(function vec4s vec4s)"; + std::string expected = + "(begin\n" + " (format\n" + " #t\n" + " \"#\"\n" + " (-> arg0 x)\n" + " (-> arg0 y)\n" + " (-> arg0 z)\n" + " (-> arg0 w)\n" + " arg0\n" + " )\n" + " arg0\n" + " )"; + test_with_expr(func, type, expected, false, "", {{"L344", "#"}}); } \ No newline at end of file diff --git a/test/offline/offline_test_main.cpp b/test/offline/offline_test_main.cpp index ef4f824902..f6a188eead 100644 --- a/test/offline/offline_test_main.cpp +++ b/test/offline/offline_test_main.cpp @@ -75,8 +75,6 @@ const std::unordered_set g_functions_to_skip_compiling = { "abs", "ash", "min", "max", "lognor", // weird PS2 specific debug registers: "breakpoint-range-set!", - // int128 fancy stuff. - "(method 3 vec4s)", "(method 2 vec4s)", // does weird stuff with the type system. "print", "printl", "inspect", // inline assembly @@ -119,9 +117,6 @@ const std::unordered_set g_functions_to_skip_compiling = { // dma-disasm "disasm-dma-list", // missing a single cast :( - // gs - "(method 3 gif-tag)", // inspect for a 128-bit type. - // math camera "transform-point-vector!", "transform-point-qword!", "transform-point-vector-scale!",