From eb2e6e8bea50a5a5a4b2d0762654b6aa99a667e0 Mon Sep 17 00:00:00 2001 From: Tillmann Karras Date: Fri, 5 Aug 2016 02:03:23 +0100 Subject: [PATCH] Implement some multiplies --- src/frontend/decoder/arm.h | 14 +-- .../disassembler/disassembler_arm.cpp | 28 +++-- .../translate/translate_arm/multiply.cpp | 118 ++++++++++++++++-- tests/arm/fuzz_arm.cpp | 35 ++++++ 4 files changed, 174 insertions(+), 21 deletions(-) diff --git a/src/frontend/decoder/arm.h b/src/frontend/decoder/arm.h index 0367b8d8..15cfa982 100644 --- a/src/frontend/decoder/arm.h +++ b/src/frontend/decoder/arm.h @@ -246,15 +246,15 @@ boost::optional&> DecodeArm(u32 instruction) { //INST(&V::arm_USAT16, "USAT16", "cccc01101110vvvvdddd11110011nnnn"), // v6 // Multiply (Normal) instructions - //INST(&V::arm_MLA, "MLA", "cccc0000001Sddddaaaammmm1001nnnn"), // v2 - //INST(&V::arm_MUL, "MUL", "cccc0000000Sdddd0000mmmm1001nnnn"), // v2 + INST(&V::arm_MLA, "MLA", "cccc0000001Sddddaaaammmm1001nnnn"), // v2 + INST(&V::arm_MUL, "MUL", "cccc0000000Sdddd0000mmmm1001nnnn"), // v2 // Multiply (Long) instructions - //INST(&V::arm_SMLAL, "SMLAL", "cccc0000111Sddddaaaammmm1001nnnn"), // v3M - //INST(&V::arm_SMULL, "SMULL", "cccc0000110Sddddaaaammmm1001nnnn"), // v3M - //INST(&V::arm_UMAAL, "UMAAL", "cccc00000100ddddaaaammmm1001nnnn"), // v6 - //INST(&V::arm_UMLAL, "UMLAL", "cccc0000101Sddddaaaammmm1001nnnn"), // v3M - //INST(&V::arm_UMULL, "UMULL", "cccc0000100Sddddaaaammmm1001nnnn"), // v3M + INST(&V::arm_SMLAL, "SMLAL", "cccc0000111Sddddaaaammmm1001nnnn"), // v3M + INST(&V::arm_SMULL, "SMULL", "cccc0000110Sddddaaaammmm1001nnnn"), // v3M + INST(&V::arm_UMAAL, "UMAAL", "cccc00000100ddddaaaammmm1001nnnn"), // v6 + INST(&V::arm_UMLAL, "UMLAL", "cccc0000101Sddddaaaammmm1001nnnn"), // v3M + INST(&V::arm_UMULL, "UMULL", "cccc0000100Sddddaaaammmm1001nnnn"), // v3M // Multiply (Halfword) instructions //INST(&V::arm_SMLALxy, "SMLALXY", "cccc00010100ddddaaaammmm1xy0nnnn"), // v5xP diff --git a/src/frontend/disassembler/disassembler_arm.cpp b/src/frontend/disassembler/disassembler_arm.cpp index 4a086ab7..eb813f15 100644 --- a/src/frontend/disassembler/disassembler_arm.cpp +++ b/src/frontend/disassembler/disassembler_arm.cpp @@ -383,15 +383,29 @@ public: std::string arm_USAT16(Cond cond, Imm4 sat_imm, Reg d, Reg n) { return "ice"; } // Multiply (Normal) instructions - std::string arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) { return "ice"; } - std::string arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n) { return "ice"; } + std::string arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) { + return Common::StringFromFormat("mla%s%s %s, %s, %s, %s", S ? "s" : "", CondToString(cond), RegToString(d), RegToString(n), RegToString(m), RegToString(a)); + } + std::string arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n) { + return Common::StringFromFormat("mul%s%s %s, %s, %s", S ? "s" : "", CondToString(cond), RegToString(d), RegToString(n), RegToString(m)); + } // Multiply (Long) instructions - std::string arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; } - std::string arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; } - std::string arm_UMAAL(Cond cond, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; } - std::string arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; } - std::string arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; } + std::string arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { + return Common::StringFromFormat("smlal%s%s %s, %s, %s, %s", S ? "s" : "", CondToString(cond), RegToString(dLo), RegToString(dHi), RegToString(n), RegToString(m)); + } + std::string arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { + return Common::StringFromFormat("smull%s%s %s, %s, %s, %s", S ? "s" : "", CondToString(cond), RegToString(dLo), RegToString(dHi), RegToString(n), RegToString(m)); + } + std::string arm_UMAAL(Cond cond, Reg dHi, Reg dLo, Reg m, Reg n) { + return Common::StringFromFormat("umaal%s %s, %s, %s, %s", CondToString(cond), RegToString(dLo), RegToString(dHi), RegToString(n), RegToString(m)); + } + std::string arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { + return Common::StringFromFormat("umlal%s%s %s, %s, %s, %s", S ? "s" : "", CondToString(cond), RegToString(dLo), RegToString(dHi), RegToString(n), RegToString(m)); + } + std::string arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { + return Common::StringFromFormat("umull%s%s %s, %s, %s, %s", S ? "s" : "", CondToString(cond), RegToString(dLo), RegToString(dHi), RegToString(n), RegToString(m)); + } // Multiply (Halfword) instructions std::string arm_SMLALxy(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, bool N, Reg n) { return "ice"; } diff --git a/src/frontend/translate/translate_arm/multiply.cpp b/src/frontend/translate/translate_arm/multiply.cpp index 9a2c11d6..4fefcc28 100644 --- a/src/frontend/translate/translate_arm/multiply.cpp +++ b/src/frontend/translate/translate_arm/multiply.cpp @@ -11,33 +11,137 @@ namespace Arm { // Multiply (Normal) instructions bool ArmTranslatorVisitor::arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) { - return InterpretThisInstruction(); + if (d == Reg::PC || n == Reg::PC || m == Reg::PC) + return UnpredictableInstruction(); + if (ConditionPassed(cond)) { + auto result = ir.Add(ir.Mul(ir.GetRegister(n), ir.GetRegister(m)), ir.GetRegister(a)); + ir.SetRegister(d, result); + if (S) { + ir.SetNFlag(ir.MostSignificantBit(result)); + ir.SetZFlag(ir.IsZero(result)); + } + } + return true; } bool ArmTranslatorVisitor::arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n) { - return InterpretThisInstruction(); + if (d == Reg::PC || n == Reg::PC || m == Reg::PC) + return UnpredictableInstruction(); + if (ConditionPassed(cond)) { + auto result = ir.Mul(ir.GetRegister(n), ir.GetRegister(m)); + ir.SetRegister(d, result); + if (S) { + ir.SetNFlag(ir.MostSignificantBit(result)); + ir.SetZFlag(ir.IsZero(result)); + } + } + return true; } // Multiply (Long) instructions bool ArmTranslatorVisitor::arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { - return InterpretThisInstruction(); + if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) + return UnpredictableInstruction(); + if (dLo == dHi) + return UnpredictableInstruction(); + if (ConditionPassed(cond)) { + auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); + auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); + auto product = ir.Mul64(n64, m64); + auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); + auto result = ir.Add64(product, addend); + auto lo = ir.LeastSignificantWord(result); + auto hi = ir.MostSignificantWord(result); + ir.SetRegister(dLo, lo); + ir.SetRegister(dHi, hi); + if (S) { + ir.SetNFlag(ir.MostSignificantBit(hi)); + ir.SetZFlag(ir.IsZero64(result)); + } + } + return true; } bool ArmTranslatorVisitor::arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { - return InterpretThisInstruction(); + if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) + return UnpredictableInstruction(); + if (dLo == dHi) + return UnpredictableInstruction(); + if (ConditionPassed(cond)) { + auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); + auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); + auto result = ir.Mul64(n64, m64); + auto lo = ir.LeastSignificantWord(result); + auto hi = ir.MostSignificantWord(result); + ir.SetRegister(dLo, lo); + ir.SetRegister(dHi, hi); + if (S) { + ir.SetNFlag(ir.MostSignificantBit(hi)); + ir.SetZFlag(ir.IsZero64(result)); + } + } + return true; } bool ArmTranslatorVisitor::arm_UMAAL(Cond cond, Reg dHi, Reg dLo, Reg m, Reg n) { - return InterpretThisInstruction(); + if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) + return UnpredictableInstruction(); + if (dLo == dHi) + return UnpredictableInstruction(); + if (ConditionPassed(cond)) { + auto lo64 = ir.ZeroExtendWordToLong(ir.GetRegister(dLo)); + auto hi64 = ir.ZeroExtendWordToLong(ir.GetRegister(dHi)); + auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n)); + auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m)); + auto result = ir.Add64(ir.Add64(ir.Mul64(n64, m64), hi64), lo64); + ir.SetRegister(dLo, ir.LeastSignificantWord(result)); + ir.SetRegister(dHi, ir.MostSignificantWord(result)); + } + return true; } bool ArmTranslatorVisitor::arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { - return InterpretThisInstruction(); + if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) + return UnpredictableInstruction(); + if (dLo == dHi) + return UnpredictableInstruction(); + if (ConditionPassed(cond)) { + auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); + auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n)); + auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m)); + auto result = ir.Add64(ir.Mul64(n64, m64), addend); + auto lo = ir.LeastSignificantWord(result); + auto hi = ir.MostSignificantWord(result); + ir.SetRegister(dLo, lo); + ir.SetRegister(dHi, hi); + if (S) { + ir.SetNFlag(ir.MostSignificantBit(hi)); + ir.SetZFlag(ir.IsZero64(result)); + } + } + return true; } bool ArmTranslatorVisitor::arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { - return InterpretThisInstruction(); + if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) + return UnpredictableInstruction(); + if (dLo == dHi) + return UnpredictableInstruction(); + if (ConditionPassed(cond)) { + auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n)); + auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m)); + auto result = ir.Mul64(n64, m64); + auto lo = ir.LeastSignificantWord(result); + auto hi = ir.MostSignificantWord(result); + ir.SetRegister(dLo, lo); + ir.SetRegister(dHi, hi); + if (S) { + ir.SetNFlag(ir.MostSignificantBit(hi)); + ir.SetZFlag(ir.IsZero64(result)); + } + } + return true; } diff --git a/tests/arm/fuzz_arm.cpp b/tests/arm/fuzz_arm.cpp index 7775b685..1435decd 100644 --- a/tests/arm/fuzz_arm.cpp +++ b/tests/arm/fuzz_arm.cpp @@ -601,3 +601,38 @@ TEST_CASE("Fuzz ARM Load/Store instructions", "[JitX64]") { } } */ + +TEST_CASE("Fuzz ARM multiply instructions", "[JitX64]") { + auto validate_d_m_n = [](u32 inst) -> bool { + return Dynarmic::Common::Bits<16, 19>(inst) != 15 && + Dynarmic::Common::Bits<8, 11>(inst) != 15 && + Dynarmic::Common::Bits<0, 3>(inst) != 15; + }; + auto validate_d_a_m_n = [&](u32 inst) -> bool { + return validate_d_m_n(inst) && + Dynarmic::Common::Bits<12, 15>(inst) != 15; + }; + auto validate_h_l_m_n = [&](u32 inst) -> bool { + return validate_d_a_m_n(inst) && + Dynarmic::Common::Bits<12, 15>(inst) != Dynarmic::Common::Bits<16, 19>(inst); + }; + + const std::array instructions = { + { + InstructionGenerator("cccc0000001Sddddaaaammmm1001nnnn", validate_d_a_m_n), // MLA + InstructionGenerator("cccc0000000Sdddd0000mmmm1001nnnn", validate_d_m_n), // MUL + + InstructionGenerator("cccc0000111Sddddaaaammmm1001nnnn", validate_h_l_m_n), // SMLAL + InstructionGenerator("cccc0000110Sddddaaaammmm1001nnnn", validate_h_l_m_n), // SMULL + InstructionGenerator("cccc00000100ddddaaaammmm1001nnnn", validate_h_l_m_n), // UMAAL + InstructionGenerator("cccc0000101Sddddaaaammmm1001nnnn", validate_h_l_m_n), // UMLAL + InstructionGenerator("cccc0000100Sddddaaaammmm1001nnnn", validate_h_l_m_n), // UMULL + } + }; + + SECTION("Multiply") { + FuzzJitArm(2, 2, 10000, [&]() -> u32 { + return instructions[RandInt(0, instructions.size() - 1)].Generate(); + }); + } +}