[llvm] [InstCombine] foldOpIntoPhi should apply to icmp with non-constant operand (PR #147676)

Ross Kirsling via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 9 04:16:21 PDT 2025


================
@@ -0,0 +1,98 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i1 @test(i1 %cond, i64 %left, i64 %right) {
+; CHECK-LABEL: define i1 @test(
+; CHECK-SAME: i1 [[COND:%.*]], i64 [[LEFT:%.*]], i64 [[RIGHT:%.*]]) {
+; CHECK-NEXT:  [[START:.*:]]
+; CHECK-NEXT:    br i1 [[COND]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
+; CHECK:       [[COND_TRUE]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i64 [[RIGHT]], [[LEFT]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[END:.*]], label %[[COND_FALSE]]
+; CHECK:       [[COND_FALSE]]:
+; CHECK-NEXT:    br label %[[END]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    ret i1 false
+;
+start:
+  br i1 %cond, label %cond.true, label %cond.false
+
+cond.true:
+  %cmp = icmp sgt i64 %right, %left
+  br i1 %cmp, label %end, label %cond.false
+
+cond.false:
+  %left_or_right = phi i64 [ %left, %start ], [ %right, %cond.true ]
+  %false = icmp sgt i64 %left_or_right, %left
+  br label %end
+
+end:
+  %result = phi i1 [ false, %cond.true ], [ %false, %cond.false ]
+  ret i1 %result
+}
+
+define i1 @test_with_wrong_icmp(i1 %cond, i64 %left, i64 %right) {
+; CHECK-LABEL: define i1 @test_with_wrong_icmp(
+; CHECK-SAME: i1 [[COND:%.*]], i64 [[LEFT:%.*]], i64 [[RIGHT:%.*]]) {
+; CHECK-NEXT:  [[START:.*]]:
+; CHECK-NEXT:    br i1 [[COND]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
+; CHECK:       [[COND_TRUE]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i64 [[RIGHT]], [[LEFT]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[END:.*]], label %[[COND_FALSE]]
+; CHECK:       [[COND_FALSE]]:
+; CHECK-NEXT:    [[LEFT_OR_RIGHT:%.*]] = phi i64 [ [[LEFT]], %[[START]] ], [ [[RIGHT]], %[[COND_TRUE]] ]
+; CHECK-NEXT:    [[FALSE:%.*]] = icmp sge i64 [[LEFT_OR_RIGHT]], [[LEFT]]
+; CHECK-NEXT:    br label %[[END]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[COND_TRUE]] ], [ [[FALSE]], %[[COND_FALSE]] ]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+start:
+  br i1 %cond, label %cond.true, label %cond.false
+
+cond.true:
+  %cmp = icmp sgt i64 %right, %left
+  br i1 %cmp, label %end, label %cond.false
+
+cond.false:
+  %left_or_right = phi i64 [ %left, %start ], [ %right, %cond.true ]
+  %false = icmp sge i64 %left_or_right, %left
+  br label %end
+
+end:
+  %result = phi i1 [ false, %cond.true ], [ %false, %cond.false ]
+  ret i1 %result
+}
+
+define i1 @test_with_unfoldable_phi(i1 %cond, i64 %left, i64 %right) {
+; CHECK-LABEL: define i1 @test_with_unfoldable_phi(
+; CHECK-SAME: i1 [[COND:%.*]], i64 [[LEFT:%.*]], i64 [[RIGHT:%.*]]) {
+; CHECK-NEXT:  [[START:.*]]:
+; CHECK-NEXT:    br i1 [[COND]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
+; CHECK:       [[COND_TRUE]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i64 [[RIGHT]], [[LEFT]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[END:.*]], label %[[COND_FALSE]]
+; CHECK:       [[COND_FALSE]]:
+; CHECK-NEXT:    [[LEFT_OR_RIGHT:%.*]] = phi i64 [ [[LEFT]], %[[START]] ], [ [[RIGHT]], %[[COND_TRUE]] ]
+; CHECK-NEXT:    [[FALSE:%.*]] = icmp sgt i64 [[LEFT_OR_RIGHT]], [[LEFT]]
+; CHECK-NEXT:    br label %[[END]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[COND_TRUE]] ], [ [[FALSE]], %[[COND_FALSE]] ]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+start:
+  br i1 %cond, label %cond.true, label %cond.false
+
+cond.true:
+  %cmp = icmp slt i64 %right, %left         ; slt instead of sgt here
+  br i1 %cmp, label %end, label %cond.false
+
+cond.false:
+  %left_or_right = phi i64 [ %left, %start ], [ %right, %cond.true ]
+  %false = icmp sgt i64 %left_or_right, %left
----------------
rkirsling wrote:

Hmm, there is such folding for `sle`, but my aim in choosing `slt` was that it wouldn't be so easy to reason about whether `%right` is in fact greater than `%left`.

With `-passes=instcombine`:
```
define i1 @test(i1 %cond, i64 %left, i64 %right) {
start:
  br i1 %cond, label %cond.true, label %cond.false

cond.true:                                        ; preds = %start
  %cmp.not = icmp sgt i64 %right, %left
  br i1 %cmp.not, label %cond.false, label %end

cond.false:                                       ; preds = %cond.true, %start
  br label %end

end:                                              ; preds = %cond.false, %cond.true
  %result = phi i1 [ false, %cond.true ], [ %cond, %cond.false ]
  ret i1 %result
}
```
With `-Os`:
```
define i1 @src(i1 %cond, i64 %left, i64 %right) local_unnamed_addr #0 {
start:
  %cmp.not = icmp sgt i64 %right, %left
  %spec.select = select i1 %cond, i1 %cmp.not, i1 false
  ret i1 %spec.select
}
```

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


More information about the llvm-commits mailing list