Files
FEX-Emu--FEX/CodeEmitter/CodeEmitter/ASIMDOps.inl
T
Ryan Houdek d3ab9bdef6 Remove Float16
We aren't using it. We won't be using it. We need unit tests in our
lives if we want this.
2024-05-16 12:06:54 -07:00

5250 lines
240 KiB
C++

// SPDX-License-Identifier: MIT
/* ASIMD instruction emitters.
*
* This contains emitters for vector operations explicitly.
* Most instructions have a `SubRegSize` as their first argument to select element size while operating.
* Additionally most emitters accept templated vector register arguments of both `QRegister` and `DRegister` types.
* Based on the combination of those two arguments, it will emit an instruction operating on a 64-bit or 128-bit wide register
* with the selected element size.
*
* Some vector operations are unsized and only operate at the one width. In these cases the instruction only
* operates at one size, the width depends on the instruction.
* The arguments for these instructions are usually `VRegister` but might be one of the other sized types as well.
*
* Only two instructions support the `i128Bit` ElementSize.
*/
public:
// Data Processing -- Scalar Floating-Point and Advanced SIMD
// Cryptographic AES
void aese(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
constexpr uint32_t Op = 0b0100'1110'0010'1000'0000'10 << 10;
CryptoAES(Op, 0b00100, rd, rn);
}
void aesd(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
constexpr uint32_t Op = 0b0100'1110'0010'1000'0000'10 << 10;
CryptoAES(Op, 0b00101, rd, rn);
}
void aesmc(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
constexpr uint32_t Op = 0b0100'1110'0010'1000'0000'10 << 10;
CryptoAES(Op, 0b00110, rd, rn);
}
void aesimc(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
constexpr uint32_t Op = 0b0100'1110'0010'1000'0000'10 << 10;
CryptoAES(Op, 0b00111, rd, rn);
}
// Cryptographic three-register SHA
void sha1c(ARMEmitter::VRegister rd, ARMEmitter::SRegister rn, ARMEmitter::VRegister rm) {
constexpr uint32_t Op = 0b0101'1110'0000'0000'0000'00 << 10;
Crypto3RegSHA(Op, 0b000, rd, rn.V(), rm);
}
void sha1p(ARMEmitter::VRegister rd, ARMEmitter::SRegister rn, ARMEmitter::VRegister rm) {
constexpr uint32_t Op = 0b0101'1110'0000'0000'0000'00 << 10;
Crypto3RegSHA(Op, 0b001, rd, rn.V(), rm);
}
void sha1m(ARMEmitter::VRegister rd, ARMEmitter::SRegister rn, ARMEmitter::VRegister rm) {
constexpr uint32_t Op = 0b0101'1110'0000'0000'0000'00 << 10;
Crypto3RegSHA(Op, 0b010, rd, rn.V(), rm);
}
void sha1su0(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) {
constexpr uint32_t Op = 0b0101'1110'0000'0000'0000'00 << 10;
Crypto3RegSHA(Op, 0b011, rd, rn, rm);
}
void sha256h(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) {
constexpr uint32_t Op = 0b0101'1110'0000'0000'0000'00 << 10;
Crypto3RegSHA(Op, 0b100, rd, rn, rm);
}
void sha256h2(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) {
constexpr uint32_t Op = 0b0101'1110'0000'0000'0000'00 << 10;
Crypto3RegSHA(Op, 0b100, rd, rn, rm);
}
void sha256su1(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) {
constexpr uint32_t Op = 0b0101'1110'0000'0000'0000'00 << 10;
Crypto3RegSHA(Op, 0b110, rd, rn, rm);
}
// Cryptographic two-register SHA
void sha1h(ARMEmitter::SRegister rd, ARMEmitter::SRegister rn) {
constexpr uint32_t Op = 0b0101'1110'0010'1000'0000'10 << 10;
Crypto2RegSHA(Op, 0b00000, rd.V(), rn.V());
}
void sha1su1(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
constexpr uint32_t Op = 0b0101'1110'0010'1000'0000'10 << 10;
Crypto2RegSHA(Op, 0b00001, rd, rn);
}
void sha256su0(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
constexpr uint32_t Op = 0b0101'1110'0010'1000'0000'10 << 10;
Crypto2RegSHA(Op, 0b00010, rd, rn);
}
// Advanced SIMD table lookup
void tbl(ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm) {
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 1, 0b00, 0b00, 0b0, rd.V(), rn.V(), rm.V());
}
void tbl(ARMEmitter::DRegister rd, ARMEmitter::QRegister rn, ARMEmitter::DRegister rm) {
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 0, 0b00, 0b00, 0b0, rd.V(), rn.V(), rm.V());
}
void tbx(ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm) {
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 1, 0b00, 0b00, 0b1, rd.V(), rn.V(), rm.V());
}
void tbx(ARMEmitter::DRegister rd, ARMEmitter::QRegister rn, ARMEmitter::DRegister rm) {
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 0, 0b00, 0b00, 0b1, rd.V(), rn.V(), rm.V());
}
void tbl(QRegister rd, QRegister rn, QRegister rn2, QRegister rm) {
LOGMAN_THROW_A_FMT(AreVectorsSequential(rn, rn2), "rn and rn2 must be sequential");
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 1, 0b00, 0b01, 0b0, rd.V(), rn.V(), rm.V());
}
void tbl(DRegister rd, QRegister rn, QRegister rn2, DRegister rm) {
LOGMAN_THROW_A_FMT(AreVectorsSequential(rn, rn2), "rn and rn2 must be sequential");
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 0, 0b00, 0b01, 0b0, rd.V(), rn.V(), rm.V());
}
void tbx(QRegister rd, QRegister rn, QRegister rn2, QRegister rm) {
LOGMAN_THROW_A_FMT(AreVectorsSequential(rn, rn2), "rn and rn2 must be sequential");
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 1, 0b00, 0b01, 0b1, rd.V(), rn.V(), rm.V());
}
void tbx(DRegister rd, QRegister rn, QRegister rn2, DRegister rm) {
LOGMAN_THROW_A_FMT(AreVectorsSequential(rn, rn2), "rn and rn2 must be sequential");
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 0, 0b00, 0b01, 0b1, rd.V(), rn.V(), rm.V());
}
void tbl(QRegister rd, QRegister rn, QRegister rn2, QRegister rn3, QRegister rm) {
LOGMAN_THROW_A_FMT(AreVectorsSequential(rn, rn2, rn3), "rn, rn2, and rn3 must be sequential");
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 1, 0b00, 0b10, 0b0, rd.V(), rn.V(), rm.V());
}
void tbl(DRegister rd, QRegister rn, QRegister rn2, QRegister rn3, DRegister rm) {
LOGMAN_THROW_A_FMT(AreVectorsSequential(rn, rn2, rn3), "rn, rn2, and rn3 must be sequential");
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 0, 0b00, 0b10, 0b0, rd.V(), rn.V(), rm.V());
}
void tbx(QRegister rd, QRegister rn, QRegister rn2, QRegister rn3, QRegister rm) {
LOGMAN_THROW_A_FMT(AreVectorsSequential(rn, rn2, rn3), "rn, rn2, and rn3 must be sequential");
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 1, 0b00, 0b10, 0b1, rd.V(), rn.V(), rm.V());
}
void tbx(DRegister rd, QRegister rn, QRegister rn2, QRegister rn3, DRegister rm) {
LOGMAN_THROW_A_FMT(AreVectorsSequential(rn, rn2, rn3), "rn, rn2, and rn3 must be sequential");
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 0, 0b00, 0b10, 0b1, rd.V(), rn.V(), rm.V());
}
void tbl(QRegister rd, QRegister rn, QRegister rn2, QRegister rn3, QRegister rn4, QRegister rm) {
LOGMAN_THROW_A_FMT(AreVectorsSequential(rn, rn2, rn3, rn4), "rn, rn2, rn3, and rn4 must be sequential");
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 1, 0b00, 0b11, 0b0, rd.V(), rn.V(), rm.V());
}
void tbl(DRegister rd, QRegister rn, QRegister rn2, QRegister rn3, QRegister rn4, DRegister rm) {
LOGMAN_THROW_A_FMT(AreVectorsSequential(rn, rn2, rn3, rn4), "rn, rn2, rn3, and rn4 must be sequential");
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 0, 0b00, 0b11, 0b0, rd.V(), rn.V(), rm.V());
}
void tbx(QRegister rd, QRegister rn, QRegister rn2, QRegister rn3, QRegister rn4, QRegister rm) {
LOGMAN_THROW_A_FMT(AreVectorsSequential(rn, rn2, rn3, rn4), "rn, rn2, rn3, and rn4 must be sequential");
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 1, 0b00, 0b11, 0b1, rd.V(), rn.V(), rm.V());
}
void tbx(DRegister rd, QRegister rn, QRegister rn2, QRegister rn3, QRegister rn4, DRegister rm) {
LOGMAN_THROW_A_FMT(AreVectorsSequential(rn, rn2, rn3, rn4), "rn, rn2, rn3, and rn4 must be sequential");
constexpr uint32_t Op = 0b0000'1110'000 << 21;
ASIMDTable(Op, 0, 0b00, 0b11, 0b1, rd.V(), rn.V(), rm.V());
}
// Advanced SIMD permute
template<ARMEmitter::SubRegSize size>
void uzp1(ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 1, size, 0b001, rd.V(), rn.V(), rm.V());
}
template<ARMEmitter::SubRegSize size>
requires (size != ARMEmitter::SubRegSize::i64Bit)
void uzp1(ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, ARMEmitter::DRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 0, size, 0b001, rd.V(), rn.V(), rm.V());
}
template<ARMEmitter::SubRegSize size>
void trn1(ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 1, size, 0b010, rd.V(), rn.V(), rm.V());
}
template<ARMEmitter::SubRegSize size>
requires (size != ARMEmitter::SubRegSize::i64Bit)
void trn1(ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, ARMEmitter::DRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 0, size, 0b010, rd.V(), rn.V(), rm.V());
}
template<ARMEmitter::SubRegSize size>
void zip1(ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 1, size, 0b011, rd.V(), rn.V(), rm.V());
}
template<ARMEmitter::SubRegSize size>
requires (size != ARMEmitter::SubRegSize::i64Bit)
void zip1(ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, ARMEmitter::DRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 0, size, 0b011, rd.V(), rn.V(), rm.V());
}
template<ARMEmitter::SubRegSize size>
void uzp2(ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 1, size, 0b101, rd.V(), rn.V(), rm.V());
}
template<ARMEmitter::SubRegSize size>
requires (size != ARMEmitter::SubRegSize::i64Bit)
void uzp2(ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, ARMEmitter::DRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 0, size, 0b101, rd.V(), rn.V(), rm.V());
}
template<ARMEmitter::SubRegSize size>
void trn2(ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 1, size, 0b110, rd.V(), rn.V(), rm.V());
}
template<ARMEmitter::SubRegSize size>
requires (size != ARMEmitter::SubRegSize::i64Bit)
void trn2(ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, ARMEmitter::DRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 0, size, 0b110, rd.V(), rn.V(), rm.V());
}
template<ARMEmitter::SubRegSize size>
void zip2(ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 1, size, 0b111, rd.V(), rn.V(), rm.V());
}
template<ARMEmitter::SubRegSize size>
requires (size != ARMEmitter::SubRegSize::i64Bit)
void zip2(ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, ARMEmitter::DRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 0, size, 0b111, rd.V(), rn.V(), rm.V());
}
void uzp1(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 1, size, 0b001, rd.V(), rn.V(), rm.V());
}
void uzp1(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, ARMEmitter::DRegister rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid 64-bit size on 64-bit permute");
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 0, size, 0b001, rd.V(), rn.V(), rm.V());
}
void trn1(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 1, size, 0b010, rd.V(), rn.V(), rm.V());
}
void trn1(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, ARMEmitter::DRegister rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid 64-bit size on 64-bit permute");
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 0, size, 0b010, rd.V(), rn.V(), rm.V());
}
void zip1(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 1, size, 0b011, rd.V(), rn.V(), rm.V());
}
void zip1(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, ARMEmitter::DRegister rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid 64-bit size on 64-bit permute");
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 0, size, 0b011, rd.V(), rn.V(), rm.V());
}
void uzp2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 1, size, 0b101, rd.V(), rn.V(), rm.V());
}
void uzp2(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, ARMEmitter::DRegister rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid 64-bit size on 64-bit permute");
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 0, size, 0b101, rd.V(), rn.V(), rm.V());
}
void trn2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 1, size, 0b110, rd.V(), rn.V(), rm.V());
}
void trn2(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, ARMEmitter::DRegister rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid 64-bit size on 64-bit permute");
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 0, size, 0b110, rd.V(), rn.V(), rm.V());
}
void zip2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 1, size, 0b111, rd.V(), rn.V(), rm.V());
}
void zip2(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, ARMEmitter::DRegister rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid 64-bit size on 64-bit permute");
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'10 << 10;
ASIMDPermute(Op, 0, size, 0b111, rd.V(), rn.V(), rm.V());
}
// Advanced SIMD extract
void ext(ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, ARMEmitter::QRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(Index < 16, "Index can't be more than 15");
constexpr uint32_t Op = 0b0010'1110'000 << 21;
ASIMDExtract(Op, 1, 0b00, Index, rd.V(), rn.V(), rm.V());
}
void ext(ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, ARMEmitter::DRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(Index < 8, "Index can't be more than 7");
constexpr uint32_t Op = 0b0010'1110'000 << 21;
ASIMDExtract(Op, 0, 0b00, Index, rd.V(), rn.V(), rm.V());
}
// Advanced SIMD copy
template <typename T>
requires(std::is_same_v<QRegister, T> || std::is_same_v<DRegister, T>)
void dup(SubRegSize size, T rd, T rn, uint32_t Index) {
if constexpr(std::is_same_v<DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i64Bit, "Invalid element size with 64-bit dup");
}
constexpr uint32_t Q = std::is_same_v<QRegister, T> ? 1 : 0;
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'01 << 10;
const uint32_t SizeImm = FEXCore::ToUnderlying(size);
const uint32_t IndexShift = SizeImm + 1;
const uint32_t ElementSize = 1U << SizeImm;
const uint32_t MaxIndex = 128U / (ElementSize * 8);
LOGMAN_THROW_AA_FMT(Index < MaxIndex, "Index too large. Index={}, Max Index: {}", Index, MaxIndex);
const uint32_t imm5 = (Index << IndexShift) | ElementSize;
ASIMDScalarCopy(Op, Q, imm5, 0b0000, rd.V(), rn.V());
}
// Advanced SIMD three same (FP16)
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
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<ARMEmitter::QRegister, T> ? 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<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void ASIMDTwoRegMiscFP16(uint32_t U, uint32_t a, uint32_t opcode, T rn, T rd) {
constexpr uint32_t Q = std::is_same_v<ARMEmitter::QRegister, T> ? 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<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void ASIMDThreeRegisterExt(uint32_t U, uint32_t opcode, ARMEmitter::SubRegSize size, T rm, T rn, T rd) {
constexpr uint32_t Q = std::is_same_v<ARMEmitter::QRegister, T> ? 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 <typename T>
requires(std::is_same_v<QRegister, T> || std::is_same_v<DRegister, T>)
void dup(SubRegSize size, T rd, Register rn) {
if constexpr(std::is_same_v<DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i64Bit, "Invalid element size with 64-bit dup");
}
constexpr uint32_t Q = std::is_same_v<QRegister, T> ? 1 : 0;
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'01 << 10;
// Upper bits of imm5 are ignored for GPR dup
const uint32_t imm5 = 1U << FEXCore::ToUnderlying(size);
ASIMDScalarCopy(Op, Q, imm5, 0b0001, rd, ToVReg(rn));
}
template <SubRegSize size>
requires(size == SubRegSize::i8Bit || size == SubRegSize::i16Bit || size == SubRegSize::i32Bit)
void smov(XRegister rd, VRegister rn, uint32_t Index) {
static_assert(size == SubRegSize::i8Bit ||
size == SubRegSize::i16Bit ||
size == SubRegSize::i32Bit, "Unsupported smov size");
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'01 << 10;
constexpr uint32_t SizeImm = FEXCore::ToUnderlying(size);
constexpr uint32_t IndexShift = SizeImm + 1;
constexpr uint32_t ElementSize = 1U << SizeImm;
constexpr uint32_t MaxIndex = 128U / (ElementSize * 8);
LOGMAN_THROW_AA_FMT(Index < MaxIndex, "Index too large. Index={}, Max Index: {}", Index, MaxIndex);
const uint32_t imm5 = (Index << IndexShift) | ElementSize;
ASIMDScalarCopy(Op, 1, imm5, 0b0101, ToVReg(rd), rn);
}
template <SubRegSize size>
requires(size == SubRegSize::i8Bit || size == SubRegSize::i16Bit)
void smov(WRegister rd, VRegister rn, uint32_t Index) {
static_assert(size == SubRegSize::i8Bit ||
size == SubRegSize::i16Bit, "Unsupported smov size");
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'01 << 10;
constexpr uint32_t SizeImm = FEXCore::ToUnderlying(size);
constexpr uint32_t IndexShift = SizeImm + 1;
constexpr uint32_t ElementSize = 1U << SizeImm;
constexpr uint32_t MaxIndex = 128U / (ElementSize * 8);
LOGMAN_THROW_AA_FMT(Index < MaxIndex, "Index too large. Index={}, Max Index: {}", Index, MaxIndex);
const uint32_t imm5 = (Index << IndexShift) | ElementSize;
ASIMDScalarCopy(Op, 0, imm5, 0b0101, ToVReg(rd), rn);
}
template <SubRegSize size>
void umov(Register rd, VRegister rn, uint32_t Index) {
static_assert(size == SubRegSize::i8Bit ||
size == SubRegSize::i16Bit ||
size == SubRegSize::i32Bit ||
size == SubRegSize::i64Bit, "Unsupported umov size");
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'01 << 10;
constexpr uint32_t Q = size == SubRegSize::i64Bit ? 1 : 0;
constexpr uint32_t SizeImm = FEXCore::ToUnderlying(size);
constexpr uint32_t IndexShift = SizeImm + 1;
constexpr uint32_t ElementSize = 1U << SizeImm;
constexpr uint32_t MaxIndex = 128U / (ElementSize * 8);
LOGMAN_THROW_AA_FMT(Index < MaxIndex, "Index too large. Index={}, Max Index: {}", Index, MaxIndex);
const uint32_t imm5 = (Index << IndexShift) | ElementSize;
ASIMDScalarCopy(Op, Q, imm5, 0b0111, ToVReg(rd), rn);
}
template <SubRegSize size>
void ins(VRegister rd, uint32_t Index, Register rn) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'01 << 10;
constexpr uint32_t SizeImm = FEXCore::ToUnderlying(size);
constexpr uint32_t IndexShift = SizeImm + 1;
constexpr uint32_t ElementSize = 1U << SizeImm;
constexpr uint32_t MaxIndex = 128U / (ElementSize * 8);
LOGMAN_THROW_AA_FMT(Index < MaxIndex, "Index too large. Index={}, Max Index: {}", Index, MaxIndex);
const uint32_t imm5 = (Index << IndexShift) | ElementSize;
ASIMDScalarCopy(Op, 1, imm5, 0b0011, rd, ToVReg(rn));
}
void ins(SubRegSize size, VRegister rd, uint32_t Index, Register rn) {
constexpr uint32_t Op = 0b0000'1110'0000'0000'0000'01 << 10;
const uint32_t SizeImm = FEXCore::ToUnderlying(size);
const uint32_t IndexShift = SizeImm + 1;
const uint32_t ElementSize = 1U << SizeImm;
const uint32_t MaxIndex = 128U / (ElementSize * 8);
LOGMAN_THROW_AA_FMT(Index < MaxIndex, "Index too large. Index={}, Max Index: {}", Index, MaxIndex);
const uint32_t imm5 = (Index << IndexShift) | ElementSize;
ASIMDScalarCopy(Op, 1, imm5, 0b0011, rd, ToVReg(rn));
}
void ins(SubRegSize size, VRegister rd, uint32_t Index, VRegister rn, uint32_t Index2) {
constexpr uint32_t Op = 0b0110'1110'0000'0000'0000'01 << 10;
const uint32_t SizeImm = FEXCore::ToUnderlying(size);
const uint32_t IndexShift = SizeImm + 1;
const uint32_t ElementSize = 1U << SizeImm;
const uint32_t MaxIndex = 128U / (ElementSize * 8);
LOGMAN_THROW_AA_FMT(Index < MaxIndex, "Index too large. Index={}, Max Index: {}", Index, MaxIndex);
LOGMAN_THROW_AA_FMT(Index2 < MaxIndex, "Index2 too large. Index2={}, Max Index: {}", Index2, MaxIndex);
const uint32_t imm5 = (Index << IndexShift) | ElementSize;
const uint32_t imm4 = Index2 << SizeImm;
ASIMDScalarCopy(Op, 1, imm5, imm4, rd, rn);
}
// Advanced SIMD three same (FP16)
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fmaxnm(T rd, T rn, T rm) {
ASIMDThreeSameFP16(0, 0, 0b000, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fmla(T rd, T rn, T rm) {
ASIMDThreeSameFP16(0, 0, 0b001, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fadd(T rd, T rn, T rm) {
ASIMDThreeSameFP16(0, 0, 0b010, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fmulx(T rd, T rn, T rm) {
ASIMDThreeSameFP16(0, 0, 0b011, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcmeq(T rd, T rn, T rm) {
ASIMDThreeSameFP16(0, 0, 0b100, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fmax(T rd, T rn, T rm) {
ASIMDThreeSameFP16(0, 0, 0b110, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void frecps(T rd, T rn, T rm) {
ASIMDThreeSameFP16(0, 0, 0b111, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fminnm(T rd, T rn, T rm) {
ASIMDThreeSameFP16(0, 1, 0b000, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fmls(T rd, T rn, T rm) {
ASIMDThreeSameFP16(0, 1, 0b001, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fsub(T rd, T rn, T rm) {
ASIMDThreeSameFP16(0, 1, 0b010, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fmin(T rd, T rn, T rm) {
ASIMDThreeSameFP16(0, 1, 0b110, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void frsqrts(T rd, T rn, T rm) {
ASIMDThreeSameFP16(0, 1, 0b111, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fmaxnmp(T rd, T rn, T rm) {
ASIMDThreeSameFP16(1, 0, 0b000, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void faddp(T rd, T rn, T rm) {
ASIMDThreeSameFP16(1, 0, 0b010, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fmul(T rd, T rn, T rm) {
ASIMDThreeSameFP16(1, 0, 0b011, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcmge(T rd, T rn, T rm) {
ASIMDThreeSameFP16(1, 0, 0b100, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void facge(T rd, T rn, T rm) {
ASIMDThreeSameFP16(1, 0, 0b101, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fmaxp(T rd, T rn, T rm) {
ASIMDThreeSameFP16(1, 0, 0b110, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fdiv(T rd, T rn, T rm) {
ASIMDThreeSameFP16(1, 0, 0b111, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fminnmp(T rd, T rn, T rm) {
ASIMDThreeSameFP16(1, 1, 0b000, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fabd(T rd, T rn, T rm) {
ASIMDThreeSameFP16(1, 1, 0b010, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcmgt(T rd, T rn, T rm) {
ASIMDThreeSameFP16(1, 1, 0b100, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void facgt(T rd, T rn, T rm) {
ASIMDThreeSameFP16(1, 1, 0b101, rm, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fminp(T rd, T rn, T rm) {
ASIMDThreeSameFP16(1, 1, 0b110, rm, rn, rd);
}
// Advanced SIMD two-register miscellaneous (FP16)
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void frintn(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 0, 0b11000, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void frintm(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 0, 0b11001, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcvtns(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 0, 0b11010, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcvtms(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 0, 0b11011, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcvtas(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 0, 0b11100, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void scvtf(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 0, 0b11101, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcmgt(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 1, 0b01100, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcmeq(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 1, 0b01101, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcmlt(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 1, 0b01110, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fabs(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 1, 0b01111, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void frintp(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 1, 0b11000, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void frintz(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 1, 0b11001, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcvtps(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 1, 0b11010, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcvtzs(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 1, 0b11011, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void frecpe(T rd, T rn) {
ASIMDTwoRegMiscFP16(0, 1, 0b11101, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void frinta(T rd, T rn) {
ASIMDTwoRegMiscFP16(1, 0, 0b11000, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void frintx(T rd, T rn) {
ASIMDTwoRegMiscFP16(1, 0, 0b11001, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcvtnu(T rd, T rn) {
ASIMDTwoRegMiscFP16(1, 0, 0b11010, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcvtmu(T rd, T rn) {
ASIMDTwoRegMiscFP16(1, 0, 0b11011, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcvtau(T rd, T rn) {
ASIMDTwoRegMiscFP16(1, 0, 0b11100, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void ucvtf(T rd, T rn) {
ASIMDTwoRegMiscFP16(1, 0, 0b11101, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcmge(T rd, T rn) {
ASIMDTwoRegMiscFP16(1, 1, 0b01100, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcmle(T rd, T rn) {
ASIMDTwoRegMiscFP16(1, 1, 0b01101, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fneg(T rd, T rn) {
ASIMDTwoRegMiscFP16(1, 1, 0b01111, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void frinti(T rd, T rn) {
ASIMDTwoRegMiscFP16(1, 1, 0b11001, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcvtpu(T rd, T rn) {
ASIMDTwoRegMiscFP16(1, 1, 0b11010, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fcvtzu(T rd, T rn) {
ASIMDTwoRegMiscFP16(1, 1, 0b11011, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void frsqrte(T rd, T rn) {
ASIMDTwoRegMiscFP16(1, 1, 0b11101, rn, rd);
}
template<ARMEmitter::SubRegSize size, typename T>
requires(size == ARMEmitter::SubRegSize::i16Bit &&
(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>))
void fsqrt(T rd, T rn) {
ASIMDTwoRegMiscFP16(1, 1, 0b11111, rn, rd);
}
// Advanced SIMD three-register extension
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void sdot(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
ASIMDThreeRegisterExt(0, 0b0010, size, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void usdot(T rd, T rn, T rm) {
ASIMDThreeRegisterExt(0, 0b0011, ARMEmitter::SubRegSize::i32Bit, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void sqrdmlah(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
ASIMDThreeRegisterExt(1, 0b0000, size, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void sqrdmlsh(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
ASIMDThreeRegisterExt(1, 0b0001, size, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void udot(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
ASIMDThreeRegisterExt(1, 0b0010, size, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcmla(ARMEmitter::SubRegSize size, T rd, T rn, T rm, ARMEmitter::Rotation Rot) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "8-bit subregsize not supported");
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
ASIMDThreeRegisterExt(1, 0b1000 | FEXCore::ToUnderlying(Rot), size, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcadd(ARMEmitter::SubRegSize size, T rd, T rn, T rm, ARMEmitter::Rotation Rot) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "8-bit subregsize not supported");
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_A_FMT(Rot == ARMEmitter::Rotation::ROTATE_90 || Rot == ARMEmitter::Rotation::ROTATE_270, "Invalid rotation");
const uint32_t ConvertedRotation =
Rot == ARMEmitter::Rotation::ROTATE_90 ? 0b00 : 0b10;
ASIMDThreeRegisterExt(1, 0b1100 | ConvertedRotation, size, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void bfdot(T rd, T rn, T rm) {
ASIMDThreeRegisterExt(1, 0b1111, ARMEmitter::SubRegSize::i16Bit, rm, rn, rd);
}
void bfmlalb(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) {
ASIMDThreeRegisterExt(1, 0b1111, ARMEmitter::SubRegSize::i64Bit, rm.D(), rn.D(), rd.D());
}
void bfmlalt(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) {
ASIMDThreeRegisterExt(1, 0b1111, ARMEmitter::SubRegSize::i64Bit, rm.Q(), rn.Q(), rd.Q());
}
void smmla(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) {
ASIMDThreeRegisterExt(0, 0b0100, ARMEmitter::SubRegSize::i32Bit, rm.Q(), rn.Q(), rd.Q());
}
void usmmla(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) {
ASIMDThreeRegisterExt(0, 0b0101, ARMEmitter::SubRegSize::i32Bit, rm.Q(), rn.Q(), rd.Q());
}
void bfmmla(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) {
ASIMDThreeRegisterExt(1, 0b1101, ARMEmitter::SubRegSize::i16Bit, rm.Q(), rn.Q(), rd.Q());
}
void ummla(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) {
ASIMDThreeRegisterExt(1, 0b0100, ARMEmitter::SubRegSize::i32Bit, rm.Q(), rn.Q(), rd.Q());
}
// Advanced SIMD two-register miscellaneous
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void rev64(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b00000, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void rev16(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i8Bit, "Only 8-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b00001, rd, rn);
}
///< size is the destination size.
///< source size is the next size up.
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void saddlp(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "Destination 8-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b00010, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void suqadd(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b00011, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void cls(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b00100, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void cnt(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i8Bit, "Only 8-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b00101, rd, rn);
}
///< size is the destination size.
///< source size is the next size up.
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void sadalp(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "Destination 8-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b00110, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void sqabs(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b00111, rd, rn);
}
// Comparison against zero
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void cmgt(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b01000, rd, rn);
}
// Comparison against zero
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void cmeq(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b01001, rd, rn);
}
// Comparison against zero
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void cmlt(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b01010, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void abs(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b01011, rd, rn);
}
///< size is the destination size.
///< source size is the next size up.
void xtn(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit destination subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc(Op, 0, size, 0b10010, rd.D(), rn.D());
}
///< size is the destination size.
///< source size is the next size up.
void xtn2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit destination subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc(Op, 0, size, 0b10010, rd.Q(), rn.Q());
}
///< size is the destination size.
///< source size is the next size up.
void sqxtn(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit destination subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc(Op, 0, size, 0b10100, rd.D(), rn.D());
}
///< size is the destination size.
///< source size is the next size up.
void sqxtn2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit destination subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc(Op, 0, size, 0b10100, rd.Q(), rn.Q());
}
///< size is the destination size.
///< source size is the next size up.
void fcvtn(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit ||
size == ARMEmitter::SubRegSize::i16Bit, "Only 16-bit & 32-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc(Op, 0, ConvertedSize, 0b10110, rd.D(), rn.D());
}
///< size is the destination size.
///< source size is the next size up.
void fcvtn2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit ||
size == ARMEmitter::SubRegSize::i16Bit, "Only 16-bit & 32-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc(Op, 0, ConvertedSize, 0b10110, rd.Q(), rn.Q());
}
///< size is the destination size.
///< source size is the next size up.
void fcvtl(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc(Op, 0, ConvertedSize, 0b10111, rd.D(), rn.D());
}
///< size is the destination size.
///< source size is the next size up.
void fcvtl2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc(Op, 0, ConvertedSize, 0b10111, rd.Q(), rn.Q());
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void frintn(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11000, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void frintm(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11001, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcvtns(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11010, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcvtms(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11011, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcvtas(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11100, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void scvtf(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11101, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void frint32z(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11110, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void frint64z(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11111, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcmgt(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b01100, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcmeq(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b01101, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcmlt(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b01110, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fabs(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b01111, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void frintp(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b11000, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void frintz(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b11001, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcvtps(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b11010, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcvtzs(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b11011, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void urecpe(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b11100, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void frecpe(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 0, size, 0b11101, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void rev32(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i8Bit ||
size == ARMEmitter::SubRegSize::i16Bit, "Only 8-bit & 16-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b00000, rd, rn);
}
///< size is the destination size.
///< source size is the next size up.
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void uaddlp(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "Destination 8-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b00010, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void usqadd(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b00011, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void clz(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b00100, rd, rn);
}
///< size is the destination size.
///< source size is the next size up.
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void uadalp(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "Destination 8-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b00110, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void sqneg(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b00111, rd, rn);
}
// Comparison against zero
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void cmge(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b01000, rd, rn);
}
// Comparison against zero
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void cmle(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b01001, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void neg(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b01011, rd, rn);
}
///< size is the destination size.
///< source size is the next size up.
void sqxtun(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit destination subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc(Op, 1, size, 0b10010, rd.D(), rn.D());
}
///< size is the destination size.
///< source size is the next size up.
void sqxtun2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit destination subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc(Op, 1, size, 0b10010, rd.Q(), rn.Q());
}
///< size is the destination size.
///< source size is the next size up.
void shll(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "Destination 8-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc(Op, 1, ConvertedSize, 0b10011, rd, rn);
}
///< size is the destination size.
///< source size is the next size up.
void shll2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "Destination 8-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc(Op, 1, ConvertedSize, 0b10011, rd, rn);
}
///< size is the destination size.
///< source size is the next size up.
void uqxtn(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc(Op, 1, size, 0b10100, rd.D(), rn.D());
}
///< size is the destination size.
///< source size is the next size up.
void uqxtn2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc(Op, 1, size, 0b10100, rd.Q(), rn.Q());
}
///< size is the destination size.
///< source size is the next size up.
void fcvtxn(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc(Op, 1, ConvertedSize, 0b10110, rd.D(), rn.D());
}
///< size is the destination size.
///< source size is the next size up.
void fcvtxn2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc(Op, 1, ConvertedSize, 0b10110, rd.Q(), rn.Q());
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void frinta(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11000, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void frintx(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11001, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcvtnu(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11010, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcvtmu(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11011, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcvtau(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11100, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void ucvtf(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11101, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void frint32x(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11110, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void frint64x(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11111, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void not_(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i8Bit, "Only 8-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, ARMEmitter::SubRegSize::i8Bit, 0b00101, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void mvn(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i8Bit, "Only 8-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, ARMEmitter::SubRegSize::i8Bit, 0b00101, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void rbit(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i8Bit, "Only 8-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, ARMEmitter::SubRegSize::i16Bit, 0b00101, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcmge(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b01100, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcmle(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b01101, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fneg(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b01111, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void frinti(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b11001, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcvtpu(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b11010, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fcvtzu(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b11011, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void ursqrte(ARMEmitter::SubRegSize size, T rd, T rn) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b11100, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void frsqrte(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b11101, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fsqrt(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
ASIMD2RegMisc<T>(Op, 1, size, 0b11111, rd, rn);
}
// Advanced SIMD across lanes
///< size is the destination size.
///< source size is the next size up.
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void saddlv(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "Destination 8-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMDAcrossLanes<T>(Op, 0, ConvertedSize, 0b00011, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void smaxv(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i32Bit && size != ARMEmitter::SubRegSize::i64Bit, "32/64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Destination 64-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
ASIMDAcrossLanes<T>(Op, 0, size, 0b01010, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void sminv(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i32Bit && size != ARMEmitter::SubRegSize::i64Bit, "32/64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Destination 64-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
ASIMDAcrossLanes<T>(Op, 0, size, 0b11010, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void addv(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i32Bit && size != ARMEmitter::SubRegSize::i64Bit, "32/64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Destination 64-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
ASIMDAcrossLanes<T>(Op, 0, size, 0b11011, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void uaddlv(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
}
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMDAcrossLanes<T>(Op, 1, ConvertedSize, 0b00011, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void umaxv(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i32Bit && size != ARMEmitter::SubRegSize::i64Bit, "32/64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Destination 64-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
ASIMDAcrossLanes<T>(Op, 1, size, 0b01010, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void uminv(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i32Bit && size != ARMEmitter::SubRegSize::i64Bit, "32/64-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Destination 64-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
ASIMDAcrossLanes<T>(Op, 1, size, 0b11010, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fmaxnmv(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i32Bit, "32-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit && size != ARMEmitter::SubRegSize::i64Bit, "Destination 8/64-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
const auto U = size == ARMEmitter::SubRegSize::i16Bit ? 0 : 1;
ASIMDAcrossLanes<T>(Op, U, ConvertedSize, 0b01100, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fmaxv(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i32Bit, "32-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit && size != ARMEmitter::SubRegSize::i64Bit, "Destination 8/64-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
const auto U = size == ARMEmitter::SubRegSize::i16Bit ? 0 : 1;
ASIMDAcrossLanes<T>(Op, U, ConvertedSize, 0b01111, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fminnmv(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i32Bit, "32-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit && size != ARMEmitter::SubRegSize::i64Bit, "Destination 8/64-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i64Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i32Bit : ARMEmitter::SubRegSize::i32Bit;
const auto U = size == ARMEmitter::SubRegSize::i16Bit ? 0 : 1;
ASIMDAcrossLanes<T>(Op, U, ConvertedSize, 0b01100, rd, rn);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fminv(ARMEmitter::SubRegSize size, T rd, T rn) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i32Bit, "32-bit subregsize not supported");
}
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit && size != ARMEmitter::SubRegSize::i64Bit, "Destination 8/64-bit subregsize unsupported");
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
const auto ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i64Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i32Bit : ARMEmitter::SubRegSize::i32Bit;
const auto U = size == ARMEmitter::SubRegSize::i16Bit ? 0 : 1;
ASIMDAcrossLanes<T>(Op, U, ConvertedSize, 0b01111, rd, rn);
}
// Advanced SIMD three different
// TODO: Double check narrowing op size limits.
// TODO: Don't enforce DRegister/QRegister for Q check
///< Size is dest size
void saddl(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b0000, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void saddl2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b0000, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void saddw(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b0001, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void saddw2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b0001, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void ssubl(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b0010, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void ssubl2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b0010, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void ssubw(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b0011, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void ssubw2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b0011, ConvertedSize, rd, rn, rm);
}
void addhn(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i64Bit, "No 64-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
ASIMD3Different(Op, 0, 0b0100, size, rd, rn, rm);
}
void addhn2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i64Bit, "No 64-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
ASIMD3Different(Op, 0, 0b0100, size, rd, rn, rm);
}
///< Size is dest size
void sabal(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b0101, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void sabal2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b0101, ConvertedSize, rd, rn, rm);
}
void subhn(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i64Bit, "No 64-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
ASIMD3Different(Op, 0, 0b0110, size, rd, rn, rm);
}
void subhn2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i64Bit, "No 64-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
ASIMD3Different(Op, 0, 0b0110, size, rd, rn, rm);
}
///< Size is dest size
void sabdl(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b0111, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void sabdl2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b0111, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void smlal(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b1000, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void smlal2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b1000, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void sqdmlal(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit && size != SubRegSize::i16Bit, "No 8/16-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b1001, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void sqdmlal2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit && size != SubRegSize::i16Bit, "No 8/16-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b1001, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void smlsl(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b1010, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void smlsl2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b1010, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void sqdmlsl(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit && size != SubRegSize::i16Bit, "No 8/16-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b1011, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void sqdmlsl2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit && size != SubRegSize::i16Bit, "No 8/16-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b1011, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void smull(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b1100, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void smull2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b1100, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void sqdmull(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit && size != SubRegSize::i16Bit, "No 8/16-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b1101, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void sqdmull2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit && size != SubRegSize::i16Bit, "No 8/16-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 0, 0b1101, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void pmull(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size == SubRegSize::i16Bit || size == SubRegSize::i128Bit, "Only 16-bit and 128-bit destination supported");
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
ASIMD3Different(Op, 0, 0b1110, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void pmull2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size == SubRegSize::i16Bit || size == SubRegSize::i128Bit, "Only 16-bit and 128-bit destination supported");
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
ASIMD3Different(Op, 0, 0b1110, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void uaddl(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b0000, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void uaddl2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b0000, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void uaddw(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b0001, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void uaddw2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b0001, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void usubl(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b0010, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void usubl2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b0010, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void usubw(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b0011, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void usubw2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b0011, ConvertedSize, rd, rn, rm);
}
// XXX: RADDHN/2
///< Size is dest size
void uabal(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b0101, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void uabal2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b0101, ConvertedSize, rd, rn, rm);
}
// XXX: RSUBHN/2
///< Size is dest size
void uabdl(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b0111, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void uabdl2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b0111, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void umlal(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b1000, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void umlal2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b1000, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void umlsl(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b1010, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void umlsl2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b1010, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void umull(SubRegSize size, DRegister rd, DRegister rn, DRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b1100, ConvertedSize, rd, rn, rm);
}
///< Size is dest size
void umull2(SubRegSize size, QRegister rd, QRegister rn, QRegister rm) {
LOGMAN_THROW_AA_FMT(size != SubRegSize::i8Bit, "No 8-bit dest support.");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'00 << 10;
const auto ConvertedSize = SubRegSize{FEXCore::ToUnderlying(size) - 1};
ASIMD3Different(Op, 1, 0b1100, ConvertedSize, rd, rn, rm);
}
// Advanced SIMD three same
template<typename T>
void shadd(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b00000, rd, rn, rm);
}
template<typename T>
void sqadd(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit sqadd");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b00001, rd, rn, rm);
}
template<typename T>
void srhadd(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b00010, rd, rn, rm);
}
template<typename T>
void shsub(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b00100, rd, rn, rm);
}
template<typename T>
void sqsub(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit sqsub");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b00101, rd, rn, rm);
}
template<typename T>
void cmgt(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit cmgt");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b00110, rd, rn, rm);
}
template<typename T>
void cmge(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit cmge");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b00111, rd, rn, rm);
}
template<typename T>
void sshl(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit sshl");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b01000, rd, rn, rm);
}
template<typename T>
void sqshl(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit sqshl");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b01001, rd, rn, rm);
}
template<typename T>
void srshl(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit srshl");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b01010, rd, rn, rm);
}
template<typename T>
void sqrshl(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit sqrshl");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b01011, rd, rn, rm);
}
template<typename T>
void smax(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b01100, rd, rn, rm);
}
template<typename T>
void smin(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b01101, rd, rn, rm);
}
template<typename T>
void sabd(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b01110, rd, rn, rm);
}
template<typename T>
void saba(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b01111, rd, rn, rm);
}
template<typename T>
void add(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit add");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b10000, rd, rn, rm);
}
template<typename T>
void cmtst(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit cmtst");
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b10001, rd, rn, rm);
}
template<typename T>
void mla(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b10010, rd, rn, rm);
}
template<typename T>
void mul(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b10011, rd, rn, rm);
}
template<typename T>
void smaxp(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b10100, rd, rn, rm);
}
template<typename T>
void sminp(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b10101, rd, rn, rm);
}
template<typename T>
void sqdmulh(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "No 8-bit dest support.");
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b10110, rd, rn, rm);
}
template<typename T>
void addp(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b10111, rd, rn, rm);
}
template<typename T>
void fmaxnm(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
const ARMEmitter::SubRegSize ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ?
ARMEmitter::SubRegSize::i16Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD3Same<T>(Op, 0, ConvertedSize, 0b11000, rd, rn, rm);
}
template<typename T>
void fmla(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
const ARMEmitter::SubRegSize ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ?
ARMEmitter::SubRegSize::i16Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD3Same<T>(Op, 0, ConvertedSize, 0b11001, rd, rn, rm);
}
template<typename T>
void fadd(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
const ARMEmitter::SubRegSize ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ?
ARMEmitter::SubRegSize::i16Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD3Same<T>(Op, 0, ConvertedSize, 0b11010, rd, rn, rm);
}
template<typename T>
void fmulx(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
const ARMEmitter::SubRegSize ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ?
ARMEmitter::SubRegSize::i16Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD3Same<T>(Op, 0, ConvertedSize, 0b11011, rd, rn, rm);
}
template<typename T>
void fcmeq(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
const ARMEmitter::SubRegSize ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ?
ARMEmitter::SubRegSize::i16Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD3Same<T>(Op, 0, ConvertedSize, 0b11100, rd, rn, rm);
}
template<typename T>
void fmax(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
const ARMEmitter::SubRegSize ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ?
ARMEmitter::SubRegSize::i16Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD3Same<T>(Op, 0, ConvertedSize, 0b11110, rd, rn, rm);
}
template<typename T>
void frecps(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
const ARMEmitter::SubRegSize ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ?
ARMEmitter::SubRegSize::i16Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD3Same<T>(Op, 0, ConvertedSize, 0b11111, rd, rn, rm);
}
template<typename T>
void and_(T rd, T rn, T rm) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, ARMEmitter::SubRegSize::i8Bit, 0b00011, rd, rn, rm);
}
template<typename T>
void fmlal(T rd, T rn, T rm) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, ARMEmitter::SubRegSize::i8Bit, 0b11101, rd, rn, rm);
}
template<typename T>
void fmlal2(T rd, T rn, T rm) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, ARMEmitter::SubRegSize::i8Bit, 0b11001, rd, rn, rm);
}
template<typename T>
void bic(T rd, T rn, T rm) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, ARMEmitter::SubRegSize::i16Bit, 0b00011, rd, rn, rm);
}
template<typename T>
void fminnm(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b11000, rd, rn, rm);
}
template<typename T>
void fmls(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b11001, rd, rn, rm);
}
template<typename T>
void fsub(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b11010, rd, rn, rm);
}
template<typename T>
void fmin(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b11110, rd, rn, rm);
}
template<typename T>
void frsqrts(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, size, 0b11111, rd, rn, rm);
}
template<typename T>
void orr(T rd, T rn, T rm) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, ARMEmitter::SubRegSize::i32Bit, 0b00011, rd, rn, rm);
}
template<typename T>
void mov(T rd, T rn) {
orr<T>(rd, rn, rn);
}
template<typename T>
void fmlsl(T rd, T rn, T rm) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, ARMEmitter::SubRegSize::i32Bit, 0b11101, rd, rn, rm);
}
template<typename T>
void fmlsl2(T rd, T rn, T rm) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, ARMEmitter::SubRegSize::i32Bit, 0b11001, rd, rn, rm);
}
template<typename T>
void orn(T rd, T rn, T rm) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 0, ARMEmitter::SubRegSize::i64Bit, 0b00011, rd, rn, rm);
}
template<typename T>
void uhadd(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b00000, rd, rn, rm);
}
template<typename T>
void uqadd(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b00001, rd, rn, rm);
}
template<typename T>
void urhadd(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b00010, rd, rn, rm);
}
template<typename T>
void uhsub(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b00100, rd, rn, rm);
}
template<typename T>
void uqsub(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b00101, rd, rn, rm);
}
template<typename T>
void cmhi(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b00110, rd, rn, rm);
}
template<typename T>
void cmhs(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b00111, rd, rn, rm);
}
template<typename T>
void ushl(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b01000, rd, rn, rm);
}
template<typename T>
void uqshl(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b01001, rd, rn, rm);
}
template<typename T>
void urshl(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b01010, rd, rn, rm);
}
template<typename T>
void uqrshl(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b01011, rd, rn, rm);
}
template<typename T>
void umax(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b01100, rd, rn, rm);
}
template<typename T>
void umin(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b01101, rd, rn, rm);
}
template<typename T>
void uabd(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b01110, rd, rn, rm);
}
template<typename T>
void uaba(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b01111, rd, rn, rm);
}
template<typename T>
void sub(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b10000, rd, rn, rm);
}
template<typename T>
void cmeq(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b10001, rd, rn, rm);
}
template<typename T>
void mls(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b10010, rd, rn, rm);
}
template<typename T>
void pmul(T rd, T rn, T rm) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, ARMEmitter::SubRegSize::i8Bit, 0b10011, rd, rn, rm);
}
template<typename T>
void umaxp(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b10100, rd, rn, rm);
}
template<typename T>
void uminp(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b10101, rd, rn, rm);
}
template<typename T>
void sqrdmulh(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit && size != ARMEmitter::SubRegSize::i8Bit, "8/64-bit subregsize not supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b10110, rd, rn, rm);
}
template<typename T>
void fmaxnmp(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
const ARMEmitter::SubRegSize ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ?
ARMEmitter::SubRegSize::i16Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD3Same<T>(Op, 1, ConvertedSize, 0b11000, rd, rn, rm);
}
template<typename T>
void faddp(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
const ARMEmitter::SubRegSize ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ?
ARMEmitter::SubRegSize::i16Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD3Same<T>(Op, 1, ConvertedSize, 0b11010, rd, rn, rm);
}
template<typename T>
void fmul(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
const ARMEmitter::SubRegSize ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ?
ARMEmitter::SubRegSize::i16Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD3Same<T>(Op, 1, ConvertedSize, 0b11011, rd, rn, rm);
}
template<typename T>
void fcmge(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
const ARMEmitter::SubRegSize ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ?
ARMEmitter::SubRegSize::i16Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD3Same<T>(Op, 1, ConvertedSize, 0b11100, rd, rn, rm);
}
template<typename T>
void facge(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
const ARMEmitter::SubRegSize ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ?
ARMEmitter::SubRegSize::i16Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD3Same<T>(Op, 1, ConvertedSize, 0b11101, rd, rn, rm);
}
template<typename T>
void fmaxp(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
const ARMEmitter::SubRegSize ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ?
ARMEmitter::SubRegSize::i16Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD3Same<T>(Op, 1, ConvertedSize, 0b11110, rd, rn, rm);
}
template<typename T>
void fdiv(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
const ARMEmitter::SubRegSize ConvertedSize =
size == ARMEmitter::SubRegSize::i64Bit ?
ARMEmitter::SubRegSize::i16Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD3Same<T>(Op, 1, ConvertedSize, 0b11111, rd, rn, rm);
}
template<typename T>
void eor(T rd, T rn, T rm) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, ARMEmitter::SubRegSize::i8Bit, 0b00011, rd, rn, rm);
}
template<typename T>
void bsl(T rd, T rn, T rm) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, ARMEmitter::SubRegSize::i16Bit, 0b00011, rd, rn, rm);
}
template<typename T>
void fminnmp(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b11000, rd, rn, rm);
}
template<typename T>
void fabd(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b11010, rd, rn, rm);
}
template<typename T>
void fcmgt(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b11100, rd, rn, rm);
}
template<typename T>
void facgt(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b11101, rd, rn, rm);
}
template<typename T>
void fminp(ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i64Bit ||
size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit and 64-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, size, 0b11110, rd, rn, rm);
}
template<typename T>
void bit(T rd, T rn, T rm) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, ARMEmitter::SubRegSize::i32Bit, 0b00011, rd, rn, rm);
}
template<typename T>
void bif(T rd, T rn, T rm) {
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'01 << 10;
ASIMD3Same<T>(Op, 1, ARMEmitter::SubRegSize::i64Bit, 0b00011, rd, rn, rm);
}
// Advanced SIMD modified immediate
// XXX: ORR - 32-bit/16-bit
// XXX: MOVI - Shifting ones
template<typename T>
void fmov(ARMEmitter::SubRegSize size, T rd, float Value) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i16Bit ||
size == ARMEmitter::SubRegSize::i32Bit ||
size == ARMEmitter::SubRegSize::i64Bit, "Unsupported fmov size");
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
uint32_t op;
uint32_t cmode = 0b1111;
uint32_t o2;
uint32_t Imm;
if (size == SubRegSize::i16Bit) {
LOGMAN_MSG_A_FMT("Unsupported");
FEX_UNREACHABLE;
}
else if (size == SubRegSize::i32Bit) {
op = 0;
o2 = 0;
Imm = FP32ToImm8(Value);
}
else if (size == SubRegSize::i64Bit) {
op = 1;
o2 = 0;
Imm = FP64ToImm8(Value);
}
else {
LOGMAN_MSG_A_FMT("Invalid subregsize");
FEX_UNREACHABLE;
}
ASIMDModifiedImm(Op, op, cmode, o2, Imm, rd);
}
// XXX: MVNI - Shifted immediate
// XXX: BIC
//void ASIMDModifiedImm(uint32_t Op, uint32_t op, uint32_t cmode, uint32_t o2, uint32_t imm, T rd) {
template<typename T>
void movi(ARMEmitter::SubRegSize size, T rd, uint64_t Imm, uint16_t Shift = 0) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i8Bit ||
size == ARMEmitter::SubRegSize::i16Bit ||
size == ARMEmitter::SubRegSize::i32Bit ||
size == ARMEmitter::SubRegSize::i64Bit, "Unsupported smov size");
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
uint32_t cmode;
uint32_t op;
if (size == SubRegSize::i8Bit) {
LOGMAN_THROW_AA_FMT(Shift == 0, "8-bit can't have shift");
LOGMAN_THROW_AA_FMT((Imm & ~0xFF) == 0, "Larger than 8-bit Imm not supported");
cmode = 0b1110;
op = 0;
}
else if (size == SubRegSize::i16Bit) {
LOGMAN_THROW_AA_FMT(Shift == 0 || Shift == 8, "Shift by invalid amount");
LOGMAN_THROW_AA_FMT((Imm & ~0xFF) == 0, "Larger than 8-bit Imm not supported");
cmode = 0b1000 | (Shift ? 0b10 : 0b00);
op = 0;
}
else if (size == SubRegSize::i32Bit) {
LOGMAN_THROW_AA_FMT(Shift == 0 || Shift == 8 || Shift == 16 || Shift == 24, "Shift by invalid amount");
LOGMAN_THROW_AA_FMT((Imm & ~0xFF) == 0, "Larger than 8-bit Imm not supported");
cmode = 0b0000 | ((Shift >> 3) << 1);
op = 0;
}
else if (size == SubRegSize::i64Bit) {
LOGMAN_THROW_AA_FMT(Shift == 0, "64-bit can't have shift");
cmode = 0b1110;
op = 1;
// 64-bit movi doesn't behave like the smaller types
// Each bit of the 8-bit imm encoding is expanded to a full 8-bits.
// This gives us a full 64-bits for the final result but needs special handling.
uint8_t NewImm{};
for (size_t i = 0; i < 8; ++i) {
const size_t BitOffset = i * 8;
uint8_t Section = (Imm >> BitOffset) & 0xFF;
LOGMAN_THROW_AA_FMT(Section == 0 || Section == 0xFF, "Invalid 64-bit constant encoding");
if (Section == 0xFF) {
NewImm |= (1 << i);
}
}
Imm = NewImm;
}
else {
LOGMAN_MSG_A_FMT("Invalid subregsize");
FEX_UNREACHABLE;
}
ASIMDModifiedImm(Op, op, cmode, 0, Imm, rd);
}
// Advanced SIMD shift by immediate
template<typename T>
void sshr(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t Shift) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - (Shift);
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm<T>(Op, 0, immh, immb, 0b00000, rn, rd);
}
template<typename T>
void ssra(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t Shift) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - (Shift);
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm<T>(Op, 0, immh, immb, 0b00010, rn, rd);
}
template<typename T>
void srshr(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t Shift) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - (Shift);
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm<T>(Op, 0, immh, immb, 0b00100, rn, rd);
}
template<typename T>
void srsra(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t Shift) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - (Shift);
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm<T>(Op, 0, immh, immb, 0b00110, rn, rd);
}
template<typename T>
void shl(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t Shift) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = immh:immb - esize but immh is /also/ used for element size.
const uint32_t InvertedShift = SubregSizeInBits + Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm<T>(Op, 0, immh, immb, 0b01010, rn, rd);
}
template<typename T>
void sqshl(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t Shift) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = immh:immb - esize but immh is /also/ used for element size.
const uint32_t InvertedShift = SubregSizeInBits + Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm<T>(Op, 0, immh, immb, 0b01110, rn, rd);
}
///< size is destination size
void shrn(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, uint32_t Shift) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - (Shift);
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 0, immh, immb, 0b10000, rn, rd);
}
///< size is destination size
void shrn2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, uint32_t Shift) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - (Shift);
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 0, immh, immb, 0b10000, rn, rd);
}
///< size is destination size
void rshrn(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, uint32_t Shift) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - (Shift);
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 0, immh, immb, 0b10001, rn, rd);
}
///< size is destination size
void rshrn2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, uint32_t Shift) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - (Shift);
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 0, immh, immb, 0b10001, rn, rd);
}
///< size is destination size
void sqshrn(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, uint32_t Shift) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - (Shift);
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 0, immh, immb, 0b10010, rn, rd);
}
///< size is destination size
void sqshrn2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, uint32_t Shift) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - (Shift);
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 0, immh, immb, 0b10010, rn, rd);
}
///< size is destination size
void sqrshrn(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, uint32_t Shift) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - (Shift);
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 0, immh, immb, 0b10011, rn, rd);
}
///< size is destination size
void sqrshrn2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, uint32_t Shift) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - (Shift);
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 0, immh, immb, 0b10011, rn, rd);
}
///< size is destination size
void sshll(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, uint32_t Shift) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "Invalid size");
size = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
LOGMAN_THROW_AA_FMT(Shift < SubregSizeInBits, "Shift must not be larger than incoming element size");
// Shift encoded a bit weirdly.
// shift = immh:immb - esize but immh is /also/ used for element size.
const uint32_t InvertedShift = SubregSizeInBits + Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 0, immh, immb, 0b10100, rn, rd);
}
///< size is destination size
void sshll2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, uint32_t Shift) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "Invalid size");
size = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
LOGMAN_THROW_AA_FMT(Shift < SubregSizeInBits, "Shift must not be larger than incoming element size");
// Shift encoded a bit weirdly.
// shift = immh:immb - esize but immh is /also/ used for element size.
const uint32_t InvertedShift = SubregSizeInBits + Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 0, immh, immb, 0b10100, rn, rd);
}
///< size is destination size
void sxtl(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
sshll(size, rd.D(), rn.D(), 0);
}
///< size is destination size
void sxtl2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
sshll2(size, rd.Q(), rn.Q(), 0);
}
template<typename T>
void scvtf(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t FractionalBits) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "Invalid size");
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
LOGMAN_THROW_AA_FMT(FractionalBits < SubregSizeInBits, "FractionalBits must not be larger than incoming element size");
// fbits encoded a bit weirdly.
// fbits = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedFractionalBits = (SubregSizeInBits * 2) - FractionalBits;
const uint32_t immh = InvertedFractionalBits >> 3;
const uint32_t immb = InvertedFractionalBits & 0b111;
ASIMDShiftByImm(Op, 0, immh, immb, 0b11100, rn, rd);
}
template<typename T>
void fcvtzs(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t FractionalBits) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "Invalid size");
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
LOGMAN_THROW_AA_FMT(FractionalBits < SubregSizeInBits, "FractionalBits must not be larger than incoming element size");
// fbits encoded a bit weirdly.
// fbits = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedFractionalBits = (SubregSizeInBits * 2) - FractionalBits;
const uint32_t immh = InvertedFractionalBits >> 3;
const uint32_t immb = InvertedFractionalBits & 0b111;
ASIMDShiftByImm(Op, 0, immh, immb, 0b11111, rn, rd);
}
template<typename T>
void ushr(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t Shift) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm<T>(Op, 1, immh, immb, 0b00000, rn, rd);
}
template<typename T>
void usra(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t Shift) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm<T>(Op, 1, immh, immb, 0b00010, rn, rd);
}
template<typename T>
void urshr(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t Shift) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm<T>(Op, 1, immh, immb, 0b00100, rn, rd);
}
template<typename T>
void ursra(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t Shift) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm<T>(Op, 1, immh, immb, 0b00110, rn, rd);
}
template<typename T>
void sri(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t Shift) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm<T>(Op, 1, immh, immb, 0b01000, rn, rd);
}
template<typename T>
void sli(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t Shift) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = immh:immb - esize but immh is /also/ used for element size.
const uint32_t InvertedShift = SubregSizeInBits + Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm<T>(Op, 1, immh, immb, 0b01010, rn, rd);
}
template<typename T>
void sqshlu(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t Shift) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = immh:immb - esize but immh is /also/ used for element size.
const uint32_t InvertedShift = SubregSizeInBits + Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm<T>(Op, 1, immh, immb, 0b01100, rn, rd);
}
///< size is destination size
template<typename T>
void uqshl(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t Shift) {
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = immh:immb - esize but immh is /also/ used for element size.
const uint32_t InvertedShift = SubregSizeInBits + Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm<T>(Op, 1, immh, immb, 0b01110, rn, rd);
}
///< size is destination size
void sqshrun(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, uint32_t Shift) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 1, immh, immb, 0b10000, rn, rd);
}
///< size is destination size
void sqshrun2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, uint32_t Shift) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 1, immh, immb, 0b10000, rn, rd);
}
///< size is destination size
void sqrshrun(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, uint32_t Shift) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 1, immh, immb, 0b10001, rn, rd);
}
///< size is destination size
void sqrshrun2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, uint32_t Shift) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 1, immh, immb, 0b10001, rn, rd);
}
///< size is destination size
void uqshrn(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, uint32_t Shift) {
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 1, immh, immb, 0b10010, rn, rd);
}
///< size is destination size
void uqshrn2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, uint32_t Shift) {
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 1, immh, immb, 0b10010, rn, rd);
}
///< size is destination size
void uqrshrn(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, uint32_t Shift) {
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 1, immh, immb, 0b10011, rn, rd);
}
///< size is destination size
void uqrshrn2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, uint32_t Shift) {
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedShift = (SubregSizeInBits * 2) - Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 1, immh, immb, 0b10011, rn, rd);
}
///< size is destination size
void ushll(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn, uint32_t Shift) {
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
size = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = immh:immb - esize but immh is /also/ used for element size.
const uint32_t InvertedShift = SubregSizeInBits + Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 1, immh, immb, 0b10100, rn, rd);
}
///< size is destination size
void ushll2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn, uint32_t Shift) {
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
size = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
const size_t SubregSizeInBits = SubRegSizeInBits(size);
// Shift encoded a bit weirdly.
// shift = immh:immb - esize but immh is /also/ used for element size.
const uint32_t InvertedShift = SubregSizeInBits + Shift;
const uint32_t immh = InvertedShift >> 3;
const uint32_t immb = InvertedShift & 0b111;
ASIMDShiftByImm(Op, 1, immh, immb, 0b10100, rn, rd);
}
void uxtl(ARMEmitter::SubRegSize size, ARMEmitter::DRegister rd, ARMEmitter::DRegister rn) {
ushll(size, rd, rn, 0);
}
void uxtl2(ARMEmitter::SubRegSize size, ARMEmitter::QRegister rd, ARMEmitter::QRegister rn) {
ushll2(size, rd, rn, 0);
}
template<typename T>
void ucvtf(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t FractionalBits) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "Invalid size");
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
LOGMAN_THROW_AA_FMT(FractionalBits < SubregSizeInBits, "FractionalBits must not be larger than incoming element size");
// fbits encoded a bit weirdly.
// fbits = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedFractionalBits = (SubregSizeInBits * 2) - FractionalBits;
const uint32_t immh = InvertedFractionalBits >> 3;
const uint32_t immb = InvertedFractionalBits & 0b111;
ASIMDShiftByImm(Op, 1, immh, immb, 0b11100, rn, rd);
}
template<typename T>
void fcvtzu(ARMEmitter::SubRegSize size, T rd, T rn, uint32_t FractionalBits) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i8Bit, "Invalid size");
if constexpr (std::is_same_v<ARMEmitter::DRegister, T>) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::SubRegSize::i64Bit, "Invalid element size with 64-bit {}", __func__);
}
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'01 << 10;
const size_t SubregSizeInBits = SubRegSizeInBits(size);
LOGMAN_THROW_AA_FMT(FractionalBits < SubregSizeInBits, "FractionalBits must not be larger than incoming element size");
// fbits encoded a bit weirdly.
// fbits = (esize * 2) - immh:immb but immh is /also/ used for element size.
const uint32_t InvertedFractionalBits = (SubregSizeInBits * 2) - FractionalBits;
const uint32_t immh = InvertedFractionalBits >> 3;
const uint32_t immb = InvertedFractionalBits & 0b111;
ASIMDShiftByImm(Op, 1, immh, immb, 0b11111, rn, rd);
}
// Advanced SIMD vector x indexed element
///< size is destination size
void smlal(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b0010, H, EncodedSubRegSize, rm.D(), rn.D(), rd.D());
}
///< size is destination size
void smlal2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b0010, H, EncodedSubRegSize, rm.Q(), rn.Q(), rd.Q());
}
///< size is destination size
void sqdmlal(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b0011, H, EncodedSubRegSize, rm.D(), rn.D(), rd.D());
}
///< size is destination size
void sqdmlal2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b0011, H, EncodedSubRegSize, rm.Q(), rn.Q(), rd.Q());
}
///< size is destination size
void smlsl(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b0110, H, EncodedSubRegSize, rm.D(), rn.D(), rd.D());
}
///< size is destination size
void smlsl2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b0110, H, EncodedSubRegSize, rm.Q(), rn.Q(), rd.Q());
}
///< size is destination size
void sqdmlsl(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b0111, H, EncodedSubRegSize, rm.D(), rn.D(), rd.D());
}
///< size is destination size
void sqdmlsl2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b0111, H, EncodedSubRegSize, rm.Q(), rn.Q(), rd.Q());
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void mul(ARMEmitter::SubRegSize size, T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i16Bit || size == ARMEmitter::SubRegSize::i32Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i16Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(size), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i16Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b1000, H, size, rm, rn, rd);
}
///< size is destination size
void smull(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b1010, H, EncodedSubRegSize, rm.D(), rn.D(), rd.D());
}
///< size is destination size
void smull2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b1010, H, EncodedSubRegSize, rm.Q(), rn.Q(), rd.Q());
}
///< size is destination size
void sqdmull(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b1011, H, EncodedSubRegSize, rm.D(), rn.D(), rd.D());
}
///< size is destination size
void sqdmull2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b1011, H, EncodedSubRegSize, rm.Q(), rn.Q(), rd.Q());
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void sqdmulh(ARMEmitter::SubRegSize size, T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i16Bit || size == ARMEmitter::SubRegSize::i32Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i16Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(size), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i16Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b1100, H, size, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void sqrdmulh(ARMEmitter::SubRegSize size, T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i16Bit || size == ARMEmitter::SubRegSize::i32Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i16Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(size), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i16Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b1101, H, size, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void sdot(T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_A_FMT(Index < 4, "Index must be less than the source register size");
uint32_t H, L, M;
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
ASIMDVectorXIndexedElement(0b0, L, M, 0b1110, H, ARMEmitter::SubRegSize::i32Bit, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void sudot(T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_A_FMT(Index < 4, "Index must be less than the source register size");
uint32_t H, L, M;
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
ASIMDVectorXIndexedElement(0b0, L, M, 0b1111, H, ARMEmitter::SubRegSize::i8Bit, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void bfdot(T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_A_FMT(Index < 4, "Index must be less than the source register size");
uint32_t H, L, M;
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
ASIMDVectorXIndexedElement(0b0, L, M, 0b1111, H, ARMEmitter::SubRegSize::i16Bit, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fmla(ARMEmitter::SubRegSize size, T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i16Bit ||
size == ARMEmitter::SubRegSize::i32Bit ||
size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(size), "Index must be less than the source register size");
uint32_t H, L, M;
auto EncodedSubRegSize = size;
if (size == ARMEmitter::SubRegSize::i16Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
// ARM in their infinite wisdom decided to encode 16-bit as an 8-bit operation even though 16-bit was unallocated.
EncodedSubRegSize = ARMEmitter::SubRegSize::i8Bit;
}
else if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
else {
LOGMAN_THROW_A_FMT(std::is_same_v<ARMEmitter::QRegister, T>, "Can't encode DRegister with i64Bit");
// Index encoded in H
H = Index;
L = 0;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b0001, H, EncodedSubRegSize, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fmls(ARMEmitter::SubRegSize size, T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i16Bit ||
size == ARMEmitter::SubRegSize::i32Bit ||
size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(size), "Index must be less than the source register size");
uint32_t H, L, M;
auto EncodedSubRegSize = size;
if (size == ARMEmitter::SubRegSize::i16Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
// ARM in their infinite wisdom decided to encode 16-bit as an 8-bit operation even though 16-bit was unallocated.
EncodedSubRegSize = ARMEmitter::SubRegSize::i8Bit;
}
else if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
else {
LOGMAN_THROW_A_FMT(std::is_same_v<ARMEmitter::QRegister, T>, "Can't encode DRegister with i64Bit");
// Index encoded in H
H = Index;
L = 0;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b0101, H, EncodedSubRegSize, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fmul(ARMEmitter::SubRegSize size, T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i16Bit ||
size == ARMEmitter::SubRegSize::i32Bit ||
size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(size), "Index must be less than the source register size");
uint32_t H, L, M;
auto EncodedSubRegSize = size;
if (size == ARMEmitter::SubRegSize::i16Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
// ARM in their infinite wisdom decided to encode 16-bit as an 8-bit operation even though 16-bit was unallocated.
EncodedSubRegSize = ARMEmitter::SubRegSize::i8Bit;
}
else if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
else {
LOGMAN_THROW_A_FMT(std::is_same_v<ARMEmitter::QRegister, T>, "Can't encode DRegister with i64Bit");
// Index encoded in H
H = Index;
L = 0;
M = 0;
}
ASIMDVectorXIndexedElement(0b0, L, M, 0b1001, H, EncodedSubRegSize, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fmlal(T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_A_FMT(Index < 8, "Index must be less than the source register size");
uint32_t H, L, M;
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
ASIMDVectorXIndexedElement(0b0, L, M, 0b0000, H, ARMEmitter::SubRegSize::i32Bit, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fmlal2(T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_A_FMT(Index < 8, "Index must be less than the source register size");
uint32_t H, L, M;
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
ASIMDVectorXIndexedElement(0b1, L, M, 0b1000, H, ARMEmitter::SubRegSize::i32Bit, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fmlsl(T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_A_FMT(Index < 8, "Index must be less than the source register size");
uint32_t H, L, M;
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
ASIMDVectorXIndexedElement(0b0, L, M, 0b0100, H, ARMEmitter::SubRegSize::i32Bit, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void fmlsl2(T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_A_FMT(Index < 8, "Index must be less than the source register size");
uint32_t H, L, M;
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
ASIMDVectorXIndexedElement(0b1, L, M, 0b1100, H, ARMEmitter::SubRegSize::i32Bit, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void usdot(T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_A_FMT(Index < 4, "Index must be less than the source register size");
uint32_t H, L, M;
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
ASIMDVectorXIndexedElement(0b0, L, M, 0b1111, H, ARMEmitter::SubRegSize::i32Bit, rm, rn, rd);
}
void bfmlalb(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
LOGMAN_THROW_A_FMT(Index < 8, "Index must be less than the source register size");
uint32_t H, L, M;
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
ASIMDVectorXIndexedElement(0b0, L, M, 0b1111, H, ARMEmitter::SubRegSize::i64Bit, rm.D(), rn.D(), rd.D());
}
void bfmlalt(ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
LOGMAN_THROW_A_FMT(Index < 8, "Index must be less than the source register size");
uint32_t H, L, M;
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
ASIMDVectorXIndexedElement(0b0, L, M, 0b1111, H, ARMEmitter::SubRegSize::i64Bit, rm.Q(), rn.Q(), rd.Q());
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void mla(ARMEmitter::SubRegSize size, T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i16Bit || size == ARMEmitter::SubRegSize::i32Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i16Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(size), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i16Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b1, L, M, 0b0000, H, size, rm, rn, rd);
}
///< size is destination size
void umlal(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b1, L, M, 0b0010, H, EncodedSubRegSize, rm.D(), rn.D(), rd.D());
}
///< size is destination size
void umlal2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b1, L, M, 0b0010, H, EncodedSubRegSize, rm.Q(), rn.Q(), rd.Q());
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void mls(ARMEmitter::SubRegSize size, T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i16Bit || size == ARMEmitter::SubRegSize::i32Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i16Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(size), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i16Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b1, L, M, 0b0100, H, size, rm, rn, rd);
}
///< size is destination size
void umlsl(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b1, L, M, 0b0110, H, EncodedSubRegSize, rm.D(), rn.D(), rd.D());
}
///< size is destination size
void umlsl2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b1, L, M, 0b0110, H, EncodedSubRegSize, rm.Q(), rn.Q(), rd.Q());
}
///< size is destination size
void umull(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b1, L, M, 0b1010, H, EncodedSubRegSize, rm.D(), rn.D(), rd.D());
}
///< size is destination size
void umull2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i32Bit || size == ARMEmitter::SubRegSize::i64Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i32Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
const auto EncodedSubRegSize = ARMEmitter::SubRegSize(FEXCore::ToUnderlying(size) - 1);
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(EncodedSubRegSize), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i32Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b1, L, M, 0b1010, H, EncodedSubRegSize, rm.Q(), rn.Q(), rd.Q());
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void sqrdmlah(ARMEmitter::SubRegSize size, T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i16Bit || size == ARMEmitter::SubRegSize::i32Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i16Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(size), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i16Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b1, L, M, 0b1101, H, size, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void udot(T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_A_FMT(Index < 4, "Index must be less than the source register size");
uint32_t H, L, M;
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
ASIMDVectorXIndexedElement(0b1, L, M, 0b1110, H, ARMEmitter::SubRegSize::i32Bit, rm, rn, rd);
}
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void sqrdmlsh(ARMEmitter::SubRegSize size, T rd, T rn, T rm, uint32_t Index) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::SubRegSize::i16Bit || size == ARMEmitter::SubRegSize::i32Bit, "Invalid destination size");
if (size == ARMEmitter::SubRegSize::i16Bit) {
LOGMAN_THROW_A_FMT(rm.Idx() < 16, "Rm can't be v16-v31 with half source size");
}
LOGMAN_THROW_A_FMT(Index < SubRegSizeInBits(size), "Index must be less than the source register size");
uint32_t H, L, M;
if (size == ARMEmitter::SubRegSize::i16Bit) {
// Index encoded in H:L:M
H = (Index >> 2) & 1;
L = (Index >> 1) & 1;
M = (Index >> 0) & 1;
}
else {
// Index encoded in H:L
// M overlaps rm register.
H = (Index >> 1) & 1;
L = (Index >> 0) & 1;
M = 0;
}
ASIMDVectorXIndexedElement(0b1, L, M, 0b1111, H, size, rm, rn, rd);
}
// Cryptographic three-register, imm2
// TODO
// Cryptographic three-register SHA 512
// TODO
// Cryptographic four-register
// TODO
// Cryptographic two-register SHA 512
// TODO
// Conversion between floating-point and fixed-point
void scvtf(ARMEmitter::ScalarRegSize ScalarSize, ARMEmitter::VRegister rd, ARMEmitter::Size GPRSize, ARMEmitter::Register rn, uint32_t FractionalBits) {
LOGMAN_THROW_A_FMT(FractionalBits >= 1 && FractionalBits <= ARMEmitter::RegSizeInBits(GPRSize), "Fractional bits out of range");
uint32_t Scale = 64 - FractionalBits;
const auto ConvertedSize =
ScalarSize == ARMEmitter::ScalarRegSize::i64Bit ? 0b01 :
ScalarSize == ARMEmitter::ScalarRegSize::i32Bit ? 0b00 :
ScalarSize == ARMEmitter::ScalarRegSize::i16Bit ? 0b11 : 0;
ScalarConvertBetweenFPAndFixed(0, 0b00, 0b010, Scale, GPRSize, ConvertedSize, rn, rd);
}
void ucvtf(ARMEmitter::ScalarRegSize ScalarSize, ARMEmitter::VRegister rd, ARMEmitter::Size GPRSize, ARMEmitter::Register rn, uint32_t FractionalBits) {
LOGMAN_THROW_A_FMT(FractionalBits >= 1 && FractionalBits <= ARMEmitter::RegSizeInBits(GPRSize), "Fractional bits out of range");
uint32_t Scale = 64 - FractionalBits;
const auto ConvertedSize =
ScalarSize == ARMEmitter::ScalarRegSize::i64Bit ? 0b01 :
ScalarSize == ARMEmitter::ScalarRegSize::i32Bit ? 0b00 :
ScalarSize == ARMEmitter::ScalarRegSize::i16Bit ? 0b11 : 0;
ScalarConvertBetweenFPAndFixed(0, 0b00, 0b011, Scale, GPRSize, ConvertedSize, rn, rd);
}
void fcvtzs(ARMEmitter::Size GPRSize, ARMEmitter::Register rd, ARMEmitter::ScalarRegSize ScalarSize, ARMEmitter::VRegister rn, uint32_t FractionalBits) {
LOGMAN_THROW_A_FMT(FractionalBits >= 1 && FractionalBits <= ARMEmitter::RegSizeInBits(GPRSize), "Fractional bits out of range");
uint32_t Scale = 64 - FractionalBits;
const auto ConvertedSize =
ScalarSize == ARMEmitter::ScalarRegSize::i64Bit ? 0b01 :
ScalarSize == ARMEmitter::ScalarRegSize::i32Bit ? 0b00 :
ScalarSize == ARMEmitter::ScalarRegSize::i16Bit ? 0b11 : 0;
ScalarConvertBetweenFPAndFixed(0, 0b11, 0b000, Scale, GPRSize, ConvertedSize, rn, rd);
}
void fcvtzu(ARMEmitter::Size GPRSize, ARMEmitter::Register rd, ARMEmitter::ScalarRegSize ScalarSize, ARMEmitter::VRegister rn, uint32_t FractionalBits) {
LOGMAN_THROW_A_FMT(FractionalBits >= 1 && FractionalBits <= ARMEmitter::RegSizeInBits(GPRSize), "Fractional bits out of range");
uint32_t Scale = 64 - FractionalBits;
const auto ConvertedSize =
ScalarSize == ARMEmitter::ScalarRegSize::i64Bit ? 0b01 :
ScalarSize == ARMEmitter::ScalarRegSize::i32Bit ? 0b00 :
ScalarSize == ARMEmitter::ScalarRegSize::i16Bit ? 0b11 : 0;
ScalarConvertBetweenFPAndFixed(0, 0b11, 0b001, Scale, GPRSize, ConvertedSize, rn, rd);
}
// Conversion between floating-point and integer
void fcvtns(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::HRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b11, 0b00, 0b000, rd, ARMEmitter::ToReg(rn));
}
void fcvtns(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::SRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b00, 0b00, 0b000, rd, ARMEmitter::ToReg(rn));
}
void fcvtns(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::DRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b00, 0b000, rd, ARMEmitter::ToReg(rn));
}
void fcvtnu(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::HRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b11, 0b00, 0b001, rd, ARMEmitter::ToReg(rn));
}
void fcvtnu(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::SRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b00, 0b00, 0b001, rd, ARMEmitter::ToReg(rn));
}
void fcvtnu(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::DRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b00, 0b001, rd, ARMEmitter::ToReg(rn));
}
void scvtf(ARMEmitter::Size size, ARMEmitter::HRegister rd, ARMEmitter::Register rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b11, 0b00, 0b010, ARMEmitter::ToReg(rd), rn);
}
void scvtf(ARMEmitter::Size size, ARMEmitter::SRegister rd, ARMEmitter::Register rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b00, 0b00, 0b010, ARMEmitter::ToReg(rd), rn);
}
void scvtf(ARMEmitter::Size size, ARMEmitter::DRegister rd, ARMEmitter::Register rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b00, 0b010, ARMEmitter::ToReg(rd), rn);
}
void ucvtf(ARMEmitter::Size size, ARMEmitter::HRegister rd, ARMEmitter::Register rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b11, 0b00, 0b011, ARMEmitter::ToReg(rd), rn);
}
void ucvtf(ARMEmitter::Size size, ARMEmitter::SRegister rd, ARMEmitter::Register rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b00, 0b00, 0b011, ARMEmitter::ToReg(rd), rn);
}
void ucvtf(ARMEmitter::Size size, ARMEmitter::DRegister rd, ARMEmitter::Register rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b00, 0b011, ARMEmitter::ToReg(rd), rn);
}
void fcvtas(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::HRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b11, 0b00, 0b100, rd, ARMEmitter::ToReg(rn));
}
void fcvtas(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::SRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b00, 0b00, 0b100, rd, ARMEmitter::ToReg(rn));
}
void fcvtas(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::DRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b00, 0b100, rd, ARMEmitter::ToReg(rn));
}
void fcvtau(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::HRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b11, 0b00, 0b101, rd, ARMEmitter::ToReg(rn));
}
void fcvtau(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::SRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b00, 0b00, 0b101, rd, ARMEmitter::ToReg(rn));
}
void fcvtau(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::DRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b00, 0b101, rd, ARMEmitter::ToReg(rn));
}
void fmov(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::HRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b11, 0b00, 0b110, rd, ARMEmitter::ToReg(rn));
}
void fmov(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::SRegister rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::Size::i64Bit, "Can't move SReg to 64-bit");
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b00, 0b00, 0b110, rd, ARMEmitter::ToReg(rn));
}
void fmov(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::DRegister rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::Size::i32Bit, "Can't move DReg to 32-bit");
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b00, 0b110, rd, ARMEmitter::ToReg(rn));
}
void fmov(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::VRegister rn, bool Upper) {
if (Upper) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::Size::i64Bit, "Can only move upper with 64-bit elements");
}
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, Upper ? 0b10 : 0b01, Upper ? 0b01 : 0b00, 0b110, rd, ARMEmitter::ToReg(rn));
}
void fmov(ARMEmitter::Size size, ARMEmitter::HRegister rd, ARMEmitter::Register rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b11, 0b00, 0b111, ARMEmitter::ToReg(rd), rn);
}
void fmov(ARMEmitter::Size size, ARMEmitter::SRegister rd, ARMEmitter::Register rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::Size::i64Bit, "Can't move SReg to 64-bit");
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b00, 0b00, 0b111, ARMEmitter::ToReg(rd), rn);
}
void fmov(ARMEmitter::Size size, ARMEmitter::DRegister rd, ARMEmitter::Register rn) {
LOGMAN_THROW_AA_FMT(size != ARMEmitter::Size::i32Bit, "Can't move DReg to 32-bit");
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b00, 0b111, ARMEmitter::ToReg(rd), rn);
}
void fmov(ARMEmitter::Size size, ARMEmitter::VRegister rd, ARMEmitter::Register rn, bool Upper) {
if (Upper) {
LOGMAN_THROW_AA_FMT(size == ARMEmitter::Size::i64Bit, "Can only move upper with 64-bit elements");
}
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, Upper ? 0b10 : 0b01, Upper ? 0b01 : 0b00, 0b111, ARMEmitter::ToReg(rd), rn);
}
void fcvtps(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::HRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b11, 0b01, 0b000, rd, ARMEmitter::ToReg(rn));
}
void fcvtps(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::SRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b00, 0b01, 0b000, rd, ARMEmitter::ToReg(rn));
}
void fcvtps(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::DRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b01, 0b000, rd, ARMEmitter::ToReg(rn));
}
void fcvtpu(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::HRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b11, 0b01, 0b001, rd, ARMEmitter::ToReg(rn));
}
void fcvtpu(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::SRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b00, 0b01, 0b001, rd, ARMEmitter::ToReg(rn));
}
void fcvtpu(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::DRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b01, 0b001, rd, ARMEmitter::ToReg(rn));
}
void fcvtms(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::HRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b11, 0b10, 0b000, rd, ARMEmitter::ToReg(rn));
}
void fcvtms(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::SRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b00, 0b10, 0b000, rd, ARMEmitter::ToReg(rn));
}
void fcvtms(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::DRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b10, 0b000, rd, ARMEmitter::ToReg(rn));
}
void fcvtmu(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::HRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b11, 0b10, 0b001, rd, ARMEmitter::ToReg(rn));
}
void fcvtmu(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::SRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b00, 0b10, 0b001, rd, ARMEmitter::ToReg(rn));
}
void fcvtmu(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::DRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b10, 0b001, rd, ARMEmitter::ToReg(rn));
}
void fcvtzs(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::HRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b11, 0b11, 0b000, rd, ARMEmitter::ToReg(rn));
}
void fcvtzs(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::SRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b00, 0b11, 0b000, rd, ARMEmitter::ToReg(rn));
}
void fcvtzs(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::DRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b11, 0b000, rd, ARMEmitter::ToReg(rn));
}
void fcvtzs(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::VRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b11, 0b000, rd, ARMEmitter::ToReg(rn));
}
void fcvtzu(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::HRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b11, 0b11, 0b001, rd, ARMEmitter::ToReg(rn));
}
void fcvtzu(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::SRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b00, 0b11, 0b001, rd, ARMEmitter::ToReg(rn));
}
void fcvtzu(ARMEmitter::Size size, ARMEmitter::Register rd, ARMEmitter::DRegister rn) {
constexpr uint32_t Op = 0b0001'1110'001 << 21;
ASIMDFloatConvBetweenInt(Op, size, 0, 0b01, 0b11, 0b001, rd, ARMEmitter::ToReg(rn));
}
private:
// Cryptographic AES
void CryptoAES(uint32_t Op, uint32_t opcode, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
uint32_t Instr = Op;
Instr |= opcode << 12;
Instr |= Encode_rn(rn);
Instr |= Encode_rd(rd);
dc32(Instr);
}
// Cryptographic three-register SHA
void Crypto3RegSHA(uint32_t Op, uint32_t opcode, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) {
uint32_t Instr = Op;
Instr |= Encode_rm(rm);
Instr |= opcode << 12;
Instr |= Encode_rn(rn);
Instr |= Encode_rd(rd);
dc32(Instr);
}
// Cryptographic two-register SHA
void Crypto2RegSHA(uint32_t Op, uint32_t opcode, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
uint32_t Instr = Op;
Instr |= opcode << 12;
Instr |= Encode_rn(rn);
Instr |= Encode_rd(rd);
dc32(Instr);
}
// Advanced SIMD table lookup
void ASIMDTable(uint32_t Op, uint32_t Q, uint32_t op2, uint32_t len, uint32_t op, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) {
uint32_t Instr = Op;
Instr |= Q << 30;
Instr |= op2 << 22;
Instr |= Encode_rm(rm);
Instr |= len << 13;
Instr |= op << 12;
Instr |= Encode_rn(rn);
Instr |= Encode_rd(rd);
dc32(Instr);
}
// Advanced SIMD permute
void ASIMDPermute(uint32_t Op, uint32_t Q, ARMEmitter::SubRegSize size, uint32_t opcode, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) {
uint32_t Instr = Op;
Instr |= Q << 30;
Instr |= FEXCore::ToUnderlying(size) << 22;
Instr |= Encode_rm(rm);
Instr |= opcode << 12;
Instr |= Encode_rn(rn);
Instr |= Encode_rd(rd);
dc32(Instr);
}
// Advanced SIMD extract
void ASIMDExtract(uint32_t Op, uint32_t Q, uint32_t op2, uint32_t imm4, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn, ARMEmitter::VRegister rm) {
uint32_t Instr = Op;
Instr |= Q << 30;
Instr |= op2 << 22;
Instr |= Encode_rm(rm);
Instr |= imm4 << 11;
Instr |= Encode_rn(rn);
Instr |= Encode_rd(rd);
dc32(Instr);
}
// Advanced SIMD two-register miscellaneous
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void ASIMD2RegMisc(uint32_t Op, uint32_t U, ARMEmitter::SubRegSize size, uint32_t opcode, T rd, T rn) {
constexpr uint32_t Q = std::is_same_v<ARMEmitter::QRegister, T> ? 1U << 30 : 0;
uint32_t Instr = Op;
Instr |= Q;
Instr |= U << 29;
Instr |= FEXCore::ToUnderlying(size) << 22;
Instr |= opcode << 12;
Instr |= Encode_rn(rn);
Instr |= Encode_rd(rd);
dc32(Instr);
}
// Advanced SIMD across lanes
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void ASIMDAcrossLanes(uint32_t Op, uint32_t U, ARMEmitter::SubRegSize size, uint32_t opcode, T rd, T rn) {
constexpr uint32_t Q = std::is_same_v<ARMEmitter::QRegister, T> ? 1U << 30 : 0;
uint32_t Instr = Op;
Instr |= Q;
Instr |= U << 29;
Instr |= FEXCore::ToUnderlying(size) << 22;
Instr |= opcode << 12;
Instr |= Encode_rn(rn);
Instr |= Encode_rd(rd);
dc32(Instr);
}
// Advanced SIMD three different
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void ASIMD3Different(uint32_t Op, uint32_t U, uint32_t opcode, ARMEmitter::SubRegSize size, T rd, T rn, T rm) {
constexpr uint32_t Q = std::is_same_v<ARMEmitter::QRegister, T> ? 1U << 30 : 0;
uint32_t Instr = Op;
Instr |= Q;
Instr |= U << 29;
Instr |= FEXCore::ToUnderlying(size) << 22;
Instr |= Encode_rm(rm);
Instr |= opcode << 12;
Instr |= Encode_rn(rn);
Instr |= Encode_rd(rd);
dc32(Instr);
}
// Advanced SIMD three same
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void ASIMD3Same(uint32_t Op, uint32_t U, ARMEmitter::SubRegSize size, uint32_t opcode, T rd, T rn, T rm) {
constexpr uint32_t Q = std::is_same_v<ARMEmitter::QRegister, T> ? 1U << 30 : 0;
uint32_t Instr = Op;
Instr |= Q;
Instr |= U << 29;
Instr |= FEXCore::ToUnderlying(size) << 22;
Instr |= Encode_rm(rm);
Instr |= opcode << 11;
Instr |= Encode_rn(rn);
Instr |= Encode_rd(rd);
dc32(Instr);
}
// Advanced SIMD modified immediate
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void ASIMDModifiedImm(uint32_t Op, uint32_t op, uint32_t cmode, uint32_t o2, uint32_t imm, T rd) {
constexpr uint32_t Q = std::is_same_v<ARMEmitter::QRegister, T> ? 1U << 30 : 0;
uint32_t Instr = Op;
Instr |= Q;
Instr |= op << 29;
Instr |= ((imm >> 7) & 1) << 18;
Instr |= ((imm >> 6) & 1) << 17;
Instr |= ((imm >> 5) & 1) << 16;
Instr |= cmode << 12;
Instr |= o2 << 11;
Instr |= ((imm >> 4) & 1) << 9;
Instr |= ((imm >> 3) & 1) << 8;
Instr |= ((imm >> 2) & 1) << 7;
Instr |= ((imm >> 1) & 1) << 6;
Instr |= ((imm >> 0) & 1) << 5;
Instr |= Encode_rd(rd);
dc32(Instr);
}
// Advanced SIMD shift by immediate
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void ASIMDShiftByImm(uint32_t Op, uint32_t U, uint32_t immh, uint32_t immb, uint32_t opcode, T rn, T rd) {
constexpr uint32_t Q = std::is_same_v<ARMEmitter::QRegister, T> ? 1U << 30 : 0;
LOGMAN_THROW_A_FMT(immh != 0, "ImmH needs to not be zero");
uint32_t Instr = Op;
Instr |= Q;
Instr |= U << 29;
Instr |= immh << 19;
Instr |= immb << 16;
Instr |= opcode << 11;
Instr |= Encode_rn(rn);
Instr |= Encode_rd(rd);
dc32(Instr);
}
// Advanced SIMD vector x indexed element
template<typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void ASIMDVectorXIndexedElement(uint32_t U, uint32_t L, uint32_t M, uint32_t opcode, uint32_t H, ARMEmitter::SubRegSize size, T rm, T rn, T rd) {
constexpr uint32_t Op = 0b0000'1111'0000'0000'0000'00 << 10;
constexpr uint32_t Q = std::is_same_v<ARMEmitter::QRegister, T> ? 1U << 30 : 0;
uint32_t Instr = Op;
Instr |= Q;
Instr |= U << 29;
Instr |= FEXCore::ToUnderlying(size) << 22;
Instr |= L << 21;
// M and Rm might overlap. It's up to the instruction emitter itself to ensure there is no conflict.
Instr |= M << 20;
Instr |= rm.Idx() << 16;
Instr |= opcode << 12;
Instr |= H << 11;
Instr |= rn.Idx() << 5;
Instr |= rd.Idx();
dc32(Instr);
}
// Conversion between floating-point and fixed-point
template<typename T, typename T2>
void ScalarConvertBetweenFPAndFixed(uint32_t S, uint32_t rmode, uint32_t opcode, uint32_t scale,
ARMEmitter::Size GPRSize, uint32_t ScalarSize,
T rn, T2 rd) {
constexpr uint32_t Op = 0b0001'1110'000 << 21;
const uint32_t SF = GPRSize == ARMEmitter::Size::i64Bit ? (1U << 31) : 0;
uint32_t Instr = Op;
Instr |= SF;
Instr |= S << 29;
Instr |= ScalarSize << 22;
Instr |= rmode << 19;
Instr |= opcode << 16;
Instr |= scale << 10;
Instr |= rn.Idx() << 5;
Instr |= rd.Idx();
dc32(Instr);
}
// Conversion between floating-point and integer
void ASIMDFloatConvBetweenInt(uint32_t Op, ARMEmitter::Size s, uint32_t S, uint32_t ptype, uint32_t rmode, uint32_t opcode, ARMEmitter::Register rd, ARMEmitter::Register rn) {
const uint32_t SF = s == ARMEmitter::Size::i64Bit ? (1U << 31) : 0;
uint32_t Instr = Op;
Instr |= SF;
Instr |= S << 29;
Instr |= ptype << 22;
Instr |= rmode << 19;
Instr |= opcode << 16;
Instr |= Encode_rn(rn);
Instr |= Encode_rd(rd);
dc32(Instr);
}
template<ARMEmitter::SubRegSize size, bool Load, typename T>
requires(std::is_same_v<ARMEmitter::QRegister, T> || std::is_same_v<ARMEmitter::DRegister, T>)
void ASIMDLoadStoreMultipleStructure(uint32_t Op, uint32_t opcode, T rt, ARMEmitter::Register rn, ARMEmitter::Register rm) {
constexpr uint32_t Q = std::is_same_v<ARMEmitter::QRegister, T> ? 1U << 30 : 0;
uint32_t Instr = Op;
Instr |= Q;
Instr |= Load ? 1 << 22: 0;
Instr |= Encode_rm(rm);
Instr |= opcode;
Instr |= FEXCore::ToUnderlying(size) << 10;
Instr |= Encode_rn(rn);
Instr |= Encode_rt(rt);
dc32(Instr);
}
template<ARMEmitter::SubRegSize size, bool Load, uint32_t Count>
void ASIMDSTLD(uint32_t Op, uint32_t Opcode, ARMEmitter::VRegister rt, uint32_t Index, ARMEmitter::Register rn, ARMEmitter::Register rm) {
LOGMAN_THROW_A_FMT(
(size == SubRegSize::i8Bit && Index < 16) ||
(size == SubRegSize::i16Bit && Index < 8) ||
(size == SubRegSize::i32Bit && Index < 4) ||
(size == SubRegSize::i64Bit && Index < 2),
"Invalid Index selected");
uint32_t Q{};
uint32_t S{};
uint32_t Size{};
// selem is for determining if we are doing 1-3 loadstore single structure operations
// eg: ST1/2/3/4 or LD1/2/3/4
constexpr uint32_t selem = Count - 1;
const uint32_t opcode = Opcode | (selem >> 1);
// Index is encoded as:
// 8-bit: Q:S:size
// 16-bit Q:S:size<1>
// 32-bit: Q:S
// 64-bit: Q
if constexpr (size == SubRegSize::i8Bit) {
Q = ((Index & 0b1000) >> 3) << 30;
S = ((Index & 0b0100) >> 2);
Size = Index & 0b11;
}
else if constexpr (size == SubRegSize::i16Bit) {
Q = ((Index & 0b0100) >> 2) << 30;
S = ((Index & 0b0010) >> 1);
Size = (Index & 0b1) << 1;
}
else if constexpr (size == SubRegSize::i32Bit) {
Q = ((Index & 0b0010) >> 1) << 30;
S = Index & 0b0001;
}
else if constexpr (size == SubRegSize::i64Bit) {
Q = (Index & 0b0001) << 30;
Size = 1;
}
// scale = opcode<2:1>
// selem = opcode<0>:R + 1
//
// scale:
// - 0
// - Index = Q:S:size - aka B[0-15]
// - 1
// - Index = Q:S:size<1> - aka H[0-7]
// - 2
// if (size == i32)
// - Index = Q:S - aka S[0-3]
// if (size == i64)
// - Index = Q - aka D[0-1]
// if (size == i128) undefined
// - 3
// Load+Replicate
// scale = size
ASIMDLoadStore(Op | Q, Load, selem & 1, opcode, S, Size, rt, rn, rm);
}
template<ARMEmitter::SubRegSize size, bool Load, uint32_t Count, typename T>
void ASIMDSTLD(uint32_t Op, uint32_t Opcode, T rt, ARMEmitter::Register rn, ARMEmitter::Register rm) {
constexpr uint32_t Q = std::is_same_v<ARMEmitter::QRegister, T> ? 1U << 30 : 0;
constexpr uint32_t S = 0;
// selem is for determining if we are doing 1-3 loadstore single structure operations
// eg: ST1/2/3/4 or LD1/2/3/4
constexpr uint32_t selem = Count - 1;
const uint32_t opcode = Opcode | (selem >> 1);
// scale = opcode<2:1>
// selem = opcode<0>:R + 1
//
// scale:
// - 0
// - Index = Q:S:size - aka B[0-15]
// - 1
// - Index = Q:S:size<1> - aka H[0-7]
// - 2
// if (size == i32)
// - Index = Q:S - aka S[0-3]
// if (size == i64)
// - Index = Q - aka D[0-1]
// if (size == i128) undefined
// - 3
// Load+Replicate
// scale = size
ASIMDLoadStore(Op | Q, Load, selem & 1, opcode, S, FEXCore::ToUnderlying(size), rt, rn, rm);
}
void ASIMDLoadStore(uint32_t Op, uint32_t L, uint32_t R, uint32_t opcode, uint32_t S, uint32_t size, ARMEmitter::VRegister rt, ARMEmitter::Register rn, ARMEmitter::Register rm) {
uint32_t Instr = Op;
Instr |= L << 22;
Instr |= R << 21;
Instr |= Encode_rm(rm);
Instr |= opcode << 13;
Instr |= S << 12;
Instr |= size << 10;
Instr |= Encode_rn(rn);
Instr |= Encode_rt(rt);
dc32(Instr);
}