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:
parent
3c742960a9
commit
82868034d3
12 changed files with 183 additions and 62 deletions
|
@ -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.)
|
||||
|
|
|
@ -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.
|
||||
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<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{
|
||||
"VMLA (scalar)",
|
||||
|
@ -50,14 +49,18 @@ std::vector<ASIMDMatcher<V>> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -73,6 +73,10 @@ std::optional<std::tuple<size_t, size_t, size_t>> 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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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,
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2020 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
|
|
75
tests/decoder_tests.cpp
Normal file
75
tests/decoder_tests.cpp
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue