[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:
Craig Topper 2026-07-30 08:08:19 -07:00 committed by GitHub
parent 11b58f0e4c
commit 7ccf4e638e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 197 additions and 31 deletions

View file

@ -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,

View file

@ -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
}