[llvm] [MIPS] fix assert on `undef` first mask element (PR #203390)
Folkert de Vries via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 11 13:47:05 PDT 2026
https://github.com/folkertdev updated https://github.com/llvm/llvm-project/pull/203390
>From f4320c37c7d6412a4cb745fe5a7791073cda23be Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Thu, 11 Jun 2026 22:32:31 +0200
Subject: [PATCH] [MIPS] fix assert on `undef` first mask element
---
llvm/lib/Target/Mips/MipsSEISelLowering.cpp | 24 +--
.../msa/shuffle-undef-first-mask-element.ll | 164 ++++++++++++++++++
2 files changed, 178 insertions(+), 10 deletions(-)
create mode 100644 llvm/test/CodeGen/Mips/msa/shuffle-undef-first-mask-element.ll
diff --git a/llvm/lib/Target/Mips/MipsSEISelLowering.cpp b/llvm/lib/Target/Mips/MipsSEISelLowering.cpp
index 7c49838732032..3be9a3d217a83 100644
--- a/llvm/lib/Target/Mips/MipsSEISelLowering.cpp
+++ b/llvm/lib/Target/Mips/MipsSEISelLowering.cpp
@@ -3132,11 +3132,8 @@ static SDValue lowerVECTOR_SHUFFLE_VSHF(SDValue Op, EVT ResTy,
SDLoc DL(Op);
int ResTyNumElts = ResTy.getVectorNumElements();
- assert(Indices[0] >= 0 &&
- "shuffle mask starts with an UNDEF, which is not expected");
-
for (int i = 0; i < ResTyNumElts; ++i) {
- // Idx == -1 means UNDEF
+ // Idx == -1 means UNDEF/poison
int Idx = Indices[i];
if (0 <= Idx && Idx < ResTyNumElts)
@@ -3144,15 +3141,22 @@ static SDValue lowerVECTOR_SHUFFLE_VSHF(SDValue Op, EVT ResTy,
if (ResTyNumElts <= Idx && Idx < ResTyNumElts * 2)
Using2ndVec = true;
}
+
+ // Find the first non-undef index use as a default when there is a leading
+ // UNDEF/poison.
int LastValidIndex = 0;
- for (size_t i = 0; i < Indices.size(); i++) {
- int Idx = Indices[i];
- if (Idx < 0) {
- // Continue using splati index or use the last valid index.
- Idx = isSPLATI ? Indices[0] : LastValidIndex;
- } else {
+ for (int Idx : Indices)
+ if (Idx >= 0) {
LastValidIndex = Idx;
+ break;
}
+
+ for (int Idx : Indices) {
+ if (Idx < 0)
+ Idx = LastValidIndex;
+ else
+ // Continue using splati index or use the last valid index.
+ LastValidIndex = isSPLATI ? LastValidIndex : Idx;
Ops.push_back(DAG.getTargetConstant(Idx, DL, MaskEltTy));
}
diff --git a/llvm/test/CodeGen/Mips/msa/shuffle-undef-first-mask-element.ll b/llvm/test/CodeGen/Mips/msa/shuffle-undef-first-mask-element.ll
new file mode 100644
index 0000000000000..ef3776935e639
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/msa/shuffle-undef-first-mask-element.ll
@@ -0,0 +1,164 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=mipsisa64r6el-unknown-linux-gnuabi64 -O3 < %s | FileCheck %s --check-prefixes=NOMSA
+; RUN: llc -mtriple=mipsisa64r6el-unknown-linux-gnuabi64 -mattr=+msa -O3 < %s | FileCheck %s --check-prefixes=MSA
+
+; Check that undef/poison (leading) lanes are filled with a valid index.
+; The lowerVECTOR_SHUFFLE_VSHF function used to crash on such inputs,
+; but leading undef/poison can be created by DAGCombiner.
+
+define void @shuffle_poison_first_mask_element(ptr %res, ptr %a, ptr %b) {
+; NOMSA-LABEL: shuffle_poison_first_mask_element:
+; NOMSA: # %bb.0:
+; NOMSA-NEXT: lw $1, 0($6)
+; NOMSA-NEXT: sw $1, 12($4)
+; NOMSA-NEXT: lw $1, 0($5)
+; NOMSA-NEXT: jr $ra
+; NOMSA-NEXT: sw $1, 8($4)
+;
+; MSA-LABEL: shuffle_poison_first_mask_element:
+; MSA: # %bb.0:
+; MSA-NEXT: lui $1, %highest(.LCPI0_0)
+; MSA-NEXT: ld.w $w0, 0($5)
+; MSA-NEXT: ld.w $w1, 0($6)
+; MSA-NEXT: daddiu $1, $1, %higher(.LCPI0_0)
+; MSA-NEXT: dsll $1, $1, 16
+; MSA-NEXT: daddiu $1, $1, %hi(.LCPI0_0)
+; MSA-NEXT: dsll $1, $1, 16
+; MSA-NEXT: daddiu $1, $1, %lo(.LCPI0_0)
+; MSA-NEXT: ld.w $w2, 0($1)
+; MSA-NEXT: vshf.w $w2, $w1, $w0
+; MSA-NEXT: jr $ra
+; MSA-NEXT: st.w $w2, 0($4)
+ %va = load <4 x i32>, ptr %a
+ %vb = load <4 x i32>, ptr %b
+ %shuf = shufflevector <4 x i32> %va, <4 x i32> %vb, <4 x i32> <i32 poison, i32 poison, i32 0, i32 4>
+ store <4 x i32> %shuf, ptr %res
+ ret void
+}
+
+; For this example the DAGCombiner would introduce a mask with a leading poison index.
+define i4 @inserted_poison_first_mask_element(<4 x i128> %0) {
+; NOMSA-LABEL: inserted_poison_first_mask_element:
+; NOMSA: # %bb.0: # %start
+; NOMSA-NEXT: or $1, $8, $9
+; NOMSA-NEXT: or $3, $10, $11
+; NOMSA-NEXT: or $2, $4, $5
+; NOMSA-NEXT: sltu $1, $zero, $1
+; NOMSA-NEXT: sltu $3, $zero, $3
+; NOMSA-NEXT: sltu $2, $zero, $2
+; NOMSA-NEXT: sll $1, $1, 2
+; NOMSA-NEXT: sll $3, $3, 3
+; NOMSA-NEXT: or $1, $3, $1
+; NOMSA-NEXT: or $3, $6, $7
+; NOMSA-NEXT: sltu $3, $zero, $3
+; NOMSA-NEXT: sll $3, $3, 1
+; NOMSA-NEXT: or $2, $2, $3
+; NOMSA-NEXT: jr $ra
+; NOMSA-NEXT: or $2, $2, $1
+;
+; MSA-LABEL: inserted_poison_first_mask_element:
+; MSA: # %bb.0: # %start
+; MSA-NEXT: daddiu $sp, $sp, -16
+; MSA-NEXT: .cfi_def_cfa_offset 16
+; MSA-NEXT: or $1, $10, $11
+; MSA-NEXT: or $2, $8, $9
+; MSA-NEXT: or $3, $4, $5
+; MSA-NEXT: or $4, $6, $7
+; MSA-NEXT: sltu $1, $zero, $1
+; MSA-NEXT: sltu $2, $zero, $2
+; MSA-NEXT: sltu $3, $zero, $3
+; MSA-NEXT: sltu $4, $zero, $4
+; MSA-NEXT: dext $1, $1, 0, 32
+; MSA-NEXT: dext $2, $2, 0, 32
+; MSA-NEXT: dext $3, $3, 0, 32
+; MSA-NEXT: dext $4, $4, 0, 32
+; MSA-NEXT: dnegu $1, $1
+; MSA-NEXT: dnegu $2, $2
+; MSA-NEXT: dnegu $3, $3
+; MSA-NEXT: dnegu $4, $4
+; MSA-NEXT: fill.d $w1, $1
+; MSA-NEXT: lui $1, %highest(.LCPI1_0)
+; MSA-NEXT: fill.d $w0, $2
+; MSA-NEXT: daddiu $1, $1, %higher(.LCPI1_0)
+; MSA-NEXT: dsll $1, $1, 16
+; MSA-NEXT: daddiu $1, $1, %hi(.LCPI1_0)
+; MSA-NEXT: dsll $1, $1, 16
+; MSA-NEXT: daddiu $1, $1, %lo(.LCPI1_0)
+; MSA-NEXT: ld.w $w2, 0($1)
+; MSA-NEXT: vshf.w $w2, $w1, $w0
+; MSA-NEXT: fill.d $w0, $3
+; MSA-NEXT: copy_s.w $3, $w0[0]
+; MSA-NEXT: fill.d $w0, $4
+; MSA-NEXT: copy_s.w $2, $w2[2]
+; MSA-NEXT: copy_s.w $1, $w2[3]
+; MSA-NEXT: splati.w $w0, $w0[0]
+; MSA-NEXT: andi $2, $2, 1
+; MSA-NEXT: sll $1, $1, 3
+; MSA-NEXT: sll $2, $2, 2
+; MSA-NEXT: copy_s.w $4, $w0[1]
+; MSA-NEXT: andi $4, $4, 1
+; MSA-NEXT: sll $4, $4, 1
+; MSA-NEXT: subu $3, $4, $3
+; MSA-NEXT: or $2, $3, $2
+; MSA-NEXT: or $1, $2, $1
+; MSA-NEXT: andi $2, $1, 15
+; MSA-NEXT: jr $ra
+; MSA-NEXT: daddiu $sp, $sp, 16
+start:
+ %1 = icmp ne <4 x i128> %0, zeroinitializer
+ %2 = bitcast <4 x i1> %1 to i4
+ ret i4 %2
+}
+
+define void @splati_with_poison(ptr %res, ptr %a) {
+; NOMSA-LABEL: splati_with_poison:
+; NOMSA: # %bb.0:
+; NOMSA-NEXT: lw $1, 4($5)
+; NOMSA-NEXT: sw $1, 12($4)
+; NOMSA-NEXT: sw $1, 8($4)
+; NOMSA-NEXT: jr $ra
+; NOMSA-NEXT: sw $1, 0($4)
+;
+; MSA-LABEL: splati_with_poison:
+; MSA: # %bb.0:
+; MSA-NEXT: ld.w $w0, 0($5)
+; MSA-NEXT: splati.w $w0, $w0[1]
+; MSA-NEXT: jr $ra
+; MSA-NEXT: st.w $w0, 0($4)
+ %va = load <4 x i32>, ptr %a
+ %shuf = shufflevector <4 x i32> %va, <4 x i32> poison, <4 x i32> <i32 1, i32 poison, i32 1, i32 1>
+ store <4 x i32> %shuf, ptr %res
+ ret void
+}
+
+define void @shuffle_poison_middle(ptr %res, ptr %a, ptr %b) {
+; NOMSA-LABEL: shuffle_poison_middle:
+; NOMSA: # %bb.0:
+; NOMSA-NEXT: lw $1, 0($6)
+; NOMSA-NEXT: sw $1, 12($4)
+; NOMSA-NEXT: ld $1, 4($5)
+; NOMSA-NEXT: sd $1, 4($4)
+; NOMSA-NEXT: lw $1, 0($5)
+; NOMSA-NEXT: jr $ra
+; NOMSA-NEXT: sw $1, 0($4)
+;
+; MSA-LABEL: shuffle_poison_middle:
+; MSA: # %bb.0:
+; MSA-NEXT: lui $1, %highest(.LCPI3_0)
+; MSA-NEXT: ld.w $w0, 0($5)
+; MSA-NEXT: ld.w $w1, 0($6)
+; MSA-NEXT: daddiu $1, $1, %higher(.LCPI3_0)
+; MSA-NEXT: dsll $1, $1, 16
+; MSA-NEXT: daddiu $1, $1, %hi(.LCPI3_0)
+; MSA-NEXT: dsll $1, $1, 16
+; MSA-NEXT: daddiu $1, $1, %lo(.LCPI3_0)
+; MSA-NEXT: ld.w $w2, 0($1)
+; MSA-NEXT: vshf.w $w2, $w1, $w0
+; MSA-NEXT: jr $ra
+; MSA-NEXT: st.w $w2, 0($4)
+ %va = load <4 x i32>, ptr %a
+ %vb = load <4 x i32>, ptr %b
+ %shuf = shufflevector <4 x i32> %va, <4 x i32> %vb, <4 x i32> <i32 0, i32 poison, i32 2, i32 4>
+ store <4 x i32> %shuf, ptr %res
+ ret void
+}
More information about the llvm-commits
mailing list