[llvm] [InstCombine] Combine select into select if one of the values match in both of them (PR #177410)

via llvm-commits llvm-commits at lists.llvm.org
Fri Jan 23 03:22:18 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Gábor Spaits (spaits)

<details>
<summary>Changes</summary>

Fixes #<!-- -->82350

Address cases like:
```
select(C0, select(C1, b, a), b) -> select(C0&!C1, a, b)
select(C0, a, select(C1, b, a)) -> select(C0|!C1, a, b)
```
    
It seem that it generates better code for the real world examples for the few targets I have checked: https://godbolt.org/z/KeEMd9b8E .
On the most generic case it generates the same assembly code for the sources and targets for all targets, expect RISC-V, where the targets seem shoretr and better (less branching): https://godbolt.org/z/3has1Td5G So I did not experience any regression on any target in no scenario.

Proofs: https://alive2.llvm.org/ce/z/DoL3zQ

For reviewers:
This PR consists of 3 commits:
- The first one introduces just the tests, so later the change in behaviour can be seen (29ada10e4cdfbf8edeed8c08245b4a79ec6659af)
- Second is the most straigh forward solution (aa9ead9b233d3ec3a6ff14bb53018c7e62e69c66)
- The third modifies the second one to a more elegant solution with no code duplication (9ad7bdcfbae3dad6e79aa9ed7fb61b7be3a75fee)

I prefer the 3rd one, the latest version, even if it means a bit more line changed, but if you like the second one more I can go with that.

---
Full diff: https://github.com/llvm/llvm-project/pull/177410.diff


5 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp (+36-12) 
- (modified) llvm/test/Transforms/InstCombine/pr63791.ll (+1-1) 
- (modified) llvm/test/Transforms/InstCombine/select-of-symmetric-selects.ll (+3-2) 
- (modified) llvm/test/Transforms/InstCombine/select-select.ll (+154) 
- (modified) llvm/test/Transforms/InstCombine/select.ll (+6-6) 


``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index d4604e05de688..51dcf028ce2b3 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -4515,15 +4515,27 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
               *TrueSI, CondVal, /*CondIsTrue=*/true, DL))
         return replaceOperand(SI, 1, V);
 
-      // select(C0, select(C1, a, b), b) -> select(C0&C1, a, b)
       // We choose this as normal form to enable folding on the And and
       // shortening paths for the values (this helps getUnderlyingObjects() for
       // example).
-      if (TrueSI->getFalseValue() == FalseVal && TrueSI->hasOneUse()) {
-        Value *And = Builder.CreateLogicalAnd(CondVal, TrueSI->getCondition());
-        replaceOperand(SI, 0, And);
-        replaceOperand(SI, 1, TrueSI->getTrueValue());
-        return &SI;
+      if (TrueSI->hasOneUse()) {
+        Value *And = nullptr, *OtherVal = nullptr;
+        // select(C0, select(C1, a, b), b) -> select(C0&C1, a, b)
+        if (TrueSI->getFalseValue() == FalseVal) {
+          And = Builder.CreateLogicalAnd(CondVal, TrueSI->getCondition());
+          OtherVal = TrueSI->getTrueValue();
+        }
+        // select(C0, select(C1, b, a), b) -> select(C0&!C1, a, b)
+        else if (TrueSI->getTrueValue() == FalseVal) {
+          Value *Negation = Builder.CreateNot(TrueSI->getCondition());
+          And = Builder.CreateLogicalAnd(CondVal, Negation);
+          OtherVal = TrueSI->getFalseValue();
+        }
+        if (And && OtherVal) {
+          replaceOperand(SI, 0, And);
+          replaceOperand(SI, 1, OtherVal);
+          return &SI;
+        }
       }
     }
   }
@@ -4535,12 +4547,24 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
               *FalseSI, CondVal, /*CondIsTrue=*/false, DL))
         return replaceOperand(SI, 2, V);
 
