[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