Files
Florian Hahn b689f8b7c6 [SCEV] Limit ControlsOnlyExit logic to BinOps with neutral elements. (#194831)
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

PR: https://github.com/llvm/llvm-project/pull/194831
2026-04-30 09:21:15 +00:00

67 lines
2.8 KiB
LLVM

; 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: br label %[[HEADER:.*]]
; CHECK: [[HEADER]]:
; 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 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: [[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: [[IV_NEXT]] = add i32 [[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
}