[llvm] [SCEV] Limit ControlsOnlyExit logic to BinOps with neutral elements. (PR #194831)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 30 01:48:58 PDT 2026


https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/194831

>From 77d65812abcfb7fc1d614ee68cf4f779325e9b69 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Tue, 28 Apr 2026 13:26:44 +0100
Subject: [PATCH 1/2] Add test

---
 .../Analysis/ScalarEvolution/or-exit-cond.ll  | 64 +++++++++++++++++++
 .../Transforms/IndVarSimplify/or-exit-cond.ll | 63 ++++++++++++++++++
 2 files changed, 127 insertions(+)
 create mode 100644 llvm/test/Analysis/ScalarEvolution/or-exit-cond.ll
 create mode 100644 llvm/test/Transforms/IndVarSimplify/or-exit-cond.ll

diff --git a/llvm/test/Analysis/ScalarEvolution/or-exit-cond.ll b/llvm/test/Analysis/ScalarEvolution/or-exit-cond.ll
new file mode 100644
index 0000000000000..1010cc1a88f4f
--- /dev/null
+++ b/llvm/test/Analysis/ScalarEvolution/or-exit-cond.ll
@@ -0,0 +1,64 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes='print<scalar-evolution>' -disable-output %s 2>&1 | FileCheck %s
+
+target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+
+define i32 @last_loaded_with_or_exit(i32 %n, ptr %arr) mustprogress {
+; CHECK-LABEL: 'last_loaded_with_or_exit'
+; CHECK-NEXT:  Classifying expressions for: @last_loaded_with_or_exit
+; CHECK-NEXT:    %iv = phi i32 [ %iv.next, %latch ], [ 0, %entry ]
+; CHECK-NEXT:    --> {0,+,1}<nuw><%header> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT:    %last_loaded = phi i32 [ %last_loaded.next, %latch ], [ 0, %entry ]
+; CHECK-NEXT:    --> %last_loaded U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Variant }
+; CHECK-NEXT:    %iv_minus_one = add i32 -1, %iv
+; CHECK-NEXT:    --> {-1,+,1}<nuw><%header> U: [-1,0) S: [-1,0) Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT:    %stay = or i1 %iv_lt_n, %iv_minus_one_lt_n
+; CHECK-NEXT:    --> (%iv_minus_one_lt_n umax %iv_lt_n) U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Variant }
+; CHECK-NEXT:    %body_iv_minus_one = add i32 -1, %iv
+; CHECK-NEXT:    --> {-1,+,1}<nuw><%header> U: [-1,0) S: [-1,0) Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT:    %idx = zext nneg i32 %body_iv_minus_one to i64
+; CHECK-NEXT:    --> {4294967295,+,1}<nuw><%header> U: [4294967295,0) S: [4294967295,0) Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT:    %addr = getelementptr inbounds i32, ptr %arr, i64 %idx
+; CHECK-NEXT:    --> {(17179869180 + %arr),+,4}<%header> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT:    %loaded = load i32, ptr %addr, align 4
+; CHECK-NEXT:    --> %loaded U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Variant }
+; CHECK-NEXT:    %last_loaded.next = phi i32 [ %last_loaded, %body ], [ %loaded, %do_load ]
+; CHECK-NEXT:    --> %last_loaded.next U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Variant }
+; CHECK-NEXT:    %iv.next = add i32 %iv, 1
+; CHECK-NEXT:    --> {1,+,1}<nw><%header> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT:  Determining loop execution counts for: @last_loaded_with_or_exit
+; CHECK-NEXT:  Loop %header: Unpredictable backedge-taken count.
+; CHECK-NEXT:  Loop %header: Unpredictable constant max backedge-taken count.
+; CHECK-NEXT:  Loop %header: Unpredictable symbolic max backedge-taken count.
+;
+entry:
+  br label %header
+
+header:
+  %iv = phi i32 [ %iv.next, %latch ], [ 0, %entry ]
+  %last_loaded = phi i32 [ %last_loaded.next, %latch ], [ 0, %entry ]
+  %iv_lt_n = icmp ult i32 %iv, %n
+  %iv_minus_one = add i32 -1, %iv
+  %iv_minus_one_lt_n = icmp ult i32 %iv_minus_one, %n
+  %stay = or i1 %iv_lt_n, %iv_minus_one_lt_n
+  br i1 %stay, label %body, label %exit
+
+body:
+  %body_iv_minus_one = add i32 -1, %iv
+  %body_iv_minus_one_nonneg = icmp sgt i32 %body_iv_minus_one, -1
+  br i1 %body_iv_minus_one_nonneg, label %do_load, label %latch
+
+do_load:
+  %idx = zext nneg i32 %body_iv_minus_one to i64
+  %addr = getelementptr inbounds i32, ptr %arr, i64 %idx
+  %loaded = load i32, ptr %addr
+  br label %latch
+
+latch:
+  %last_loaded.next = phi i32 [ %last_loaded, %body ], [ %loaded, %do_load ]
+  %iv.next = add i32 %iv, 1
+  br label %header
+
+exit:
+  ret i32 %last_loaded
+}
diff --git a/llvm/test/Transforms/IndVarSimplify/or-exit-cond.ll b/llvm/test/Transforms/IndVarSimplify/or-exit-cond.ll
new file mode 100644
index 0000000000000..a82172a17405b
--- /dev/null
+++ b/llvm/test/Transforms/IndVarSimplify/or-exit-cond.ll
@@ -0,0 +1,63 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=indvars -S %s | FileCheck %s
+
+target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+
+define i32 @last_loaded_with_or_exit(i32 %n, ptr %arr) mustprogress {
+; CHECK-LABEL: define i32 @last_loaded_with_or_exit(
+; CHECK-SAME: i32 [[N:%.*]], ptr [[ARR:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = zext i32 [[N]] to i64
+; CHECK-NEXT:    br label %[[HEADER:.*]]
+; CHECK:       [[HEADER]]:
+; CHECK-NEXT:    [[INDVARS_IV:%.*]] = phi i64 [ [[INDVARS_IV_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT:    [[LAST_LOADED:%.*]] = phi i32 [ [[LAST_LOADED_NEXT:%.*]], %[[LATCH]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT:    [[IV_LT_N:%.*]] = icmp ult i64 [[INDVARS_IV]], [[TMP0]]
+; CHECK-NEXT:    [[STAY:%.*]] = or i1 [[IV_LT_N]], false
+; CHECK-NEXT:    br i1 [[STAY]], label %[[BODY:.*]], label %[[EXIT:.*]]
+; CHECK:       [[BODY]]:
+; CHECK-NEXT:    [[IDX:%.*]] = add nuw i64 4294967295, [[INDVARS_IV]]
+; CHECK-NEXT:    br i1 false, label %[[DO_LOAD:.*]], label %[[LATCH]]
+; CHECK:       [[DO_LOAD]]:
+; CHECK-NEXT:    [[ADDR:%.*]] = getelementptr inbounds i32, ptr [[ARR]], i64 [[IDX]]
+; CHECK-NEXT:    [[LOADED:%.*]] = load i32, ptr [[ADDR]], align 4
+; CHECK-NEXT:    br label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    [[LAST_LOADED_NEXT]] = phi i32 [ [[LAST_LOADED]], %[[BODY]] ], [ [[LOADED]], %[[DO_LOAD]] ]
+; CHECK-NEXT:    [[INDVARS_IV_NEXT]] = add i64 [[INDVARS_IV]], 1
+; CHECK-NEXT:    br label %[[HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[LAST_LOADED_LCSSA:%.*]] = phi i32 [ [[LAST_LOADED]], %[[HEADER]] ]
+; CHECK-NEXT:    ret i32 [[LAST_LOADED_LCSSA]]
+;
+entry:
+  br label %header
+
+header:
+  %iv = phi i32 [ %iv.next, %latch ], [ 0, %entry ]
+  %last_loaded = phi i32 [ %last_loaded.next, %latch ], [ 0, %entry ]
+  %iv_lt_n = icmp ult i32 %iv, %n
+  %iv_minus_one = add i32 -1, %iv
+  %iv_minus_one_lt_n = icmp ult i32 %iv_minus_one, %n
+  %stay = or i1 %iv_lt_n, %iv_minus_one_lt_n
+  br i1 %stay, label %body, label %exit
+
+body:
+  %body_iv_minus_one = add i32 -1, %iv
+  %body_iv_minus_one_nonneg = icmp sgt i32 %body_iv_minus_one, -1
+  br i1 %body_iv_minus_one_nonneg, label %do_load, label %latch
+
+do_load:
+  %idx = zext nneg i32 %body_iv_minus_one to i64
+  %addr = getelementptr inbounds i32, ptr %arr, i64 %idx
+  %loaded = load i32, ptr %addr
+  br label %latch
+
+latch:
+  %last_loaded.next = phi i32 [ %last_loaded, %body ], [ %loaded, %do_load ]
+  %iv.next = add i32 %iv, 1
+  br label %header
+
+exit:
+  ret i32 %last_loaded
+}

>From aac082a9921f4396afa57049094e866b861bd7f2 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Wed, 29 Apr 2026 10:50:30 +0100
Subject: [PATCH 2/2] [SCEV] Limit ControlsOnlyExit logic to BinOps with
 neutral elements.

As far as I can tell, ControlsOnlyExit is used during reasoning to assume
that we stay in the loop as long as the condition is true/false, and are
guaranteed to exit otherwise.

But unless I am missing something, a sub-condition of an AND/OR never
solely controls the exit, whether we exit always depends on both conditions.

Pass false to ControlsOnlyExit, as otherwise we would incorrectly assume
that we must exit if either conditions is true, when we would only exit
if both are true.

For now, ControlsOnlyExit is only used for the neutral element case, it
may be worth removing this.

Alive2 proof of IndVars mis-compile: https://alive2.llvm.org/ce/z/kWs4hE
---
 llvm/lib/Analysis/ScalarEvolution.cpp         | 30 ++++++++++---------
 .../Analysis/ScalarEvolution/or-exit-cond.ll  | 12 ++++----
 .../trip-count-andor-selectform.ll            | 24 +++++++--------
 .../ScalarEvolution/trip-count-andor.ll       | 24 +++++++--------
 .../Transforms/IndVarSimplify/or-exit-cond.ll | 17 ++++++-----
 5 files changed, 56 insertions(+), 51 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 9f362deb7cca9..676292ebe0346 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -9361,7 +9361,7 @@ ScalarEvolution::computeExitLimitFromCondFromBinOp(
     bool ControlsOnlyExit, bool AllowPredicates) {
   // Check if the controlling expression for this loop is an And or Or.
   Value *Op0, *Op1;
-  bool IsAnd = false;
+  bool IsAnd;
   if (match(ExitCond, m_LogicalAnd(m_Value(Op0), m_Value(Op1))))
     IsAnd = true;
   else if (match(ExitCond, m_LogicalOr(m_Value(Op0), m_Value(Op1))))
@@ -9369,23 +9369,25 @@ ScalarEvolution::computeExitLimitFromCondFromBinOp(
   else
     return std::nullopt;
 
+  // Be robust against unsimplified IR for the form "op i1 X, NeutralElement".
+  const Constant *NeutralElement = ConstantInt::get(ExitCond->getType(), IsAnd);
+  if (Op0 == NeutralElement)
+    std::swap(Op0, Op1);
+  if (Op1 == NeutralElement)
+    return computeExitLimitFromCondCached(Cache, L, Op0, ExitIfTrue,
+                                          ControlsOnlyExit, AllowPredicates);
+
+  // A sub-condition of a non-trivial binop never solely controls the exit,
+  // whether we exit always depends on both conditions.
+  ExitLimit EL0 = computeExitLimitFromCondCached(
+      Cache, L, Op0, ExitIfTrue, /*ControlsOnlyExit=*/false, AllowPredicates);
+  ExitLimit EL1 = computeExitLimitFromCondCached(
+      Cache, L, Op1, ExitIfTrue, /*ControlsOnlyExit=*/false, AllowPredicates);
+
   // EitherMayExit is true in these two cases:
   //   br (and Op0 Op1), loop, exit
   //   br (or  Op0 Op1), exit, loop
   bool EitherMayExit = IsAnd ^ ExitIfTrue;
-  ExitLimit EL0 = computeExitLimitFromCondCached(
-      Cache, L, Op0, ExitIfTrue, ControlsOnlyExit && !EitherMayExit,
-      AllowPredicates);
-  ExitLimit EL1 = computeExitLimitFromCondCached(
-      Cache, L, Op1, ExitIfTrue, ControlsOnlyExit && !EitherMayExit,
-      AllowPredicates);
-
-  // Be robust against unsimplified IR for the form "op i1 X, NeutralElement"
-  const Constant *NeutralElement = ConstantInt::get(ExitCond->getType(), IsAnd);
-  if (isa<ConstantInt>(Op1))
-    return Op1 == NeutralElement ? EL0 : EL1;
-  if (isa<ConstantInt>(Op0))
-    return Op0 == NeutralElement ? EL1 : EL0;
 
   const SCEV *BECount = getCouldNotCompute();
   const SCEV *ConstantMaxBECount = getCouldNotCompute();
diff --git a/llvm/test/Analysis/ScalarEvolution/or-exit-cond.ll b/llvm/test/Analysis/ScalarEvolution/or-exit-cond.ll
index 1010cc1a88f4f..f918b20fdb552 100644
--- a/llvm/test/Analysis/ScalarEvolution/or-exit-cond.ll
+++ b/llvm/test/Analysis/ScalarEvolution/or-exit-cond.ll
@@ -7,25 +7,25 @@ define i32 @last_loaded_with_or_exit(i32 %n, ptr %arr) mustprogress {
 ; CHECK-LABEL: 'last_loaded_with_or_exit'
 ; CHECK-NEXT:  Classifying expressions for: @last_loaded_with_or_exit
 ; CHECK-NEXT:    %iv = phi i32 [ %iv.next, %latch ], [ 0, %entry ]
-; CHECK-NEXT:    --> {0,+,1}<nuw><%header> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT:    --> {0,+,1}<%header> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
 ; CHECK-NEXT:    %last_loaded = phi i32 [ %last_loaded.next, %latch ], [ 0, %entry ]
 ; CHECK-NEXT:    --> %last_loaded U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Variant }
 ; CHECK-NEXT:    %iv_minus_one = add i32 -1, %iv
-; CHECK-NEXT:    --> {-1,+,1}<nuw><%header> U: [-1,0) S: [-1,0) Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT:    --> {-1,+,1}<%header> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
 ; CHECK-NEXT:    %stay = or i1 %iv_lt_n, %iv_minus_one_lt_n
 ; CHECK-NEXT:    --> (%iv_minus_one_lt_n umax %iv_lt_n) U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Variant }
 ; CHECK-NEXT:    %body_iv_minus_one = add i32 -1, %iv
-; CHECK-NEXT:    --> {-1,+,1}<nuw><%header> U: [-1,0) S: [-1,0) Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT:    --> {-1,+,1}<%header> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
 ; CHECK-NEXT:    %idx = zext nneg i32 %body_iv_minus_one to i64
-; CHECK-NEXT:    --> {4294967295,+,1}<nuw><%header> U: [4294967295,0) S: [4294967295,0) Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT:    --> (zext i32 {-1,+,1}<%header> to i64) U: [0,4294967296) S: [0,4294967296) Exits: <<Unknown>> LoopDispositions: { %header: Computable }
 ; CHECK-NEXT:    %addr = getelementptr inbounds i32, ptr %arr, i64 %idx
-; CHECK-NEXT:    --> {(17179869180 + %arr),+,4}<%header> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT:    --> ((4 * (zext i32 {-1,+,1}<%header> to i64))<nuw><nsw> + %arr) U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
 ; CHECK-NEXT:    %loaded = load i32, ptr %addr, align 4
 ; CHECK-NEXT:    --> %loaded U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Variant }
 ; CHECK-NEXT:    %last_loaded.next = phi i32 [ %last_loaded, %body ], [ %loaded, %do_load ]
 ; CHECK-NEXT:    --> %last_loaded.next U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Variant }
 ; CHECK-NEXT:    %iv.next = add i32 %iv, 1
-; CHECK-NEXT:    --> {1,+,1}<nw><%header> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT:    --> {1,+,1}<%header> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
 ; CHECK-NEXT:  Determining loop execution counts for: @last_loaded_with_or_exit
 ; CHECK-NEXT:  Loop %header: Unpredictable backedge-taken count.
 ; CHECK-NEXT:  Loop %header: Unpredictable constant max backedge-taken count.
diff --git a/llvm/test/Analysis/ScalarEvolution/trip-count-andor-selectform.ll b/llvm/test/Analysis/ScalarEvolution/trip-count-andor-selectform.ll
index ebdf7d309ca46..7e052d65d1c8e 100644
--- a/llvm/test/Analysis/ScalarEvolution/trip-count-andor-selectform.ll
+++ b/llvm/test/Analysis/ScalarEvolution/trip-count-andor-selectform.ll
@@ -51,9 +51,9 @@ leave:
 define void @unsimplified_and3(i32 %n) {
 ; CHECK-LABEL: 'unsimplified_and3'
 ; CHECK-NEXT:  Determining loop execution counts for: @unsimplified_and3
-; CHECK-NEXT:  Loop %loop: backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i1 false
+; CHECK-NEXT:  Loop %loop: backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i32 0
 ; CHECK-NEXT:  Loop %loop: Trip multiple is 1
 ;
 entry:
@@ -73,9 +73,9 @@ leave:
 define void @unsimplified_and4(i32 %n) {
 ; CHECK-LABEL: 'unsimplified_and4'
 ; CHECK-NEXT:  Determining loop execution counts for: @unsimplified_and4
-; CHECK-NEXT:  Loop %loop: backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i1 false
+; CHECK-NEXT:  Loop %loop: backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i32 0
 ; CHECK-NEXT:  Loop %loop: Trip multiple is 1
 ;
 entry:
@@ -267,9 +267,9 @@ leave:
 define void @reversed_or1(i32 %n) {
 ; CHECK-LABEL: 'reversed_or1'
 ; CHECK-NEXT:  Determining loop execution counts for: @reversed_or1
-; CHECK-NEXT:  Loop %loop: backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i1 false
+; CHECK-NEXT:  Loop %loop: backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i32 0
 ; CHECK-NEXT:  Loop %loop: Trip multiple is 1
 ;
 entry:
@@ -289,9 +289,9 @@ leave:
 define void @reversed_or2(i32 %n) {
 ; CHECK-LABEL: 'reversed_or2'
 ; CHECK-NEXT:  Determining loop execution counts for: @reversed_or2
-; CHECK-NEXT:  Loop %loop: backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i1 false
+; CHECK-NEXT:  Loop %loop: backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i32 0
 ; CHECK-NEXT:  Loop %loop: Trip multiple is 1
 ;
 entry:
diff --git a/llvm/test/Analysis/ScalarEvolution/trip-count-andor.ll b/llvm/test/Analysis/ScalarEvolution/trip-count-andor.ll
index 9917a72c47e1a..8df88f2d58621 100644
--- a/llvm/test/Analysis/ScalarEvolution/trip-count-andor.ll
+++ b/llvm/test/Analysis/ScalarEvolution/trip-count-andor.ll
@@ -51,9 +51,9 @@ leave:
 define void @unsimplified_and3(i32 %n) {
 ; CHECK-LABEL: 'unsimplified_and3'
 ; CHECK-NEXT:  Determining loop execution counts for: @unsimplified_and3
-; CHECK-NEXT:  Loop %loop: backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i1 false
+; CHECK-NEXT:  Loop %loop: backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i32 0
 ; CHECK-NEXT:  Loop %loop: Trip multiple is 1
 ;
 entry:
@@ -73,9 +73,9 @@ leave:
 define void @unsimplified_and4(i32 %n) {
 ; CHECK-LABEL: 'unsimplified_and4'
 ; CHECK-NEXT:  Determining loop execution counts for: @unsimplified_and4
-; CHECK-NEXT:  Loop %loop: backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i1 false
+; CHECK-NEXT:  Loop %loop: backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i32 0
 ; CHECK-NEXT:  Loop %loop: Trip multiple is 1
 ;
 entry:
@@ -267,9 +267,9 @@ leave:
 define void @reversed_or1(i32 %n) {
 ; CHECK-LABEL: 'reversed_or1'
 ; CHECK-NEXT:  Determining loop execution counts for: @reversed_or1
-; CHECK-NEXT:  Loop %loop: backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i1 false
+; CHECK-NEXT:  Loop %loop: backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i32 0
 ; CHECK-NEXT:  Loop %loop: Trip multiple is 1
 ;
 entry:
@@ -289,9 +289,9 @@ leave:
 define void @reversed_or2(i32 %n) {
 ; CHECK-LABEL: 'reversed_or2'
 ; CHECK-NEXT:  Determining loop execution counts for: @reversed_or2
-; CHECK-NEXT:  Loop %loop: backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i1 false
-; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i1 false
+; CHECK-NEXT:  Loop %loop: backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i32 0
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i32 0
 ; CHECK-NEXT:  Loop %loop: Trip multiple is 1
 ;
 entry:
diff --git a/llvm/test/Transforms/IndVarSimplify/or-exit-cond.ll b/llvm/test/Transforms/IndVarSimplify/or-exit-cond.ll
index a82172a17405b..221f4e86df60a 100644
--- a/llvm/test/Transforms/IndVarSimplify/or-exit-cond.ll
+++ b/llvm/test/Transforms/IndVarSimplify/or-exit-cond.ll
@@ -7,24 +7,27 @@ define i32 @last_loaded_with_or_exit(i32 %n, ptr %arr) mustprogress {
 ; CHECK-LABEL: define i32 @last_loaded_with_or_exit(
 ; CHECK-SAME: i32 [[N:%.*]], ptr [[ARR:%.*]]) #[[ATTR0:[0-9]+]] {
 ; CHECK-NEXT:  [[ENTRY:.*]]:
-; CHECK-NEXT:    [[TMP0:%.*]] = zext i32 [[N]] to i64
 ; CHECK-NEXT:    br label %[[HEADER:.*]]
 ; CHECK:       [[HEADER]]:
-; CHECK-NEXT:    [[INDVARS_IV:%.*]] = phi i64 [ [[INDVARS_IV_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[ENTRY]] ]
 ; CHECK-NEXT:    [[LAST_LOADED:%.*]] = phi i32 [ [[LAST_LOADED_NEXT:%.*]], %[[LATCH]] ], [ 0, %[[ENTRY]] ]
-; CHECK-NEXT:    [[IV_LT_N:%.*]] = icmp ult i64 [[INDVARS_IV]], [[TMP0]]
-; CHECK-NEXT:    [[STAY:%.*]] = or i1 [[IV_LT_N]], false
+; CHECK-NEXT:    [[IV_LT_N:%.*]] = icmp ult i32 [[IV]], [[N]]
+; CHECK-NEXT:    [[IV_MINUS_ONE:%.*]] = add i32 -1, [[IV]]
+; CHECK-NEXT:    [[IV_MINUS_ONE_LT_N:%.*]] = icmp ult i32 [[IV_MINUS_ONE]], [[N]]
+; CHECK-NEXT:    [[STAY:%.*]] = or i1 [[IV_LT_N]], [[IV_MINUS_ONE_LT_N]]
 ; CHECK-NEXT:    br i1 [[STAY]], label %[[BODY:.*]], label %[[EXIT:.*]]
 ; CHECK:       [[BODY]]:
-; CHECK-NEXT:    [[IDX:%.*]] = add nuw i64 4294967295, [[INDVARS_IV]]
-; CHECK-NEXT:    br i1 false, label %[[DO_LOAD:.*]], label %[[LATCH]]
+; CHECK-NEXT:    [[BODY_IV_MINUS_ONE:%.*]] = add i32 -1, [[IV]]
+; CHECK-NEXT:    [[BODY_IV_MINUS_ONE_NONNEG:%.*]] = icmp sgt i32 [[BODY_IV_MINUS_ONE]], -1
+; CHECK-NEXT:    br i1 [[BODY_IV_MINUS_ONE_NONNEG]], label %[[DO_LOAD:.*]], label %[[LATCH]]
 ; CHECK:       [[DO_LOAD]]:
+; CHECK-NEXT:    [[IDX:%.*]] = zext nneg i32 [[BODY_IV_MINUS_ONE]] to i64
 ; CHECK-NEXT:    [[ADDR:%.*]] = getelementptr inbounds i32, ptr [[ARR]], i64 [[IDX]]
 ; CHECK-NEXT:    [[LOADED:%.*]] = load i32, ptr [[ADDR]], align 4
 ; CHECK-NEXT:    br label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    [[LAST_LOADED_NEXT]] = phi i32 [ [[LAST_LOADED]], %[[BODY]] ], [ [[LOADED]], %[[DO_LOAD]] ]
-; CHECK-NEXT:    [[INDVARS_IV_NEXT]] = add i64 [[INDVARS_IV]], 1
+; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
 ; CHECK-NEXT:    br label %[[HEADER]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    [[LAST_LOADED_LCSSA:%.*]] = phi i32 [ [[LAST_LOADED]], %[[HEADER]] ]



More information about the llvm-commits mailing list