From 82868034d32d6ab0ca16fa314b6b095d4ef65ba1 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sun, 5 Jul 2020 18:21:12 +0100 Subject: [PATCH] A32/ASIMD: Ensure decoder table is correct * Raise a DecoderError instead of ASSERT-ing on a decode error * Correct ASIMD decode table * Write a test which verifies every possible ASIMD instruction --- include/dynarmic/A32/config.h | 2 + src/frontend/A32/decoder/asimd.h | 23 +++--- src/frontend/A32/decoder/asimd.inc | 37 ++++++--- .../impl/asimd_load_store_structures.cpp | 16 +++- .../A32/translate/impl/asimd_three_regs.cpp | 11 ++- .../translate/impl/asimd_two_regs_scalar.cpp | 12 +-- .../translate/impl/asimd_two_regs_shift.cpp | 56 +++++++------- .../A32/translate/impl/translate_arm.h | 1 + src/frontend/A32/translate/translate_arm.cpp | 6 ++ tests/CMakeLists.txt | 1 + tests/cpu_info.cpp | 5 ++ tests/decoder_tests.cpp | 75 +++++++++++++++++++ 12 files changed, 183 insertions(+), 62 deletions(-) create mode 100644 tests/decoder_tests.cpp diff --git a/include/dynarmic/A32/config.h b/include/dynarmic/A32/config.h index 0747697c..0c0919fc 100644 --- a/include/dynarmic/A32/config.h +++ b/include/dynarmic/A32/config.h @@ -29,6 +29,8 @@ enum class Exception { /// An unpredictable instruction is to be executed. Implementation-defined behaviour should now happen. /// This behaviour is up to the user of this library to define. UnpredictableInstruction, + /// A decode error occurred when decoding this instruction. This should never happen. + DecodeError, /// A SEV instruction was executed. The event register of all PEs should be set. (Hint instruction.) SendEvent, /// A SEVL instruction was executed. The event register of the current PE should be set. (Hint instruction.) diff --git a/src/frontend/A32/decoder/asimd.h b/src/frontend/A32/decoder/asimd.h index b0134166..5316430a 100644 --- a/src/frontend/A32/decoder/asimd.h +++ b/src/frontend/A32/decoder/asimd.h @@ -31,14 +31,13 @@ std::vector> GetASIMDDecodeTable() { }; - // If a matcher has more bits in its mask it is more specific, so it should come first. - std::stable_sort(table.begin(), table.end(), [](const auto& matcher1, const auto& matcher2) { - return Common::BitCount(matcher1.GetMask()) > Common::BitCount(matcher2.GetMask()); - }); - - // Exceptions to the above rule of thumb. + // Exceptions to the rule of thumb. const std::set comes_first{ - "VBIC, VMOV, VMVN, VORR (immediate)" + "VBIC, VMOV, VMVN, VORR (immediate)", + "VEXT", + "VTBL", + "VTBX", + "VDUP (scalar)", }; const std::set comes_last{ "VMLA (scalar)", @@ -50,14 +49,18 @@ std::vector> GetASIMDDecodeTable() { "VQDMULH (scalar)", "VQRDMULH (scalar)", }; - - std::stable_partition(table.begin(), table.end(), [&](const auto& matcher) { + const auto sort_begin = std::stable_partition(table.begin(), table.end(), [&](const auto& matcher) { return comes_first.count(matcher.GetName()) > 0; }); - std::stable_partition(table.begin(), table.end(), [&](const auto& matcher) { + const auto sort_end = std::stable_partition(table.begin(), table.end(), [&](const auto& matcher) { return comes_last.count(matcher.GetName()) == 0; }); + // If a matcher has more bits in its mask it is more specific, so it should come first. + std::stable_sort(sort_begin, sort_end, [](const auto& matcher1, const auto& matcher2) { + return Common::BitCount(matcher1.GetMask()) > Common::BitCount(matcher2.GetMask()); + }); + return table; } diff --git a/src/frontend/A32/decoder/asimd.inc b/src/frontend/A32/decoder/asimd.inc index 05a95be2..bf71fe8e 100644 --- a/src/frontend/A32/decoder/asimd.inc +++ b/src/frontend/A32/decoder/asimd.inc @@ -96,10 +96,6 @@ INST(asimd_VCVT_fixed, "VCVT (fixed-point)", "1111001U1Diiiiiidddd111 // Two registers, miscellaneous INST(asimd_VREV, "VREV{16,32,64}", "111100111D11zz00dddd000ooQM0mmmm") // ASIMD INST(asimd_VPADDL, "VPADDL", "111100111D11zz00dddd0010oQM0mmmm") // ASIMD -INST(v8_AESE, "AESE", "111100111D11zz00dddd001100M0mmmm") // v8 -INST(v8_AESD, "AESD", "111100111D11zz00dddd001101M0mmmm") // v8 -INST(v8_AESMC, "AESMC", "111100111D11zz00dddd001110M0mmmm") // v8 -INST(v8_AESIMC, "AESIMC", "111100111D11zz00dddd001111M0mmmm") // v8 INST(asimd_VCLS, "VCLS", "111100111D11zz00dddd01000QM0mmmm") // ASIMD INST(asimd_VCLZ, "VCLZ", "111100111D11zz00dddd01001QM0mmmm") // ASIMD INST(asimd_VCNT, "VCNT", "111100111D11zz00dddd01010QM0mmmm") // ASIMD @@ -112,9 +108,11 @@ INST(asimd_VCGE_zero, "VCGE (zero)", "111100111D11zz01dddd0F0 INST(asimd_VCEQ_zero, "VCEQ (zero)", "111100111D11zz01dddd0F010QM0mmmm") // ASIMD INST(asimd_VCLE_zero, "VCLE (zero)", "111100111D11zz01dddd0F011QM0mmmm") // ASIMD INST(asimd_VCLT_zero, "VCLT (zero)", "111100111D11zz01dddd0F100QM0mmmm") // ASIMD +INST(arm_UDF, "UNALLOCATED", "111100111-11--01----01101--0----") // v8 INST(asimd_VABS, "VABS", "111100111D11zz01dddd0F110QM0mmmm") // ASIMD INST(asimd_VNEG, "VNEG", "111100111D11zz01dddd0F111QM0mmmm") // ASIMD INST(asimd_VSWP, "VSWP", "111100111D110010dddd00000QM0mmmm") // ASIMD +INST(arm_UDF, "UNALLOCATED", "111100111-11--10----00000--0----") // ASIMD INST(asimd_VTRN, "VTRN", "111100111D11zz10dddd00001QM0mmmm") // ASIMD INST(asimd_VUZP, "VUZP", "111100111D11zz10dddd00010QM0mmmm") // ASIMD INST(asimd_VZIP, "VZIP", "111100111D11zz10dddd00011QM0mmmm") // ASIMD @@ -122,19 +120,41 @@ INST(asimd_VMOVN, "VMOVN", "111100111D11zz10dddd001 INST(asimd_VQMOVUN, "VQMOVUN", "111100111D11zz10dddd001001M0mmmm") // ASIMD INST(asimd_VQMOVN, "VQMOVN", "111100111D11zz10dddd00101oM0mmmm") // ASIMD INST(asimd_VSHLL_max, "VSHLL_max", "111100111D11zz10dddd001100M0mmmm") // ASIMD -//INST(asimd_VCVT_half, "VCVT (half-precision)", "111100111-11--10----011x00-0----") // ASIMD +INST(arm_UDF, "UNALLOCATED (VRINTN)", "111100111-11--10----01000--0----") +INST(arm_UDF, "UNALLOCATED (VRINTX)", "111100111-11--10----01001--0----") +INST(arm_UDF, "UNALLOCATED (VRINTA)", "111100111-11--10----01010--0----") +INST(arm_UDF, "UNALLOCATED (VRINTZ)", "111100111-11--10----01011--0----") +INST(arm_UDF, "UNALLOCATED (VRINTM)", "111100111-11--10----01101--0----") +INST(arm_UDF, "UNALLOCATED (VRINTP)", "111100111-11--10----01111--0----") +INST(arm_UDF, "UNALLOCATED (VCVT half)", "111100111-11--10----011-00-0----") // ASIMD +INST(arm_UDF, "UNALLOCATED", "111100111-11--10----011-01-0----") // ASIMD +INST(arm_UDF, "UNALLOCATED (VCVTA)", "111100111-11--11----0000---0----") +INST(arm_UDF, "UNALLOCATED (VCVTN)", "111100111-11--11----0001---0----") +INST(arm_UDF, "UNALLOCATED (VCVTP)", "111100111-11--11----0010---0----") +INST(arm_UDF, "UNALLOCATED (VCVTM)", "111100111-11--11----0011---0----") INST(asimd_VRECPE, "VRECPE", "111100111D11zz11dddd010F0QM0mmmm") // ASIMD INST(asimd_VRSQRTE, "VRSQRTE", "111100111D11zz11dddd010F1QM0mmmm") // ASIMD INST(asimd_VCVT_integer, "VCVT (integer)", "111100111D11zz11dddd011oUQM0mmmm") // ASIMD +// Two registers, cryptography +INST(v8_AESE, "AESE", "111100111D11zz00dddd001100M0mmmm") // v8 +INST(v8_AESD, "AESD", "111100111D11zz00dddd001101M0mmmm") // v8 +INST(v8_AESMC, "AESMC", "111100111D11zz00dddd001110M0mmmm") // v8 +INST(v8_AESIMC, "AESIMC", "111100111D11zz00dddd001111M0mmmm") // v8 +INST(arm_UDF, "UNALLOCATED", "111100111-11--01----001010-0----") // v8 +INST(arm_UDF, "UNALLOCATED (SHA1H)", "111100111-11--01----001011-0----") // v8 +INST(arm_UDF, "UNALLOCATED (SHA1SU1)", "111100111-11--10----001110-0----") // v8 +INST(arm_UDF, "UNALLOCATED (SHA256SU0)", "111100111-11--10----001111-0----") // v8 + +// One register and modified immediate +INST(asimd_VMOV_imm, "VBIC, VMOV, VMVN, VORR (immediate)", "1111001a1D000bcdVVVVmmmm0Qo1efgh") // ASIMD + // Miscellaneous INST(asimd_VEXT, "VEXT", "111100101D11nnnnddddiiiiNQM0mmmm") // ASIMD INST(asimd_VTBL, "VTBL", "111100111D11nnnndddd10zzN0M0mmmm") // ASIMD INST(asimd_VTBX, "VTBX", "111100111D11nnnndddd10zzN1M0mmmm") // ASIMD INST(asimd_VDUP_scalar, "VDUP (scalar)", "111100111D11iiiidddd11000QM0mmmm") // ASIMD - -// One register and modified immediate -INST(asimd_VMOV_imm, "VBIC, VMOV, VMVN, VORR (immediate)", "1111001a1D000bcdVVVVmmmm0Qo1efgh") // ASIMD +INST(arm_UDF, "UNALLOCATED", "111100111-11--------11-----0----") // ASIMD // Advanced SIMD load/store structures INST(v8_VST_multiple, "VST{1-4} (multiple)", "111101000D00nnnnddddxxxxzzaammmm") // v8 @@ -143,6 +163,5 @@ INST(arm_UDF, "UNALLOCATED", "111101000--0--------101 INST(arm_UDF, "UNALLOCATED", "111101000--0--------11----------") // v8 INST(arm_UDF, "UNALLOCATED", "111101001-00--------11----------") // v8 INST(v8_VLD_all_lanes, "VLD{1-4} (all lanes)", "111101001D10nnnndddd11nnzzTammmm") // v8 -INST(arm_UDF, "UNALLOCATED", "111101001-10--------1110---1----") // v8 INST(v8_VST_single, "VST{1-4} (single)", "111101001D00nnnnddddzzNNaaaammmm") // v8 INST(v8_VLD_single, "VLD{1-4} (single)", "111101001D10nnnnddddzzNNaaaammmm") // v8 diff --git a/src/frontend/A32/translate/impl/asimd_load_store_structures.cpp b/src/frontend/A32/translate/impl/asimd_load_store_structures.cpp index 87e01999..e4f66480 100644 --- a/src/frontend/A32/translate/impl/asimd_load_store_structures.cpp +++ b/src/frontend/A32/translate/impl/asimd_load_store_structures.cpp @@ -73,6 +73,10 @@ std::optional> DecodeType(Imm<4> type, size_t } // anoynmous namespace bool ArmTranslatorVisitor::v8_VST_multiple(bool D, Reg n, size_t Vd, Imm<4> type, size_t size, size_t align, Reg m) { + if (type == 0b1011 || type.Bits<2, 3>() == 0b11) { + return DecodeError(); + } + const auto decoded_type = DecodeType(type, size, align); if (!decoded_type) { return UndefinedInstruction(); @@ -118,6 +122,10 @@ bool ArmTranslatorVisitor::v8_VST_multiple(bool D, Reg n, size_t Vd, Imm<4> type } bool ArmTranslatorVisitor::v8_VLD_multiple(bool D, Reg n, size_t Vd, Imm<4> type, size_t size, size_t align, Reg m) { + if (type == 0b1011 || type.Bits<2, 3>() == 0b11) { + return DecodeError(); + } + const auto decoded_type = DecodeType(type, size, align); if (!decoded_type) { return UndefinedInstruction(); @@ -238,7 +246,9 @@ bool ArmTranslatorVisitor::v8_VLD_all_lanes(bool D, Reg n, size_t Vd, size_t nn, bool ArmTranslatorVisitor::v8_VST_single(bool D, Reg n, size_t Vd, size_t sz, size_t nn, size_t index_align, Reg m) { const size_t nelem = nn + 1; - ASSERT_MSG(sz != 0b11, "Decode Error"); + if (sz == 0b11) { + return DecodeError(); + } if (nelem == 1 && Common::Bit(sz, index_align)) { return UndefinedInstruction(); @@ -300,7 +310,9 @@ bool ArmTranslatorVisitor::v8_VST_single(bool D, Reg n, size_t Vd, size_t sz, si bool ArmTranslatorVisitor::v8_VLD_single(bool D, Reg n, size_t Vd, size_t sz, size_t nn, size_t index_align, Reg m) { const size_t nelem = nn + 1; - ASSERT_MSG(sz != 0b11, "Decode Error"); + if (sz == 0b11) { + return DecodeError(); + } if (nelem == 1 && Common::Bit(sz, index_align)) { return UndefinedInstruction(); diff --git a/src/frontend/A32/translate/impl/asimd_three_regs.cpp b/src/frontend/A32/translate/impl/asimd_three_regs.cpp index 9b4ba5a2..467b24ba 100644 --- a/src/frontend/A32/translate/impl/asimd_three_regs.cpp +++ b/src/frontend/A32/translate/impl/asimd_three_regs.cpp @@ -185,7 +185,7 @@ bool AbsoluteDifference(ArmTranslatorVisitor& v, bool U, bool D, size_t sz, size bool AbsoluteDifferenceLong(ArmTranslatorVisitor& v, bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm, AccumulateBehavior accumulate) { if (sz == 0b11) { - return v.UndefinedInstruction(); + return v.DecodeError(); } if (Common::Bit<0>(Vd)) { @@ -223,8 +223,7 @@ bool WideInstruction(ArmTranslatorVisitor& v, bool U, bool D, size_t sz, size_t const bool widen_first = widen_behaviour == WidenBehaviour::Both; if (sz == 0b11) { - // Decode error - return v.UndefinedInstruction(); + return v.DecodeError(); } if (Common::Bit<0>(Vd) || (!widen_first && Common::Bit<0>(Vn))) { @@ -868,7 +867,11 @@ bool ArmTranslatorVisitor::asimd_VMLAL(bool U, bool D, size_t sz, size_t Vn, siz } bool ArmTranslatorVisitor::asimd_VMULL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool P, bool N, bool M, size_t Vm) { - if (sz == 0b11 || (P & (U || sz == 0b10)) || Common::Bit<0>(Vd)) { + if (sz == 0b11) { + return DecodeError(); + } + + if ((P & (U || sz == 0b10)) || Common::Bit<0>(Vd)) { return UndefinedInstruction(); } diff --git a/src/frontend/A32/translate/impl/asimd_two_regs_scalar.cpp b/src/frontend/A32/translate/impl/asimd_two_regs_scalar.cpp index 4cb1f462..ab2e4ce2 100644 --- a/src/frontend/A32/translate/impl/asimd_two_regs_scalar.cpp +++ b/src/frontend/A32/translate/impl/asimd_two_regs_scalar.cpp @@ -32,8 +32,7 @@ enum class Rounding { bool ScalarMultiply(ArmTranslatorVisitor& v, bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool F, bool N, bool M, size_t Vm, MultiplyBehavior multiply) { if (sz == 0b11) { - // TODO: This should be a decode error. - return v.UndefinedInstruction(); + return v.DecodeError(); } if (sz == 0b00 || (F && sz == 0b01)) { @@ -75,8 +74,7 @@ bool ScalarMultiply(ArmTranslatorVisitor& v, bool Q, bool D, size_t sz, size_t V bool ScalarMultiplyLong(ArmTranslatorVisitor& v, bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm, MultiplyBehavior multiply) { if (sz == 0b11) { - // TODO: This should be a decode error. - return v.UndefinedInstruction(); + return v.DecodeError(); } if (sz == 0b00 || Common::Bit<0>(Vd)) { @@ -114,8 +112,7 @@ bool ScalarMultiplyLong(ArmTranslatorVisitor& v, bool U, bool D, size_t sz, size bool ScalarMultiplyReturnHigh(ArmTranslatorVisitor& v, bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm, Rounding round) { if (sz == 0b11) { - // TODO: This should be a decode error. - return v.UndefinedInstruction(); + return v.DecodeError(); } if (sz == 0b00) { @@ -171,8 +168,7 @@ bool ArmTranslatorVisitor::asimd_VMULL_scalar(bool U, bool D, size_t sz, size_t bool ArmTranslatorVisitor::asimd_VQDMULL_scalar(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm) { if (sz == 0b11) { - // TODO: This should be a decode error. - return UndefinedInstruction(); + return DecodeError(); } if (sz == 0b00 || Common::Bit<0>(Vd)) { diff --git a/src/frontend/A32/translate/impl/asimd_two_regs_shift.cpp b/src/frontend/A32/translate/impl/asimd_two_regs_shift.cpp index 2ca471de..427fb668 100644 --- a/src/frontend/A32/translate/impl/asimd_two_regs_shift.cpp +++ b/src/frontend/A32/translate/impl/asimd_two_regs_shift.cpp @@ -59,13 +59,12 @@ std::pair ElementSizeAndShiftAmount(bool right_shift, bool L, si bool ShiftRight(ArmTranslatorVisitor& v, bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm, Accumulating accumulate, Rounding rounding) { - if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) { - return v.UndefinedInstruction(); + if (!L && Common::Bits<3, 5>(imm6) == 0) { + return v.DecodeError(); } - // Technically just a related encoding (One register and modified immediate instructions) - if (!L && Common::Bits<3, 5>(imm6) == 0) { - ASSERT_FALSE(); + if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) { + return v.UndefinedInstruction(); } const auto [esize, shift_amount] = ElementSizeAndShiftAmount(true, L, imm6); @@ -93,8 +92,7 @@ bool ShiftRight(ArmTranslatorVisitor& v, bool U, bool D, size_t imm6, size_t Vd, bool ShiftRightNarrowing(ArmTranslatorVisitor& v, bool D, size_t imm6, size_t Vd, bool M, size_t Vm, Rounding rounding, Narrowing narrowing, Signedness signedness) { if (Common::Bits<3, 5>(imm6) == 0) { - // TODO: Decode error - return v.UndefinedInstruction(); + return v.DecodeError(); } if (Common::Bit<0>(Vm)) { @@ -163,13 +161,12 @@ bool ArmTranslatorVisitor::asimd_VRSRA(bool U, bool D, size_t imm6, size_t Vd, b } bool ArmTranslatorVisitor::asimd_VSRI(bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) { - if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) { - return UndefinedInstruction(); + if (!L && Common::Bits<3, 5>(imm6) == 0) { + return DecodeError(); } - // Technically just a related encoding (One register and modified immediate instructions) - if (!L && Common::Bits<3, 5>(imm6) == 0) { - ASSERT_FALSE(); + if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) { + return UndefinedInstruction(); } const auto [esize, shift_amount] = ElementSizeAndShiftAmount(true, L, imm6); @@ -190,12 +187,11 @@ bool ArmTranslatorVisitor::asimd_VSRI(bool D, size_t imm6, size_t Vd, bool L, bo } bool ArmTranslatorVisitor::asimd_VSLI(bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) { - if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) { - return UndefinedInstruction(); + if (!L && Common::Bits<3, 5>(imm6) == 0) { + return DecodeError(); } - // Technically just a related encoding (One register and modified immediate instructions) - if (!L && Common::Bits<3, 5>(imm6) == 0) { + if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) { return UndefinedInstruction(); } @@ -217,6 +213,10 @@ bool ArmTranslatorVisitor::asimd_VSLI(bool D, size_t imm6, size_t Vd, bool L, bo } bool ArmTranslatorVisitor::asimd_VQSHL(bool U, bool D, size_t imm6, size_t Vd, bool op, bool L, bool Q, bool M, size_t Vm) { + if (!L && Common::Bits<3, 5>(imm6) == 0) { + return DecodeError(); + } + if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) { return UndefinedInstruction(); } @@ -225,11 +225,6 @@ bool ArmTranslatorVisitor::asimd_VQSHL(bool U, bool D, size_t imm6, size_t Vd, b return UndefinedInstruction(); } - // Technically just a related encoding (One register and modified immediate instructions) - if (!L && Common::Bits<3, 5>(imm6) == 0) { - ASSERT_FALSE(); - } - const auto d = ToVector(Q, Vd, D); const auto m = ToVector(Q, Vm, M); const auto result = [&] { @@ -256,13 +251,12 @@ bool ArmTranslatorVisitor::asimd_VQSHL(bool U, bool D, size_t imm6, size_t Vd, b } bool ArmTranslatorVisitor::asimd_VSHL(bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) { - if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) { - return UndefinedInstruction(); + if (!L && Common::Bits<3, 5>(imm6) == 0) { + return DecodeError(); } - // Technically just a related encoding (One register and modified immediate instructions) - if (!L && Common::Bits<3, 5>(imm6) == 0) { - ASSERT_FALSE(); + if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) { + return UndefinedInstruction(); } const auto [esize, shift_amount] = ElementSizeAndShiftAmount(false, L, imm6); @@ -307,7 +301,9 @@ bool ArmTranslatorVisitor::asimd_VQRSHRN(bool U, bool D, size_t imm6, size_t Vd, } bool ArmTranslatorVisitor::asimd_VSHLL(bool U, bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { - ASSERT_MSG((Common::Bits<3, 5>(imm6) != 0), "Decode error"); + if (Common::Bits<3, 5>(imm6) == 0) { + return DecodeError(); + } if (Common::Bit<0>(Vd)) { return UndefinedInstruction(); @@ -327,12 +323,14 @@ bool ArmTranslatorVisitor::asimd_VSHLL(bool U, bool D, size_t imm6, size_t Vd, b } bool ArmTranslatorVisitor::asimd_VCVT_fixed(bool U, bool D, size_t imm6, size_t Vd, bool to_fixed, bool Q, bool M, size_t Vm) { + if (Common::Bits<3, 5>(imm6) == 0) { + return DecodeError(); + } + if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) { return UndefinedInstruction(); } - ASSERT_MSG((Common::Bits<3, 5>(imm6) != 0), "Decode error"); - if (!Common::Bit<5>(imm6)) { return UndefinedInstruction(); } diff --git a/src/frontend/A32/translate/impl/translate_arm.h b/src/frontend/A32/translate/impl/translate_arm.h index 119da898..c433820c 100644 --- a/src/frontend/A32/translate/impl/translate_arm.h +++ b/src/frontend/A32/translate/impl/translate_arm.h @@ -43,6 +43,7 @@ struct ArmTranslatorVisitor final { bool InterpretThisInstruction(); bool UnpredictableInstruction(); bool UndefinedInstruction(); + bool DecodeError(); bool RaiseException(Exception exception); static u32 ArmExpandImm(int rotate, Imm<8> imm8) { diff --git a/src/frontend/A32/translate/translate_arm.cpp b/src/frontend/A32/translate/translate_arm.cpp index 1405a492..203774d0 100644 --- a/src/frontend/A32/translate/translate_arm.cpp +++ b/src/frontend/A32/translate/translate_arm.cpp @@ -168,6 +168,12 @@ bool ArmTranslatorVisitor::UndefinedInstruction() { return false; } +bool ArmTranslatorVisitor::DecodeError() { + ir.ExceptionRaised(Exception::DecodeError); + ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}}); + return false; +} + bool ArmTranslatorVisitor::RaiseException(Exception exception) { ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 4)); ir.ExceptionRaised(exception); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index faaf90b3..f4a596c2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,6 +10,7 @@ add_executable(dynarmic_tests A64/a64.cpp A64/testenv.h cpu_info.cpp + decoder_tests.cpp fp/FPToFixed.cpp fp/FPValue.cpp fp/mantissa_util_tests.cpp diff --git a/tests/cpu_info.cpp b/tests/cpu_info.cpp index 14c51b13..e70fc8fc 100644 --- a/tests/cpu_info.cpp +++ b/tests/cpu_info.cpp @@ -1,3 +1,8 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2020 MerryMage + * SPDX-License-Identifier: 0BSD + */ + #include #include diff --git a/tests/decoder_tests.cpp b/tests/decoder_tests.cpp new file mode 100644 index 00000000..afc4ebae --- /dev/null +++ b/tests/decoder_tests.cpp @@ -0,0 +1,75 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2020 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include +#include +#include + +#include + +#include +#include "common/assert.h" +#include "frontend/A32/decoder/asimd.h" +#include "frontend/A32/translate/impl/translate_arm.h" +#include "frontend/ir/opcodes.h" + +using namespace Dynarmic; + +TEST_CASE("ASIMD Decoder: Ensure table order correctness", "[decode][a32]") { + const auto table = A32::GetASIMDDecodeTable(); + + const auto get_ir = [](const A32::ASIMDMatcher& matcher, u32 instruction) { + ASSERT(matcher.Matches(instruction)); + + const A32::LocationDescriptor location{0, {}, {}}; + IR::Block block{location}; + A32::ArmTranslatorVisitor visitor{block, location, {}}; + matcher.call(visitor, instruction); + + return block; + }; + + const auto is_decode_error = [&get_ir](const A32::ASIMDMatcher& matcher, u32 instruction){ + const auto block = get_ir(matcher, instruction); + + for (const auto& ir_inst : block) { + if (ir_inst.GetOpcode() == IR::Opcode::A32ExceptionRaised) { + if (static_cast(ir_inst.GetArg(1).GetU64()) == A32::Exception::DecodeError) { + return true; + } + } + } + return false; + }; + + for (auto iter = table.cbegin(); iter != table.cend(); ++iter) { + if (std::strncmp(iter->GetName(), "UNALLOCATED", 11) == 0) { + continue; + } + + const u32 expect = iter->GetExpected(); + const u32 mask = iter->GetMask(); + u32 x = 0; + do { + const u32 instruction = expect | x; + + const bool iserr = is_decode_error(*iter, instruction); + const auto alternative = std::find_if(table.cbegin(), iter, [instruction](const auto& m) { return m.Matches(instruction); }); + const bool altiserr = is_decode_error(*alternative, instruction); + + INFO("Instruction: " << std::hex << std::setfill('0') << std::setw(8) << instruction); + INFO("Expect: " << std::hex << std::setfill('0') << std::setw(8) << expect); + INFO("Fill: " << std::hex << std::setfill('0') << std::setw(8) << x); + INFO("Name: " << iter->GetName()); + INFO("iserr: " << iserr); + INFO("alternative: " << alternative->GetName()); + INFO("altiserr: " << altiserr); + + REQUIRE(((!iserr && alternative == iter) || (iserr && alternative != iter && !altiserr))); + + x = ((x | mask) + 1) & ~mask; + } while (x != 0); + } +}