[llvm] [AggressiveInstCombine] Bail out if irreducible uses exist (PR #215573)

Hongyu Chen via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 11 07:12:58 PDT 2026


https://github.com/XChy created https://github.com/llvm/llvm-project/pull/215573

Fixes #213688.
For the case below:
```llvm
define i8 @insert_index_is_reduced_value() {
  %cast = trunc i64 0 to i32
  %vecins = insertelement <1 x i32> zeroinitializer, i32 %cast, i32 %cast
  %vecext = extractelement <1 x i32> %vecins, i32 0
  %trunc = trunc i32 %vecext to i8
  ret i8 %trunc
}
```
We do not currently consider the index operand of insertelement reducible. So `%vecins = insertelement <1 x i32> zeroinitializer, i32 %cast, i32 %cast` cannot be reduced without duplicating `%cast = trunc i64 0 to i32`. In this case, we should reject the reduction.

Assisted-by: Codex

>From 3bc7f05813be8fb49e2f25cddf97b43b8498b2d0 Mon Sep 17 00:00:00 2001
From: XChy <xxs_chy at outlook.com>
Date: Tue, 11 Aug 2026 22:01:31 +0800
Subject: [PATCH] [AggressiveInstCombine] Bail out if irreducible uses exist

---
 .../TruncInstCombine.cpp                      | 44 ++++++++++---------
 .../trunc_vector_instrs.ll                    | 16 +++++++
 2 files changed, 40 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/Transforms/AggressiveInstCombine/TruncInstCombine.cpp b/llvm/lib/Transforms/AggressiveInstCombine/TruncInstCombine.cpp
index 9150b58d0acf1..e07012938e09c 100644
--- a/llvm/lib/Transforms/AggressiveInstCombine/TruncInstCombine.cpp
+++ b/llvm/lib/Transforms/AggressiveInstCombine/TruncInstCombine.cpp
@@ -43,9 +43,8 @@ STATISTIC(NumExprsReduced, "Number of truncations eliminated by reducing bit "
 STATISTIC(NumInstrsReduced,
           "Number of instructions whose bit width was reduced");
 
-/// Given an instruction and a container, it fills all the relevant operands of
-/// that instruction, with respect to the Trunc expression graph optimizaton.
-static void getRelevantOperands(Instruction *I, SmallVectorImpl<Value *> &Ops) {
+/// Return whether operand \p OpNo of \p I is reducible.
+static bool isRelevantOperand(const Instruction *I, unsigned OpNo) {
   unsigned Opc = I->getOpcode();
   switch (Opc) {
   case Instruction::Trunc:
@@ -53,7 +52,7 @@ static void getRelevantOperands(Instruction *I, SmallVectorImpl<Value *> &Ops) {
   case Instruction::SExt:
     // These CastInst are considered leaves of the evaluated expression, thus,
     // their operands are not relevent.
-    break;
+    return false;
   case Instruction::Add:
   case Instruction::Sub:
   case Instruction::Mul:
@@ -65,25 +64,28 @@ static void getRelevantOperands(Instruction *I, SmallVectorImpl<Value *> &Ops) {
   case Instruction::AShr:
   case Instruction::UDiv:
   case Instruction::URem:
+    return true;
   case Instruction::InsertElement:
-    Ops.push_back(I->getOperand(0));
-    Ops.push_back(I->getOperand(1));
-    break;
+    return OpNo < 2;
   case Instruction::ExtractElement:
-    Ops.push_back(I->getOperand(0));
-    break;
+    return OpNo == 0;
   case Instruction::Select:
-    Ops.push_back(I->getOperand(1));
-    Ops.push_back(I->getOperand(2));
-    break;
+    return OpNo != 0;
   case Instruction::PHI:
-    llvm::append_range(Ops, cast<PHINode>(I)->incoming_values());
-    break;
+    return true;
   default:
     llvm_unreachable("Unreachable!");
   }
 }
 
+/// Given an instruction and a container, it fills all the relevant operands of
+/// that instruction, with respect to the Trunc expression graph optimizaton.
+static void getRelevantOperands(Instruction *I, SmallVectorImpl<Value *> &Ops) {
+  for (Use &Op : I->operands())
+    if (isRelevantOperand(I, Op.getOperandNo()))
+      Ops.push_back(Op.get());
+}
+
 bool TruncInstCombine::buildTruncExpressionGraph() {
   SmallVector<Value *, 8> Worklist;
   SmallVector<Instruction *, 8> Stack;
@@ -267,18 +269,20 @@ Type *TruncInstCombine::getBestTruncatedType() {
     return nullptr;
 
   // We don't want to duplicate instructions, which isn't profitable. Thus, we
-  // can't shrink something that has multiple users, unless all users are
-  // post-dominated by the trunc instruction, i.e., were visited during the
-  // expression evaluation.
+  // can't shrink something that has multiple uses, unless all uses can be
+  // reduced and all users are post-dominated by the trunc instruction,
+  // i.e., were visited during the expression evaluation.
   unsigned DesiredBitWidth = 0;
   for (auto Itr : InstInfoMap) {
     Instruction *I = Itr.first;
     if (I->hasOneUse())
       continue;
     bool IsExtInst = (isa<ZExtInst>(I) || isa<SExtInst>(I));
-    for (auto *U : I->users())
-      if (auto *UI = dyn_cast<Instruction>(U))
-        if (UI != CurrentTruncInst && !InstInfoMap.count(UI)) {
+    for (Use &U : I->uses())
+      if (auto *UI = dyn_cast<Instruction>(U.getUser()))
+        if (UI != CurrentTruncInst &&
+            (!InstInfoMap.count(UI) ||
+             !isRelevantOperand(UI, U.getOperandNo()))) {
           if (!IsExtInst)
             return nullptr;
           // If this is an extension from the dest type, we can eliminate it,
diff --git a/llvm/test/Transforms/AggressiveInstCombine/trunc_vector_instrs.ll b/llvm/test/Transforms/AggressiveInstCombine/trunc_vector_instrs.ll
index d431c8ebaa56a..fd84dce33c092 100644
--- a/llvm/test/Transforms/AggressiveInstCombine/trunc_vector_instrs.ll
+++ b/llvm/test/Transforms/AggressiveInstCombine/trunc_vector_instrs.ll
@@ -87,3 +87,19 @@ define <2 x i16> @extract_mul_insert(<2 x i8> %x) {
   %trunc = trunc <2 x i32> %insr to <2 x i16>
   ret <2 x i16> %trunc
 }
+
+; The index is not part of the expression graph and must remain available to
+; the rebuilt insertelement.
+define i8 @insert_index_is_reduced_value() {
+; CHECK-LABEL: @insert_index_is_reduced_value(
+; CHECK-NEXT:    [[VECINS:%.*]] = insertelement <1 x i32> zeroinitializer, i32 0, i32 0
+; CHECK-NEXT:    [[VECEXT:%.*]] = extractelement <1 x i32> [[VECINS]], i32 0
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i32 [[VECEXT]] to i8
+; CHECK-NEXT:    ret i8 [[TRUNC]]
+;
+  %cast = trunc i64 0 to i32
+  %vecins = insertelement <1 x i32> zeroinitializer, i32 %cast, i32 %cast
+  %vecext = extractelement <1 x i32> %vecins, i32 0
+  %trunc = trunc i32 %vecext to i8
+  ret i8 %trunc
+}



More information about the llvm-commits mailing list