[llvm] [LoongArch] Custom legalize vector_shuffle to `xvextrins` (PR #164375)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 23 04:38:45 PDT 2026
https://github.com/zhaoqi5 updated https://github.com/llvm/llvm-project/pull/164375
>From 30b2deb95d34d16e2b275165baf4e9b5538fe416 Mon Sep 17 00:00:00 2001
From: Qi Zhao <zhaoqi01 at loongson.cn>
Date: Tue, 21 Oct 2025 16:07:29 +0800
Subject: [PATCH 1/4] [LoongArch] Custom legalize vector_shuffle to `xvextrins`
---
.../LoongArch/LoongArchISelLowering.cpp | 79 +++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index 446d27805fb35..d94df40f7db23 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -2707,6 +2707,82 @@ static SDValue lowerVECTOR_SHUFFLE_XVPICKOD(const SDLoc &DL, ArrayRef<int> Mask,
return DAG.getNode(LoongArchISD::VPICKOD, DL, VT, V2, V1);
}
+/// Lower VECTOR_SHUFFLE into XVEXTRINS (if possible).
+static SDValue
+lowerVECTOR_SHUFFLE_XVEXTRINS(const SDLoc &DL, ArrayRef<int> Mask, MVT VT,
+ SDValue V1, SDValue V2, SelectionDAG &DAG,
+ const LoongArchSubtarget &Subtarget) {
+ int NumElts = VT.getVectorNumElements();
+ MVT EltVT = VT.getVectorElementType();
+ MVT GRLenVT = Subtarget.getGRLenVT();
+
+ if ((int)Mask.size() != NumElts)
+ return SDValue();
+
+ auto tryLowerToExtrAndIns = [&](int Base) -> SDValue {
+ SmallVector<int> DiffPos;
+ for (int i = 0; i < NumElts; ++i) {
+ if (Mask[i] == -1)
+ continue;
+ if (Mask[i] != Base + i) {
+ DiffPos.push_back(i);
+ if (DiffPos.size() > 2)
+ return SDValue();
+ }
+ }
+
+ // Need exactly two differing element to lower into XVEXTRINS.
+ if (DiffPos.size() != 2 || DiffPos[1] != DiffPos[0] + NumElts / 2)
+ return SDValue();
+
+ // DiffMask must be in its low or high part.
+ int DiffMaskLo = Mask[DiffPos[0]];
+ int DiffMaskHi = Mask[DiffPos[1]];
+ if (!(DiffMaskLo >= 0 && DiffMaskLo < NumElts / 2) &&
+ !(DiffMaskLo >= NumElts && DiffMaskLo < NumElts + NumElts / 2))
+ return SDValue();
+ if (!(DiffMaskHi >= NumElts / 2 && DiffMaskHi < NumElts) &&
+ !(DiffMaskHi >= NumElts + NumElts / 2 && DiffMaskHi < 2 * NumElts))
+ return SDValue();
+ if (DiffMaskHi != DiffMaskLo + NumElts / 2)
+ return SDValue();
+
+ // Determine source vector and source index.
+ SDValue SrcVec = (DiffMaskLo < NumElts / 2) ? V1 : V2;
+ int SrcIdxLo =
+ (DiffMaskLo < NumElts / 2) ? DiffMaskLo : (DiffMaskLo - NumElts);
+ bool IsEltFP = EltVT.isFloatingPoint();
+
+ auto extractVal = [&](int Idx) -> SDValue {
+ SDValue Extracted =
+ DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, IsEltFP ? EltVT : GRLenVT,
+ SrcVec, DAG.getConstant(Idx, DL, GRLenVT));
+ SDValue InsertVal = Extracted;
+ if (!IsEltFP && EltVT != GRLenVT)
+ InsertVal =
+ DAG.getNode(ISD::ANY_EXTEND, DL, GRLenVT,
+ DAG.getNode(ISD::TRUNCATE, DL, EltVT, Extracted));
+ return InsertVal;
+ };
+
+ // Replace with 2*EXTRACT_VECTOR_ELT + 2*INSERT_VECTOR_ELT, it will match
+ // the patterns of XVEXTRINS in tablegen.
+ SDValue InsertLo = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, VT,
+ (Base == 0) ? V1 : V2, extractVal(SrcIdxLo),
+ DAG.getConstant(DiffPos[0], DL, GRLenVT));
+ SDValue Result = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, VT, InsertLo,
+ extractVal(SrcIdxLo + NumElts / 2),
+ DAG.getConstant(DiffPos[1], DL, GRLenVT));
+
+ return Result;
+ };
+
+ // Try [0, n-1) insertion then [n, 2n-1) insertion.
+ if (SDValue Result = tryLowerToExtrAndIns(0))
+ return Result;
+ return tryLowerToExtrAndIns(NumElts);
+}
+
/// Lower VECTOR_SHUFFLE into XVINSVE0 (if possible).
static SDValue
lowerVECTOR_SHUFFLE_XVINSVE0(const SDLoc &DL, ArrayRef<int> Mask, MVT VT,
@@ -3033,6 +3109,9 @@ static SDValue lower256BitShuffle(const SDLoc &DL, ArrayRef<int> Mask, MVT VT,
return Result;
if ((Result = lowerVECTOR_SHUFFLE_XVPICKOD(DL, Mask, VT, V1, V2, DAG)))
return Result;
+ if ((Result =
+ lowerVECTOR_SHUFFLE_XVEXTRINS(DL, Mask, VT, V1, V2, DAG, Subtarget)))
+ return Result;
if ((Result = lowerVECTOR_SHUFFLEAsShift(DL, Mask, VT, V1, V2, DAG, Subtarget,
Zeroable)))
return Result;
>From ea68d29d4b440a2e14bb9f26c87e823dce8a5c06 Mon Sep 17 00:00:00 2001
From: Qi Zhao <zhaoqi01 at loongson.cn>
Date: Tue, 21 Oct 2025 16:16:41 +0800
Subject: [PATCH 2/4] update tests
---
.../ir-instruction/shuffle-as-xvextrins.ll | 22 ++++++-------------
.../ir-instruction/shuffle-as-xvshuf4i.ll | 5 +----
2 files changed, 8 insertions(+), 19 deletions(-)
diff --git a/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/shuffle-as-xvextrins.ll b/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/shuffle-as-xvextrins.ll
index 841f383e1bff8..a5b8a7a6e98dc 100644
--- a/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/shuffle-as-xvextrins.ll
+++ b/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/shuffle-as-xvextrins.ll
@@ -8,9 +8,7 @@ define void @shufflevector_v32i8(ptr %res, ptr %a, ptr %b) nounwind {
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: xvld $xr0, $a1, 0
; CHECK-NEXT: xvld $xr1, $a2, 0
-; CHECK-NEXT: pcalau12i $a1, %pc_hi20(.LCPI0_0)
-; CHECK-NEXT: xvld $xr2, $a1, %pc_lo12(.LCPI0_0)
-; CHECK-NEXT: xvshuf.b $xr0, $xr1, $xr0, $xr2
+; CHECK-NEXT: xvextrins.b $xr0, $xr1, 240
; CHECK-NEXT: xvst $xr0, $a0, 0
; CHECK-NEXT: ret
entry:
@@ -26,10 +24,8 @@ define void @shufflevector_v16i16(ptr %res, ptr %a, ptr %b) nounwind {
; CHECK-LABEL: shufflevector_v16i16:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: xvld $xr0, $a1, 0
-; CHECK-NEXT: pcalau12i $a1, %pc_hi20(.LCPI1_0)
-; CHECK-NEXT: xvld $xr1, $a1, %pc_lo12(.LCPI1_0)
-; CHECK-NEXT: xvshuf.h $xr1, $xr0, $xr0
-; CHECK-NEXT: xvst $xr1, $a0, 0
+; CHECK-NEXT: xvextrins.h $xr0, $xr0, 66
+; CHECK-NEXT: xvst $xr0, $a0, 0
; CHECK-NEXT: ret
entry:
%va = load <16 x i16>, ptr %a
@@ -45,10 +41,8 @@ define void @shufflevector_v8i32(ptr %res, ptr %a, ptr %b) nounwind {
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: xvld $xr0, $a1, 0
; CHECK-NEXT: xvld $xr1, $a2, 0
-; CHECK-NEXT: pcalau12i $a1, %pc_hi20(.LCPI2_0)
-; CHECK-NEXT: xvld $xr2, $a1, %pc_lo12(.LCPI2_0)
-; CHECK-NEXT: xvshuf.w $xr2, $xr1, $xr0
-; CHECK-NEXT: xvst $xr2, $a0, 0
+; CHECK-NEXT: xvextrins.w $xr1, $xr0, 3
+; CHECK-NEXT: xvst $xr1, $a0, 0
; CHECK-NEXT: ret
entry:
%va = load <8 x i32>, ptr %a
@@ -64,10 +58,8 @@ define void @shufflevector_v8f32(ptr %res, ptr %a, ptr %b) nounwind {
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: xvld $xr0, $a1, 0
; CHECK-NEXT: xvld $xr1, $a2, 0
-; CHECK-NEXT: pcalau12i $a1, %pc_hi20(.LCPI3_0)
-; CHECK-NEXT: xvld $xr2, $a1, %pc_lo12(.LCPI3_0)
-; CHECK-NEXT: xvshuf.w $xr2, $xr1, $xr0
-; CHECK-NEXT: xvst $xr2, $a0, 0
+; CHECK-NEXT: xvextrins.w $xr1, $xr0, 48
+; CHECK-NEXT: xvst $xr1, $a0, 0
; CHECK-NEXT: ret
entry:
%va = load <8 x float>, ptr %a
diff --git a/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/shuffle-as-xvshuf4i.ll b/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/shuffle-as-xvshuf4i.ll
index 69437a24282b2..74976f9507525 100644
--- a/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/shuffle-as-xvshuf4i.ll
+++ b/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/shuffle-as-xvshuf4i.ll
@@ -59,10 +59,7 @@ define <8 x float> @shufflevector_xvshuf4i_v8f32(<8 x float> %a, <8 x float> %b)
define <4 x double> @shufflevector_xvshuf4i_v4f64(<4 x double> %a, <4 x double> %b) {
; CHECK-LABEL: shufflevector_xvshuf4i_v4f64:
; CHECK: # %bb.0:
-; CHECK-NEXT: pcalau12i $a0, %pc_hi20(.LCPI5_0)
-; CHECK-NEXT: xvld $xr2, $a0, %pc_lo12(.LCPI5_0)
-; CHECK-NEXT: xvshuf.d $xr2, $xr1, $xr0
-; CHECK-NEXT: xvori.b $xr0, $xr2, 0
+; CHECK-NEXT: xvextrins.d $xr0, $xr1, 0
; CHECK-NEXT: ret
%c = shufflevector <4 x double> %a, <4 x double> %b, <4 x i32> <i32 4, i32 1, i32 6, i32 3>
ret <4 x double> %c
>From f84f4445b59170fde12312448e784bc2dc7019ef Mon Sep 17 00:00:00 2001
From: Qi Zhao <zhaoqi01 at loongson.cn>
Date: Fri, 17 Apr 2026 16:05:18 +0800
Subject: [PATCH 3/4] remove redundant trunc+extend
---
.../LoongArch/LoongArchISelLowering.cpp | 44 ++++++++-----------
1 file changed, 19 insertions(+), 25 deletions(-)
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index d94df40f7db23..ebc25223c5f6d 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -2713,6 +2713,7 @@ lowerVECTOR_SHUFFLE_XVEXTRINS(const SDLoc &DL, ArrayRef<int> Mask, MVT VT,
SDValue V1, SDValue V2, SelectionDAG &DAG,
const LoongArchSubtarget &Subtarget) {
int NumElts = VT.getVectorNumElements();
+ int HalfSize = NumElts / 2;
MVT EltVT = VT.getVectorElementType();
MVT GRLenVT = Subtarget.getGRLenVT();
@@ -2732,46 +2733,39 @@ lowerVECTOR_SHUFFLE_XVEXTRINS(const SDLoc &DL, ArrayRef<int> Mask, MVT VT,
}
// Need exactly two differing element to lower into XVEXTRINS.
- if (DiffPos.size() != 2 || DiffPos[1] != DiffPos[0] + NumElts / 2)
+ if (DiffPos.size() != 2 || DiffPos[1] != DiffPos[0] + HalfSize)
return SDValue();
// DiffMask must be in its low or high part.
int DiffMaskLo = Mask[DiffPos[0]];
int DiffMaskHi = Mask[DiffPos[1]];
- if (!(DiffMaskLo >= 0 && DiffMaskLo < NumElts / 2) &&
- !(DiffMaskLo >= NumElts && DiffMaskLo < NumElts + NumElts / 2))
+ if (!(DiffMaskLo >= 0 && DiffMaskLo < HalfSize) &&
+ !(DiffMaskLo >= NumElts && DiffMaskLo < NumElts + HalfSize))
return SDValue();
- if (!(DiffMaskHi >= NumElts / 2 && DiffMaskHi < NumElts) &&
- !(DiffMaskHi >= NumElts + NumElts / 2 && DiffMaskHi < 2 * NumElts))
+ if (!(DiffMaskHi >= HalfSize && DiffMaskHi < NumElts) &&
+ !(DiffMaskHi >= NumElts + HalfSize && DiffMaskHi < 2 * NumElts))
return SDValue();
- if (DiffMaskHi != DiffMaskLo + NumElts / 2)
+ if (DiffMaskHi != DiffMaskLo + HalfSize)
return SDValue();
// Determine source vector and source index.
- SDValue SrcVec = (DiffMaskLo < NumElts / 2) ? V1 : V2;
+ SDValue SrcVec = (DiffMaskLo < HalfSize) ? V1 : V2;
int SrcIdxLo =
- (DiffMaskLo < NumElts / 2) ? DiffMaskLo : (DiffMaskLo - NumElts);
+ (DiffMaskLo < HalfSize) ? DiffMaskLo : (DiffMaskLo - NumElts);
bool IsEltFP = EltVT.isFloatingPoint();
- auto extractVal = [&](int Idx) -> SDValue {
- SDValue Extracted =
- DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, IsEltFP ? EltVT : GRLenVT,
- SrcVec, DAG.getConstant(Idx, DL, GRLenVT));
- SDValue InsertVal = Extracted;
- if (!IsEltFP && EltVT != GRLenVT)
- InsertVal =
- DAG.getNode(ISD::ANY_EXTEND, DL, GRLenVT,
- DAG.getNode(ISD::TRUNCATE, DL, EltVT, Extracted));
- return InsertVal;
- };
-
// Replace with 2*EXTRACT_VECTOR_ELT + 2*INSERT_VECTOR_ELT, it will match
// the patterns of XVEXTRINS in tablegen.
- SDValue InsertLo = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, VT,
- (Base == 0) ? V1 : V2, extractVal(SrcIdxLo),
- DAG.getConstant(DiffPos[0], DL, GRLenVT));
- SDValue Result = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, VT, InsertLo,
- extractVal(SrcIdxLo + NumElts / 2),
+ SDValue BaseVec = (Base == 0) ? V1 : V2;
+ SDValue EltLo =
+ DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, IsEltFP ? EltVT : GRLenVT,
+ SrcVec, DAG.getConstant(SrcIdxLo, DL, GRLenVT));
+ SDValue InsLo = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, VT, BaseVec, EltLo,
+ DAG.getConstant(DiffPos[0], DL, GRLenVT));
+ SDValue EltHi =
+ DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, IsEltFP ? EltVT : GRLenVT,
+ SrcVec, DAG.getConstant(SrcIdxLo + HalfSize, DL, GRLenVT));
+ SDValue Result = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, VT, InsLo, EltHi,
DAG.getConstant(DiffPos[1], DL, GRLenVT));
return Result;
>From d1e27306e9a7f04038eafa755c92f5e54371c48d Mon Sep 17 00:00:00 2001
From: Qi Zhao <zhaoqi01 at loongson.cn>
Date: Fri, 17 Apr 2026 16:56:24 +0800
Subject: [PATCH 4/4] deal with undef
---
.../LoongArch/LoongArchISelLowering.cpp | 12 +++++++
.../ir-instruction/shuffle-as-xvextrins.ll | 33 +++++++++++++++++++
2 files changed, 45 insertions(+)
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index ebc25223c5f6d..588626f4d6362 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -2733,12 +2733,24 @@ lowerVECTOR_SHUFFLE_XVEXTRINS(const SDLoc &DL, ArrayRef<int> Mask, MVT VT,
}
// Need exactly two differing element to lower into XVEXTRINS.
+ // If only one differing element, the element at a distance of
+ // HalfSize from it must be undef.
+ if (DiffPos.size() == 1) {
+ if (DiffPos[0] < HalfSize && Mask[DiffPos[0] + HalfSize] == -1)
+ DiffPos.push_back(DiffPos[0] + HalfSize);
+ else if (DiffPos[0] >= HalfSize && Mask[DiffPos[0] - HalfSize] == -1)
+ DiffPos.insert(DiffPos.begin(), DiffPos[0] - HalfSize);
+ else
+ return SDValue();
+ }
if (DiffPos.size() != 2 || DiffPos[1] != DiffPos[0] + HalfSize)
return SDValue();
// DiffMask must be in its low or high part.
int DiffMaskLo = Mask[DiffPos[0]];
int DiffMaskHi = Mask[DiffPos[1]];
+ DiffMaskLo = DiffMaskLo == -1 ? DiffMaskHi - HalfSize : DiffMaskLo;
+ DiffMaskHi = DiffMaskHi == -1 ? DiffMaskLo + HalfSize : DiffMaskHi;
if (!(DiffMaskLo >= 0 && DiffMaskLo < HalfSize) &&
!(DiffMaskLo >= NumElts && DiffMaskLo < NumElts + HalfSize))
return SDValue();
diff --git a/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/shuffle-as-xvextrins.ll b/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/shuffle-as-xvextrins.ll
index a5b8a7a6e98dc..7ace579299167 100644
--- a/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/shuffle-as-xvextrins.ll
+++ b/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/shuffle-as-xvextrins.ll
@@ -35,6 +35,22 @@ entry:
ret void
}
+;; xvextrins.h
+define void @shufflevector_v16i16_undef(ptr %res, ptr %a, ptr %b) nounwind {
+; CHECK-LABEL: shufflevector_v16i16_undef:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: xvld $xr0, $a1, 0
+; CHECK-NEXT: xvextrins.h $xr0, $xr0, 66
+; CHECK-NEXT: xvst $xr0, $a0, 0
+; CHECK-NEXT: ret
+entry:
+ %va = load <16 x i16>, ptr %a
+ %vb = load <16 x i16>, ptr %b
+ %c = shufflevector <16 x i16> %va, <16 x i16> %vb, <16 x i32> <i32 0, i32 1, i32 2, i32 3, i32 poison, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11, i32 10, i32 13, i32 14, i32 15>
+ store <16 x i16> %c, ptr %res
+ ret void
+}
+
;; xvextrins.w
define void @shufflevector_v8i32(ptr %res, ptr %a, ptr %b) nounwind {
; CHECK-LABEL: shufflevector_v8i32:
@@ -68,3 +84,20 @@ entry:
store <8 x float> %c, ptr %res
ret void
}
+
+;; xvextrins.w
+define void @shufflevector_v8f32_undef(ptr %res, ptr %a, ptr %b) nounwind {
+; CHECK-LABEL: shufflevector_v8f32_undef:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: xvld $xr0, $a1, 0
+; CHECK-NEXT: xvld $xr1, $a2, 0
+; CHECK-NEXT: xvextrins.w $xr1, $xr0, 3
+; CHECK-NEXT: xvst $xr1, $a0, 0
+; CHECK-NEXT: ret
+entry:
+ %va = load <8 x float>, ptr %a
+ %vb = load <8 x float>, ptr %b
+ %c = shufflevector <8 x float> %va, <8 x float> %vb, <8 x i32> <i32 3, i32 9, i32 10, i32 11, i32 poison, i32 13, i32 14, i32 15>
+ store <8 x float> %c, ptr %res
+ ret void
+}
More information about the llvm-commits
mailing list