
There are a number of issues with the current code for converting ule -> ult (etc) predicates for comparisons controlling finite loops: * It sets nowrap flags, which may only hold for that particular comparison, not globally. (PR60944) * It doesn't check that the RHS is invariant. (I'm not sure this can cause practical issues independently of the previous point.) * It runs before simplifications that may be more profitable. (PR54191) This patch moves the handling for this into computeExitLimitFromICmp(), because it is somewhat tightly coupled with assumptions in that code, and addresses the aforementioned issues. Fixes https://github.com/llvm/llvm-project/issues/60944. Fixes https://github.com/llvm/llvm-project/issues/54191. Differential Revision: https://reviews.llvm.org/D145510
200 lines
5.9 KiB
LLVM
200 lines
5.9 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt -passes='loop(indvars),instcombine' -S < %s | FileCheck %s
|
|
|
|
;; Test that loop's exit value is rewritten to its initial
|
|
;; value from loop preheader
|
|
define i32 @test1(ptr %var) {
|
|
; CHECK-LABEL: @test1(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp eq ptr [[VAR:%.*]], null
|
|
; CHECK-NEXT: br label [[HEADER:%.*]]
|
|
; CHECK: header:
|
|
; CHECK-NEXT: br i1 [[COND]], label [[LOOP:%.*]], label [[EXIT:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: br label [[HEADER]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
entry:
|
|
%cond = icmp eq ptr %var, null
|
|
br label %header
|
|
|
|
header:
|
|
%phi_indvar = phi i32 [0, %entry], [%indvar, %loop]
|
|
br i1 %cond, label %loop, label %exit
|
|
|
|
loop:
|
|
%indvar = add i32 %phi_indvar, 1
|
|
br label %header
|
|
|
|
exit:
|
|
ret i32 %phi_indvar
|
|
}
|
|
|
|
;; Test that we can not rewrite loop exit value if it's not
|
|
;; a phi node (%indvar is an add instruction in this test).
|
|
define i32 @test2(ptr %var) {
|
|
; CHECK-LABEL: @test2(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp eq ptr [[VAR:%.*]], null
|
|
; CHECK-NEXT: br label [[HEADER:%.*]]
|
|
; CHECK: header:
|
|
; CHECK-NEXT: [[PHI_INDVAR:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INDVAR:%.*]], [[HEADER]] ]
|
|
; CHECK-NEXT: [[INDVAR]] = add i32 [[PHI_INDVAR]], 1
|
|
; CHECK-NEXT: br i1 [[COND]], label [[HEADER]], label [[EXIT:%.*]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret i32 [[INDVAR]]
|
|
;
|
|
entry:
|
|
%cond = icmp eq ptr %var, null
|
|
br label %header
|
|
|
|
header:
|
|
%phi_indvar = phi i32 [0, %entry], [%indvar, %header]
|
|
%indvar = add i32 %phi_indvar, 1
|
|
br i1 %cond, label %header, label %exit
|
|
|
|
exit:
|
|
ret i32 %indvar
|
|
}
|
|
|
|
;; Test that we can not rewrite loop exit value if the condition
|
|
;; is not in loop header.
|
|
define i32 @test3(ptr %var) {
|
|
; CHECK-LABEL: @test3(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[COND1:%.*]] = icmp eq ptr [[VAR:%.*]], null
|
|
; CHECK-NEXT: br label [[HEADER:%.*]]
|
|
; CHECK: header:
|
|
; CHECK-NEXT: [[PHI_INDVAR:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INDVAR:%.*]], [[HEADER_BACKEDGE:%.*]] ]
|
|
; CHECK-NEXT: [[INDVAR]] = add i32 [[PHI_INDVAR]], 1
|
|
; CHECK-NEXT: [[COND2:%.*]] = icmp eq i32 [[INDVAR]], 10
|
|
; CHECK-NEXT: br i1 [[COND2]], label [[HEADER_BACKEDGE]], label [[BODY:%.*]]
|
|
; CHECK: header.backedge:
|
|
; CHECK-NEXT: br label [[HEADER]]
|
|
; CHECK: body:
|
|
; CHECK-NEXT: br i1 [[COND1]], label [[HEADER_BACKEDGE]], label [[EXIT:%.*]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret i32 [[PHI_INDVAR]]
|
|
;
|
|
entry:
|
|
%cond1 = icmp eq ptr %var, null
|
|
br label %header
|
|
|
|
header:
|
|
%phi_indvar = phi i32 [0, %entry], [%indvar, %header], [%indvar, %body]
|
|
%indvar = add i32 %phi_indvar, 1
|
|
%cond2 = icmp eq i32 %indvar, 10
|
|
br i1 %cond2, label %header, label %body
|
|
|
|
body:
|
|
br i1 %cond1, label %header, label %exit
|
|
|
|
exit:
|
|
ret i32 %phi_indvar
|
|
}
|
|
|
|
|
|
; Multiple exits dominating latch
|
|
define i32 @test4(i1 %cond1, i1 %cond2) {
|
|
; CHECK-LABEL: @test4(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[HEADER:%.*]]
|
|
; CHECK: header:
|
|
; CHECK-NEXT: br i1 [[COND1:%.*]], label [[LOOP:%.*]], label [[EXIT:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: br i1 [[COND2:%.*]], label [[HEADER]], label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
entry:
|
|
br label %header
|
|
|
|
header:
|
|
%phi_indvar = phi i32 [0, %entry], [%indvar, %loop]
|
|
br i1 %cond1, label %loop, label %exit
|
|
|
|
loop:
|
|
%indvar = add i32 %phi_indvar, 1
|
|
br i1 %cond2, label %header, label %exit
|
|
|
|
exit:
|
|
ret i32 %phi_indvar
|
|
}
|
|
|
|
; A conditionally executed exit.
|
|
define i32 @test5(ptr %addr, i1 %cond2) {
|
|
; CHECK-LABEL: @test5(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[HEADER:%.*]]
|
|
; CHECK: header:
|
|
; CHECK-NEXT: [[PHI_INDVAR:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INDVAR:%.*]], [[LOOP:%.*]] ]
|
|
; CHECK-NEXT: [[COND1:%.*]] = load volatile i1, ptr [[ADDR:%.*]], align 1
|
|
; CHECK-NEXT: br i1 [[COND1]], label [[LOOP]], label [[MAYBE:%.*]]
|
|
; CHECK: maybe:
|
|
; CHECK-NEXT: br i1 [[COND2:%.*]], label [[LOOP]], label [[EXIT:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: [[INDVAR]] = add i32 [[PHI_INDVAR]], 1
|
|
; CHECK-NEXT: br label [[HEADER]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret i32 [[PHI_INDVAR]]
|
|
;
|
|
entry:
|
|
br label %header
|
|
|
|
header:
|
|
%phi_indvar = phi i32 [0, %entry], [%indvar, %loop]
|
|
%cond1 = load volatile i1, ptr %addr
|
|
br i1 %cond1, label %loop, label %maybe
|
|
|
|
maybe:
|
|
br i1 %cond2, label %loop, label %exit
|
|
|
|
loop:
|
|
%indvar = add i32 %phi_indvar, 1
|
|
br label %header
|
|
|
|
exit:
|
|
ret i32 %phi_indvar
|
|
}
|
|
|
|
define i16 @pr57336(i16 %end, i16 %m) mustprogress {
|
|
; CHECK-LABEL: @pr57336(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
|
|
; CHECK: for.body:
|
|
; CHECK-NEXT: [[INC8:%.*]] = phi i16 [ [[INC:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
|
|
; CHECK-NEXT: [[INC]] = add nuw nsw i16 [[INC8]], 1
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul nsw i16 [[INC8]], [[M:%.*]]
|
|
; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp sgt i16 [[MUL]], [[END:%.*]]
|
|
; CHECK-NEXT: br i1 [[CMP_NOT]], label [[CRIT_EDGE:%.*]], label [[FOR_BODY]]
|
|
; CHECK: crit_edge:
|
|
; CHECK-NEXT: [[TMP0:%.*]] = add i16 [[END]], 1
|
|
; CHECK-NEXT: [[SMAX:%.*]] = call i16 @llvm.smax.i16(i16 [[TMP0]], i16 0)
|
|
; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i16 [[END]], 32767
|
|
; CHECK-NEXT: [[UMIN:%.*]] = zext i1 [[TMP1]] to i16
|
|
; CHECK-NEXT: [[TMP2:%.*]] = sub nsw i16 [[SMAX]], [[UMIN]]
|
|
; CHECK-NEXT: [[UMAX:%.*]] = call i16 @llvm.umax.i16(i16 [[M]], i16 1)
|
|
; CHECK-NEXT: [[TMP3:%.*]] = udiv i16 [[TMP2]], [[UMAX]]
|
|
; CHECK-NEXT: [[TMP4:%.*]] = add i16 [[TMP3]], [[UMIN]]
|
|
; CHECK-NEXT: ret i16 [[TMP4]]
|
|
;
|
|
entry:
|
|
br label %for.body
|
|
|
|
for.body:
|
|
%inc8 = phi i16 [ %inc, %for.body ], [ 0, %entry ]
|
|
%inc137 = phi i32 [ %inc1, %for.body ], [ 0, %entry ]
|
|
%inc1 = add nsw i32 %inc137, 1
|
|
%inc = add nsw i16 %inc8, 1
|
|
%mul = mul nsw i16 %m, %inc8
|
|
%cmp.not = icmp slt i16 %end, %mul
|
|
br i1 %cmp.not, label %crit_edge, label %for.body
|
|
|
|
crit_edge:
|
|
%inc137.lcssa = phi i32 [ %inc137, %for.body ]
|
|
%conv = trunc i32 %inc137.lcssa to i16
|
|
ret i16 %conv
|
|
}
|
|
|