-      // select(C0, a, select(C1, a, b)) -> select(C0|C1, a, b)
-      if (FalseSI->getTrueValue() == TrueVal && FalseSI->hasOneUse()) {
-        Value *Or = Builder.CreateLogicalOr(CondVal, FalseSI->getCondition());
-        replaceOperand(SI, 0, Or);
-        replaceOperand(SI, 2, FalseSI->getFalseValue());
-        return &SI;
+      if (FalseSI->hasOneUse()) {
+        Value *Or = nullptr, *OtherVal = nullptr;
+        // select(C0, a, select(C1, a, b)) -> select(C0|C1, a, b)
+        if (FalseSI->getTrueValue() == TrueVal) {
+          Or = Builder.CreateLogicalOr(CondVal, FalseSI->getCondition());
+          OtherVal = FalseSI->getFalseValue();
+        }
+        // select(C0, a, select(C1, b, a)) -> select(C0|!C1, a, b)
+        else if (FalseSI->getFalseValue() == TrueVal) {
+          Value *Negation = Builder.CreateNot(FalseSI->getCondition());
+          Or = Builder.CreateLogicalOr(CondVal, Negation);
+          OtherVal = FalseSI->getTrueValue();
+        }
+        if (Or && OtherVal) {
+          replaceOperand(SI, 0, Or);
+          replaceOperand(SI, 2, OtherVal);
+          return &SI;
+        }
       }
     }
   }
