diff --git a/decompiler/Disasm/InstructionParser.cpp b/decompiler/Disasm/InstructionParser.cpp index 4540d2023..f01ab0247 100644 --- a/decompiler/Disasm/InstructionParser.cpp +++ b/decompiler/Disasm/InstructionParser.cpp @@ -44,7 +44,8 @@ InstructionParser::InstructionParser() { InstructionKind::BGEZL, InstructionKind::MTC1, InstructionKind::MFC1, InstructionKind::MFLO, InstructionKind::MFHI, InstructionKind::MTLO1, InstructionKind::MFLO1, InstructionKind::SYNCL, InstructionKind::PCPYUD, - InstructionKind::PEXTUW, InstructionKind::POR}) { + InstructionKind::PEXTUW, InstructionKind::POR, InstructionKind::VMOVE, + InstructionKind::VSUB, InstructionKind::LQC2, InstructionKind::SQC2}) { auto& info = gOpcodeInfo[int(i)]; if (info.defined) { m_opcode_name_lookup[info.name] = int(i); @@ -72,6 +73,29 @@ std::string get_until_space(std::string& instr) { return name; } +std::string get_instr_name(std::string& instr) { + assert(!instr.empty()); + size_t i; + for (i = 0; i < instr.length(); i++) { + if (instr[i] == ' ') { + break; + } + + // add.s should not stop at the . + if (instr.size() > (i + 1) && instr[0] == 'v' && instr[i + 1] != 's' && instr[i + 1] != 'l' && + instr[i + 1] != 'e' && instr[i] == '.') { + break; + } + } + auto name = instr.substr(0, i); + if (i == instr.length()) { + instr.clear(); + } else { + instr = instr.substr(i + 1); + } + return name; +} + std::string get_comma_separated(std::string& instr) { assert(!instr.empty()); auto arg = get_until_space(instr); @@ -149,12 +173,52 @@ std::vector string_to_lines(const std::string& str) { } } +u8 cop2_dst(const std::string& str) { + auto ptr = str.data(); + u8 result = 0; + if (!*ptr) { + return result; + } + if (*ptr == 'x') { + result |= 8; + ptr++; + } + + if (!*ptr) { + return result; + } + if (*ptr == 'y') { + result |= 4; + ptr++; + } + + if (!*ptr) { + return result; + } + if (*ptr == 'z') { + result |= 2; + ptr++; + } + + if (!*ptr) { + return result; + } + if (*ptr == 'w') { + result |= 1; + ptr++; + } + + if (*ptr) { + assert(false); + } + return result; +} } // namespace Instruction InstructionParser::parse_single_instruction( std::string str, const std::vector& labels) { - auto name = get_until_space(str); + auto name = get_instr_name(str); auto lookup = m_opcode_name_lookup.find(name); if (lookup == m_opcode_name_lookup.end()) { throw std::runtime_error("InstructionParser cannot handle opcode " + name); @@ -198,6 +262,19 @@ Instruction InstructionParser::parse_single_instruction( } } break; + case DecodeType::VF: { + auto reg_name = get_comma_separated(str); + Register reg(reg_name); + assert(reg.get_kind() == Reg::VF); + InstructionAtom atom; + atom.set_reg(reg); + if (step.is_src) { + instr.add_src(atom); + } else { + instr.add_dst(atom); + } + } break; + case DecodeType::IMM: { InstructionAtom atom; std::string atom_str; @@ -248,6 +325,13 @@ Instruction InstructionParser::parse_single_instruction( instr.add_dst(atom); } } break; + + case DecodeType::DEST: { + auto thing = get_until_space(str); + instr.cop2_dest = cop2_dst(thing); + break; + } + default: assert(false); } diff --git a/decompiler/Disasm/Register.cpp b/decompiler/Disasm/Register.cpp index 1b54d76a7..846df4686 100644 --- a/decompiler/Disasm/Register.cpp +++ b/decompiler/Disasm/Register.cpp @@ -150,6 +150,14 @@ Register::Register(const std::string& name) { } } + // next vfs + for (int i = 0; i < 32; i++) { + if (name == vf_names[i]) { + id = (Reg::VF << REG_CATEGORY_SHIFT) | i; + return; + } + } + throw std::runtime_error("Unknown register name: " + name); } diff --git a/decompiler/IR2/AtomicOpForm.cpp b/decompiler/IR2/AtomicOpForm.cpp index 614cf3778..a1dd5616e 100644 --- a/decompiler/IR2/AtomicOpForm.cpp +++ b/decompiler/IR2/AtomicOpForm.cpp @@ -383,8 +383,9 @@ FormElement* StoreOp::get_as_form(FormPool& pool, const Env& env) const { // we pass along the register offset because code generation seems to be a bit // different in different cases. - auto source = pool.alloc_element( - ro.var, tokens, input_type.get_multiplier(), ro.offset); + auto source = + pool.alloc_element(ro.var, tokens, input_type.get_multiplier(), + ro.offset, input_type.flipped_add_order()); // auto val = pool.alloc_single_element_form( // nullptr, m_value.as_expr(), m_my_idx); @@ -529,8 +530,8 @@ Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const { // we pass along the register offset because code generation seems to be a bit // different in different cases. - return pool.alloc_single_element_form(nullptr, ro.var, tokens, - rd_in.stride, ro.offset); + return pool.alloc_single_element_form( + nullptr, ro.var, tokens, rd_in.stride, ro.offset, input_type.flipped_add_order()); } } diff --git a/decompiler/IR2/AtomicOpTypeAnalysis.cpp b/decompiler/IR2/AtomicOpTypeAnalysis.cpp index a0ce930fd..a1018a759 100644 --- a/decompiler/IR2/AtomicOpTypeAnalysis.cpp +++ b/decompiler/IR2/AtomicOpTypeAnalysis.cpp @@ -492,17 +492,19 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input, } if (m_kind == Kind::ADD && arg0_type.is_product() && arg1_type.kind == TP_Type::Kind::TYPESPEC) { - return TP_Type::make_object_plus_product(arg1_type.typespec(), arg0_type.get_multiplier()); + return TP_Type::make_object_plus_product(arg1_type.typespec(), arg0_type.get_multiplier(), + true); } if (m_kind == Kind::ADD && arg1_type.is_product() && arg0_type.kind == TP_Type::Kind::TYPESPEC) { - return TP_Type::make_object_plus_product(arg0_type.typespec(), arg1_type.get_multiplier()); + return TP_Type::make_object_plus_product(arg0_type.typespec(), arg1_type.get_multiplier(), + false); } if ((m_kind == Kind::ADD || m_kind == Kind::SUB) && arg0_type.typespec().base_type() == "pointer" && tc(dts, TypeSpec("integer"), arg1_type)) { if (m_kind == Kind::ADD && !m_args[1].is_int()) { - return TP_Type::make_object_plus_product(arg0_type.typespec(), 1); + return TP_Type::make_object_plus_product(arg0_type.typespec(), 1, false); } // plain pointer plus integer = plain pointer return TP_Type::make_from_ts(arg0_type.typespec()); @@ -527,7 +529,7 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input, // byte access of offset array field trick. // arg1 holds a structure. // arg0 is an integer in a register. - return TP_Type::make_object_plus_product(arg1_type.typespec(), 1); + return TP_Type::make_object_plus_product(arg1_type.typespec(), 1, true); } } diff --git a/decompiler/IR2/Form.cpp b/decompiler/IR2/Form.cpp index 48f622246..835bdf096 100644 --- a/decompiler/IR2/Form.cpp +++ b/decompiler/IR2/Form.cpp @@ -1886,11 +1886,13 @@ void DynamicMethodAccess::get_modified_regs(RegSet&) const {} ArrayFieldAccess::ArrayFieldAccess(RegisterAccess source, const std::vector& deref_tokens, int expected_stride, - int constant_offset) + int constant_offset, + bool flipped) : m_source(source), m_deref_tokens(deref_tokens), m_expected_stride(expected_stride), - m_constant_offset(constant_offset) { + m_constant_offset(constant_offset), + m_flipped(flipped) { for (auto& token : m_deref_tokens) { if (token.kind() == DerefToken::Kind::INTEGER_EXPRESSION) { token.expr()->parent_element = this; diff --git a/decompiler/IR2/Form.h b/decompiler/IR2/Form.h index f84106662..f6ac6f932 100644 --- a/decompiler/IR2/Form.h +++ b/decompiler/IR2/Form.h @@ -1097,7 +1097,8 @@ class ArrayFieldAccess : public FormElement { ArrayFieldAccess(RegisterAccess source, const std::vector& deref_tokens, int expected_stride, - int constant_offset); + int constant_offset, + bool flipped); 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; @@ -1115,11 +1116,14 @@ class ArrayFieldAccess : public FormElement { std::vector* result, bool allow_side_effects); + bool flipped() const { return m_flipped; } + private: RegisterAccess m_source; std::vector m_deref_tokens; int m_expected_stride = -1; int m_constant_offset = -1; + bool m_flipped = false; }; class GetMethodElement : public FormElement { @@ -1353,6 +1357,8 @@ class VectorFloatLoadStoreElement : public FormElement { void get_modified_regs(RegSet& regs) const override; void push_to_stack(const Env& env, FormPool& pool, FormStack& stack) override; void collect_vf_regs(RegSet& regs) const; + bool is_load() const { return m_is_load; } + Register vf_reg() const { return m_vf_reg; } private: Register m_vf_reg; diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index 721566321..8e6a13fba 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -3468,6 +3468,10 @@ void ArrayFieldAccess::update_with_val(Form* new_val, auto base = match_result.maps.forms.at(0); assert(idx && base); + if (m_flipped) { + std::swap(idx, base); + } + std::vector tokens = m_deref_tokens; for (auto& x : tokens) { if (x.kind() == DerefToken::Kind::EXPRESSION_PLACEHOLDER) { diff --git a/decompiler/util/TP_Type.h b/decompiler/util/TP_Type.h index faf6e3250..990cb9326 100644 --- a/decompiler/util/TP_Type.h +++ b/decompiler/util/TP_Type.h @@ -215,11 +215,15 @@ class TP_Type { return result; } - static TP_Type make_object_plus_product(const TypeSpec& ts, int64_t multiplier) { + /*! + * flipped means it's int + obj. + */ + static TP_Type make_object_plus_product(const TypeSpec& ts, int64_t multiplier, bool flipped) { TP_Type result; result.kind = Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT; result.m_ts = ts; result.m_int = multiplier; + result.m_flipped_order = flipped; return result; } @@ -314,6 +318,11 @@ class TP_Type { return m_method_id; } + bool flipped_add_order() const { + assert(kind == Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT); + return m_flipped_order; + } + private: TypeSpec m_ts; TypeSpec m_method_from_type; @@ -321,6 +330,7 @@ class TP_Type { std::string m_str; int64_t m_int = 0; bool m_pcpyud = false; // have we extracted the top doubleword of a bitfield? + bool m_flipped_order = false; int64_t m_extra_multiplier = 0; }; diff --git a/test/decompiler/test_InstructionParser.cpp b/test/decompiler/test_InstructionParser.cpp index 277c6b14c..d8a2f486e 100644 --- a/test/decompiler/test_InstructionParser.cpp +++ b/test/decompiler/test_InstructionParser.cpp @@ -39,4 +39,13 @@ TEST(DecompilerInstructionParser, ProgramLabels) { " jr ra\n"; auto result = parser.parse_program(program); EXPECT_EQ(result.print(), program); +} + +TEST(DecompilerInstructionParser, VU) { + InstructionParser parser; + std::string program = + " vmove.xy vf1, vf2\n" + " vsub.yw vf1, vf2, vf25\n"; + auto result = parser.parse_program(program); + EXPECT_EQ(result.print(), program); } \ No newline at end of file