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
This commit is contained in:
MerryMage 2020-07-05 18:21:12 +01:00
parent 3c742960a9
commit 82868034d3
12 changed files with 183 additions and 62 deletions

View file

@ -29,6 +29,8 @@ enum class Exception {
/// An unpredictable instruction is to be executed. Implementation-defined behaviour should now happen. /// 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. /// This behaviour is up to the user of this library to define.
UnpredictableInstruction, 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.) /// A SEV instruction was executed. The event register of all PEs should be set. (Hint instruction.)
SendEvent, SendEvent,
/// A SEVL instruction was executed. The event register of the current PE should be set. (Hint instruction.) /// A SEVL instruction was executed. The event register of the current PE should be set. (Hint instruction.)

View file

@ -31,14 +31,13 @@ std::vector<ASIMDMatcher<V>> GetASIMDDecodeTable() {
}; };
// If a matcher has more bits in its mask it is more specific, so it should come first. // Exceptions to the rule of thumb.
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.
const std::set<std::string> comes_first{ const std::set<std::string> comes_first{
"VBIC, VMOV, VMVN, VORR (immediate)" "VBIC, VMOV, VMVN, VORR (immediate)",
"VEXT",
"VTBL",
"VTBX",
"VDUP (scalar)",
}; };
const std::set<std::string> comes_last{ const std::set<std::string> comes_last{
"VMLA (scalar)", "VMLA (scalar)",
@ -50,14 +49,18 @@ std::vector<ASIMDMatcher<V>> GetASIMDDecodeTable() {
"VQDMULH (scalar)", "VQDMULH (scalar)",
"VQRDMULH (scalar)", "VQRDMULH (scalar)",
}; };
const auto sort_begin = std::stable_partition(table.begin(), table.end(), [&](const auto& matcher) {
std::stable_partition(table.begin(), table.end(), [&](const auto& matcher) {
return comes_first.count(matcher.GetName()) > 0; 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; 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; return table;
} }

View file

@ -96,10 +96,6 @@ INST(asimd_VCVT_fixed, "VCVT (fixed-point)", "1111001U1Diiiiiidddd111
// Two registers, miscellaneous // Two registers, miscellaneous
INST(asimd_VREV, "VREV{16,32,64}", "111100111D11zz00dddd000ooQM0mmmm") // ASIMD INST(asimd_VREV, "VREV{16,32,64}", "111100111D11zz00dddd000ooQM0mmmm") // ASIMD
INST(asimd_VPADDL, "VPADDL", "111100111D11zz00dddd0010oQM0mmmm") // 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_VCLS, "VCLS", "111100111D11zz00dddd01000QM0mmmm") // ASIMD
INST(asimd_VCLZ, "VCLZ", "111100111D11zz00dddd01001QM0mmmm") // ASIMD INST(asimd_VCLZ, "VCLZ", "111100111D11zz00dddd01001QM0mmmm") // ASIMD
INST(asimd_VCNT, "VCNT", "111100111D11zz00dddd01010QM0mmmm") // 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_VCEQ_zero, "VCEQ (zero)", "111100111D11zz01dddd0F010QM0mmmm") // ASIMD
INST(asimd_VCLE_zero, "VCLE (zero)", "111100111D11zz01dddd0F011QM0mmmm") // ASIMD INST(asimd_VCLE_zero, "VCLE (zero)", "111100111D11zz01dddd0F011QM0mmmm") // ASIMD
INST(asimd_VCLT_zero, "VCLT (zero)", "111100111D11zz01dddd0F100QM0mmmm") // 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_VABS, "VABS", "111100111D11zz01dddd0F110QM0mmmm") // ASIMD
INST(asimd_VNEG, "VNEG", "111100111D11zz01dddd0F111QM0mmmm") // ASIMD INST(asimd_VNEG, "VNEG", "111100111D11zz01dddd0F111QM0mmmm") // ASIMD
INST(asimd_VSWP, "VSWP", "111100111D110010dddd00000QM0mmmm") // 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_VTRN, "VTRN", "111100111D11zz10dddd00001QM0mmmm") // ASIMD
INST(asimd_VUZP, "VUZP", "111100111D11zz10dddd00010QM0mmmm") // ASIMD INST(asimd_VUZP, "VUZP", "111100111D11zz10dddd00010QM0mmmm") // ASIMD
INST(asimd_VZIP, "VZIP", "111100111D11zz10dddd00011QM0mmmm") // ASIMD INST(asimd_VZIP, "VZIP", "111100111D11zz10dddd00011QM0mmmm") // ASIMD
@ -122,19 +120,41 @@ INST(asimd_VMOVN, "VMOVN", "111100111D11zz10dddd001
INST(asimd_VQMOVUN, "VQMOVUN", "111100111D11zz10dddd001001M0mmmm") // ASIMD INST(asimd_VQMOVUN, "VQMOVUN", "111100111D11zz10dddd001001M0mmmm") // ASIMD
INST(asimd_VQMOVN, "VQMOVN", "111100111D11zz10dddd00101oM0mmmm") // ASIMD INST(asimd_VQMOVN, "VQMOVN", "111100111D11zz10dddd00101oM0mmmm") // ASIMD
INST(asimd_VSHLL_max, "VSHLL_max", "111100111D11zz10dddd001100M0mmmm") // 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_VRECPE, "VRECPE", "111100111D11zz11dddd010F0QM0mmmm") // ASIMD
INST(asimd_VRSQRTE, "VRSQRTE", "111100111D11zz11dddd010F1QM0mmmm") // ASIMD INST(asimd_VRSQRTE, "VRSQRTE", "111100111D11zz11dddd010F1QM0mmmm") // ASIMD
INST(asimd_VCVT_integer, "VCVT (integer)", "111100111D11zz11dddd011oUQM0mmmm") // 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 // Miscellaneous
INST(asimd_VEXT, "VEXT", "111100101D11nnnnddddiiiiNQM0mmmm") // ASIMD INST(asimd_VEXT, "VEXT", "111100101D11nnnnddddiiiiNQM0mmmm") // ASIMD
INST(asimd_VTBL, "VTBL", "111100111D11nnnndddd10zzN0M0mmmm") // ASIMD INST(asimd_VTBL, "VTBL", "111100111D11nnnndddd10zzN0M0mmmm") // ASIMD
INST(asimd_VTBX, "VTBX", "111100111D11nnnndddd10zzN1M0mmmm") // ASIMD INST(asimd_VTBX, "VTBX", "111100111D11nnnndddd10zzN1M0mmmm") // ASIMD
INST(asimd_VDUP_scalar, "VDUP (scalar)", "111100111D11iiiidddd11000QM0mmmm") // ASIMD INST(asimd_VDUP_scalar, "VDUP (scalar)", "111100111D11iiiidddd11000QM0mmmm") // ASIMD
INST(arm_UDF, "UNALLOCATED", "111100111-11--------11-----0----") // ASIMD
// One register and modified immediate
INST(asimd_VMOV_imm, "VBIC, VMOV, VMVN, VORR (immediate)", "1111001a1D000bcdVVVVmmmm0Qo1efgh") // ASIMD
// Advanced SIMD load/store structures // Advanced SIMD load/store structures
INST(v8_VST_multiple, "VST{1-4} (multiple)", "111101000D00nnnnddddxxxxzzaammmm") // v8 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", "111101000--0--------11----------") // v8
INST(arm_UDF, "UNALLOCATED", "111101001-00--------11----------") // v8 INST(arm_UDF, "UNALLOCATED", "111101001-00--------11----------") // v8
INST(v8_VLD_all_lanes, "VLD{1-4} (all lanes)", "111101001D10nnnndddd11nnzzTammmm") // 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_VST_single, "VST{1-4} (single)", "111101001D00nnnnddddzzNNaaaammmm") // v8
INST(v8_VLD_single, "VLD{1-4} (single)", "111101001D10nnnnddddzzNNaaaammmm") // v8 INST(v8_VLD_single, "VLD{1-4} (single)", "111101001D10nnnnddddzzNNaaaammmm") // v8

View file

@ -73,6 +73,10 @@ std::optional<std::tuple<size_t, size_t, size_t>> DecodeType(Imm<4> type, size_t
} // anoynmous namespace } // 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) { 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); const auto decoded_type = DecodeType(type, size, align);
if (!decoded_type) { if (!decoded_type) {
return UndefinedInstruction(); 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) { 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); const auto decoded_type = DecodeType(type, size, align);
if (!decoded_type) { if (!decoded_type) {
return UndefinedInstruction(); 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) { 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; 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)) { if (nelem == 1 && Common::Bit(sz, index_align)) {
return UndefinedInstruction(); 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) { 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; 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)) { if (nelem == 1 && Common::Bit(sz, index_align)) {
return UndefinedInstruction(); return UndefinedInstruction();

View file

@ -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, 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) { AccumulateBehavior accumulate) {
if (sz == 0b11) { if (sz == 0b11) {
return v.UndefinedInstruction(); return v.DecodeError();
} }
if (Common::Bit<0>(Vd)) { 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; const bool widen_first = widen_behaviour == WidenBehaviour::Both;
if (sz == 0b11) { if (sz == 0b11) {
// Decode error return v.DecodeError();
return v.UndefinedInstruction();
} }
if (Common::Bit<0>(Vd) || (!widen_first && Common::Bit<0>(Vn))) { 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) { 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(); return UndefinedInstruction();
} }

View file

@ -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, 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) { MultiplyBehavior multiply) {
if (sz == 0b11) { if (sz == 0b11) {
// TODO: This should be a decode error. return v.DecodeError();
return v.UndefinedInstruction();
} }
if (sz == 0b00 || (F && sz == 0b01)) { 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) { 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) { if (sz == 0b11) {
// TODO: This should be a decode error. return v.DecodeError();
return v.UndefinedInstruction();
} }
if (sz == 0b00 || Common::Bit<0>(Vd)) { 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, 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) { Rounding round) {
if (sz == 0b11) { if (sz == 0b11) {
// TODO: This should be a decode error. return v.DecodeError();
return v.UndefinedInstruction();
} }
if (sz == 0b00) { 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) { 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) { if (sz == 0b11) {
// TODO: This should be a decode error. return DecodeError();
return UndefinedInstruction();
} }
if (sz == 0b00 || Common::Bit<0>(Vd)) { if (sz == 0b00 || Common::Bit<0>(Vd)) {

View file

@ -59,13 +59,12 @@ std::pair<size_t, size_t> 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, 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) { Accumulating accumulate, Rounding rounding) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) { if (!L && Common::Bits<3, 5>(imm6) == 0) {
return v.UndefinedInstruction(); return v.DecodeError();
} }
// Technically just a related encoding (One register and modified immediate instructions) if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
if (!L && Common::Bits<3, 5>(imm6) == 0) { return v.UndefinedInstruction();
ASSERT_FALSE();
} }
const auto [esize, shift_amount] = ElementSizeAndShiftAmount(true, L, imm6); 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, bool ShiftRightNarrowing(ArmTranslatorVisitor& v, bool D, size_t imm6, size_t Vd, bool M, size_t Vm,
Rounding rounding, Narrowing narrowing, Signedness signedness) { Rounding rounding, Narrowing narrowing, Signedness signedness) {
if (Common::Bits<3, 5>(imm6) == 0) { if (Common::Bits<3, 5>(imm6) == 0) {
// TODO: Decode error return v.DecodeError();
return v.UndefinedInstruction();
} }
if (Common::Bit<0>(Vm)) { 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) { 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))) { if (!L && Common::Bits<3, 5>(imm6) == 0) {
return UndefinedInstruction(); return DecodeError();
} }
// Technically just a related encoding (One register and modified immediate instructions) if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
if (!L && Common::Bits<3, 5>(imm6) == 0) { return UndefinedInstruction();
ASSERT_FALSE();
} }
const auto [esize, shift_amount] = ElementSizeAndShiftAmount(true, L, imm6); 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) { 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))) { if (!L && Common::Bits<3, 5>(imm6) == 0) {
return UndefinedInstruction(); return DecodeError();
} }
// Technically just a related encoding (One register and modified immediate instructions) if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
if (!L && Common::Bits<3, 5>(imm6) == 0) {
return UndefinedInstruction(); 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) { 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))) { if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction(); return UndefinedInstruction();
} }
@ -225,11 +225,6 @@ bool ArmTranslatorVisitor::asimd_VQSHL(bool U, bool D, size_t imm6, size_t Vd, b
return UndefinedInstruction(); 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 d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M); const auto m = ToVector(Q, Vm, M);
const auto result = [&] { 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) { 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))) { if (!L && Common::Bits<3, 5>(imm6) == 0) {
return UndefinedInstruction(); return DecodeError();
} }
// Technically just a related encoding (One register and modified immediate instructions) if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
if (!L && Common::Bits<3, 5>(imm6) == 0) { return UndefinedInstruction();
ASSERT_FALSE();
} }
const auto [esize, shift_amount] = ElementSizeAndShiftAmount(false, L, imm6); 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) { 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)) { if (Common::Bit<0>(Vd)) {
return UndefinedInstruction(); 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) { 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))) { if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction(); return UndefinedInstruction();
} }
ASSERT_MSG((Common::Bits<3, 5>(imm6) != 0), "Decode error");
if (!Common::Bit<5>(imm6)) { if (!Common::Bit<5>(imm6)) {
return UndefinedInstruction(); return UndefinedInstruction();
} }

