[llvm] 7ccf4e6 - [RISCV] Improve lowerVECTOR_SHUFFLEAsPPair. (#212895)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 30 08:08:24 PDT 2026
Author: Craig Topper
Date: 2026-07-30T08:08:19-07:00
New Revision: 7ccf4e638e3f0106b3c8f36a3e4d9e67c40b8d1d
URL: https://github.com/llvm/llvm-project/commit/7ccf4e638e3f0106b3c8f36a3e4d9e67c40b8d1d
DIFF: https://github.com/llvm/llvm-project/commit/7ccf4e638e3f0106b3c8f36a3e4d9e67c40b8d1d.diff
LOG: [RISCV] Improve lowerVECTOR_SHUFFLEAsPPair. (#212895)
Allow V1 and V2 to be swapped. Allow V1 or V2 to be used twice.
Assisted-by: Claude
Added:
Modified:
llvm/lib/Target/RISCV/RISCVISelLowering.cpp
llvm/test/CodeGen/RISCV/rvp-simd-64.ll
Removed:
################################################################################
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index b546a21e1c43c..fe905f686d855 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -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();
+
+ // 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();
+
+ bool EvenIsOdd = Parity[0].value_or(false);
+ bool OddIsOdd = Parity[1].value_or(false);
unsigned Opc;
- if (IsStrided(0, 0))
+ if (!EvenIsOdd && !OddIsOdd)
Opc = RISCVISD::PPAIRE;
- else if (IsStrided(1, 1))
+ else if (EvenIsOdd && OddIsOdd)
Opc = RISCVISD::PPAIRO;
- else if (IsStrided(0, 1))
+ else if (!EvenIsOdd && OddIsOdd)
Opc = RISCVISD::PPAIREO;
- else if (IsStrided(1, 0))
- Opc = RISCVISD::PPAIROE;
else
- return SDValue();
+ Opc = RISCVISD::PPAIROE;
- return DAG.getNode(Opc, DL, VT, V1, V2);
+ return DAG.getNode(Opc, DL, VT, Src[0], Src[1]);
}
SDValue RISCVTargetLowering::lowerVECTOR_SHUFFLE(SDValue Op,
diff --git a/llvm/test/CodeGen/RISCV/rvp-simd-64.ll b/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
index a6e01ab13a39e..d31942feb113b 100644
--- a/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
+++ b/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
@@ -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
+}
More information about the llvm-commits
mailing list