mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 16:00:18 +02:00
IR: Add option to disable vector shift range clamping
The range check and clamping is necessary in the cases of passing x86 shift amounts directly through VUSHL/VSSHR. Some AVX operations are still using these with range clamping. A future investigation task should be the check if they can be switched over to the wide variants that we implemented for the SSE instructions. When consuming our own controlled data, we don't want the range clamping to be enabled.
This commit is contained in:
1 parent
0195bb6e5a
commit
8d110738ac
3 files changed
+85
-46
No files matched your search
@@ -2242,8 +2242,9 @@ DEF_OP(VUShl) {
|
||||
const auto MaxShift = ElementSize * 8;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto ShiftVector = GetVReg(Op->ShiftVector.ID());
|
||||
auto ShiftVector = GetVReg(Op->ShiftVector.ID());
|
||||
const auto Vector = GetVReg(Op->Vector.ID());
|
||||
const auto RangeCheck = Op->RangeCheck;
|
||||
|
||||
LOGMAN_THROW_AA_FMT(ElementSize == 1 || ElementSize == 2 || ElementSize == 4 || ElementSize == 8, "Invalid size");
|
||||
const auto SubRegSize =
|
||||
@@ -2255,28 +2256,40 @@ DEF_OP(VUShl) {
|
||||
if (HostSupportsSVE256 && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Merging();
|
||||
|
||||
dup_imm(SubRegSize, VTMP2.Z(), MaxShift);
|
||||
umin(SubRegSize, VTMP2.Z(), Mask, VTMP2.Z(), ShiftVector.Z());
|
||||
if (RangeCheck) {
|
||||
dup_imm(SubRegSize, VTMP2.Z(), MaxShift);
|
||||
umin(SubRegSize, VTMP2.Z(), Mask, VTMP2.Z(), ShiftVector.Z());
|
||||
ShiftVector = VTMP2;
|
||||
}
|
||||
|
||||
if (Dst == ShiftVector) {
|
||||
// If destination aliases the shift vector then we need to move it temporarily.
|
||||
mov(VTMP2.Z(), ShiftVector.Z());
|
||||
ShiftVector = VTMP2;
|
||||
}
|
||||
|
||||
// If Dst aliases Vector, then we can skip the move.
|
||||
if (Dst != Vector) {
|
||||
movprfx(Dst.Z(), Vector.Z());
|
||||
}
|
||||
lsl(SubRegSize, Dst.Z(), Mask, Dst.Z(), VTMP2.Z());
|
||||
lsl(SubRegSize, Dst.Z(), Mask, Dst.Z(), ShiftVector.Z());
|
||||
} else {
|
||||
if (ElementSize < 8) {
|
||||
movi(SubRegSize, VTMP1.Q(), MaxShift);
|
||||
umin(SubRegSize, VTMP1.Q(), VTMP1.Q(), ShiftVector.Q());
|
||||
} else {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, MaxShift);
|
||||
dup(SubRegSize, VTMP1.Q(), TMP1.R());
|
||||
if (RangeCheck) {
|
||||
if (ElementSize < 8) {
|
||||
movi(SubRegSize, VTMP1.Q(), MaxShift);
|
||||
umin(SubRegSize, VTMP1.Q(), VTMP1.Q(), ShiftVector.Q());
|
||||
} else {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, MaxShift);
|
||||
dup(SubRegSize, VTMP1.Q(), TMP1.R());
|
||||
|
||||
// UMIN is silly on Adv.SIMD and doesn't have a variant that handles 64-bit elements
|
||||
cmhi(SubRegSize, VTMP2.Q(), ShiftVector.Q(), VTMP1.Q());
|
||||
bif(VTMP1.Q(), ShiftVector.Q(), VTMP2.Q());
|
||||
// UMIN is silly on Adv.SIMD and doesn't have a variant that handles 64-bit elements
|
||||
cmhi(SubRegSize, VTMP2.Q(), ShiftVector.Q(), VTMP1.Q());
|
||||
bif(VTMP1.Q(), ShiftVector.Q(), VTMP2.Q());
|
||||
}
|
||||
ShiftVector = VTMP1;
|
||||
}
|
||||
|
||||
ushl(SubRegSize, Dst.Q(), Vector.Q(), VTMP1.Q());
|
||||
ushl(SubRegSize, Dst.Q(), Vector.Q(), ShiftVector.Q());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2289,8 +2302,9 @@ DEF_OP(VUShr) {
|
||||
const auto MaxShift = ElementSize * 8;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto ShiftVector = GetVReg(Op->ShiftVector.ID());
|
||||
auto ShiftVector = GetVReg(Op->ShiftVector.ID());
|
||||
const auto Vector = GetVReg(Op->Vector.ID());
|
||||
const auto RangeCheck = Op->RangeCheck;
|
||||
|
||||
LOGMAN_THROW_AA_FMT(ElementSize == 1 || ElementSize == 2 || ElementSize == 4 || ElementSize == 8, "Invalid size");
|
||||
const auto SubRegSize =
|
||||
@@ -2302,30 +2316,42 @@ DEF_OP(VUShr) {
|
||||
if (HostSupportsSVE256 && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Merging();
|
||||
|
||||
dup_imm(SubRegSize, VTMP2.Z(), MaxShift);
|
||||
umin(SubRegSize, VTMP2.Z(), Mask, VTMP2.Z(), ShiftVector.Z());
|
||||
if (RangeCheck) {
|
||||
dup_imm(SubRegSize, VTMP2.Z(), MaxShift);
|
||||
umin(SubRegSize, VTMP2.Z(), Mask, VTMP2.Z(), ShiftVector.Z());
|
||||
ShiftVector = VTMP2;
|
||||
}
|
||||
|
||||
if (Dst == ShiftVector) {
|
||||
// If destination aliases the shift vector then we need to move it temporarily.
|
||||
mov(VTMP2.Z(), ShiftVector.Z());
|
||||
ShiftVector = VTMP2;
|
||||
}
|
||||
|
||||
// If Dst aliases Vector, then we can skip the move.
|
||||
if (Dst != Vector) {
|
||||
movprfx(Dst.Z(), Vector.Z());
|
||||
}
|
||||
lsr(SubRegSize, Dst.Z(), Mask, Dst.Z(), VTMP2.Z());
|
||||
lsr(SubRegSize, Dst.Z(), Mask, Dst.Z(), ShiftVector.Z());
|
||||
} else {
|
||||
if (ElementSize < 8) {
|
||||
movi(SubRegSize, VTMP1.Q(), MaxShift);
|
||||
umin(SubRegSize, VTMP1.Q(), VTMP1.Q(), ShiftVector.Q());
|
||||
} else {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, MaxShift);
|
||||
dup(SubRegSize, VTMP1.Q(), TMP1.R());
|
||||
if (RangeCheck) {
|
||||
if (ElementSize < 8) {
|
||||
movi(SubRegSize, VTMP1.Q(), MaxShift);
|
||||
umin(SubRegSize, VTMP1.Q(), VTMP1.Q(), ShiftVector.Q());
|
||||
} else {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, MaxShift);
|
||||
dup(SubRegSize, VTMP1.Q(), TMP1.R());
|
||||
|
||||
// UMIN is silly on Adv.SIMD and doesn't have a variant that handles 64-bit elements
|
||||
cmhi(SubRegSize, VTMP2.Q(), ShiftVector.Q(), VTMP1.Q());
|
||||
bif(VTMP1.Q(), ShiftVector.Q(), VTMP2.Q());
|
||||
// UMIN is silly on Adv.SIMD and doesn't have a variant that handles 64-bit elements
|
||||
cmhi(SubRegSize, VTMP2.Q(), ShiftVector.Q(), VTMP1.Q());
|
||||
bif(VTMP1.Q(), ShiftVector.Q(), VTMP2.Q());
|
||||
}
|
||||
ShiftVector = VTMP1;
|
||||
}
|
||||
|
||||
// Need to invert shift values to perform a right shift with USHL
|
||||
// (USHR only has an immediate variant).
|
||||
neg(SubRegSize, VTMP1.Q(), VTMP1.Q());
|
||||
neg(SubRegSize, VTMP1.Q(), ShiftVector.Q());
|
||||
ushl(SubRegSize, Dst.Q(), Vector.Q(), VTMP1.Q());
|
||||
}
|
||||
}
|
||||
@@ -2337,9 +2363,10 @@ DEF_OP(VSShr) {
|
||||
const auto ElementSize = IROp->ElementSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto MaxShift = (ElementSize * 8) - 1;
|
||||
const auto RangeCheck = Op->RangeCheck;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto ShiftVector = GetVReg(Op->ShiftVector.ID());
|
||||
auto ShiftVector = GetVReg(Op->ShiftVector.ID());
|
||||
const auto Vector = GetVReg(Op->Vector.ID());
|
||||
|
||||
LOGMAN_THROW_AA_FMT(ElementSize == 1 || ElementSize == 2 || ElementSize == 4 || ElementSize == 8, "Invalid size");
|
||||
@@ -2352,30 +2379,42 @@ DEF_OP(VSShr) {
|
||||
if (HostSupportsSVE256 && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Merging();
|
||||
|
||||
dup_imm(SubRegSize, VTMP1.Z(), MaxShift);
|
||||
umin(SubRegSize, VTMP1.Z(), Mask, VTMP1.Z(), ShiftVector.Z());
|
||||
if (RangeCheck) {
|
||||
dup_imm(SubRegSize, VTMP1.Z(), MaxShift);
|
||||
umin(SubRegSize, VTMP1.Z(), Mask, VTMP1.Z(), ShiftVector.Z());
|
||||
ShiftVector = VTMP1;
|
||||
}
|
||||
|
||||
if (Dst == ShiftVector) {
|
||||
// If destination aliases the shift vector then we need to move it temporarily.
|
||||
mov(VTMP2.Z(), ShiftVector.Z());
|
||||
ShiftVector = VTMP1;
|
||||
}
|
||||
|
||||
// If Dst aliases Vector, then we can skip the move.
|
||||
if (Dst != Vector) {
|
||||
movprfx(Dst.Z(), Vector.Z());
|
||||
}
|
||||
asr(SubRegSize, Dst.Z(), Mask, Dst.Z(), VTMP1.Z());
|
||||
asr(SubRegSize, Dst.Z(), Mask, Dst.Z(), ShiftVector.Z());
|
||||
} else {
|
||||
if (ElementSize < 8) {
|
||||
movi(SubRegSize, VTMP1.Q(), MaxShift);
|
||||
umin(SubRegSize, VTMP1.Q(), VTMP1.Q(), ShiftVector.Q());
|
||||
} else {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, MaxShift);
|
||||
dup(SubRegSize, VTMP1.Q(), TMP1.R());
|
||||
if (RangeCheck) {
|
||||
if (ElementSize < 8) {
|
||||
movi(SubRegSize, VTMP1.Q(), MaxShift);
|
||||
umin(SubRegSize, VTMP1.Q(), VTMP1.Q(), ShiftVector.Q());
|
||||
} else {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, MaxShift);
|
||||
dup(SubRegSize, VTMP1.Q(), TMP1.R());
|
||||
|
||||
// UMIN is silly on Adv.SIMD and doesn't have a variant that handles 64-bit elements
|
||||
cmhi(SubRegSize, VTMP2.Q(), ShiftVector.Q(), VTMP1.Q());
|
||||
bif(VTMP1.Q(), ShiftVector.Q(), VTMP2.Q());
|
||||
// UMIN is silly on Adv.SIMD and doesn't have a variant that handles 64-bit elements
|
||||
cmhi(SubRegSize, VTMP2.Q(), ShiftVector.Q(), VTMP1.Q());
|
||||
bif(VTMP1.Q(), ShiftVector.Q(), VTMP2.Q());
|
||||
}
|
||||
ShiftVector = VTMP1;
|
||||
}
|
||||
|
||||
// Need to invert shift values to perform a right shift with SSHL
|
||||
// (SSHR only has an immediate variant).
|
||||
neg(SubRegSize, VTMP1.Q(), VTMP1.Q());
|
||||
neg(SubRegSize, VTMP1.Q(), ShiftVector.Q());
|
||||
sshl(SubRegSize, Dst.Q(), Vector.Q(), VTMP1.Q());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1932,7 +1932,7 @@ void OpDispatchBuilder::AVXVariableShiftImpl(OpcodeArgs, IROps IROp) {
|
||||
OrderedNode *Vector = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], DstSize, Op->Flags, -1);
|
||||
OrderedNode *ShiftVector = LoadSource_WithOpSize(FPRClass, Op, Op->Src[1], DstSize, Op->Flags, -1);
|
||||
|
||||
auto Shift = _VUShr(DstSize, SrcSize, Vector, ShiftVector);
|
||||
auto Shift = _VUShr(DstSize, SrcSize, Vector, ShiftVector, true);
|
||||
Shift.first->Header.Op = IROp;
|
||||
|
||||
StoreResult(FPRClass, Op, Shift, -1);
|
||||
|
||||
@@ -1430,15 +1430,15 @@
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / (ElementSize << 1)"
|
||||
},
|
||||
"FPR = VUShl u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, FPR:$ShiftVector": {
|
||||
"FPR = VUShl u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, FPR:$ShiftVector, i1:$RangeCheck": {
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
"FPR = VUShr u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, FPR:$ShiftVector": {
|
||||
"FPR = VUShr u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, FPR:$ShiftVector, i1:$RangeCheck": {
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
"FPR = VSShr u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, FPR:$ShiftVector": {
|
||||
"FPR = VSShr u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, FPR:$ShiftVector, i1:$RangeCheck": {
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user