diff --git a/External/FEXCore/Source/Interface/Core/ArchHelpers/CodeEmitter/ASIMDOps.inl b/External/FEXCore/Source/Interface/Core/ArchHelpers/CodeEmitter/ASIMDOps.inl index 88744bb3b..d20521150 100644 --- a/External/FEXCore/Source/Interface/Core/ArchHelpers/CodeEmitter/ASIMDOps.inl +++ b/External/FEXCore/Source/Interface/Core/ArchHelpers/CodeEmitter/ASIMDOps.inl @@ -335,6 +335,59 @@ public: ASIMDScalarCopy(Op, Q, imm5, 0b0000, rd.V(), rn.V()); } + // Advanced SIMD three same (FP16) + template + requires(std::is_same_v || std::is_same_v) + void ASIMDThreeSameFP16(uint32_t U, uint32_t a, uint32_t opcode, T rm, T rn, T rd) { + constexpr uint32_t Q = std::is_same_v ? 1U << 30 : 0; + constexpr uint32_t Op = 0b0000'1110'0100'0000'0000'01 << 10; + + uint32_t Instr = Op; + Instr |= Q; + Instr |= U << 29; + Instr |= a << 23; + Instr |= rm.Idx() << 16; + Instr |= opcode << 11; + Instr |= rn.Idx() << 5; + Instr |= rd.Idx(); + dc32(Instr); + } + + // Advanced SIMD two-register miscellaneous (FP16) + template + requires(std::is_same_v || std::is_same_v) + void ASIMDTwoRegMiscFP16(uint32_t U, uint32_t a, uint32_t opcode, T rn, T rd) { + constexpr uint32_t Q = std::is_same_v ? 1U << 30 : 0; + constexpr uint32_t Op = 0b0000'1110'0111'1000'0000'10 << 10; + + uint32_t Instr = Op; + Instr |= Q; + Instr |= U << 29; + Instr |= a << 23; + Instr |= opcode << 12; + Instr |= rn.Idx() << 5; + Instr |= rd.Idx(); + dc32(Instr); + } + + // Advanced SIMD three-register extension + template + requires(std::is_same_v || std::is_same_v) + void ASIMDThreeRegisterExt(uint32_t U, uint32_t opcode, FEXCore::ARMEmitter::SubRegSize size, T rm, T rn, T rd) { + constexpr uint32_t Q = std::is_same_v ? 1U << 30 : 0; + constexpr uint32_t Op = 0b0000'1110'0000'0000'1000'01 << 10; + + uint32_t Instr = Op; + Instr |= Q; + Instr |= U << 29; + Instr |= FEXCore::ToUnderlying(size) << 22; + Instr |= rm.Idx() << 16; + Instr |= opcode << 11; + Instr |= rn.Idx() << 5; + Instr |= rd.Idx(); + dc32(Instr); + } + template requires(std::is_same_v || std::is_same_v) void dup(FEXCore::ARMEmitter::SubRegSize size, T rd, FEXCore::ARMEmitter::Register rn) { @@ -532,11 +585,420 @@ public: // Advanced SIMD three same (FP16) - // TODO + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fmaxnm(T rd, T rn, T rm) { + ASIMDThreeSameFP16(0, 0, 0b000, rm, rn, rd); + } + + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fmla(T rd, T rn, T rm) { + ASIMDThreeSameFP16(0, 0, 0b001, rm, rn, rd); + } + + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fadd(T rd, T rn, T rm) { + ASIMDThreeSameFP16(0, 0, 0b010, rm, rn, rd); + } + + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fmulx(T rd, T rn, T rm) { + ASIMDThreeSameFP16(0, 0, 0b011, rm, rn, rd); + } + + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcmeq(T rd, T rn, T rm) { + ASIMDThreeSameFP16(0, 0, 0b100, rm, rn, rd); + } + + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fmax(T rd, T rn, T rm) { + ASIMDThreeSameFP16(0, 0, 0b110, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void frecps(T rd, T rn, T rm) { + ASIMDThreeSameFP16(0, 0, 0b111, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fminnm(T rd, T rn, T rm) { + ASIMDThreeSameFP16(0, 1, 0b000, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fmls(T rd, T rn, T rm) { + ASIMDThreeSameFP16(0, 1, 0b001, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fsub(T rd, T rn, T rm) { + ASIMDThreeSameFP16(0, 1, 0b010, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fmin(T rd, T rn, T rm) { + ASIMDThreeSameFP16(0, 1, 0b110, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void frsqrts(T rd, T rn, T rm) { + ASIMDThreeSameFP16(0, 1, 0b111, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fmaxnmp(T rd, T rn, T rm) { + ASIMDThreeSameFP16(1, 0, 0b000, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void faddp(T rd, T rn, T rm) { + ASIMDThreeSameFP16(1, 0, 0b010, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fmul(T rd, T rn, T rm) { + ASIMDThreeSameFP16(1, 0, 0b011, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcmge(T rd, T rn, T rm) { + ASIMDThreeSameFP16(1, 0, 0b100, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void facge(T rd, T rn, T rm) { + ASIMDThreeSameFP16(1, 0, 0b101, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fmaxp(T rd, T rn, T rm) { + ASIMDThreeSameFP16(1, 0, 0b110, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fdiv(T rd, T rn, T rm) { + ASIMDThreeSameFP16(1, 0, 0b111, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fminnmp(T rd, T rn, T rm) { + ASIMDThreeSameFP16(1, 1, 0b000, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fabd(T rd, T rn, T rm) { + ASIMDThreeSameFP16(1, 1, 0b010, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcmgt(T rd, T rn, T rm) { + ASIMDThreeSameFP16(1, 1, 0b100, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void facgt(T rd, T rn, T rm) { + ASIMDThreeSameFP16(1, 1, 0b101, rm, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fminp(T rd, T rn, T rm) { + ASIMDThreeSameFP16(1, 1, 0b110, rm, rn, rd); + } + // Advanced SIMD two-register miscellaneous (FP16) - // TODO + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void frintn(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 0, 0b11000, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void frintm(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 0, 0b11001, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcvtns(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 0, 0b11010, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcvtms(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 0, 0b11011, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcvtas(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 0, 0b11100, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void scvtf(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 0, 0b11101, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcmgt(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 1, 0b01100, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcmeq(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 1, 0b01101, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcmlt(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 1, 0b01110, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fabs(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 1, 0b01111, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void frintp(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 1, 0b11000, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void frintz(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 1, 0b11001, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcvtps(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 1, 0b11010, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcvtzs(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 1, 0b11011, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void frecpe(T rd, T rn) { + ASIMDTwoRegMiscFP16(0, 1, 0b11101, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void frinta(T rd, T rn) { + ASIMDTwoRegMiscFP16(1, 0, 0b11000, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void frintx(T rd, T rn) { + ASIMDTwoRegMiscFP16(1, 0, 0b11001, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcvtnu(T rd, T rn) { + ASIMDTwoRegMiscFP16(1, 0, 0b11010, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcvtmu(T rd, T rn) { + ASIMDTwoRegMiscFP16(1, 0, 0b11011, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcvtau(T rd, T rn) { + ASIMDTwoRegMiscFP16(1, 0, 0b11100, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void ucvtf(T rd, T rn) { + ASIMDTwoRegMiscFP16(1, 0, 0b11101, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcmge(T rd, T rn) { + ASIMDTwoRegMiscFP16(1, 1, 0b01100, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcmle(T rd, T rn) { + ASIMDTwoRegMiscFP16(1, 1, 0b01101, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fneg(T rd, T rn) { + ASIMDTwoRegMiscFP16(1, 1, 0b01111, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void frinti(T rd, T rn) { + ASIMDTwoRegMiscFP16(1, 1, 0b11001, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcvtpu(T rd, T rn) { + ASIMDTwoRegMiscFP16(1, 1, 0b11010, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fcvtzu(T rd, T rn) { + ASIMDTwoRegMiscFP16(1, 1, 0b11011, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void frsqrte(T rd, T rn) { + ASIMDTwoRegMiscFP16(1, 1, 0b11101, rn, rd); + } + template + requires(size == FEXCore::ARMEmitter::SubRegSize::i16Bit && + (std::is_same_v || std::is_same_v)) + void fsqrt(T rd, T rn) { + ASIMDTwoRegMiscFP16(1, 1, 0b11111, rn, rd); + } + // Advanced SIMD three-register extension - // TODO + template + requires(std::is_same_v || std::is_same_v) + void sdot(FEXCore::ARMEmitter::SubRegSize size, T rd, T rn, T rm) { + if constexpr (std::is_same_v) { + LOGMAN_THROW_AA_FMT(size != FEXCore::ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported"); + } + ASIMDThreeRegisterExt(0, 0b0010, size, rm, rn, rd); + } + + template + requires(std::is_same_v || std::is_same_v) + void usdot(T rd, T rn, T rm) { + ASIMDThreeRegisterExt(0, 0b0011, FEXCore::ARMEmitter::SubRegSize::i32Bit, rm, rn, rd); + } + + template + requires(std::is_same_v || std::is_same_v) + void sqrdmlah(FEXCore::ARMEmitter::SubRegSize size, T rd, T rn, T rm) { + if constexpr (std::is_same_v) { + LOGMAN_THROW_AA_FMT(size != FEXCore::ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported"); + } + ASIMDThreeRegisterExt(1, 0b0000, size, rm, rn, rd); + } + template + requires(std::is_same_v || std::is_same_v) + void sqrdmlsh(FEXCore::ARMEmitter::SubRegSize size, T rd, T rn, T rm) { + if constexpr (std::is_same_v) { + LOGMAN_THROW_AA_FMT(size != FEXCore::ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported"); + } + ASIMDThreeRegisterExt(1, 0b0001, size, rm, rn, rd); + } + template + requires(std::is_same_v || std::is_same_v) + void udot(FEXCore::ARMEmitter::SubRegSize size, T rd, T rn, T rm) { + if constexpr (std::is_same_v) { + LOGMAN_THROW_AA_FMT(size != FEXCore::ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported"); + } + ASIMDThreeRegisterExt(1, 0b0010, size, rm, rn, rd); + } + + template + requires(std::is_same_v || std::is_same_v) + void fcmla(FEXCore::ARMEmitter::SubRegSize size, T rd, T rn, T rm, FEXCore::ARMEmitter::Rotation Rot) { + LOGMAN_THROW_AA_FMT(size != FEXCore::ARMEmitter::SubRegSize::i8Bit, "8-bit subregsize not supported"); + + if constexpr (std::is_same_v) { + LOGMAN_THROW_AA_FMT(size != FEXCore::ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported"); + } + ASIMDThreeRegisterExt(1, 0b1000 | FEXCore::ToUnderlying(Rot), size, rm, rn, rd); + } + template + requires(std::is_same_v || std::is_same_v) + void fcadd(FEXCore::ARMEmitter::SubRegSize size, T rd, T rn, T rm, FEXCore::ARMEmitter::Rotation Rot) { + LOGMAN_THROW_AA_FMT(size != FEXCore::ARMEmitter::SubRegSize::i8Bit, "8-bit subregsize not supported"); + + if constexpr (std::is_same_v) { + LOGMAN_THROW_AA_FMT(size != FEXCore::ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported"); + } + LOGMAN_THROW_A_FMT(Rot == FEXCore::ARMEmitter::Rotation::ROTATE_90 || Rot == FEXCore::ARMEmitter::Rotation::ROTATE_270, "Invalid rotation"); + const uint32_t ConvertedRotation = + Rot == FEXCore::ARMEmitter::Rotation::ROTATE_90 ? 0b00 : 0b10; + ASIMDThreeRegisterExt(1, 0b1100 | ConvertedRotation, size, rm, rn, rd); + } + template + requires(std::is_same_v || std::is_same_v) + void bfdot(T rd, T rn, T rm) { + ASIMDThreeRegisterExt(1, 0b1111, FEXCore::ARMEmitter::SubRegSize::i16Bit, rm, rn, rd); + } + void bfmlalb(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) { + ASIMDThreeRegisterExt(1, 0b1111, FEXCore::ARMEmitter::SubRegSize::i64Bit, rm.D(), rn.D(), rd.D()); + } + void bfmlalt(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) { + ASIMDThreeRegisterExt(1, 0b1111, FEXCore::ARMEmitter::SubRegSize::i64Bit, rm.Q(), rn.Q(), rd.Q()); + } + void smmla(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) { + ASIMDThreeRegisterExt(0, 0b0100, FEXCore::ARMEmitter::SubRegSize::i32Bit, rm.Q(), rn.Q(), rd.Q()); + } + void usmmla(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) { + ASIMDThreeRegisterExt(0, 0b0101, FEXCore::ARMEmitter::SubRegSize::i32Bit, rm.Q(), rn.Q(), rd.Q()); + } + void bfmmla(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) { + ASIMDThreeRegisterExt(1, 0b1101, FEXCore::ARMEmitter::SubRegSize::i16Bit, rm.Q(), rn.Q(), rd.Q()); + } + void ummla(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) { + ASIMDThreeRegisterExt(1, 0b0100, FEXCore::ARMEmitter::SubRegSize::i32Bit, rm.Q(), rn.Q(), rd.Q()); + } + // Advanced SIMD two-register miscellaneous template requires(std::is_same_v || std::is_same_v) diff --git a/External/FEXCore/Source/Interface/Core/ArchHelpers/CodeEmitter/Emitter.h b/External/FEXCore/Source/Interface/Core/ArchHelpers/CodeEmitter/Emitter.h index 2b0283021..80bba4970 100644 --- a/External/FEXCore/Source/Interface/Core/ArchHelpers/CodeEmitter/Emitter.h +++ b/External/FEXCore/Source/Interface/Core/ArchHelpers/CodeEmitter/Emitter.h @@ -535,6 +535,14 @@ namespace FEXCore::ARMEmitter { ForwardLabel Forward; }; + // Some FCMA ASIMD instructions support a rotation argument. + enum class Rotation : uint32_t { + ROTATE_0 = 0b00, + ROTATE_90 = 0b01, + ROTATE_180 = 0b10, + ROTATE_270 = 0b11, + }; + // This is an emitter that is designed around the smallest code bloat as possible. // Eschewing most developer convenience in order to keep code as small as possible. diff --git a/External/FEXCore/unittests/Emitter/ASIMD_Tests.cpp b/External/FEXCore/unittests/Emitter/ASIMD_Tests.cpp index c7d56652c..8639f633e 100644 --- a/External/FEXCore/unittests/Emitter/ASIMD_Tests.cpp +++ b/External/FEXCore/unittests/Emitter/ASIMD_Tests.cpp @@ -249,13 +249,169 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD copy") { TEST_SINGLE(ins(SubRegSize::i64Bit, VReg::v30, 1, VReg::v29, 0), "mov v30.d[1], v29.d[0]"); } TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD three same (FP16)") { - // TODO: Implement in emitter. + TEST_SINGLE(fmaxnm(QReg::q30, QReg::q29, QReg::q28), "fmaxnm v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fmla(QReg::q30, QReg::q29, QReg::q28), "fmla v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fadd(QReg::q30, QReg::q29, QReg::q28), "fadd v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fmulx(QReg::q30, QReg::q29, QReg::q28), "fmulx v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fcmeq(QReg::q30, QReg::q29, QReg::q28), "fcmeq v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fmax(QReg::q30, QReg::q29, QReg::q28), "fmax v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(frecps(QReg::q30, QReg::q29, QReg::q28), "frecps v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fminnm(QReg::q30, QReg::q29, QReg::q28), "fminnm v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fmls(QReg::q30, QReg::q29, QReg::q28), "fmls v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fsub(QReg::q30, QReg::q29, QReg::q28), "fsub v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fmin(QReg::q30, QReg::q29, QReg::q28), "fmin v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(frsqrts(QReg::q30, QReg::q29, QReg::q28), "frsqrts v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fmaxnmp(QReg::q30, QReg::q29, QReg::q28), "fmaxnmp v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(faddp(QReg::q30, QReg::q29, QReg::q28), "faddp v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fmul(QReg::q30, QReg::q29, QReg::q28), "fmul v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fcmge(QReg::q30, QReg::q29, QReg::q28), "fcmge v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(facge(QReg::q30, QReg::q29, QReg::q28), "facge v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fmaxp(QReg::q30, QReg::q29, QReg::q28), "fmaxp v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fdiv(QReg::q30, QReg::q29, QReg::q28), "fdiv v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fminnmp(QReg::q30, QReg::q29, QReg::q28), "fminnmp v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fabd(QReg::q30, QReg::q29, QReg::q28), "fabd v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fcmgt(QReg::q30, QReg::q29, QReg::q28), "fcmgt v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(facgt(QReg::q30, QReg::q29, QReg::q28), "facgt v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(fminp(QReg::q30, QReg::q29, QReg::q28), "fminp v30.8h, v29.8h, v28.8h"); } TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD two-register miscellaneous (FP16)") { - // TODO: Implement in emitter. + TEST_SINGLE(frintn(QReg::q30, QReg::q29), "frintn v30.8h, v29.8h"); + TEST_SINGLE(frintm(QReg::q30, QReg::q29), "frintm v30.8h, v29.8h"); + TEST_SINGLE(fcvtns(QReg::q30, QReg::q29), "fcvtns v30.8h, v29.8h"); + TEST_SINGLE(fcvtms(QReg::q30, QReg::q29), "fcvtms v30.8h, v29.8h"); + TEST_SINGLE(fcvtas(QReg::q30, QReg::q29), "fcvtas v30.8h, v29.8h"); + TEST_SINGLE(scvtf(QReg::q30, QReg::q29), "scvtf v30.8h, v29.8h"); + TEST_SINGLE(fcmgt(QReg::q30, QReg::q29), "fcmgt v30.8h, v29.8h, #0.0"); + TEST_SINGLE(fcmeq(QReg::q30, QReg::q29), "fcmeq v30.8h, v29.8h, #0.0"); + TEST_SINGLE(fcmlt(QReg::q30, QReg::q29), "fcmlt v30.8h, v29.8h, #0.0"); + TEST_SINGLE(fabs(QReg::q30, QReg::q29), "fabs v30.8h, v29.8h"); + TEST_SINGLE(frintp(QReg::q30, QReg::q29), "frintp v30.8h, v29.8h"); + TEST_SINGLE(frintz(QReg::q30, QReg::q29), "frintz v30.8h, v29.8h"); + TEST_SINGLE(fcvtps(QReg::q30, QReg::q29), "fcvtps v30.8h, v29.8h"); + TEST_SINGLE(fcvtzs(QReg::q30, QReg::q29), "fcvtzs v30.8h, v29.8h"); + TEST_SINGLE(frecpe(QReg::q30, QReg::q29), "frecpe v30.8h, v29.8h"); + TEST_SINGLE(frinta(QReg::q30, QReg::q29), "frinta v30.8h, v29.8h"); + TEST_SINGLE(frintx(QReg::q30, QReg::q29), "frintx v30.8h, v29.8h"); + TEST_SINGLE(fcvtnu(QReg::q30, QReg::q29), "fcvtnu v30.8h, v29.8h"); + TEST_SINGLE(fcvtmu(QReg::q30, QReg::q29), "fcvtmu v30.8h, v29.8h"); + TEST_SINGLE(fcvtau(QReg::q30, QReg::q29), "fcvtau v30.8h, v29.8h"); + TEST_SINGLE(ucvtf(QReg::q30, QReg::q29), "ucvtf v30.8h, v29.8h"); + TEST_SINGLE(fcmge(QReg::q30, QReg::q29), "fcmge v30.8h, v29.8h, #0.0"); + TEST_SINGLE(fcmle(QReg::q30, QReg::q29), "fcmle v30.8h, v29.8h, #0.0"); + TEST_SINGLE(fneg(QReg::q30, QReg::q29), "fneg v30.8h, v29.8h"); + TEST_SINGLE(frinti(QReg::q30, QReg::q29), "frinti v30.8h, v29.8h"); + TEST_SINGLE(fcvtpu(QReg::q30, QReg::q29), "fcvtpu v30.8h, v29.8h"); + TEST_SINGLE(fcvtzu(QReg::q30, QReg::q29), "fcvtzu v30.8h, v29.8h"); + TEST_SINGLE(frsqrte(QReg::q30, QReg::q29), "frsqrte v30.8h, v29.8h"); + TEST_SINGLE(fsqrt(QReg::q30, QReg::q29), "fsqrt v30.8h, v29.8h"); } TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD three-register extension") { - // TODO: Implement in emitter. + TEST_SINGLE(sdot(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28), "sdot v30.16b, v29.16b, v28.16b"); + TEST_SINGLE(sdot(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28), "sdot v30.8h, v29.16b, v28.16b"); + TEST_SINGLE(sdot(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28), "sdot v30.4s, v29.16b, v28.16b"); + TEST_SINGLE(sdot(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28), "sdot v30.2d, v29.16b, v28.16b"); + TEST_SINGLE(sdot(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28), "sdot v30.8b, v29.8b, v28.8b"); + TEST_SINGLE(sdot(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28), "sdot v30.4h, v29.8b, v28.8b"); + TEST_SINGLE(sdot(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28), "sdot v30.2s, v29.8b, v28.8b"); + //TEST_SINGLE(sdot(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28), "sdot v30.1d, v29.8b, v28.8b"); + + TEST_SINGLE(usdot(QReg::q30, QReg::q29, QReg::q28), "usdot v30.4s, v29.16b, v28.16b"); + TEST_SINGLE(usdot(DReg::d30, DReg::d29, DReg::d28), "usdot v30.2s, v29.8b, v28.8b"); + + TEST_SINGLE(sqrdmlah(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlah v30.16b, v29.16b, v28.16b"); + TEST_SINGLE(sqrdmlah(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlah v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlah v30.4s, v29.4s, v28.4s"); + TEST_SINGLE(sqrdmlah(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlah v30.2d, v29.2d, v28.2d"); + TEST_SINGLE(sqrdmlah(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlah v30.8b, v29.8b, v28.8b"); + TEST_SINGLE(sqrdmlah(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlah v30.4h, v29.4h, v28.4h"); + TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlah v30.2s, v29.2s, v28.2s"); + //TEST_SINGLE(sqrdmlah(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlah v30.1d, v29.1d, v28.1d"); + + TEST_SINGLE(sqrdmlsh(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlsh v30.16b, v29.16b, v28.16b"); + TEST_SINGLE(sqrdmlsh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlsh v30.8h, v29.8h, v28.8h"); + TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlsh v30.4s, v29.4s, v28.4s"); + TEST_SINGLE(sqrdmlsh(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlsh v30.2d, v29.2d, v28.2d"); + TEST_SINGLE(sqrdmlsh(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlsh v30.8b, v29.8b, v28.8b"); + TEST_SINGLE(sqrdmlsh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlsh v30.4h, v29.4h, v28.4h"); + TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlsh v30.2s, v29.2s, v28.2s"); + //TEST_SINGLE(sqrdmlsh(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlsh v30.1d, v29.1d, v28.1d"); + + TEST_SINGLE(udot(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28), "udot v30.16b, v29.16b, v28.16b"); + TEST_SINGLE(udot(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28), "udot v30.8h, v29.16b, v28.16b"); + TEST_SINGLE(udot(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28), "udot v30.4s, v29.16b, v28.16b"); + TEST_SINGLE(udot(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28), "udot v30.2d, v29.16b, v28.16b"); + TEST_SINGLE(udot(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28), "udot v30.8b, v29.8b, v28.8b"); + TEST_SINGLE(udot(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28), "udot v30.4h, v29.8b, v28.8b"); + TEST_SINGLE(udot(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28), "udot v30.2s, v29.8b, v28.8b"); + //TEST_SINGLE(udot(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28), "udot v30.1d, v29.8b, v28.8b"); + + //TEST_SINGLE(fcmla(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_0), "fcmla v30.16b, v29.16b, v28.16b, #0"); + TEST_SINGLE(fcmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_0), "fcmla v30.8h, v29.8h, v28.8h, #0"); + TEST_SINGLE(fcmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_0), "fcmla v30.4s, v29.4s, v28.4s, #0"); + TEST_SINGLE(fcmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_0), "fcmla v30.2d, v29.2d, v28.2d, #0"); + //TEST_SINGLE(fcmla(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_0), "fcmla v30.8b, v29.8b, v28.8b, #0"); + TEST_SINGLE(fcmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_0), "fcmla v30.4h, v29.4h, v28.4h, #0"); + TEST_SINGLE(fcmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_0), "fcmla v30.2s, v29.2s, v28.2s, #0"); + //TEST_SINGLE(fcmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_0), "fcmla v30.1d, v29.1d, v28.1d, #0"); + + //TEST_SINGLE(fcmla(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcmla v30.16b, v29.16b, v28.16b, #90"); + TEST_SINGLE(fcmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcmla v30.8h, v29.8h, v28.8h, #90"); + TEST_SINGLE(fcmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcmla v30.4s, v29.4s, v28.4s, #90"); + TEST_SINGLE(fcmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcmla v30.2d, v29.2d, v28.2d, #90"); + //TEST_SINGLE(fcmla(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcmla v30.8b, v29.8b, v28.8b, #90"); + TEST_SINGLE(fcmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcmla v30.4h, v29.4h, v28.4h, #90"); + TEST_SINGLE(fcmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcmla v30.2s, v29.2s, v28.2s, #90"); + //TEST_SINGLE(fcmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcmla v30.1d, v29.1d, v28.1d, #90"); + + // Vixl disassembler has a bug that claims 8-bit fcmla exists + //TEST_SINGLE(fcmla(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_180), "fcmla v30.16b, v29.16b, v28.16b, #180"); + TEST_SINGLE(fcmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_180), "fcmla v30.8h, v29.8h, v28.8h, #180"); + TEST_SINGLE(fcmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_180), "fcmla v30.4s, v29.4s, v28.4s, #180"); + TEST_SINGLE(fcmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_180), "fcmla v30.2d, v29.2d, v28.2d, #180"); + //TEST_SINGLE(fcmla(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_180), "fcmla v30.8b, v29.8b, v28.8b, #180"); + TEST_SINGLE(fcmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_180), "fcmla v30.4h, v29.4h, v28.4h, #180"); + TEST_SINGLE(fcmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_180), "fcmla v30.2s, v29.2s, v28.2s, #180"); + //TEST_SINGLE(fcmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_180), "fcmla v30.1d, v29.1d, v28.1d, #180"); + + //TEST_SINGLE(fcmla(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcmla v30.16b, v29.16b, v28.16b, #270"); + TEST_SINGLE(fcmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcmla v30.8h, v29.8h, v28.8h, #270"); + TEST_SINGLE(fcmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcmla v30.4s, v29.4s, v28.4s, #270"); + TEST_SINGLE(fcmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcmla v30.2d, v29.2d, v28.2d, #270"); + //TEST_SINGLE(fcmla(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcmla v30.8b, v29.8b, v28.8b, #270"); + TEST_SINGLE(fcmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcmla v30.4h, v29.4h, v28.4h, #270"); + TEST_SINGLE(fcmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcmla v30.2s, v29.2s, v28.2s, #270"); + //TEST_SINGLE(fcmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcmla v30.1d, v29.1d, v28.1d, #270"); + + // Vixl disassembler has a bug that claims 8-bit fcadd exists + //TEST_SINGLE(fcadd(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcadd v30.16b, v29.16b, v28.16b, #90"); + TEST_SINGLE(fcadd(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcadd v30.8h, v29.8h, v28.8h, #90"); + TEST_SINGLE(fcadd(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcadd v30.4s, v29.4s, v28.4s, #90"); + TEST_SINGLE(fcadd(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcadd v30.2d, v29.2d, v28.2d, #90"); + //TEST_SINGLE(fcadd(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcadd v30.8b, v29.8b, v28.8b, #90"); + TEST_SINGLE(fcadd(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcadd v30.4h, v29.4h, v28.4h, #90"); + TEST_SINGLE(fcadd(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcadd v30.2s, v29.2s, v28.2s, #90"); + //TEST_SINGLE(fcadd(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcadd v30.1d, v29.1d, v28.1d, #90"); + + //TEST_SINGLE(fcadd(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcadd v30.16b, v29.16b, v28.16b, #270"); + TEST_SINGLE(fcadd(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcadd v30.8h, v29.8h, v28.8h, #270"); + TEST_SINGLE(fcadd(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcadd v30.4s, v29.4s, v28.4s, #270"); + TEST_SINGLE(fcadd(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcadd v30.2d, v29.2d, v28.2d, #270"); + //TEST_SINGLE(fcadd(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcadd v30.8b, v29.8b, v28.8b, #270"); + TEST_SINGLE(fcadd(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcadd v30.4h, v29.4h, v28.4h, #270"); + TEST_SINGLE(fcadd(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcadd v30.2s, v29.2s, v28.2s, #270"); + //TEST_SINGLE(fcadd(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcadd v30.1d, v29.1d, v28.1d, #270"); + + // TODO: Enable once vixl disassembler supports these instructions + // TEST_SINGLE(bfdot(QReg::q30, QReg::q29, QReg::q28), "bfdot v30.4s, v29.8h, v28.8h"); + // TEST_SINGLE(bfdot(DReg::d30, DReg::d29, DReg::d28), "bfdot v30.2s, v29.4h, v28.4h"); + // TEST_SINGLE(bfmlalb(VReg::v30, VReg::v29, VReg::v28), "bfmlalb v30.4s, v29.8h, v28.8h"); + // TEST_SINGLE(bfmlalt(VReg::v30, VReg::v29, VReg::v28), "bfmlalt v30.4s, v29.8h, v28.8h"); + + TEST_SINGLE(smmla(VReg::v30, VReg::v29, VReg::v28), "smmla v30.4s, v29.16b, v28.16b"); + TEST_SINGLE(usmmla(VReg::v30, VReg::v29, VReg::v28), "usmmla v30.4s, v29.16b, v28.16b"); + // TODO: Enable once vixl disassembler supports these instructions + //TEST_SINGLE(bfmmla(VReg::v30, VReg::v29, VReg::v28), "bfmmla v30.4s, v29.8h, v28.8h"); + TEST_SINGLE(ummla(VReg::v30, VReg::v29, VReg::v28), "ummla v30.4s, v29.16b, v28.16b"); } TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD two-register miscellaneous") { // Commented out lines showcase unallocated encodings.