mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-28 09:00:52 +01:00
AVX: Reduce inserts in VBLENDPS/VPBLENDD/VPBLENDW
Lets us reduce inserts by seeing which bits in the selector mask indicates a particular source is used more than the other one, and then just uses that as the base to be inserted into, cutting down on overall insertion overhead. In some cases, this can be quite drastic, like with: vpblendw ymm0, ymm1, ymm2, 0b00000001 being cut down from 98 instructions to 14.
This commit is contained in:
1 parent
681c5e8097
commit
a6e7fba433
3 files changed
+141
-569
No files matched your search
@@ -596,7 +596,7 @@ public:
|
||||
|
||||
void VANDNOp(OpcodeArgs);
|
||||
|
||||
Ref VBLENDOpImpl(IR::OpSize VecSize, IR::OpSize ElementSize, Ref Src1, Ref Src2, Ref ZeroRegister, uint64_t Selector);
|
||||
Ref VBLENDOpImpl(IR::OpSize VecSize, IR::OpSize ElementSize, Ref Src1, Ref Src2, uint64_t Selector);
|
||||
void VBLENDPDOp(OpcodeArgs);
|
||||
void VPBLENDDOp(OpcodeArgs);
|
||||
void VPBLENDWOp(OpcodeArgs);
|
||||
|
||||
@@ -5009,15 +5009,48 @@ void OpDispatchBuilder::VPERMQOp(OpcodeArgs) {
|
||||
StoreResultFPR(Op, Result);
|
||||
}
|
||||
|
||||
Ref OpDispatchBuilder::VBLENDOpImpl(IR::OpSize VecSize, IR::OpSize ElementSize, Ref Src1, Ref Src2, Ref ZeroRegister, uint64_t Selector) {
|
||||
const std::array Sources {Src1, Src2};
|
||||
Ref OpDispatchBuilder::VBLENDOpImpl(IR::OpSize VecSize, IR::OpSize ElementSize, Ref Src1, Ref Src2, uint64_t Selector) {
|
||||
const auto IsWordElements = ElementSize == OpSize::i16Bit;
|
||||
const auto Is256Bit = VecSize == OpSize::i256Bit;
|
||||
|
||||
const auto ElementsPerLane = uint32_t(IR::NumElements(OpSize::i128Bit, ElementSize));
|
||||
|
||||
// PBLENDW uses the same immediate size for 128-bit and 256-bit
|
||||
// while all the others double in size.
|
||||
const auto MaskSize = Is256Bit && !IsWordElements ? ElementsPerLane * 2 : ElementsPerLane;
|
||||
const auto Mask = (1U << MaskSize) - 1;
|
||||
|
||||
// Now, we determine which mask portion has the higher population count.
|
||||
// we use this to determine which source we use as the base to insert into.
|
||||
//
|
||||
// For example each even bit in a mask that is set, means that element in Src2
|
||||
// will be inserted into the destination. If that bit is not set, then it's
|
||||
// the element in Src1 that will be inserted instead.
|
||||
//
|
||||
// If we have more bits incoming from a particular source, then we can reappropriate
|
||||
// that source as the destination and only perform inserts from the other source
|
||||
// where necessary, which reduces the overall amount of element insertions that
|
||||
// need to take place.
|
||||
//
|
||||
// In the event we tie, then we can just use Src1 and only perform incoming insertions
|
||||
// that come from Src2.
|
||||
const auto NumSrc2Bits = uint32_t(std::popcount(Selector & Mask));
|
||||
const auto NumSrc1Bits = MaskSize - NumSrc2Bits;
|
||||
const auto IsUsingSrc1 = NumSrc1Bits >= NumSrc2Bits;
|
||||
Ref Result = IsUsingSrc1 ? Src1 : Src2;
|
||||
Ref Source = IsUsingSrc1 ? Src2 : Src1;
|
||||
|
||||
Ref Result = ZeroRegister;
|
||||
const auto NumElements = IR::NumElements(VecSize, ElementSize);
|
||||
for (int i = 0; i < NumElements; i++) {
|
||||
const auto SelectorIndex = (Selector >> i) & 1;
|
||||
if (IsUsingSrc1 && SelectorIndex == 0) {
|
||||
continue;
|
||||
}
|
||||
if (!IsUsingSrc1 && SelectorIndex != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Result = _VInsElement(VecSize, ElementSize, i, i, Result, Sources[SelectorIndex]);
|
||||
Result = _VInsElement(VecSize, ElementSize, i, i, Result, Source);
|
||||
}
|
||||
|
||||
return Result;
|
||||
@@ -5044,8 +5077,7 @@ void OpDispatchBuilder::VBLENDPDOp(OpcodeArgs) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto ZeroRegister = LoadZeroVector(DstSize);
|
||||
Ref Result = VBLENDOpImpl(DstSize, OpSize::i64Bit, Src1, Src2, ZeroRegister, Selector);
|
||||
Ref Result = VBLENDOpImpl(DstSize, OpSize::i64Bit, Src1, Src2, Selector);
|
||||
StoreResultFPR(Op, Result);
|
||||
}
|
||||
|
||||
@@ -5085,11 +5117,7 @@ void OpDispatchBuilder::VPBLENDDOp(OpcodeArgs) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto ZeroRegister = LoadZeroVector(DstSize);
|
||||
Ref Result = VBLENDOpImpl(DstSize, OpSize::i32Bit, Src1, Src2, ZeroRegister, Selector);
|
||||
if (!Is256Bit) {
|
||||
Result = _VMov(OpSize::i128Bit, Result);
|
||||
}
|
||||
Ref Result = VBLENDOpImpl(DstSize, OpSize::i32Bit, Src1, Src2, Selector);
|
||||
StoreResultFPR(Op, Result);
|
||||
}
|
||||
|
||||
@@ -5117,11 +5145,7 @@ void OpDispatchBuilder::VPBLENDWOp(OpcodeArgs) {
|
||||
// imm for the helper function to use.
|
||||
const auto NewSelector = Selector << 8 | Selector;
|
||||
|
||||
const auto ZeroRegister = LoadZeroVector(DstSize);
|
||||
Ref Result = VBLENDOpImpl(DstSize, OpSize::i16Bit, Src1, Src2, ZeroRegister, NewSelector);
|
||||
if (Is128Bit) {
|
||||
Result = _VMov(OpSize::i128Bit, Result);
|
||||
}
|
||||
Ref Result = VBLENDOpImpl(DstSize, OpSize::i16Bit, Src1, Src2, NewSelector);
|
||||
StoreResultFPR(Op, Result);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user