[llvm] Avoid making vector more poisonous in visitCONCAT_VECTORS (PR #223492)

via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 14 11:52:48 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-selectiondag

Author: Björn Pettersson (bjope)

<details>
<summary>Changes</summary>

Fix a miscompile bug found in visitCONCAT_VECTORS.

In commit 544c300f43961 (from January 25 2026) the DAGCombiner started to use poison instead of undef when rewriting a CONCAT_VECTORS as a BUILD_VECTOR. Problem was that it was using poison also for elements that were undef before the fold, making the result more poisonous. That caused miscompiles as reported in PR #<!-- -->222857.

The fix is to make sure we only use a poison value as operand in the new BUILD_VECTOR if the input to CONCAT_VECTORS also is poison.

Same kind of fixup is done for combineConcatVectorOfConcatVectors and combineConcatVectorOfShuffleAndItsOperands (both helpers used by visitCONCAT_VECTORS). Those funtions were also modified in commit 544c300f43961, all without any motivating test cases showing that it is correct to use poison. I do not have any test cases proving that those two helpers also could cause miscompiles, but neither that the rewrites was correct after commit 544c300f43.

Fixes https://github.com/llvm/llvm-project/issues/222857

Change-Id: I43afc6fbd467f0fc571f2fdfda7cd1b9fadfdbac

---
Full diff: https://github.com/llvm/llvm-project/pull/223492.diff


2 Files Affected:

- (modified) llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp (+8-3) 
- (added) llvm/test/CodeGen/X86/dagcombine-concat-undef-vs-poison.ll (+49) 


``````````diff
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index a829d7a34d5a6..87671aba39029 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -27243,7 +27243,9 @@ static SDValue combineConcatVectorOfConcatVectors(SDNode *N,
   SmallVector<SDValue> ConcatOps;
   for (const SDValue &Op : N->ops()) {
     if (Op.isUndef()) {
-      ConcatOps.append(FirstConcat->getNumOperands(), DAG.getPOISON(SubVT));
+      SDValue Fill = Op.getOpcode() == ISD::POISON ? DAG.getPOISON(SubVT)
+                                                   : DAG.getUNDEF(SubVT);
+      ConcatOps.append(FirstConcat->getNumOperands(), Fill);
       continue;
     }
     ConcatOps.append(Op->op_begin(), Op->op_end());
@@ -27480,7 +27482,8 @@ static SDValue combineConcatVectorOfShuffleAndItsOperands(
     SDValue ShufOp = std::get<0>(I);
     SDValue &NewShufOp = std::get<1>(I);
     if (ShufOp.isUndef())
-      NewShufOp = DAG.getPOISON(VT);
+      NewShufOp = ShufOp.getOpcode() == ISD::POISON ? DAG.getPOISON(VT)
+                                                    : DAG.getUNDEF(VT);
     else {
       SmallVector<SDValue, 2> ShufOpParts(N->getNumOperands(),
                                           DAG.getPOISON(OpVT));
@@ -27703,7 +27706,9 @@ SDValue DAGCombiner::visitCONCAT_VECTORS(SDNode *N) {
       unsigned NumElts = OpVT.getVectorNumElements();
 
       if (Op.isUndef())
-        Opnds.append(NumElts, DAG.getPOISON(MinVT));
+        Opnds.append(NumElts, Op.getOpcode() == ISD::POISON
+                                  ? DAG.getPOISON(MinVT)
+                                  : DAG.getUNDEF(MinVT));
 
       if (ISD::BUILD_VECTOR == Op.getOpcode()) {
         if (SVT.isFloatingPoint()) {
diff --git a/llvm/test/CodeGen/X86/dagcombine-concat-undef-vs-poison.ll b/llvm/test/CodeGen/X86/dagcombine-concat-undef-vs-poison.ll
new file mode 100644
index 0000000000000..8d0dc87ab3561
--- /dev/null
+++ b/llvm/test/CodeGen/X86/dagcombine-concat-undef-vs-poison.ll
@@ -0,0 +1,49 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=i686-unknown -mcpu=penryn | FileCheck %s
+
+; This test case started to miscompile after commit 544c300f4396119bf5a2ea4239d32774908a882d
+; Given this initial DAG:
+;
+;            t5: v2i32 = BUILD_VECTOR Constant:i32<0>, Constant:i32<0>
+;           t7: v2i32 = insert_vector_elt t5, Constant:i32<1>, Constant:i32<1>
+;         t9: v4i32 = concat_vectors t7, undef:v2i32
+;         t11: v4i32 = BUILD_VECTOR Constant:i32<0>, Constant:i32<0>, Constant:i32<-1>, Constant:i32<-1>
+;       t12: v4i32 = or t9, t11
+;
+; Then the fault was that the concat_vectors was incorrectly combined into
+;   v4i32 = BUILD_VECTOR Constant:i32<0>, Constant:i32<1>, poison:i32, poison:i32
+; making the end result poison for vector indices 2 and 3.
+;
+define void @bbi_120753_undef(ptr %p) {
+; CHECK-LABEL: bbi_120753_undef:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; CHECK-NEXT:    movaps {{.*#+}} xmm0 = [0,1,4294967295,4294967295]
+; CHECK-NEXT:    movaps %xmm0, (%eax)
+; CHECK-NEXT:    retl
+entry:
+  %0 = insertelement <2 x i32> zeroinitializer, i32 1, i32 1
+  %1 = shufflevector <2 x i32> %0, <2 x i32> undef, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+  %2 = or <4 x i32> %1, <i32 0, i32 0, i32 -1, i32 -1>
+  store <4 x i32> %2, ptr %p
+  ret void
+}
+
+; Same test as above, but using poison in the shufflevector.
+; This test is included to show that we actually skip storing the last
+; two lanes as result still is poison after the or operation.
+;
+define void @bbi_120753_poison(ptr %p) nounwind {
+; CHECK-LABEL: bbi_120753_poison:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; CHECK-NEXT:    movsd {{.*#+}} xmm0 = [0,1,0,0]
+; CHECK-NEXT:    movaps %xmm0, (%eax)
+; CHECK-NEXT:    retl
+entry:
+  %0 = insertelement <2 x i32> zeroinitializer, i32 1, i32 1
+  %1 = shufflevector <2 x i32> %0, <2 x i32> poison, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+  %2 = or <4 x i32> %1, <i32 0, i32 0, i32 -1, i32 -1>
+  store <4 x i32> %2, ptr %p
+  ret void
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/223492


More information about the llvm-commits mailing list