[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