Nikita Popov 3127b659fa
[CVP] Infer range return attribute (#99620)
We already infer this in IPSCCP (which runs very early, so cannot
benefit from inlining and simplifications) and SCCP (which runs without
PredicateInfo, so does not use assumes). Do it in CVP as well, so it can
handle cases that IPSCCP/SCCP can't.

Fixes https://github.com/llvm/llvm-project/issues/98946 (everything
apart from f2, where the assume is dropped by the frontend).
2024-09-20 14:29:19 +02:00

568 lines
15 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
; RUN: opt < %s -passes=correlated-propagation -S | FileCheck %s
define void @test0(i32 %a) {
; CHECK-LABEL: define void @test0(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[A]], 100
; CHECK-NEXT: br i1 [[CMP]], label [[BB:%.*]], label [[EXIT:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp = icmp slt i32 %a, 100
br i1 %cmp, label %bb, label %exit
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
define void @test1(i32 %a) {
; CHECK-LABEL: define void @test1(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[A]], 100
; CHECK-NEXT: br i1 [[CMP]], label [[BB:%.*]], label [[EXIT:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp = icmp ult i32 %a, 100
br i1 %cmp, label %bb, label %exit
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
define void @test2(i32 %a) {
; CHECK-LABEL: define void @test2(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[A]], -1
; CHECK-NEXT: br i1 [[CMP]], label [[BB:%.*]], label [[EXIT:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add nuw i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp = icmp ult i32 %a, -1
br i1 %cmp, label %bb, label %exit
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
define void @test3(i32 %a) {
; CHECK-LABEL: define void @test3(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[A]], -1
; CHECK-NEXT: br i1 [[CMP]], label [[BB:%.*]], label [[EXIT:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp = icmp ule i32 %a, -1
br i1 %cmp, label %bb, label %exit
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
define void @test4(i32 %a) {
; CHECK-LABEL: define void @test4(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[A]], 2147483647
; CHECK-NEXT: br i1 [[CMP]], label [[BB:%.*]], label [[EXIT:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp = icmp slt i32 %a, 2147483647
br i1 %cmp, label %bb, label %exit
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
define void @test5(i32 %a) {
; CHECK-LABEL: define void @test5(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 [[A]], 2147483647
; CHECK-NEXT: br i1 [[CMP]], label [[BB:%.*]], label [[EXIT:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp = icmp sle i32 %a, 2147483647
br i1 %cmp, label %bb, label %exit
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
; Check for a corner case where an integer value is represented with a constant
; LVILatticeValue instead of constantrange. Check that we don't fail with an
; assertion in this case.
@b = global i32 0, align 4
define void @test6(i32 %a) {
; CHECK-LABEL: define void @test6(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: bb:
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A]], ptrtoint (ptr @b to i32)
; CHECK-NEXT: ret void
;
bb:
%add = add i32 %a, ptrtoint (ptr @b to i32)
ret void
}
; Check that we can gather information for conditions is the form of
; and ( i s< 100, Unknown )
define void @test7(i32 %a, i1 %flag) {
; CHECK-LABEL: define void @test7(
; CHECK-SAME: i32 [[A:%.*]], i1 [[FLAG:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP_1:%.*]] = icmp slt i32 [[A]], 100
; CHECK-NEXT: [[CMP:%.*]] = and i1 [[CMP_1]], [[FLAG]]
; CHECK-NEXT: br i1 [[CMP]], label [[BB:%.*]], label [[EXIT:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp.1 = icmp slt i32 %a, 100
%cmp = and i1 %cmp.1, %flag
br i1 %cmp, label %bb, label %exit
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
; Check that we can gather information for conditions is the form of
; and ( i s< 100, i s> 0 )
define void @test8(i32 %a) {
; CHECK-LABEL: define void @test8(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP_1:%.*]] = icmp slt i32 [[A]], 100
; CHECK-NEXT: [[CMP_2:%.*]] = icmp sgt i32 [[A]], 0
; CHECK-NEXT: [[CMP:%.*]] = and i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT: br i1 [[CMP]], label [[BB:%.*]], label [[EXIT:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp.1 = icmp slt i32 %a, 100
%cmp.2 = icmp sgt i32 %a, 0
%cmp = and i1 %cmp.1, %cmp.2
br i1 %cmp, label %bb, label %exit
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
; Check that for conditions is the form of cond1 && cond2 we don't mistakenly
; assume that !cond1 && !cond2 holds down to false path.
define void @test8_neg(i32 %a) {
; CHECK-LABEL: define void @test8_neg(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP_1:%.*]] = icmp sge i32 [[A]], 100
; CHECK-NEXT: [[CMP_2:%.*]] = icmp sle i32 [[A]], 0
; CHECK-NEXT: [[CMP:%.*]] = and i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[BB:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp.1 = icmp sge i32 %a, 100
%cmp.2 = icmp sle i32 %a, 0
%cmp = and i1 %cmp.1, %cmp.2
br i1 %cmp, label %exit, label %bb
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
; Check that we can gather information for conditions is the form of
; and ( i s< 100, and (i s> 0, Unknown )
define void @test9(i32 %a, i1 %flag) {
; CHECK-LABEL: define void @test9(
; CHECK-SAME: i32 [[A:%.*]], i1 [[FLAG:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP_1:%.*]] = icmp slt i32 [[A]], 100
; CHECK-NEXT: [[CMP_2:%.*]] = icmp sgt i32 [[A]], 0
; CHECK-NEXT: [[CMP_3:%.*]] = and i1 [[CMP_2]], [[FLAG]]
; CHECK-NEXT: [[CMP:%.*]] = and i1 [[CMP_1]], [[CMP_3]]
; CHECK-NEXT: br i1 [[CMP]], label [[BB:%.*]], label [[EXIT:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp.1 = icmp slt i32 %a, 100
%cmp.2 = icmp sgt i32 %a, 0
%cmp.3 = and i1 %cmp.2, %flag
%cmp = and i1 %cmp.1, %cmp.3
br i1 %cmp, label %bb, label %exit
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
; Check that we can gather information for conditions is the form of
; and ( i s< Unknown, ... )
define void @test10(i32 %a, i32 %b, i1 %flag) {
; CHECK-LABEL: define void @test10(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]], i1 [[FLAG:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP_1:%.*]] = icmp slt i32 [[A]], [[B]]
; CHECK-NEXT: [[CMP:%.*]] = and i1 [[CMP_1]], [[FLAG]]
; CHECK-NEXT: br i1 [[CMP]], label [[BB:%.*]], label [[EXIT:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp.1 = icmp slt i32 %a, %b
%cmp = and i1 %cmp.1, %flag
br i1 %cmp, label %bb, label %exit
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
@limit = external global i32
define i32 @test11(ptr %p, i32 %i) {
; CHECK-LABEL: define range(i32 0, 2147483645) i32 @test11(
; CHECK-SAME: ptr [[P:%.*]], i32 [[I:%.*]]) {
; CHECK-NEXT: [[LIMIT:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0:![0-9]+]]
; CHECK-NEXT: [[WITHIN_1:%.*]] = icmp ugt i32 [[LIMIT]], [[I]]
; CHECK-NEXT: [[I_PLUS_7:%.*]] = add i32 [[I]], 7
; CHECK-NEXT: [[WITHIN_2:%.*]] = icmp ugt i32 [[LIMIT]], [[I_PLUS_7]]
; CHECK-NEXT: [[WITHIN:%.*]] = and i1 [[WITHIN_1]], [[WITHIN_2]]
; CHECK-NEXT: br i1 [[WITHIN]], label [[THEN:%.*]], label [[ELSE:%.*]]
; CHECK: then:
; CHECK-NEXT: [[I_PLUS_6:%.*]] = add nuw nsw i32 [[I]], 6
; CHECK-NEXT: ret i32 [[I_PLUS_6]]
; CHECK: else:
; CHECK-NEXT: ret i32 0
;
%limit = load i32, ptr %p, !range !{i32 0, i32 2147483647}
%within.1 = icmp ugt i32 %limit, %i
%i.plus.7 = add i32 %i, 7
%within.2 = icmp ugt i32 %limit, %i.plus.7
%within = and i1 %within.1, %within.2
br i1 %within, label %then, label %else
then:
%i.plus.6 = add i32 %i, 6
ret i32 %i.plus.6
else:
ret i32 0
}
; Check that we can gather information for conditions is the form of
; or ( i s>= 100, Unknown )
define void @test12(i32 %a, i1 %flag) {
; CHECK-LABEL: define void @test12(
; CHECK-SAME: i32 [[A:%.*]], i1 [[FLAG:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP_1:%.*]] = icmp sge i32 [[A]], 100
; CHECK-NEXT: [[CMP:%.*]] = or i1 [[CMP_1]], [[FLAG]]
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[BB:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp.1 = icmp sge i32 %a, 100
%cmp = or i1 %cmp.1, %flag
br i1 %cmp, label %exit, label %bb
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
; Check that we can gather information for conditions is the form of
; or ( i s>= 100, i s<= 0 )
define void @test13(i32 %a) {
; CHECK-LABEL: define void @test13(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP_1:%.*]] = icmp sge i32 [[A]], 100
; CHECK-NEXT: [[CMP_2:%.*]] = icmp sle i32 [[A]], 0
; CHECK-NEXT: [[CMP:%.*]] = or i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[BB:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp.1 = icmp sge i32 %a, 100
%cmp.2 = icmp sle i32 %a, 0
%cmp = or i1 %cmp.1, %cmp.2
br i1 %cmp, label %exit, label %bb
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
; Check that for conditions is the form of cond1 || cond2 we don't mistakenly
; assume that cond1 || cond2 holds down to true path.
define void @test13_neg(i32 %a) {
; CHECK-LABEL: define void @test13_neg(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP_1:%.*]] = icmp slt i32 [[A]], 100
; CHECK-NEXT: [[CMP_2:%.*]] = icmp sgt i32 [[A]], 0
; CHECK-NEXT: [[CMP:%.*]] = or i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT: br i1 [[CMP]], label [[BB:%.*]], label [[EXIT:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp.1 = icmp slt i32 %a, 100
%cmp.2 = icmp sgt i32 %a, 0
%cmp = or i1 %cmp.1, %cmp.2
br i1 %cmp, label %bb, label %exit
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
; Check that we can gather information for conditions is the form of
; or ( i s>=100, or (i s<= 0, Unknown )
define void @test14(i32 %a, i1 %flag) {
; CHECK-LABEL: define void @test14(
; CHECK-SAME: i32 [[A:%.*]], i1 [[FLAG:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP_1:%.*]] = icmp sge i32 [[A]], 100
; CHECK-NEXT: [[CMP_2:%.*]] = icmp sle i32 [[A]], 0
; CHECK-NEXT: [[CMP_3:%.*]] = or i1 [[CMP_2]], [[FLAG]]
; CHECK-NEXT: [[CMP:%.*]] = or i1 [[CMP_1]], [[CMP_3]]
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[BB:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp.1 = icmp sge i32 %a, 100
%cmp.2 = icmp sle i32 %a, 0
%cmp.3 = or i1 %cmp.2, %flag
%cmp = or i1 %cmp.1, %cmp.3
br i1 %cmp, label %exit, label %bb
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
; Check that we can gather information for conditions is the form of
; or ( i s>= Unknown, ... )
define void @test15(i32 %a, i32 %b, i1 %flag) {
; CHECK-LABEL: define void @test15(
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]], i1 [[FLAG:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP_1:%.*]] = icmp sge i32 [[A]], [[B]]
; CHECK-NEXT: [[CMP:%.*]] = or i1 [[CMP_1]], [[FLAG]]
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[BB:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%cmp.1 = icmp sge i32 %a, %b
%cmp = or i1 %cmp.1, %flag
br i1 %cmp, label %exit, label %bb
bb:
%add = add i32 %a, 1
br label %exit
exit:
ret void
}
; single basic block loop
; because the loop exit condition is SLT, we can supplement the iv add
; (iv.next def) with an nsw.
define i32 @test16(ptr %n, ptr %a) {
; CHECK-LABEL: define i32 @test16(
; CHECK-SAME: ptr [[N:%.*]], ptr [[A:%.*]]) {
; CHECK-NEXT: preheader:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[PREHEADER:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT: [[ACC:%.*]] = phi i32 [ 0, [[PREHEADER]] ], [ [[ACC_CURR:%.*]], [[LOOP]] ]
; CHECK-NEXT: [[X:%.*]] = load atomic i32, ptr [[A]] unordered, align 8
; CHECK-NEXT: fence acquire
; CHECK-NEXT: [[ACC_CURR]] = add i32 [[ACC]], [[X]]
; CHECK-NEXT: [[IV_NEXT]] = add nsw i32 [[IV]], 1
; CHECK-NEXT: [[NVAL:%.*]] = load atomic i32, ptr [[N]] unordered, align 8
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV_NEXT]], [[NVAL]]
; CHECK-NEXT: br i1 [[CMP]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[ACC_CURR]]
;
preheader:
br label %loop
loop:
%iv = phi i32 [ 0, %preheader ], [ %iv.next, %loop ]
%acc = phi i32 [ 0, %preheader ], [ %acc.curr, %loop ]
%x = load atomic i32, ptr %a unordered, align 8
fence acquire
%acc.curr = add i32 %acc, %x
%iv.next = add i32 %iv, 1
%nval = load atomic i32, ptr %n unordered, align 8
%cmp = icmp slt i32 %iv.next, %nval
br i1 %cmp, label %loop, label %exit
exit:
ret i32 %acc.curr
}
define i32 @test_undef_range(i32 %x) {
; CHECK-LABEL: define i32 @test_undef_range(
; CHECK-SAME: i32 [[X:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i32 [[X]], label [[JOIN:%.*]] [
; CHECK-NEXT: i32 1, label [[CASE1:%.*]]
; CHECK-NEXT: i32 2, label [[CASE2:%.*]]
; CHECK-NEXT: ]
; CHECK: case1:
; CHECK-NEXT: br label [[JOIN]]
; CHECK: case2:
; CHECK-NEXT: br label [[JOIN]]
; CHECK: join:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 1, [[CASE1]] ], [ 2, [[CASE2]] ], [ undef, [[ENTRY:%.*]] ]
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[PHI]], 1
; CHECK-NEXT: ret i32 [[ADD]]
;
entry:
switch i32 %x, label %join [
i32 1, label %case1
i32 2, label %case2
]
case1:
br label %join
case2:
br label %join
join:
%phi = phi i32 [ 1, %case1 ], [ 2, %case2 ], [ undef, %entry ]
%add = add i32 %phi, 1
ret i32 %add
}
;.
; CHECK: [[RNG0]] = !{i32 0, i32 2147483647}
;.