[llvm] [InstCombine] Fold inner select (PR #226371)
Henry Jiang via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 24 23:18:01 PDT 2026
https://github.com/mustartt created https://github.com/llvm/llvm-project/pull/226371
None
>From e279d9a08c0083ac729c1f6454f30b999ddbec67 Mon Sep 17 00:00:00 2001
From: Henry Jiang <henry_jiang2 at apple.com>
Date: Thu, 24 Sep 2026 23:15:37 -0700
Subject: [PATCH] Limited InstCombine fold
---
.../InstCombine/InstCombineInternal.h | 1 +
.../InstCombine/InstCombineSelect.cpp | 41 ++++
.../select-arm-same-cond-select.ll | 203 ++++++++++++++++++
llvm/test/Transforms/InstCombine/select.ll | 5 +-
4 files changed, 247 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/Transforms/InstCombine/select-arm-same-cond-select.ll
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index f2549bad3a8523..8df6233e3a05c6 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -831,6 +831,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
bool replaceInInstruction(Value *V, Value *Old, Value *New,
unsigned Depth = 0);
+ Instruction *foldSelectArmOperandsOnSameCond(SelectInst &SI, bool TrueArm);
Value *insertRangeTest(Value *V, const APInt &Lo, const APInt &Hi,
bool isSigned, bool Inside);
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index a4061bbd607707..ab2d8a9e02431b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1685,6 +1685,40 @@ bool InstCombinerImpl::replaceInInstruction(Value *V, Value *Old, Value *New,
return Changed;
}
+Instruction *InstCombinerImpl::foldSelectArmOperandsOnSameCond(SelectInst &SI,
+ bool TrueArm) {
+ Value *Cond = SI.getCondition();
+ unsigned ArmIdx = TrueArm ? 1 : 2;
+ auto *I = dyn_cast<Instruction>(SI.getOperand(ArmIdx));
+ if (!I || isa<PHINode>(I) ||
+ !isSafeToSpeculativelyExecuteWithVariableReplaced(I))
+ return nullptr;
+
+ if (Cond->getType()->isVectorTy() && !isNotCrossLaneOperation(I))
+ return nullptr;
+
+ auto GetPickedValue = [&](Value *V) -> Value * {
+ auto *Inner = dyn_cast<SelectInst>(V);
+ if (!Inner || Inner->getCondition() != Cond)
+ return nullptr;
+ return TrueArm ? Inner->getTrueValue() : Inner->getFalseValue();
+ };
+ if (none_of(I->operands(), [&](Value *V) { return GetPickedValue(V); }))
+ return nullptr;
+
+ for (Use &U : I->uses()) {
+ auto *User = dyn_cast<SelectInst>(U.getUser());
+ if (!User || User->getCondition() != Cond || U.getOperandNo() != ArmIdx)
+ return nullptr;
+ }
+
+ for (Use &Op : I->operands())
+ if (Value *V = GetPickedValue(Op))
+ replaceUse(Op, V);
+ Worklist.add(I);
+ return &SI;
+}
+
/// If we have a select with an equality comparison, then we know the value in
/// one of the arms of the select. See if substituting this value into an arm
/// and simplifying the result yields the same value as the other arm.
@@ -4648,6 +4682,13 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
return &SI;
}
+ if (!isa<Constant>(CondVal)) {
+ if (Instruction *R = foldSelectArmOperandsOnSameCond(SI, /*TrueArm=*/true))
+ return R;
+ if (Instruction *R = foldSelectArmOperandsOnSameCond(SI, /*TrueArm=*/false))
+ return R;
+ }
+
if (Instruction *R = foldSelectOfBools(SI))
return R;
diff --git a/llvm/test/Transforms/InstCombine/select-arm-same-cond-select.ll b/llvm/test/Transforms/InstCombine/select-arm-same-cond-select.ll
new file mode 100644
index 00000000000000..a557336565b599
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/select-arm-same-cond-select.ll
@@ -0,0 +1,203 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @use(i32)
+
+define { i32, i32 } @arm_multiuse_same_side(i1 %c, i32 %a, i32 %b, i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @arm_multiuse_same_side(
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]
+; CHECK-NEXT: call void @use(i32 [[S]])
+; CHECK-NEXT: [[V:%.*]] = mul i32 [[A]], [[X:%.*]]
+; CHECK-NEXT: [[R0:%.*]] = select i1 [[C]], i32 [[V]], i32 [[Y:%.*]]
+; CHECK-NEXT: [[R1:%.*]] = select i1 [[C]], i32 [[V]], i32 [[Z:%.*]]
+; CHECK-NEXT: [[I0:%.*]] = insertvalue { i32, i32 } poison, i32 [[R0]], 0
+; CHECK-NEXT: [[I1:%.*]] = insertvalue { i32, i32 } [[I0]], i32 [[R1]], 1
+; CHECK-NEXT: ret { i32, i32 } [[I1]]
+;
+ %s = select i1 %c, i32 %a, i32 %b
+ call void @use(i32 %s)
+ %v = mul i32 %s, %x
+ %r0 = select i1 %c, i32 %v, i32 %y
+ %r1 = select i1 %c, i32 %v, i32 %z
+ %i0 = insertvalue { i32, i32 } poison, i32 %r0, 0
+ %i1 = insertvalue { i32, i32 } %i0, i32 %r1, 1
+ ret { i32, i32 } %i1
+}
+
+define { i32, i32 } @counter_pair(i1 %c, i32 %a, i32 %b) {
+; CHECK-LABEL: @counter_pair(
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C:%.*]], i32 [[B:%.*]], i32 [[A:%.*]]
+; CHECK-NEXT: [[INC:%.*]] = add i32 [[S]], 1
+; CHECK-NEXT: [[AN:%.*]] = select i1 [[C]], i32 [[A]], i32 [[INC]]
+; CHECK-NEXT: [[BN:%.*]] = select i1 [[C]], i32 [[INC]], i32 [[B]]
+; CHECK-NEXT: [[I0:%.*]] = insertvalue { i32, i32 } poison, i32 [[AN]], 0
+; CHECK-NEXT: [[I1:%.*]] = insertvalue { i32, i32 } [[I0]], i32 [[BN]], 1
+; CHECK-NEXT: ret { i32, i32 } [[I1]]
+;
+ %s = select i1 %c, i32 %b, i32 %a
+ %inc = add i32 %s, 1
+ %an = select i1 %c, i32 %a, i32 %inc
+ %bn = select i1 %c, i32 %inc, i32 %b
+ %i0 = insertvalue { i32, i32 } poison, i32 %an, 0
+ %i1 = insertvalue { i32, i32 } %i0, i32 %bn, 1
+ ret { i32, i32 } %i1
+}
+
+define { i32, i32, i32 } @counter_pair_live_inner(i1 %c, i32 %a, i32 %b) {
+; CHECK-LABEL: @counter_pair_live_inner(
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C:%.*]], i32 [[B:%.*]], i32 [[A:%.*]]
+; CHECK-NEXT: [[INC:%.*]] = add nuw i32 [[S]], 1
+; CHECK-NEXT: [[AN:%.*]] = select i1 [[C]], i32 [[A]], i32 [[INC]]
+; CHECK-NEXT: [[BN:%.*]] = select i1 [[C]], i32 [[INC]], i32 [[B]]
+; CHECK-NEXT: [[I0:%.*]] = insertvalue { i32, i32, i32 } poison, i32 [[AN]], 0
+; CHECK-NEXT: [[I1:%.*]] = insertvalue { i32, i32, i32 } [[I0]], i32 [[BN]], 1
+; CHECK-NEXT: [[I2:%.*]] = insertvalue { i32, i32, i32 } [[I1]], i32 [[S]], 2
+; CHECK-NEXT: ret { i32, i32, i32 } [[I2]]
+;
+ %s = select i1 %c, i32 %b, i32 %a
+ %inc = add nuw i32 %s, 1
+ %an = select i1 %c, i32 %a, i32 %inc
+ %bn = select i1 %c, i32 %inc, i32 %b
+ %i0 = insertvalue { i32, i32, i32 } poison, i32 %an, 0
+ %i1 = insertvalue { i32, i32, i32 } %i0, i32 %bn, 1
+ %i2 = insertvalue { i32, i32, i32 } %i1, i32 %s, 2
+ ret { i32, i32, i32 } %i2
+}
+
+define { i32, i32 } @not_pair(i1 %c, i32 %a, i32 %b) {
+; CHECK-LABEL: @not_pair(
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]
+; CHECK-NEXT: [[N:%.*]] = xor i32 [[S]], -1
+; CHECK-NEXT: [[AN:%.*]] = select i1 [[C]], i32 [[N]], i32 [[A]]
+; CHECK-NEXT: [[BN:%.*]] = select i1 [[C]], i32 [[B]], i32 [[N]]
+; CHECK-NEXT: [[I0:%.*]] = insertvalue { i32, i32 } poison, i32 [[AN]], 0
+; CHECK-NEXT: [[I1:%.*]] = insertvalue { i32, i32 } [[I0]], i32 [[BN]], 1
+; CHECK-NEXT: ret { i32, i32 } [[I1]]
+;
+ %s = select i1 %c, i32 %a, i32 %b
+ %n = xor i32 %s, -1
+ %an = select i1 %c, i32 %n, i32 %a
+ %bn = select i1 %c, i32 %b, i32 %n
+ %i0 = insertvalue { i32, i32 } poison, i32 %an, 0
+ %i1 = insertvalue { i32, i32 } %i0, i32 %bn, 1
+ ret { i32, i32 } %i1
+}
+
+define { <4 x i32>, <4 x i32> } @counter_pair_vec(<4 x i1> %c, <4 x i32> %a, <4 x i32> %b) {
+; CHECK-LABEL: @counter_pair_vec(
+; CHECK-NEXT: [[S:%.*]] = select <4 x i1> [[C:%.*]], <4 x i32> [[B:%.*]], <4 x i32> [[A:%.*]]
+; CHECK-NEXT: [[INC:%.*]] = add <4 x i32> [[S]], splat (i32 1)
+; CHECK-NEXT: [[AN:%.*]] = select <4 x i1> [[C]], <4 x i32> [[A]], <4 x i32> [[INC]]
+; CHECK-NEXT: [[BN:%.*]] = select <4 x i1> [[C]], <4 x i32> [[INC]], <4 x i32> [[B]]
+; CHECK-NEXT: [[I0:%.*]] = insertvalue { <4 x i32>, <4 x i32> } poison, <4 x i32> [[AN]], 0
+; CHECK-NEXT: [[I1:%.*]] = insertvalue { <4 x i32>, <4 x i32> } [[I0]], <4 x i32> [[BN]], 1
+; CHECK-NEXT: ret { <4 x i32>, <4 x i32> } [[I1]]
+;
+ %s = select <4 x i1> %c, <4 x i32> %b, <4 x i32> %a
+ %inc = add <4 x i32> %s, splat (i32 1)
+ %an = select <4 x i1> %c, <4 x i32> %a, <4 x i32> %inc
+ %bn = select <4 x i1> %c, <4 x i32> %inc, <4 x i32> %b
+ %i0 = insertvalue { <4 x i32>, <4 x i32> } poison, <4 x i32> %an, 0
+ %i1 = insertvalue { <4 x i32>, <4 x i32> } %i0, <4 x i32> %bn, 1
+ ret { <4 x i32>, <4 x i32> } %i1
+}
+
+define { i32, i32 } @opposite_sides_variable_operand(i1 %c, i32 %a, i32 %b, i32 %x) {
+; CHECK-LABEL: @opposite_sides_variable_operand(
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]
+; CHECK-NEXT: [[V:%.*]] = mul i32 [[S]], [[X:%.*]]
+; CHECK-NEXT: [[AN:%.*]] = select i1 [[C]], i32 [[V]], i32 [[A]]
+; CHECK-NEXT: [[BN:%.*]] = select i1 [[C]], i32 [[B]], i32 [[V]]
+; CHECK-NEXT: [[I0:%.*]] = insertvalue { i32, i32 } poison, i32 [[AN]], 0
+; CHECK-NEXT: [[I1:%.*]] = insertvalue { i32, i32 } [[I0]], i32 [[BN]], 1
+; CHECK-NEXT: ret { i32, i32 } [[I1]]
+;
+ %s = select i1 %c, i32 %a, i32 %b
+ %v = mul i32 %s, %x
+ %an = select i1 %c, i32 %v, i32 %a
+ %bn = select i1 %c, i32 %b, i32 %v
+ %i0 = insertvalue { i32, i32 } poison, i32 %an, 0
+ %i1 = insertvalue { i32, i32 } %i0, i32 %bn, 1
+ ret { i32, i32 } %i1
+}
+
+define i32 @arm_used_outside_select(i1 %c, i32 %a, i32 %b, i32 %x) {
+; CHECK-LABEL: @arm_used_outside_select(
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]
+; CHECK-NEXT: call void @use(i32 [[S]])
+; CHECK-NEXT: [[V:%.*]] = mul i32 [[S]], [[X:%.*]]
+; CHECK-NEXT: call void @use(i32 [[V]])
+; CHECK-NEXT: [[R:%.*]] = select i1 [[C]], i32 [[V]], i32 [[B]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %s = select i1 %c, i32 %a, i32 %b
+ call void @use(i32 %s)
+ %v = mul i32 %s, %x
+ call void @use(i32 %v)
+ %r = select i1 %c, i32 %v, i32 %b
+ ret i32 %r
+}
+
+define i32 @arm_used_by_select_on_other_cond(i1 %c, i1 %d, i32 %a, i32 %b, i32 %x) {
+; CHECK-LABEL: @arm_used_by_select_on_other_cond(
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]
+; CHECK-NEXT: call void @use(i32 [[S]])
+; CHECK-NEXT: [[V:%.*]] = mul i32 [[S]], [[X:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[D:%.*]], i1 true, i1 [[C]]
+; CHECK-NEXT: [[Q:%.*]] = select i1 [[TMP1]], i32 [[V]], i32 [[B]]
+; CHECK-NEXT: ret i32 [[Q]]
+;
+ %s = select i1 %c, i32 %a, i32 %b
+ call void @use(i32 %s)
+ %v = mul i32 %s, %x
+ %r = select i1 %c, i32 %v, i32 %b
+ %q = select i1 %d, i32 %v, i32 %r
+ ret i32 %q
+}
+
+define i32 @inner_select_on_other_cond(i1 %c, i1 %d, i32 %a, i32 %b, i32 %x) {
+; CHECK-LABEL: @inner_select_on_other_cond(
+; CHECK-NEXT: [[S:%.*]] = select i1 [[D:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]
+; CHECK-NEXT: call void @use(i32 [[S]])
+; CHECK-NEXT: [[V:%.*]] = mul i32 [[S]], [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = select i1 [[C:%.*]], i32 [[V]], i32 [[B]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %s = select i1 %d, i32 %a, i32 %b
+ call void @use(i32 %s)
+ %v = mul i32 %s, %x
+ %r = select i1 %c, i32 %v, i32 %b
+ ret i32 %r
+}
+
+define i32 @udiv_not_speculatable(i1 %c, i32 %a, i32 %x) {
+; CHECK-LABEL: @udiv_not_speculatable(
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C:%.*]], i32 [[A:%.*]], i32 1
+; CHECK-NEXT: call void @use(i32 [[S]])
+; CHECK-NEXT: [[V:%.*]] = udiv i32 [[X:%.*]], [[S]]
+; CHECK-NEXT: [[R:%.*]] = select i1 [[C]], i32 [[V]], i32 0
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %s = select i1 %c, i32 %a, i32 1
+ call void @use(i32 %s)
+ %v = udiv i32 %x, %s
+ %r = select i1 %c, i32 %v, i32 0
+ ret i32 %r
+}
+
+define <4 x i32> @lane_crossing(<4 x i1> %c, <4 x i32> %a, <4 x i32> %b, <4 x i32> %x) {
+; CHECK-LABEL: @lane_crossing(
+; CHECK-NEXT: [[S:%.*]] = select <4 x i1> [[C:%.*]], <4 x i32> [[A:%.*]], <4 x i32> [[B:%.*]]
+; CHECK-NEXT: call void @usev(<4 x i32> [[S]])
+; CHECK-NEXT: [[V:%.*]] = shufflevector <4 x i32> [[S]], <4 x i32> poison, <4 x i32> <i32 1, i32 0, i32 3, i32 2>
+; CHECK-NEXT: [[R:%.*]] = select <4 x i1> [[C]], <4 x i32> [[V]], <4 x i32> [[X:%.*]]
+; CHECK-NEXT: ret <4 x i32> [[R]]
+;
+ %s = select <4 x i1> %c, <4 x i32> %a, <4 x i32> %b
+ call void @usev(<4 x i32> %s)
+ %v = shufflevector <4 x i32> %s, <4 x i32> poison, <4 x i32> <i32 1, i32 0, i32 3, i32 2>
+ %r = select <4 x i1> %c, <4 x i32> %v, <4 x i32> %x
+ ret <4 x i32> %r
+}
+
+declare void @usev(<4 x i32>)
diff --git a/llvm/test/Transforms/InstCombine/select.ll b/llvm/test/Transforms/InstCombine/select.ll
index 203f15cd6f2fc2..133a4a614bb72e 100644
--- a/llvm/test/Transforms/InstCombine/select.ll
+++ b/llvm/test/Transforms/InstCombine/select.ll
@@ -4943,8 +4943,7 @@ define i32 @sequence_select_with_same_cond_true(i1 %c1, i1 %c2){
define double @sequence_select_with_same_cond_double(double %a, i1 %c1, i1 %c2, double %r1, double %r2){
; CHECK-LABEL: define double @sequence_select_with_same_cond_double(
; CHECK-SAME: double [[A:%.*]], i1 [[C1:%.*]], i1 [[C2:%.*]], double [[R1:%.*]], double [[R2:%.*]]) {
-; CHECK-NEXT: [[S1:%.*]] = select i1 [[C1]], double 1.000000e+00, double 0.000000e+00
-; CHECK-NEXT: [[S2:%.*]] = select i1 [[C2]], double [[S1]], double 2.000000e+00
+; CHECK-NEXT: [[S2:%.*]] = select i1 [[C2]], double 1.000000e+00, double 2.000000e+00
; CHECK-NEXT: [[S3:%.*]] = select i1 [[C1]], double [[S2]], double 3.000000e+00
; CHECK-NEXT: ret double [[S3]]
;
@@ -4961,7 +4960,7 @@ define i32 @sequence_select_with_same_cond_extra_use(i1 %c1, i1 %c2){
; CHECK-SAME: i1 [[C1:%.*]], i1 [[C2:%.*]]) {
; CHECK-NEXT: [[S1:%.*]] = select i1 [[C1]], i32 23, i32 45
; CHECK-NEXT: call void @use32(i32 [[S1]])
-; CHECK-NEXT: [[S2:%.*]] = select i1 [[C2]], i32 666, i32 [[S1]]
+; CHECK-NEXT: [[S2:%.*]] = select i1 [[C2]], i32 666, i32 45
; CHECK-NEXT: [[S3:%.*]] = select i1 [[C1]], i32 789, i32 [[S2]]
; CHECK-NEXT: ret i32 [[S3]]
;
More information about the llvm-commits
mailing list