A32: Implement ARM-mode SDIV/UDIV

Now that we have Unicorn in place, we can freely implement instructions
introduced in newer versions of the ARM architecture.
This commit is contained in:
Lioncash 2019-04-20 08:06:18 -04:00 committed by MerryMage
parent d4a531c21f
commit b2f7a0e7ba
6 changed files with 77 additions and 0 deletions

View file

@ -98,6 +98,7 @@ add_library(dynarmic
frontend/A32/translate/translate_arm/branch.cpp frontend/A32/translate/translate_arm/branch.cpp
frontend/A32/translate/translate_arm/coprocessor.cpp frontend/A32/translate/translate_arm/coprocessor.cpp
frontend/A32/translate/translate_arm/data_processing.cpp frontend/A32/translate/translate_arm/data_processing.cpp
frontend/A32/translate/translate_arm/divide.cpp
frontend/A32/translate/translate_arm/exception_generating.cpp frontend/A32/translate/translate_arm/exception_generating.cpp
frontend/A32/translate/translate_arm/extension.cpp frontend/A32/translate/translate_arm/extension.cpp
frontend/A32/translate/translate_arm/load_store.cpp frontend/A32/translate/translate_arm/load_store.cpp

View file

@ -187,6 +187,10 @@ INST(arm_SSAT16, "SSAT16", "cccc01101010vvvvdddd11110011nnnn
INST(arm_USAT, "USAT", "cccc0110111vvvvvddddvvvvvr01nnnn") // v6 INST(arm_USAT, "USAT", "cccc0110111vvvvvddddvvvvvr01nnnn") // v6
INST(arm_USAT16, "USAT16", "cccc01101110vvvvdddd11110011nnnn") // v6 INST(arm_USAT16, "USAT16", "cccc01101110vvvvdddd11110011nnnn") // v6
// Divide instructions
INST(arm_SDIV, "SDIV", "cccc01110001dddd1111mmmm0001nnnn") // v7a
INST(arm_UDIV, "UDIV", "cccc01110011dddd1111mmmm0001nnnn") // v7a
// Multiply (Normal) instructions // Multiply (Normal) instructions
INST(arm_MLA, "MLA", "cccc0000001Sddddaaaammmm1001nnnn") // v2 INST(arm_MLA, "MLA", "cccc0000001Sddddaaaammmm1001nnnn") // v2
INST(arm_MUL, "MUL", "cccc0000000Sdddd0000mmmm1001nnnn") // v2 INST(arm_MUL, "MUL", "cccc0000000Sdddd0000mmmm1001nnnn") // v2

View file

@ -634,6 +634,14 @@ public:
return fmt::format("usat16{} {}, #{}, {}", CondToString(cond), d, sat_imm, n); return fmt::format("usat16{} {}, #{}, {}", CondToString(cond), d, sat_imm, n);
} }
// Divide instructions
std::string arm_SDIV(Cond cond, Reg d, Reg m, Reg n) {
return fmt::format("sdiv{} {}, {}, {}", CondToString(cond), d, n, m);
}
std::string arm_UDIV(Cond cond, Reg d, Reg m, Reg n) {
return fmt::format("udiv{} {}, {}, {}", CondToString(cond), d, n, m);
}
// Multiply (Normal) instructions // Multiply (Normal) instructions
std::string arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) { std::string arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) {
return fmt::format("mla{}{} {}, {}, {}, {}", S ? "s" : "", CondToString(cond), d, n, m, a); return fmt::format("mla{}{} {}, {}, {}, {}", S ? "s" : "", CondToString(cond), d, n, m, a);

View file

@ -0,0 +1,42 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
namespace Dynarmic::A32 {
namespace {
using DivideFunction = IR::U32U64 (IREmitter::*)(const IR::U32U64&, const IR::U32U64&);
bool DivideOperation(ArmTranslatorVisitor& v, Cond cond, Reg d, Reg m, Reg n,
DivideFunction fn) {
if (d == Reg::PC || m == Reg::PC || n == Reg::PC) {
return v.UnpredictableInstruction();
}
if (!v.ConditionPassed(cond)) {
return true;
}
const IR::U32 operand1 = v.ir.GetRegister(n);
const IR::U32 operand2 = v.ir.GetRegister(m);
const IR::U32 result = (v.ir.*fn)(operand1, operand2);
v.ir.SetRegister(d, result);
return true;
}
} // Anonymous namespace
// SDIV<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SDIV(Cond cond, Reg d, Reg m, Reg n) {
return DivideOperation(*this, cond, d, m, n, &IREmitter::SignedDiv);
}
// UDIV<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UDIV(Cond cond, Reg d, Reg m, Reg n) {
return DivideOperation(*this, cond, d, m, n, &IREmitter::UnsignedDiv);
}
} // namespace Dynarmic::A32

View file

@ -230,6 +230,10 @@ struct ArmTranslatorVisitor final {
bool arm_USAT(Cond cond, Imm5 sat_imm, Reg d, Imm5 imm5, bool sh, Reg n); bool arm_USAT(Cond cond, Imm5 sat_imm, Reg d, Imm5 imm5, bool sh, Reg n);
bool arm_USAT16(Cond cond, Imm4 sat_imm, Reg d, Reg n); bool arm_USAT16(Cond cond, Imm4 sat_imm, Reg d, Reg n);
// Divide instructions
bool arm_SDIV(Cond cond, Reg d, Reg m, Reg n);
bool arm_UDIV(Cond cond, Reg d, Reg m, Reg n);
// Multiply (Normal) instructions // Multiply (Normal) instructions
bool arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n); bool arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n);
bool arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n); bool arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n);

View file

@ -812,6 +812,24 @@ TEST_CASE("Fuzz ARM extension instructions", "[JitX64][A32]") {
} }
} }
TEST_CASE("Fuzz ARM divide instructions", "[JitX64][A32]") {
const auto is_valid = [](u32 instr) {
return Bits<0, 3>(instr) != 0b1111 &&
Bits<8, 11>(instr) != 0b1111 &&
Bits<16, 19>(instr) != 0b1111;
};
const std::array instructions = {
InstructionGenerator("cccc01110001dddd1111mmmm0001nnnn", is_valid), // SDIV
InstructionGenerator("cccc01110011dddd1111mmmm0001nnnn", is_valid), // UDIV
};
FuzzJitArm(1, 1, 5000, [&instructions]() -> u32 {
return instructions[RandInt<size_t>(0, instructions.size() - 1)].Generate();
});
}
TEST_CASE("Fuzz ARM multiply instructions", "[JitX64][A32]") { TEST_CASE("Fuzz ARM multiply instructions", "[JitX64][A32]") {
const auto validate_d_m_n = [](u32 inst) -> bool { const auto validate_d_m_n = [](u32 inst) -> bool {
return Bits<16, 19>(inst) != 15 && return Bits<16, 19>(inst) != 15 &&