A32: Implement ARM-mode CRC32 instructions

Implements the ARM-mode variants of the CRC32 instructions introduced
within ARMv8. This is also one of the instruction cases where there is
UNPREDICTABLE behavior that is constrained (we must do one of the
options indicated by the reference manual).

In both documented cases of constrained unpredictable behavior, we treat
the instructions as unpredictable in order to allow library users to
hook the unpredictable exception to provide the intended behavior they
desire.
This commit is contained in:
Lioncash 2019-05-01 20:32:47 -04:00 committed by MerryMage
parent b1953c1cd4
commit e37689315d
5 changed files with 122 additions and 0 deletions

View file

@ -99,6 +99,7 @@ add_library(dynarmic
frontend/A32/translate/translate_arm/barrier.cpp
frontend/A32/translate/translate_arm/branch.cpp
frontend/A32/translate/translate_arm/coprocessor.cpp
frontend/A32/translate/translate_arm/crc32.cpp
frontend/A32/translate/translate_arm/data_processing.cpp
frontend/A32/translate/translate_arm/divide.cpp
frontend/A32/translate/translate_arm/exception_generating.cpp

View file

@ -11,6 +11,10 @@ INST(arm_BL, "BL", "cccc1011vvvvvvvvvvvvvvvvvvvvvvvv
INST(arm_BX, "BX", "cccc000100101111111111110001mmmm") // v4T
INST(arm_BXJ, "BXJ", "cccc000100101111111111110010mmmm") // v5J
// CRC32 instructions
INST(arm_CRC32, "CRC32", "cccc00010zz0nnnndddd00000100mmmm") // v8
INST(arm_CRC32C, "CRC32C", "cccc00010zz0nnnndddd00100100mmmm") // v8
// Coprocessor instructions
INST(arm_CDP, "CDP", "cccc1110ooooNNNNDDDDppppooo0MMMM") // v2 (CDP2: v5)
INST(arm_LDC, "LDC", "cccc110pudw1nnnnDDDDppppvvvvvvvv") // v2 (LDC2: v5)

View file

@ -224,6 +224,22 @@ public:
return "<internal error>";
}
// CRC32 instructions
std::string arm_CRC32([[maybe_unused]] Cond cond, Imm<2> sz, Reg n, Reg d, Reg m) {
static constexpr std::array data_type{
"b", "h", "w", "invalid",
};
return fmt::format("crc32{} {}, {}, {}", data_type[sz.ZeroExtend()], d, n, m);
}
std::string arm_CRC32C([[maybe_unused]] Cond cond, Imm<2> sz, Reg n, Reg d, Reg m) {
static constexpr std::array data_type{
"b", "h", "w", "invalid",
};
return fmt::format("crc32c{} {}, {}, {}", data_type[sz.ZeroExtend()], d, n, m);
}
// Data processing instructions
std::string arm_ADC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm<8> imm8) {
return fmt::format("adc{}{} {}, {}, #{}", CondToString(cond), S ? "s" : "", d, n, ArmExpandImm(rotate, imm8));

View file

@ -0,0 +1,97 @@
/* 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 {
// It's considered constrained UNPREDICTABLE behavior if either
// CRC32 instruction variant is executed with a condition code
// that is *not* 0xE (Always execute). ARM defines one of the following
// as being a requirement in this case. Either:
//
// 1. The instruction is undefined.
// 2. The instruction executes as a NOP.
// 3. The instruction executes unconditionally.
// 4. The instruction executes conditionally.
//
// It's also considered constrained UNPREDICTABLE behavior if
// either CRC32 instruction variant is executed with a size specifier
// of 64-bit (sz -> 0b11)
//
// In this case, either:
//
// 1. The instruction is undefined
// 2. The instruction executes as a NOP.
// 3. The instruction executes with the additional decode: size = 32.
//
// In both cases, we treat as unpredictable, to allow
// library users to provide their own intended behavior
// in the unpredictable exception handler.
namespace {
enum class CRCType {
Castagnoli,
ISO,
};
bool CRC32Variant(ArmTranslatorVisitor& v, Cond cond, Imm<2> sz, Reg n, Reg d, Reg m, CRCType type) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return v.UnpredictableInstruction();
}
if (sz == 0b11) {
return v.UnpredictableInstruction();
}
if (cond != Cond::AL) {
return v.UnpredictableInstruction();
}
const IR::U32 result = [m, n, sz, type, &v] {
const IR::U32 accumulator = v.ir.GetRegister(n);
const IR::U32 data = v.ir.GetRegister(m);
if (type == CRCType::ISO) {
switch (sz.ZeroExtend()) {
case 0b00:
return v.ir.CRC32ISO8(accumulator, v.ir.And(data, v.ir.Imm32(0xFF)));
case 0b01:
return v.ir.CRC32ISO16(accumulator, v.ir.And(data, v.ir.Imm32(0xFFFF)));
case 0b10:
return v.ir.CRC32ISO32(accumulator, data);
}
} else {
switch (sz.ZeroExtend()) {
case 0b00:
return v.ir.CRC32Castagnoli8(accumulator, v.ir.And(data, v.ir.Imm32(0xFF)));
case 0b01:
return v.ir.CRC32Castagnoli16(accumulator, v.ir.And(data, v.ir.Imm32(0xFFFF)));
case 0b10:
return v.ir.CRC32Castagnoli32(accumulator, data);
}
}
UNREACHABLE();
return IR::U32{};
}();
v.ir.SetRegister(d, result);
return true;
}
} // Anonymous namespace
// CRC32{B,H,W}{<q>} <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_CRC32(Cond cond, Imm<2> sz, Reg n, Reg d, Reg m) {
return CRC32Variant(*this, cond, sz, n, d, m, CRCType::ISO);
}
// CRC32C{B,H,W}{<q>} <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_CRC32C(Cond cond, Imm<2> sz, Reg n, Reg d, Reg m) {
return CRC32Variant(*this, cond, sz, n, d, m, CRCType::Castagnoli);
}
} // namespace Dynarmic::A32

View file

@ -87,6 +87,10 @@ struct ArmTranslatorVisitor final {
bool arm_MRRC(Cond cond, Reg t2, Reg t, size_t coproc_no, size_t opc, CoprocReg CRm);
bool arm_STC(Cond cond, bool p, bool u, bool d, bool w, Reg n, CoprocReg CRd, size_t coproc_no, Imm<8> imm8);
// CRC32 instructions
bool arm_CRC32(Cond cond, Imm<2> sz, Reg n, Reg d, Reg m);
bool arm_CRC32C(Cond cond, Imm<2> sz, Reg n, Reg d, Reg m);
// Data processing instructions
bool arm_ADC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm<8> imm8);
bool arm_ADC_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5, ShiftType shift, Reg m);