[RISCV] Improve lowerVECTOR_SHUFFLEAsPPair. (#212895)
Allow V1 and V2 to be swapped. Allow V1 or V2 to be used twice. Assisted-by: Claude
This commit is contained in:
parent
11b58f0e4c
commit
7ccf4e638e
2 changed files with 197 additions and 31 deletions
|
|
@ -6541,8 +6541,6 @@ static SDValue lowerVECTOR_SHUFFLEAsPPair(ShuffleVectorSDNode *SVN,
|
|||
SDLoc DL(SVN);
|
||||
unsigned NumElts = VT.getVectorNumElements();
|
||||
ArrayRef<int> Mask = SVN->getMask();
|
||||
if (V2.isUndef())
|
||||
return SDValue();
|
||||
|
||||
// A splat operand's lanes are all equal, so a lane selecting from it matches
|
||||
// any of its positions. This covers the zero operand XformToShuffleWithZero
|
||||
|
|
@ -6551,38 +6549,59 @@ static SDValue lowerVECTOR_SHUFFLEAsPPair(ShuffleVectorSDNode *SVN,
|
|||
bool V1IsSplat = DAG.isSplatValue(V1);
|
||||
bool V2IsSplat = DAG.isSplatValue(V2);
|
||||
|
||||
// Match the packed pair shuffle forms:
|
||||
// <V1Start, N+V2Start, V1Start+2, N+V2Start+2, ...>
|
||||
// where each start is 0 for even lanes or 1 for odd lanes. This covers all
|
||||
// four PPAIRE/PPAIREO/PPAIROE/PPAIRO combinations. Trailing lanes may be
|
||||
// undef when a 4-byte source was widened to v8i8.
|
||||
auto IsStrided = [&](unsigned V1Start, unsigned V2Start) {
|
||||
for (unsigned I = 0; I != NumElts / 2; ++I) {
|
||||
int M0 = Mask[2 * I];
|
||||
int M1 = Mask[2 * I + 1];
|
||||
if (M0 >= 0 &&
|
||||
(V1IsSplat ? M0 >= (int)NumElts : M0 != (int)(V1Start + 2 * I)))
|
||||
return false;
|
||||
if (M1 >= 0 && (V2IsSplat ? M1 < (int)NumElts
|
||||
: M1 != (int)(NumElts + V2Start + 2 * I)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
// Walk the mask once, tracking the operand feeding the destination's even
|
||||
// lanes (index 0) and the operand feeding its odd lanes (index 1) — either
|
||||
// may turn out to be V1 or V2 — along with whether each pulls the even or
|
||||
// odd element out of its pair. All even (resp. odd) lanes must agree on
|
||||
// both the operand and the parity used; a splat operand's lanes are all
|
||||
// equal so it never constrains the parity.
|
||||
SDValue Src[2];
|
||||
std::optional<bool> Parity[2];
|
||||
for (unsigned I = 0; I != NumElts; ++I) {
|
||||
int M = Mask[I];
|
||||
if (M < 0)
|
||||
continue;
|
||||
unsigned Lane = I % 2;
|
||||
bool FromV1 = (unsigned)M < NumElts;
|
||||
SDValue Cand = FromV1 ? V1 : V2;
|
||||
unsigned Local = (unsigned)M % NumElts;
|
||||
if (!Src[Lane])
|
||||
Src[Lane] = Cand;
|
||||
else if (Src[Lane] != Cand)
|
||||
return SDValue();
|
||||
|
||||
unsigned Opc;
|
||||
if (IsStrided(0, 0))
|
||||
Opc = RISCVISD::PPAIRE;
|
||||
else if (IsStrided(1, 1))
|
||||
Opc = RISCVISD::PPAIRO;
|
||||
else if (IsStrided(0, 1))
|
||||
Opc = RISCVISD::PPAIREO;
|
||||
else if (IsStrided(1, 0))
|
||||
Opc = RISCVISD::PPAIROE;
|
||||
else
|
||||
// Splats don't constrain parity.
|
||||
if (FromV1 ? V1IsSplat : V2IsSplat)
|
||||
continue;
|
||||
|
||||
// The index must be from the even/odd element of its pair.
|
||||
if (Local / 2 != I / 2)
|
||||
return SDValue();
|
||||
|
||||
bool P = Local % 2;
|
||||
if (!Parity[Lane])
|
||||
Parity[Lane] = P;
|
||||
else if (*Parity[Lane] != P)
|
||||
return SDValue();
|
||||
}
|
||||
|
||||
// Make sure we have a source for both lanes.
|
||||
if (!Src[0] || !Src[1])
|
||||
return SDValue();
|
||||
|
||||
return DAG.getNode(Opc, DL, VT, V1, V2);
|
||||
bool EvenIsOdd = Parity[0].value_or(false);
|
||||
bool OddIsOdd = Parity[1].value_or(false);
|
||||
unsigned Opc;
|
||||
if (!EvenIsOdd && !OddIsOdd)
|
||||
Opc = RISCVISD::PPAIRE;
|
||||
else if (EvenIsOdd && OddIsOdd)
|
||||
Opc = RISCVISD::PPAIRO;
|
||||
else if (!EvenIsOdd && OddIsOdd)
|
||||
Opc = RISCVISD::PPAIREO;
|
||||
else
|
||||
Opc = RISCVISD::PPAIROE;
|
||||
|
||||
return DAG.getNode(Opc, DL, VT, Src[0], Src[1]);
|
||||
}
|
||||
|
||||
SDValue RISCVTargetLowering::lowerVECTOR_SHUFFLE(SDValue Op,
|
||||
|
|
|
|||
|
|
@ -6224,3 +6224,150 @@ define <4 x i16> @test_ppairoe_v4i16(<4 x i16> %a, <4 x i16> %b) {
|
|||
%res = shufflevector <4 x i16> %a, <4 x i16> %b, <4 x i32> <i32 1, i32 4, i32 3, i32 6>
|
||||
ret <4 x i16> %res
|
||||
}
|
||||
|
||||
; The tests below swap the usual operand roles: the destination's even lanes
|
||||
; come from the second shuffle operand and its odd lanes from the first.
|
||||
|
||||
define <8 x i8> @test_ppaire_swapped_v8i8(<8 x i8> %a, <8 x i8> %b) {
|
||||
; RV32-LABEL: test_ppaire_swapped_v8i8:
|
||||
; RV32: # %bb.0:
|
||||
; RV32-NEXT: ppaire.db a0, a2, a0
|
||||
; RV32-NEXT: ret
|
||||
;
|
||||
; RV64-LABEL: test_ppaire_swapped_v8i8:
|
||||
; RV64: # %bb.0:
|
||||
; RV64-NEXT: ppaire.b a0, a1, a0
|
||||
; RV64-NEXT: ret
|
||||
%res = shufflevector <8 x i8> %a, <8 x i8> %b, <8 x i32> <i32 8, i32 0, i32 10, i32 2, i32 12, i32 4, i32 14, i32 6>
|
||||
ret <8 x i8> %res
|
||||
}
|
||||
|
||||
define <8 x i8> @test_ppairo_swapped_v8i8(<8 x i8> %a, <8 x i8> %b) {
|
||||
; RV32-LABEL: test_ppairo_swapped_v8i8:
|
||||
; RV32: # %bb.0:
|
||||
; RV32-NEXT: ppairo.db a0, a2, a0
|
||||
; RV32-NEXT: ret
|
||||
;
|
||||
; RV64-LABEL: test_ppairo_swapped_v8i8:
|
||||
; RV64: # %bb.0:
|
||||
; RV64-NEXT: ppairo.b a0, a1, a0
|
||||
; RV64-NEXT: ret
|
||||
%res = shufflevector <8 x i8> %a, <8 x i8> %b, <8 x i32> <i32 9, i32 1, i32 11, i32 3, i32 13, i32 5, i32 15, i32 7>
|
||||
ret <8 x i8> %res
|
||||
}
|
||||
|
||||
define <8 x i8> @test_ppaireo_swapped_v8i8(<8 x i8> %a, <8 x i8> %b) {
|
||||
; RV32-LABEL: test_ppaireo_swapped_v8i8:
|
||||
; RV32: # %bb.0:
|
||||
; RV32-NEXT: ppaireo.db a0, a2, a0
|
||||
; RV32-NEXT: ret
|
||||
;
|
||||
; RV64-LABEL: test_ppaireo_swapped_v8i8:
|
||||
; RV64: # %bb.0:
|
||||
; RV64-NEXT: ppaireo.b a0, a1, a0
|
||||
; RV64-NEXT: ret
|
||||
%res = shufflevector <8 x i8> %a, <8 x i8> %b, <8 x i32> <i32 8, i32 1, i32 10, i32 3, i32 12, i32 5, i32 14, i32 7>
|
||||
ret <8 x i8> %res
|
||||
}
|
||||
|
||||
define <8 x i8> @test_ppairoe_swapped_v8i8(<8 x i8> %a, <8 x i8> %b) {
|
||||
; RV32-LABEL: test_ppairoe_swapped_v8i8:
|
||||
; RV32: # %bb.0:
|
||||
; RV32-NEXT: ppairoe.db a0, a2, a0
|
||||
; RV32-NEXT: ret
|
||||
;
|
||||
; RV64-LABEL: test_ppairoe_swapped_v8i8:
|
||||
; RV64: # %bb.0:
|
||||
; RV64-NEXT: ppairoe.b a0, a1, a0
|
||||
; RV64-NEXT: ret
|
||||
%res = shufflevector <8 x i8> %a, <8 x i8> %b, <8 x i32> <i32 9, i32 0, i32 11, i32 2, i32 13, i32 4, i32 15, i32 6>
|
||||
ret <8 x i8> %res
|
||||
}
|
||||
|
||||
define <4 x i16> @test_ppaire_swapped_v4i16(<4 x i16> %a, <4 x i16> %b) {
|
||||
; RV32-LABEL: test_ppaire_swapped_v4i16:
|
||||
; RV32: # %bb.0:
|
||||
; RV32-NEXT: ppaire.dh a0, a2, a0
|
||||
; RV32-NEXT: ret
|
||||
;
|
||||
; RV64-LABEL: test_ppaire_swapped_v4i16:
|
||||
; RV64: # %bb.0:
|
||||
; RV64-NEXT: ppaire.h a0, a1, a0
|
||||
; RV64-NEXT: ret
|
||||
%res = shufflevector <4 x i16> %a, <4 x i16> %b, <4 x i32> <i32 4, i32 0, i32 6, i32 2>
|
||||
ret <4 x i16> %res
|
||||
}
|
||||
|
||||
define <4 x i16> @test_ppairo_swapped_v4i16(<4 x i16> %a, <4 x i16> %b) {
|
||||
; RV32-LABEL: test_ppairo_swapped_v4i16:
|
||||
; RV32: # %bb.0:
|
||||
; RV32-NEXT: ppairo.dh a0, a2, a0
|
||||
; RV32-NEXT: ret
|
||||
;
|
||||
; RV64-LABEL: test_ppairo_swapped_v4i16:
|
||||
; RV64: # %bb.0:
|
||||
; RV64-NEXT: ppairo.h a0, a1, a0
|
||||
; RV64-NEXT: ret
|
||||
%res = shufflevector <4 x i16> %a, <4 x i16> %b, <4 x i32> <i32 5, i32 1, i32 7, i32 3>
|
||||
ret <4 x i16> %res
|
||||
}
|
||||
|
||||
define <4 x i16> @test_ppaireo_swapped_v4i16(<4 x i16> %a, <4 x i16> %b) {
|
||||
; RV32-LABEL: test_ppaireo_swapped_v4i16:
|
||||
; RV32: # %bb.0:
|
||||
; RV32-NEXT: ppaireo.dh a0, a2, a0
|
||||
; RV32-NEXT: ret
|
||||
;
|
||||
; RV64-LABEL: test_ppaireo_swapped_v4i16:
|
||||
; RV64: # %bb.0:
|
||||
; RV64-NEXT: ppaireo.h a0, a1, a0
|
||||
; RV64-NEXT: ret
|
||||
%res = shufflevector <4 x i16> %a, <4 x i16> %b, <4 x i32> <i32 4, i32 1, i32 6, i32 3>
|
||||
ret <4 x i16> %res
|
||||
}
|
||||
|
||||
define <4 x i16> @test_ppairoe_swapped_v4i16(<4 x i16> %a, <4 x i16> %b) {
|
||||
; RV32-LABEL: test_ppairoe_swapped_v4i16:
|
||||
; RV32: # %bb.0:
|
||||
; RV32-NEXT: ppairoe.dh a0, a2, a0
|
||||
; RV32-NEXT: ret
|
||||
;
|
||||
; RV64-LABEL: test_ppairoe_swapped_v4i16:
|
||||
; RV64: # %bb.0:
|
||||
; RV64-NEXT: ppairoe.h a0, a1, a0
|
||||
; RV64-NEXT: ret
|
||||
%res = shufflevector <4 x i16> %a, <4 x i16> %b, <4 x i32> <i32 5, i32 0, i32 7, i32 2>
|
||||
ret <4 x i16> %res
|
||||
}
|
||||
|
||||
; The tests below use a single operand for both the even-lane and odd-lane
|
||||
; groups (the other operand is either poison or simply unreferenced by the
|
||||
; mask), rather than splitting the groups across V1 and V2.
|
||||
|
||||
define <8 x i8> @test_ppair_v1_used_twice_v8i8(<8 x i8> %a) {
|
||||
; RV32-LABEL: test_ppair_v1_used_twice_v8i8:
|
||||
; RV32: # %bb.0:
|
||||
; RV32-NEXT: ppairoe.db a0, a0, a0
|
||||
; RV32-NEXT: ret
|
||||
;
|
||||
; RV64-LABEL: test_ppair_v1_used_twice_v8i8:
|
||||
; RV64: # %bb.0:
|
||||
; RV64-NEXT: ppairoe.b a0, a0, a0
|
||||
; RV64-NEXT: ret
|
||||
%res = shufflevector <8 x i8> %a, <8 x i8> poison, <8 x i32> <i32 1, i32 0, i32 3, i32 2, i32 5, i32 4, i32 7, i32 6>
|
||||
ret <8 x i8> %res
|
||||
}
|
||||
|
||||
define <8 x i8> @test_ppair_v2_used_twice_v8i8(<8 x i8> %a, <8 x i8> %b) {
|
||||
; RV32-LABEL: test_ppair_v2_used_twice_v8i8:
|
||||
; RV32: # %bb.0:
|
||||
; RV32-NEXT: ppairoe.db a0, a2, a2
|
||||
; RV32-NEXT: ret
|
||||
;
|
||||
; RV64-LABEL: test_ppair_v2_used_twice_v8i8:
|
||||
; RV64: # %bb.0:
|
||||
; RV64-NEXT: ppairoe.b a0, a1, a1
|
||||
; RV64-NEXT: ret
|
||||
%res = shufflevector <8 x i8> %a, <8 x i8> %b, <8 x i32> <i32 9, i32 8, i32 11, i32 10, i32 13, i32 12, i32 15, i32 14>
|
||||
ret <8 x i8> %res
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue