[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