AVX128: Improve VPERMILPS/PD and VPSHUFD

VPSHUFD and VPERMILPS are aliases of each other.

Reuses the implementation path from the PSHUFD implementation which has
a few swizzles and then a table lookup.

VPERMILPD is a very simple swizzle per 128-bit lane.

Fixes #3797
Fixes #3784
This commit is contained in:
Ryan Houdek committed 2024-07-18 04:10:58 -07:00
1 parent 17a55fbb39
commit b0bd8a62a2
3 files changed
+29 -30

No files matched your search

@@ -958,6 +958,8 @@ public:
void UnimplementedOp(OpcodeArgs);
void PermissionRestrictedOp(OpcodeArgs);
///< Helper for PSHUD and VPERMILPS(imm) since they are the same instruction
Ref Single128Bit4ByteVectorShuffle(Ref Src, uint8_t Shuffle);
// AVX 128-bit operations
Ref AVX128_LoadXMMRegister(uint32_t XMM, bool High);
void AVX128_StoreXMMRegister(uint32_t XMM, const Ref Src, bool High);
@@ -158,7 +158,7 @@ void OpDispatchBuilder::InstallAVX128Handlers() {
{OPD(1, 0b01, 0x6F), 1, &OpDispatchBuilder::AVX128_VMOVAPS},
{OPD(1, 0b10, 0x6F), 1, &OpDispatchBuilder::AVX128_VMOVAPS},
{OPD(1, 0b01, 0x70), 1, &OpDispatchBuilder::AVX128_VPSHUF<4, true>},
{OPD(1, 0b01, 0x70), 1, &OpDispatchBuilder::AVX128_VPERMILImm<4>},
{OPD(1, 0b10, 0x70), 1, &OpDispatchBuilder::AVX128_VPSHUF<2, false>},
{OPD(1, 0b11, 0x70), 1, &OpDispatchBuilder::AVX128_VPSHUF<2, true>},
@@ -1937,26 +1937,28 @@ void OpDispatchBuilder::AVX128_VPERMILImm(OpcodeArgs) {
RefPair Result = AVX128_Zext(LoadZeroVector(OpSize::i128Bit));
///< TODO: This could be optimized.
if constexpr (ElementSize == OpSize::i64Bit) {
Result.Low = _VInsElement(OpSize::i128Bit, ElementSize, 0, Selector & 0b0001, Result.Low, Src.Low);
Result.Low = _VInsElement(OpSize::i128Bit, ElementSize, 1, (Selector & 0b0010) >> 1, Result.Low, Src.Low);
auto DoSwizzle64 = [this](Ref Src, uint8_t Selector) -> Ref {
switch (Selector) {
case 0b00:
case 0b11: return _VDupElement(OpSize::i128Bit, OpSize::i64Bit, Src, Selector & 1);
case 0b01: return _VExtr(OpSize::i128Bit, OpSize::i8Bit, Src, Src, 8);
case 0b10:
// No swizzle
return Src;
default: FEX_UNREACHABLE;
}
};
Result.Low = DoSwizzle64(Src.Low, Selector & 0b11);
if (!Is128Bit) {
Result.High = _VInsElement(OpSize::i128Bit, ElementSize, 0, ((Selector & 0b0100) >> 2), Result.High, Src.High);
Result.High = _VInsElement(OpSize::i128Bit, ElementSize, 1, ((Selector & 0b1000) >> 3), Result.High, Src.High);
Result.High = DoSwizzle64(Src.High, (Selector >> 2) & 0b11);
}
} else {
Result.Low = _VInsElement(OpSize::i128Bit, ElementSize, 0, Selector & 0b00000011, Result.Low, Src.Low);
Result.Low = _VInsElement(OpSize::i128Bit, ElementSize, 1, (Selector & 0b00001100) >> 2, Result.Low, Src.Low);
Result.Low = _VInsElement(OpSize::i128Bit, ElementSize, 2, (Selector & 0b00110000) >> 4, Result.Low, Src.Low);
Result.Low = _VInsElement(OpSize::i128Bit, ElementSize, 3, (Selector & 0b11000000) >> 6, Result.Low, Src.Low);
Result.Low = Single128Bit4ByteVectorShuffle(Src.Low, Selector);
if (!Is128Bit) {
Result.High = _VInsElement(OpSize::i128Bit, ElementSize, 0, (Selector & 0b00000011), Result.High, Src.High);
Result.High = _VInsElement(OpSize::i128Bit, ElementSize, 1, ((Selector & 0b00001100) >> 2), Result.High, Src.High);
Result.High = _VInsElement(OpSize::i128Bit, ElementSize, 2, ((Selector & 0b00110000) >> 4), Result.High, Src.High);
Result.High = _VInsElement(OpSize::i128Bit, ElementSize, 3, ((Selector & 0b11000000) >> 6), Result.High, Src.High);
Result.High = Single128Bit4ByteVectorShuffle(Src.High, Selector);
}
}
@@ -1003,20 +1003,14 @@ void OpDispatchBuilder::PSHUFWOp(OpcodeArgs) {
template void OpDispatchBuilder::PSHUFWOp<false>(OpcodeArgs);
template void OpDispatchBuilder::PSHUFWOp<true>(OpcodeArgs);
void OpDispatchBuilder::PSHUFDOp(OpcodeArgs) {
Ref OpDispatchBuilder::Single128Bit4ByteVectorShuffle(Ref Src, uint8_t Shuffle) {
constexpr auto IdentityCopy = 0b11'10'01'00;
uint16_t Shuffle = Op->Src[1].Data.Literal.Value;
const auto Size = GetSrcSize(Op);
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Dest {};
// TODO: There can be more optimized copies here.
switch (Shuffle) {
case IdentityCopy: {
// Special case identity copy.
Dest = Src;
break;
return Src;
}
case 0b01'01'00'00: {
// Zip with self.
@@ -1024,27 +1018,28 @@ void OpDispatchBuilder::PSHUFDOp(OpcodeArgs) {
// Dest[1] = Src[0]
// Dest[2] = Src[1]
// Dest[3] = Src[1]
Dest = _VZip(Size, 4, Src, Src);
break;
return _VZip(OpSize::i128Bit, OpSize::i32Bit, Src, Src);
}
case 0b00'00'00'00:
case 0b01'01'01'01:
case 0b10'10'10'10:
case 0b11'11'11'11: {
// Special case element duplicate and broadcast to low or high 64-bits.
Dest = _VDupElement(Size, 4, Src, Shuffle & 0b11);
break;
return _VDupElement(OpSize::i128Bit, OpSize::i32Bit, Src, Shuffle & 0b11);
}
default: {
// PSHUFD needs to scale index by 16.
auto LookupIndexes =
LoadAndCacheIndexedNamedVectorConstant(Size, FEXCore::IR::IndexNamedVectorConstant::INDEXED_NAMED_VECTOR_PSHUFD, Shuffle * 16);
Dest = _VTBL1(Size, Src, LookupIndexes);
break;
LoadAndCacheIndexedNamedVectorConstant(OpSize::i128Bit, FEXCore::IR::IndexNamedVectorConstant::INDEXED_NAMED_VECTOR_PSHUFD, Shuffle * 16);
return _VTBL1(OpSize::i128Bit, Src, LookupIndexes);
}
}
}
StoreResult(FPRClass, Op, Dest, -1);
void OpDispatchBuilder::PSHUFDOp(OpcodeArgs) {
uint16_t Shuffle = Op->Src[1].Data.Literal.Value;
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
StoreResult(FPRClass, Op, Single128Bit4ByteVectorShuffle(Src, Shuffle), -1);
}
template<size_t ElementSize, bool Low>