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
67 lines
2.8 KiB
LLVM
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
|
|
}
|