View file

@ -43,6 +43,7 @@ struct ArmTranslatorVisitor final {
bool InterpretThisInstruction(); bool InterpretThisInstruction();
bool UnpredictableInstruction(); bool UnpredictableInstruction();
bool UndefinedInstruction(); bool UndefinedInstruction();
bool DecodeError();
bool RaiseException(Exception exception); bool RaiseException(Exception exception);
static u32 ArmExpandImm(int rotate, Imm<8> imm8) { static u32 ArmExpandImm(int rotate, Imm<8> imm8) {

View file

@ -168,6 +168,12 @@ bool ArmTranslatorVisitor::UndefinedInstruction() {
return false; return false;
} }
bool ArmTranslatorVisitor::DecodeError() {
ir.ExceptionRaised(Exception::DecodeError);
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
bool ArmTranslatorVisitor::RaiseException(Exception exception) { bool ArmTranslatorVisitor::RaiseException(Exception exception) {
ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 4)); ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 4));
ir.ExceptionRaised(exception); ir.ExceptionRaised(exception);

View file

@ -10,6 +10,7 @@ add_executable(dynarmic_tests
A64/a64.cpp A64/a64.cpp
A64/testenv.h A64/testenv.h
cpu_info.cpp cpu_info.cpp
decoder_tests.cpp
fp/FPToFixed.cpp fp/FPToFixed.cpp
fp/FPValue.cpp fp/FPValue.cpp
fp/mantissa_util_tests.cpp fp/mantissa_util_tests.cpp

View file

@ -1,3 +1,8 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2020 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <array> #include <array>
#include <utility> #include <utility>

75
tests/decoder_tests.cpp Normal file
View file

@ -0,0 +1,75 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2020 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <cstring>
#include <iostream>
#include <iomanip>
#include <catch.hpp>
#include <dynarmic/A32/config.h>
#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<A32::ArmTranslatorVisitor>();
const auto get_ir = [](const A32::ASIMDMatcher<A32::ArmTranslatorVisitor>& 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<A32::ArmTranslatorVisitor>& 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<A32::Exception>(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);
}
}