From e37689315d18e668a10f61f1ecac5824fe80ba65 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 1 May 2019 20:32:47 -0400 Subject: [PATCH] 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. --- src/CMakeLists.txt | 1 + src/frontend/A32/decoder/arm.inc | 4 + .../A32/disassembler/disassembler_arm.cpp | 16 +++ .../A32/translate/translate_arm/crc32.cpp | 97 +++++++++++++++++++ .../translate/translate_arm/translate_arm.h | 4 + 5 files changed, 122 insertions(+) create mode 100644 src/frontend/A32/translate/translate_arm/crc32.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c3c748cd..820f3700 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/frontend/A32/decoder/arm.inc b/src/frontend/A32/decoder/arm.inc index ea3a96b5..0935d7ca 100644 --- a/src/frontend/A32/decoder/arm.inc +++ b/src/frontend/A32/decoder/arm.inc @@ -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) diff --git a/src/frontend/A32/disassembler/disassembler_arm.cpp b/src/frontend/A32/disassembler/disassembler_arm.cpp index f922bc6e..337d7ec7 100644 --- a/src/frontend/A32/disassembler/disassembler_arm.cpp +++ b/src/frontend/A32/disassembler/disassembler_arm.cpp @@ -224,6 +224,22 @@ public: return ""; } + // 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)); diff --git a/src/frontend/A32/translate/translate_arm/crc32.cpp b/src/frontend/A32/translate/translate_arm/crc32.cpp new file mode 100644 index 00000000..7ce1ec07 --- /dev/null +++ b/src/frontend/A32/translate/translate_arm/crc32.cpp @@ -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}{} , , +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}{} , , +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 diff --git a/src/frontend/A32/translate/translate_arm/translate_arm.h b/src/frontend/A32/translate/translate_arm/translate_arm.h index 0a1160b9..ea6e3b12 100644 --- a/src/frontend/A32/translate/translate_arm/translate_arm.h +++ b/src/frontend/A32/translate/translate_arm/translate_arm.h @@ -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);