diff --git a/llvm/test/Transforms/InstCombine/pr63791.ll b/llvm/test/Transforms/InstCombine/pr63791.ll
index 78cc1130fb33f..73a559f989261 100644
--- a/llvm/test/Transforms/InstCombine/pr63791.ll
+++ b/llvm/test/Transforms/InstCombine/pr63791.ll
@@ -15,7 +15,7 @@ define void @y() {
 ; CHECK-NEXT:    store i1 true, ptr poison, align 1
 ; CHECK-NEXT:    br i1 poison, label [[FOR_COND_I]], label [[FOR_COND5_PREHEADER_I]]
 ; CHECK:       for.cond5.preheader.i:
-; CHECK-NEXT:    br i1 false, label [[FOR_INC19_I:%.*]], label [[FOR_COND1_LOOPEXIT_I:%.*]]
+; CHECK-NEXT:    br i1 true, label [[FOR_COND1_LOOPEXIT_I:%.*]], label [[FOR_INC19_I:%.*]]
 ; CHECK:       for.inc19.i:
 ; CHECK-NEXT:    br i1 poison, label [[FOR_INC19_I]], label [[FOR_COND1_LOOPEXIT_I]]
 ;
diff --git a/llvm/test/Transforms/InstCombine/select-of-symmetric-selects.ll b/llvm/test/Transforms/InstCombine/select-of-symmetric-selects.ll
index 0936f58ac9443..70c89109f3dbc 100644
--- a/llvm/test/Transforms/InstCombine/select-of-symmetric-selects.ll
+++ b/llvm/test/Transforms/InstCombine/select-of-symmetric-selects.ll
@@ -15,8 +15,9 @@ define i32 @select_of_symmetric_selects(i32 %a, i32 %b, i1 %c1, i1 %c2) {
 
 define i32 @select_of_symmetric_selects_negative1(i32 %a, i32 %b, i1 %c1, i1 %c2) {
 ; CHECK-LABEL: @select_of_symmetric_selects_negative1(
-; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[C1:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]
-; CHECK-NEXT:    [[RET:%.*]] = select i1 [[C2:%.*]], i32 [[SEL1]], i32 [[A]]
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i1 [[C1:%.*]], true
+; CHECK-NEXT:    [[C2:%.*]] = select i1 [[C3:%.*]], i1 [[TMP1]], i1 false
+; CHECK-NEXT:    [[RET:%.*]] = select i1 [[C2]], i32 [[SEL1:%.*]], i32 [[A:%.*]]
 ; CHECK-NEXT:    ret i32 [[RET]]
 ;
   %sel1 = select i1 %c1, i32 %a, i32 %b
diff --git a/llvm/test/Transforms/InstCombine/select-select.ll b/llvm/test/Transforms/InstCombine/select-select.ll
index 94e88c2f6cbe6..7a63e8efed104 100644
--- a/llvm/test/Transforms/InstCombine/select-select.ll
+++ b/llvm/test/Transforms/InstCombine/select-select.ll
@@ -531,7 +531,161 @@ define <2 x i8> @strong_order_cmp_eq_ugt_vector_poison3(<2 x i32> %a, <2 x i32>
   ret <2 x i8> %sel.gt
 }
 
+; Minimal code that triigers the optimizations.
 
+define i32 @selectSelect11(i1 %cond1, i1 %cond2, i32 %var, i32 %defaultVal) {
+; CHECK-LABEL: @selectSelect11(
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[COND2:%.*]], i1 true, i1 [[COND1:%.*]]
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[TMP1]], i32 [[VAR:%.*]], i32 [[DEFAULTVAL:%.*]]
+; CHECK-NEXT:    ret i32 [[SEL2]]
+;
+  %sel1 = select i1 %cond1, i32 %defaultVal, i32 %var
+  %sel2 = select i1 %cond2, i32 %defaultVal, i32 %sel1
+  ret i32 %sel2
+}
+
+define i32 @selectSelect22(i1 %cond1, i1 %cond2, i32 %var, i32 %defaultVal) {
+; CHECK-LABEL: @selectSelect22(
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[COND2:%.*]], i1 [[COND1:%.*]], i1 false
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[TMP1]], i32 [[VAR:%.*]], i32 [[DEFAULTVAL:%.*]]
+; CHECK-NEXT:    ret i32 [[SEL2]]
+;
+  %sel1 = select i1 %cond1, i32 %var, i32 %defaultVal
+  %sel2 = select i1 %cond2, i32 %sel1, i32 %defaultVal
+  ret i32 %sel2
+}
+
+define i32 @selectSelect12(i1 %cond1, i1 %cond2, i32 %var, i32 %defaultVal) {
+; CHECK-LABEL: @selectSelect12(
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i1 [[COND1:%.*]], true
+; CHECK-NEXT:    [[COND2:%.*]] = select i1 [[COND3:%.*]], i1 [[TMP1]], i1 false
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[COND2]], i32 [[SEL3:%.*]], i32 [[DEFAULTVAL:%.*]]
+; CHECK-NEXT:    ret i32 [[SEL2]]
+;
+  %sel1 = select i1 %cond1, i32 %defaultVal,i32 %var
+  %sel2 = select i1 %cond2, i32 %sel1, i32 %defaultVal
+  ret i32 %sel2
+}
+
+define i32 @selectSelect21(i1 %cond1, i1 %cond2, i32 %var, i32 %defaultVal) {
+; CHECK-LABEL: @selectSelect21(
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i1 [[COND1:%.*]], true
+; CHECK-NEXT:    [[COND2:%.*]] = select i1 [[COND3:%.*]], i1 true, i1 [[TMP1]]
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[COND2]], i32 [[DEFAULTVAL1:%.*]], i32 [[DEFAULTVAL:%.*]]
+; CHECK-NEXT:    ret i32 [[SEL2]]
+;
+  %sel1 = select i1 %cond1, i32 %var, i32 %defaultVal
+  %sel2 = select i1 %cond2, i32 %defaultVal, i32 %sel1
+  ret i32 %sel2
+}
+
+define i32 @selectSelect11Use(i1 %cond1, i1 %cond2, i32 %var, i32 %defaultVal) {
+; CHECK-LABEL: @selectSelect11Use(
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND1:%.*]], i32 [[DEFAULTVAL:%.*]], i32 [[VAR:%.*]]
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[COND2:%.*]], i32 [[DEFAULTVAL]], i32 [[SEL1]]
+; CHECK-NEXT:    call void @use32(i32 [[SEL1]])
+; CHECK-NEXT:    ret i32 [[SEL2]]
+;
+  %sel1 = select i1 %cond1, i32 %defaultVal, i32 %var
+  %sel2 = select i1 %cond2, i32 %defaultVal, i32 %sel1
+  call void @use32(i32 %sel1)
+  ret i32 %sel2
+}
+
+define i32 @selectSelect22Use(i1 %cond1, i1 %cond2, i32 %var, i32 %defaultVal) {
+; CHECK-LABEL: @selectSelect22Use(
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND1:%.*]], i32 [[VAR:%.*]], i32 [[DEFAULTVAL:%.*]]
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[COND2:%.*]], i32 [[SEL1]], i32 [[DEFAULTVAL]]
+; CHECK-NEXT:    call void @use32(i32 [[SEL1]])
+; CHECK-NEXT:    ret i32 [[SEL2]]
+;
+  %sel1 = select i1 %cond1, i32 %var, i32 %defaultVal
+  %sel2 = select i1 %cond2, i32 %sel1, i32 %defaultVal
+  call void @use32(i32 %sel1)
+  ret i32 %sel2
+}
+
+define i32 @selectSelect12Use(i1 %cond1, i1 %cond2, i32 %var, i32 %defaultVal) {
+; CHECK-LABEL: @selectSelect12Use(
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND1:%.*]], i32 [[DEFAULTVAL:%.*]], i32 [[VAR:%.*]]
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[COND2:%.*]], i32 [[SEL1]], i32 [[DEFAULTVAL]]
+; CHECK-NEXT:    call void @use32(i32 [[SEL1]])
+; CHECK-NEXT:    ret i32 [[SEL2]]
+;
+  %sel1 = select i1 %cond1, i32 %defaultVal,i32 %var
+  %sel2 = select i1 %cond2, i32 %sel1, i32 %defaultVal
+  call void @use32(i32 %sel1)
+  ret i32 %sel2
+}
+
+define i32 @selectSelect21Use(i1 %cond1, i1 %cond2, i32 %var, i32 %defaultVal) {
+; CHECK-LABEL: @selectSelect21Use(
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND1:%.*]], i32 [[VAR:%.*]], i32 [[DEFAULTVAL:%.*]]
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[COND2:%.*]], i32 [[DEFAULTVAL]], i32 [[SEL1]]
+; CHECK-NEXT:    call void @use32(i32 [[SEL1]])
+; CHECK-NEXT:    ret i32 [[SEL2]]
+;
+  %sel1 = select i1 %cond1, i32 %var, i32 %defaultVal
+  %sel2 = select i1 %cond2, i32 %defaultVal, i32 %sel1
+  call void @use32(i32 %sel1)
+  ret i32 %sel2
+}
+
+; Examples from real world prgrams.
+
+define i32 @abseil_cpp_example(i32 %a, i64 %b, i32 %c) {
+; CHECK-LABEL: @abseil_cpp_example(
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp sgt i64 [[B:%.*]], -1
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[CMP2]], i32 [[A:%.*]], i32 0
+; CHECK-NEXT:    ret i32 [[TMP1]]
+;
+  %cmp1 = icmp eq i64 %b, 0
+  %cmp2 = icmp slt i64 %b, 0
+  %spec.select632 = select i1 %cmp2, i32 0, i32 %a
+  %85 = select i1 %cmp1, i32 %a, i32 %spec.select632
+  ret i32 %85
+}
+
+define i32 @hermes_example(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @hermes_example(
+; CHECK-NEXT:    [[TMP1:%.*]] = add i32 [[A:%.*]], -17
+; CHECK-NEXT:    [[CMP108_I:%.*]] = icmp ult i32 [[TMP1]], -8
+; CHECK-NEXT:    [[Z_5_I1:%.*]] = select i1 [[CMP108_I]], i32 [[C:%.*]], i32 [[Z_5_I:%.*]]
+; CHECK-NEXT:    ret i32 [[Z_5_I1]]
+;
+  %cmp108.i = icmp slt i32 %a, 9
+  %cmp113.i = icmp slt i32 %a, 17
+  %spec.select.i = select i1 %cmp113.i, i32 %c, i32 %b
+  %z.5.i = select i1 %cmp108.i, i32 %b, i32 %spec.select.i
+  ret i32 %z.5.i
+}
+
+define i64 @hermes_example2(i32 %a, i64 %b, i64 %c) {
+; CHECK-LABEL: @hermes_example2(
+; CHECK-NEXT:    [[CMP16_I_I:%.*]] = icmp ult i32 [[A:%.*]], 512
+; CHECK-NEXT:    [[RETVAL_0_I_I1:%.*]] = select i1 [[CMP16_I_I]], i64 [[B:%.*]], i64 [[RETVAL_0_I_I:%.*]]
+; CHECK-NEXT:    ret i64 [[RETVAL_0_I_I1]]
+;
+  %cmp16.i.i = icmp eq i32 %a, 0
+  %cmp20.i.i = icmp ugt i32 %a, 511
+  %spec.select11.i = select i1 %cmp20.i.i, i64 %b, i64 %c
+  %retval.0.i.i = select i1 %cmp16.i.i, i64 %c, i64 %spec.select11.i
+  ret i64 %retval.0.i.i
+}
+
+define i32 @gh82350(i32 %x, i32 %y, i32 %z, i1 %cmp1) {
+; CHECK-LABEL: @gh82350(
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i32 [[Z:%.*]], 0
+; CHECK-NEXT:    [[DOTNOT:%.*]] = select i1 [[CMP2]], i1 true, i1 [[CMP1:%.*]]
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[DOTNOT]], i32 [[X:%.*]], i32 [[Y:%.*]]
+; CHECK-NEXT:    ret i32 [[SEL2]]
+;
+  %cmp2 = icmp eq i32 %z, 0
+  %sel1 = select i1 %cmp1, i32 %x, i32 %y
+  %sel2 = select i1 %cmp2, i32 %sel1, i32 %x
+  ret i32 %sel2
+}
 
 declare void @use1(i1)
 declare void @use8(i8)
+declare void @use32(i32)
diff --git a/llvm/test/Transforms/InstCombine/select.ll b/llvm/test/Transforms/InstCombine/select.ll
index db117aa28cad6..6076a18b6501c 100644
--- a/llvm/test/Transforms/InstCombine/select.ll
+++ b/llvm/test/Transforms/InstCombine/select.ll
@@ -1400,10 +1400,10 @@ define i128 @test86(i1 %flag) {
 define i32 @test_select_select0(i32 %a, i32 %r0, i32 %r1, i32 %v1, i32 %v2) {
 ; CHECK-LABEL: define i32 @test_select_select0(
 ; CHECK-SAME: i32 [[A:%.*]], i32 [[R0:%.*]], i32 [[R1:%.*]], i32 [[V1:%.*]], i32 [[V2:%.*]]) {
-; CHECK-NEXT:    [[C0_NOT:%.*]] = icmp slt i32 [[A]], [[V1]]
-; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C0_NOT]], i32 [[R1]], i32 [[R0]]
+; CHECK-NEXT:    [[C0_NOT:%.*]] = icmp sge i32 [[A]], [[V1]]
 ; CHECK-NEXT:    [[C1:%.*]] = icmp slt i32 [[A]], [[V2]]
-; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i32 [[S0]], i32 [[R1]]
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[C1]], i1 [[C0_NOT]], i1 false
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[TMP1]], i32 [[R0]], i32 [[R1]]
 ; CHECK-NEXT:    ret i32 [[S1]]
 ;
   %c0 = icmp sge i32 %a, %v1
@@ -1416,10 +1416,10 @@ define i32 @test_select_select0(i32 %a, i32 %r0, i32 %r1, i32 %v1, i32 %v2) {
 define i32 @test_select_select1(i32 %a, i32 %r0, i32 %r1, i32 %v1, i32 %v2) {
 ; CHECK-LABEL: define i32 @test_select_select1(
 ; CHECK-SAME: i32 [[A:%.*]], i32 [[R0:%.*]], i32 [[R1:%.*]], i32 [[V1:%.*]], i32 [[V2:%.*]]) {
-; CHECK-NEXT:    [[C0_NOT:%.*]] = icmp slt i32 [[A]], [[V1]]
-; CHECK-NEXT:    [[S0:%.*]] = select i1 [[C0_NOT]], i32 [[R1]], i32 [[R0]]
+; CHECK-NEXT:    [[C0_NOT:%.*]] = icmp sge i32 [[A]], [[V1]]
 ; CHECK-NEXT:    [[C1:%.*]] = icmp slt i32 [[A]], [[V2]]
-; CHECK-NEXT:    [[S1:%.*]] = select i1 [[C1]], i32 [[R0]], i32 [[S0]]
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[C1]], i1 true, i1 [[C0_NOT]]
+; CHECK-NEXT:    [[S1:%.*]] = select i1 [[TMP1]], i32 [[R0]], i32 [[R1]]
 ; CHECK-NEXT:    ret i32 [[S1]]
 ;
   %c0 = icmp sge i32 %a, %v1

``````````

</details>


https://github.com/llvm/llvm-project/pull/177410


More information about the llvm-commits mailing list