[llvm] [InstCombine] Skip redundant demanded element queries in insert chains (PR #205948)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 26 13:46:56 PDT 2026
https://github.com/Chengjunp updated https://github.com/llvm/llvm-project/pull/205948
>From e2502dd151bca242556ff4720adc5da2ca12ab55 Mon Sep 17 00:00:00 2001
From: chengjunp <chengjunp at nvidia.com>
Date: Thu, 25 Jun 2026 23:17:26 +0000
Subject: [PATCH 1/3] [InstCombine] Skip redundant demanded element queries in
insert chains
---
.../InstCombine/InstCombineInternal.h | 6 ++
.../InstCombineSimplifyDemanded.cpp | 53 +++++++++++
.../InstCombine/InstCombineVectorOps.cpp | 19 ++--
.../InstCombine/vec_demanded_elts.ll | 88 +++++++++++++++++++
4 files changed, 159 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 8b759e701da60..0b3b39cc917ab 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -481,6 +481,12 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
return Sel;
}
+ /// Return true if the all-lanes demanded-elements query can be skipped for
+ /// this intermediate insertelement chain node because a bounded scan proves
+ /// it cannot simplify the chain before reaching the depth limit.
+ bool canSkipDemandedEltsInInsertChain(InsertElementInst &IE,
+ unsigned VWidth);
+
public:
/// Create and insert the idiom we use to indicate a block is unreachable
/// without having to rewrite the CFG from within InstCombine.
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index cca297a5b8dc2..76f442092929b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -37,6 +37,59 @@ static cl::opt<unsigned> SimplifyDemandedVectorEltsDepthLimit(
"Depth limit when simplifying vector instructions and their operands"),
cl::Hidden, cl::init(10));
+bool InstCombinerImpl::canSkipDemandedEltsInInsertChain(InsertElementInst &IE,
+ unsigned VWidth) {
+ unsigned DepthLimit = SimplifyDemandedVectorEltsDepthLimit;
+
+ // For narrow vectors, SimplifyDemandedVectorElts may reach the point where
+ // only the inserted lane is demanded and fold extract/insert pairs.
+ if (VWidth <= DepthLimit)
+ return false;
+
+ // Only skip intermediate chain nodes; the root still runs the full query.
+ if (!IE.hasOneUse())
+ return false;
+ auto *UserIE = dyn_cast<InsertElementInst>(IE.user_back());
+ if (!UserIE || UserIE->getOperand(0) != &IE)
+ return false;
+
+ SmallVector<unsigned, 16> SeenIndices;
+ auto HasNewIndexInRange = [&](InsertElementInst &Insert) {
+ auto *Idx = dyn_cast<ConstantInt>(Insert.getOperand(2));
+ // Let the normal SDVE path handle variable or out-of-range indices. The
+ // latter may simplify the chain and must not be passed to getZExtValue().
+ if (!Idx || Idx->getValue().uge(VWidth))
+ return false;
+
+ unsigned Index = Idx->getZExtValue();
+ if (is_contained(SeenIndices, Index))
+ return false;
+
+ SeenIndices.push_back(Index);
+ return true;
+ };
+
+ auto *Cur = &IE;
+ for (unsigned Depth = 0; Depth != DepthLimit; ++Depth) {
+ // This loop scans the same base-chain window that the SDVE query would
+ // inspect before hitting its depth limit. With distinct insert indices in
+ // that window, the all-lanes query cannot remove a dead insert; with
+ // VWidth > DepthLimit, it also cannot narrow demand to a single lane.
+ if (!HasNewIndexInRange(*Cur))
+ return false;
+
+ Value *Base = Cur->getOperand(0);
+ if (match(Base, m_Poison()))
+ return true;
+
+ Cur = dyn_cast<InsertElementInst>(Base);
+ if (!Cur || Cur->getType() != IE.getType() || !Cur->hasOneUse())
+ return false;
+ }
+
+ return true;
+}
+
/// Check to see if the specified operand of the specified instruction is a
/// constant integer. If so, check to see if there are any bits set in the
/// constant that are not demanded. If so, shrink the constant and return true.
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineVectorOps.cpp b/llvm/lib/Transforms/InstCombine/InstCombineVectorOps.cpp
index 6a3ee719db899..18293db1e7500 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineVectorOps.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineVectorOps.cpp
@@ -1835,13 +1835,18 @@ Instruction *InstCombinerImpl::visitInsertElementInst(InsertElementInst &IE) {
if (auto VecTy = dyn_cast<FixedVectorType>(VecOp->getType())) {
unsigned VWidth = VecTy->getNumElements();
- APInt PoisonElts(VWidth, 0);
- APInt AllOnesEltMask(APInt::getAllOnes(VWidth));
- if (Value *V = SimplifyDemandedVectorElts(&IE, AllOnesEltMask,
- PoisonElts)) {
- if (V != &IE)
- return replaceInstUsesWith(IE, V);
- return &IE;
+ // Avoid re-running the all-lanes demanded-elements query on intermediate
+ // insert-chain nodes when a bounded scan proves it cannot change the IR.
+ bool SkipSDVE = canSkipDemandedEltsInInsertChain(IE, VWidth);
+ if (!SkipSDVE) {
+ APInt PoisonElts(VWidth, 0);
+ APInt AllOnesEltMask(APInt::getAllOnes(VWidth));
+ if (Value *V =
+ SimplifyDemandedVectorElts(&IE, AllOnesEltMask, PoisonElts)) {
+ if (V != &IE)
+ return replaceInstUsesWith(IE, V);
+ return &IE;
+ }
}
}
diff --git a/llvm/test/Transforms/InstCombine/vec_demanded_elts.ll b/llvm/test/Transforms/InstCombine/vec_demanded_elts.ll
index 55755735a97cc..495afaa6886ff 100644
--- a/llvm/test/Transforms/InstCombine/vec_demanded_elts.ll
+++ b/llvm/test/Transforms/InstCombine/vec_demanded_elts.ll
@@ -1251,3 +1251,91 @@ define <2 x i8> @common_binop_demand_via_splat_mask_poison_3(<2 x i8> %x, <2 x i
%res = add <2 x i8> %vv, %msplat
ret <2 x i8> %res
}
+
+define <11 x i32> @wide_distinct_insert_chain(i32 %a0, i32 %a1, i32 %a2, i32 %a3, i32 %a4, i32 %a5, i32 %a6, i32 %a7, i32 %a8, i32 %a9, i32 %a10) {
+; CHECK-LABEL: @wide_distinct_insert_chain(
+; CHECK-NEXT: [[V0:%.*]] = insertelement <11 x i32> poison, i32 [[A0:%.*]], i64 0
+; CHECK-NEXT: [[V1:%.*]] = insertelement <11 x i32> [[V0]], i32 [[A1:%.*]], i64 1
+; CHECK-NEXT: [[V2:%.*]] = insertelement <11 x i32> [[V1]], i32 [[A2:%.*]], i64 2
+; CHECK-NEXT: [[V3:%.*]] = insertelement <11 x i32> [[V2]], i32 [[A3:%.*]], i64 3
+; CHECK-NEXT: [[V4:%.*]] = insertelement <11 x i32> [[V3]], i32 [[A4:%.*]], i64 4
+; CHECK-NEXT: [[V5:%.*]] = insertelement <11 x i32> [[V4]], i32 [[A5:%.*]], i64 5
+; CHECK-NEXT: [[V6:%.*]] = insertelement <11 x i32> [[V5]], i32 [[A6:%.*]], i64 6
+; CHECK-NEXT: [[V7:%.*]] = insertelement <11 x i32> [[V6]], i32 [[A7:%.*]], i64 7
+; CHECK-NEXT: [[V8:%.*]] = insertelement <11 x i32> [[V7]], i32 [[A8:%.*]], i64 8
+; CHECK-NEXT: [[V9:%.*]] = insertelement <11 x i32> [[V8]], i32 [[A9:%.*]], i64 9
+; CHECK-NEXT: [[V10:%.*]] = insertelement <11 x i32> [[V9]], i32 [[A10:%.*]], i64 10
+; CHECK-NEXT: ret <11 x i32> [[V10]]
+;
+ %v0 = insertelement <11 x i32> poison, i32 %a0, i64 0
+ %v1 = insertelement <11 x i32> %v0, i32 %a1, i64 1
+ %v2 = insertelement <11 x i32> %v1, i32 %a2, i64 2
+ %v3 = insertelement <11 x i32> %v2, i32 %a3, i64 3
+ %v4 = insertelement <11 x i32> %v3, i32 %a4, i64 4
+ %v5 = insertelement <11 x i32> %v4, i32 %a5, i64 5
+ %v6 = insertelement <11 x i32> %v5, i32 %a6, i64 6
+ %v7 = insertelement <11 x i32> %v6, i32 %a7, i64 7
+ %v8 = insertelement <11 x i32> %v7, i32 %a8, i64 8
+ %v9 = insertelement <11 x i32> %v8, i32 %a9, i64 9
+ %v10 = insertelement <11 x i32> %v9, i32 %a10, i64 10
+ ret <11 x i32> %v10
+}
+
+define <12 x i32> @wide_insert_chain_deep_duplicate(i32 %a0, i32 %a1, i32 %a2, i32 %a3, i32 %a4, i32 %a5, i32 %a6, i32 %a7, i32 %a8, i32 %a9, i32 %a10, i32 %a11) {
+; CHECK-LABEL: @wide_insert_chain_deep_duplicate(
+; CHECK-NEXT: [[V1:%.*]] = insertelement <12 x i32> poison, i32 [[A1:%.*]], i64 0
+; CHECK-NEXT: [[V2:%.*]] = insertelement <12 x i32> [[V1]], i32 [[A2:%.*]], i64 2
+; CHECK-NEXT: [[V3:%.*]] = insertelement <12 x i32> [[V2]], i32 [[A3:%.*]], i64 3
+; CHECK-NEXT: [[V4:%.*]] = insertelement <12 x i32> [[V3]], i32 [[A4:%.*]], i64 4
+; CHECK-NEXT: [[V5:%.*]] = insertelement <12 x i32> [[V4]], i32 [[A5:%.*]], i64 5
+; CHECK-NEXT: [[V6:%.*]] = insertelement <12 x i32> [[V5]], i32 [[A6:%.*]], i64 6
+; CHECK-NEXT: [[V7:%.*]] = insertelement <12 x i32> [[V6]], i32 [[A7:%.*]], i64 7
+; CHECK-NEXT: [[V8:%.*]] = insertelement <12 x i32> [[V7]], i32 [[A8:%.*]], i64 8
+; CHECK-NEXT: [[V9:%.*]] = insertelement <12 x i32> [[V8]], i32 [[A9:%.*]], i64 9
+; CHECK-NEXT: [[V10:%.*]] = insertelement <12 x i32> [[V9]], i32 [[A10:%.*]], i64 10
+; CHECK-NEXT: [[V11:%.*]] = insertelement <12 x i32> [[V10]], i32 [[A11:%.*]], i64 11
+; CHECK-NEXT: ret <12 x i32> [[V11]]
+;
+ %v0 = insertelement <12 x i32> poison, i32 %a0, i64 0
+ %v1 = insertelement <12 x i32> %v0, i32 %a1, i64 0
+ %v2 = insertelement <12 x i32> %v1, i32 %a2, i64 2
+ %v3 = insertelement <12 x i32> %v2, i32 %a3, i64 3
+ %v4 = insertelement <12 x i32> %v3, i32 %a4, i64 4
+ %v5 = insertelement <12 x i32> %v4, i32 %a5, i64 5
+ %v6 = insertelement <12 x i32> %v5, i32 %a6, i64 6
+ %v7 = insertelement <12 x i32> %v6, i32 %a7, i64 7
+ %v8 = insertelement <12 x i32> %v7, i32 %a8, i64 8
+ %v9 = insertelement <12 x i32> %v8, i32 %a9, i64 9
+ %v10 = insertelement <12 x i32> %v9, i32 %a10, i64 10
+ %v11 = insertelement <12 x i32> %v10, i32 %a11, i64 11
+ ret <12 x i32> %v11
+}
+
+define <11 x i32> @wide_insert_chain_out_of_range(i32 %a0, i32 %bad, i32 %a1, i32 %a2, i32 %a3, i32 %a4, i32 %a5, i32 %a6, i32 %a7, i32 %a8, i32 %a9, i32 %a10) {
+; CHECK-LABEL: @wide_insert_chain_out_of_range(
+; CHECK-NEXT: [[V1:%.*]] = insertelement <11 x i32> poison, i32 [[A1:%.*]], i64 1
+; CHECK-NEXT: [[V2:%.*]] = insertelement <11 x i32> [[V1]], i32 [[A2:%.*]], i64 2
+; CHECK-NEXT: [[V3:%.*]] = insertelement <11 x i32> [[V2]], i32 [[A3:%.*]], i64 3
+; CHECK-NEXT: [[V4:%.*]] = insertelement <11 x i32> [[V3]], i32 [[A4:%.*]], i64 4
+; CHECK-NEXT: [[V5:%.*]] = insertelement <11 x i32> [[V4]], i32 [[A5:%.*]], i64 5
+; CHECK-NEXT: [[V6:%.*]] = insertelement <11 x i32> [[V5]], i32 [[A6:%.*]], i64 6
+; CHECK-NEXT: [[V7:%.*]] = insertelement <11 x i32> [[V6]], i32 [[A7:%.*]], i64 7
+; CHECK-NEXT: [[V8:%.*]] = insertelement <11 x i32> [[V7]], i32 [[A8:%.*]], i64 8
+; CHECK-NEXT: [[V9:%.*]] = insertelement <11 x i32> [[V8]], i32 [[A9:%.*]], i64 9
+; CHECK-NEXT: [[V10:%.*]] = insertelement <11 x i32> [[V9]], i32 [[A10:%.*]], i64 10
+; CHECK-NEXT: ret <11 x i32> [[V10]]
+;
+ %v0 = insertelement <11 x i32> poison, i32 %a0, i64 0
+ %badins = insertelement <11 x i32> %v0, i32 %bad, i64 11
+ %v1 = insertelement <11 x i32> %badins, i32 %a1, i64 1
+ %v2 = insertelement <11 x i32> %v1, i32 %a2, i64 2
+ %v3 = insertelement <11 x i32> %v2, i32 %a3, i64 3
+ %v4 = insertelement <11 x i32> %v3, i32 %a4, i64 4
+ %v5 = insertelement <11 x i32> %v4, i32 %a5, i64 5
+ %v6 = insertelement <11 x i32> %v5, i32 %a6, i64 6
+ %v7 = insertelement <11 x i32> %v6, i32 %a7, i64 7
+ %v8 = insertelement <11 x i32> %v7, i32 %a8, i64 8
+ %v9 = insertelement <11 x i32> %v8, i32 %a9, i64 9
+ %v10 = insertelement <11 x i32> %v9, i32 %a10, i64 10
+ ret <11 x i32> %v10
+}
>From f106580b27821b195664017a07c883e79da7decb Mon Sep 17 00:00:00 2001
From: chengjunp <chengjunp at nvidia.com>
Date: Thu, 25 Jun 2026 23:28:50 +0000
Subject: [PATCH 2/3] [InstCombine] Format insert chain helper declaration
---
llvm/lib/Transforms/InstCombine/InstCombineInternal.h | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 0b3b39cc917ab..54929aeecde54 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -484,8 +484,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
/// Return true if the all-lanes demanded-elements query can be skipped for
/// this intermediate insertelement chain node because a bounded scan proves
/// it cannot simplify the chain before reaching the depth limit.
- bool canSkipDemandedEltsInInsertChain(InsertElementInst &IE,
- unsigned VWidth);
+ bool canSkipDemandedEltsInInsertChain(InsertElementInst &IE, unsigned VWidth);
public:
/// Create and insert the idiom we use to indicate a block is unreachable
>From ec808bd55d0134a80e63a6856de85036f66cdab2 Mon Sep 17 00:00:00 2001
From: chengjunp <chengjunp at nvidia.com>
Date: Fri, 26 Jun 2026 20:44:48 +0000
Subject: [PATCH 3/3] [InstCombine] Move insert chain SDVE skip into demanded
elements
---
.../InstCombine/InstCombineInternal.h | 5 -----
.../InstCombineSimplifyDemanded.cpp | 12 +++++++++---
.../InstCombine/InstCombineVectorOps.cpp | 19 +++++++------------
3 files changed, 16 insertions(+), 20 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 54929aeecde54..8b759e701da60 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -481,11 +481,6 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
return Sel;
}
- /// Return true if the all-lanes demanded-elements query can be skipped for
- /// this intermediate insertelement chain node because a bounded scan proves
- /// it cannot simplify the chain before reaching the depth limit.
- bool canSkipDemandedEltsInInsertChain(InsertElementInst &IE, unsigned VWidth);
-
public:
/// Create and insert the idiom we use to indicate a block is unreachable
/// without having to rewrite the CFG from within InstCombine.
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 76f442092929b..a57ef42171cf3 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -37,8 +37,8 @@ static cl::opt<unsigned> SimplifyDemandedVectorEltsDepthLimit(
"Depth limit when simplifying vector instructions and their operands"),
cl::Hidden, cl::init(10));
-bool InstCombinerImpl::canSkipDemandedEltsInInsertChain(InsertElementInst &IE,
- unsigned VWidth) {
+static bool canSkipDemandedEltsInInsertChain(InsertElementInst &IE,
+ unsigned VWidth) {
unsigned DepthLimit = SimplifyDemandedVectorEltsDepthLimit;
// For narrow vectors, SimplifyDemandedVectorElts may reach the point where
@@ -46,7 +46,8 @@ bool InstCombinerImpl::canSkipDemandedEltsInInsertChain(InsertElementInst &IE,
if (VWidth <= DepthLimit)
return false;
- // Only skip intermediate chain nodes; the root still runs the full query.
+ // Only skip chain nodes that feed another insertelement; the final chain root
+ // still runs the full query.
if (!IE.hasOneUse())
return false;
auto *UserIE = dyn_cast<InsertElementInst>(IE.user_back());
@@ -1660,6 +1661,11 @@ Value *InstCombinerImpl::SimplifyDemandedVectorElts(Value *V,
break;
}
case Instruction::InsertElement: {
+ auto *IE = cast<InsertElementInst>(I);
+ if (Depth == 0 && DemandedElts.isAllOnes() &&
+ canSkipDemandedEltsInInsertChain(*IE, VWidth))
+ return nullptr;
+
// If this is a variable index, we don't know which element it overwrites.
// demand exactly the same input as we produce.
ConstantInt *Idx = dyn_cast<ConstantInt>(I->getOperand(2));
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineVectorOps.cpp b/llvm/lib/Transforms/InstCombine/InstCombineVectorOps.cpp
index 18293db1e7500..6a3ee719db899 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineVectorOps.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineVectorOps.cpp
@@ -1835,18 +1835,13 @@ Instruction *InstCombinerImpl::visitInsertElementInst(InsertElementInst &IE) {
if (auto VecTy = dyn_cast<FixedVectorType>(VecOp->getType())) {
unsigned VWidth = VecTy->getNumElements();
- // Avoid re-running the all-lanes demanded-elements query on intermediate
- // insert-chain nodes when a bounded scan proves it cannot change the IR.
- bool SkipSDVE = canSkipDemandedEltsInInsertChain(IE, VWidth);
- if (!SkipSDVE) {
- APInt PoisonElts(VWidth, 0);
- APInt AllOnesEltMask(APInt::getAllOnes(VWidth));
- if (Value *V =
- SimplifyDemandedVectorElts(&IE, AllOnesEltMask, PoisonElts)) {
- if (V != &IE)
- return replaceInstUsesWith(IE, V);
- return &IE;
- }
+ APInt PoisonElts(VWidth, 0);
+ APInt AllOnesEltMask(APInt::getAllOnes(VWidth));
+ if (Value *V = SimplifyDemandedVectorElts(&IE, AllOnesEltMask,
+ PoisonElts)) {
+ if (V != &IE)
+ return replaceInstUsesWith(IE, V);
+ return &IE;
}
}
More information about the llvm-commits
mailing list