mirror of
https://github.com/RPCS3/llvm.git
synced 2026-01-31 01:25:19 +01:00
IRCE for unsigned latch conditions was temporarily disabled by rL314881. The motivating example contained an unsigned latch condition and a signed range check. One of the safe iteration ranges was `[1, SINT_MAX + 1]`. Its right border was incorrectly interpreted as a negative value in `IntersectRange` function, this lead to a miscompile under which we deleted a range check without inserting a postloop where it was needed. This patch brings back IRCE for unsigned latch conditions. Now we treat range intersection more carefully. If the latch condition was unsigned, we only try to consider a range check for deletion if: 1. The range check is also unsigned, or 2. Safe iteration range of the range check lies within `[0, SINT_MAX]`. The same is done for signed latch. Values from `[0, SINT_MAX]` are unambiguous, these values are non-negative under any interpretation, and all values of a range intersected with such range are also non-negative. We also use signed/unsigned min/max functions for range intersection depending on type of the latch condition. Differential Revision: https://reviews.llvm.org/D38581 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@316552 91177308-0d34-0410-b5e6-96231b3b80d8
272 lines
11 KiB
LLVM
272 lines
11 KiB
LLVM
; RUN: opt -verify-loop-info -irce-print-changed-loops -irce -S < %s 2>&1 | FileCheck %s
|
|
|
|
; CHECK-LABEL: irce: in function test_01: constrained Loop at depth 1 containing:
|
|
; CHECK-LABEL: irce: in function test_02: constrained Loop at depth 1 containing:
|
|
; CHECK-NOT: irce: in function test_03: constrained Loop
|
|
; CHECK-NOT: irce: in function test_04: constrained Loop
|
|
; CHECK-LABEL: irce: in function test_05: constrained Loop at depth 1 containing:
|
|
|
|
; This test used to demonstrate a miscompile: the outer loop's IV iterates in
|
|
; range of [2, 400) and the range check is done against value 331. Due to a bug
|
|
; in range intersection IRCE manages to eliminate the range check without
|
|
; inserting a postloop, which is incorrect. We treat the range of this test as
|
|
; an unsigned range and are able to intersect ranges correctly and insert a
|
|
; postloop.
|
|
|
|
define void @test_01() {
|
|
|
|
; CHECK-LABEL: test_01
|
|
; CHECK-NOT: preloop
|
|
; CHECK: range_check_block: ; preds = %inner_loop
|
|
; CHECK-NEXT: %range_check = icmp slt i32 %iv, 331
|
|
; CHECK-NEXT: br i1 true, label %loop_latch
|
|
; CHECK: loop_latch:
|
|
; CHECK-NEXT: %iv_next = add i32 %iv, 1
|
|
; CHECK-NEXT: %loop_cond = icmp ult i32 %iv_next, 400
|
|
; CHECK-NEXT: [[COND:%[^ ]+]] = icmp ult i32 %iv_next, 331
|
|
; CHECK-NEXT: br i1 [[COND]], label %loop_header, label %main.exit.selector
|
|
; CHECK: main.exit.selector: ; preds = %loop_latch
|
|
; CHECK-NEXT: %iv_next.lcssa = phi i32 [ %iv_next, %loop_latch ]
|
|
; CHECK-NEXT: %iv.lcssa = phi i32 [ %iv, %loop_latch ]
|
|
; CHECK-NEXT: [[MES_COND:%[^ ]+]] = icmp ult i32 %iv_next.lcssa, 400
|
|
; CHECK-NEXT: br i1 [[MES_COND]], label %main.pseudo.exit, label %exit
|
|
; CHECK: loop_latch.postloop: ; preds = %range_check_block.postloop
|
|
; CHECK-NEXT: %iv_next.postloop = add i32 %iv.postloop, 1
|
|
; CHECK-NEXT: %loop_cond.postloop = icmp ult i32 %iv_next.postloop, 400
|
|
; CHECK-NEXT: br i1 %loop_cond.postloop, label %loop_header.postloop, label %exit.loopexit
|
|
|
|
entry:
|
|
br label %loop_header
|
|
|
|
loop_header: ; preds = %loop_latch, %entry
|
|
%iv = phi i32 [ 2, %entry ], [ %iv_next, %loop_latch ]
|
|
%iv.prev = phi i32 [ 1, %entry ], [ %iv, %loop_latch ]
|
|
%tmp2 = icmp sgt i32 %iv.prev, -1
|
|
br i1 %tmp2, label %loop_header.split.us, label %exit
|
|
|
|
loop_header.split.us: ; preds = %loop_header
|
|
br label %inner_loop
|
|
|
|
inner_loop: ; preds = %inner_loop, %loop_header.split.us
|
|
%inner_iv = phi i32 [ 1, %loop_header.split.us ], [ %inner_iv_next, %inner_loop ]
|
|
%inner_iv_next = add nuw nsw i32 %inner_iv, 1
|
|
%inner_cond = icmp ult i32 %inner_iv_next, 31
|
|
br i1 %inner_cond, label %inner_loop, label %range_check_block
|
|
|
|
exit: ; preds = %loop_latch, %loop_header
|
|
ret void
|
|
|
|
range_check_block: ; preds = %inner_loop
|
|
%range_check = icmp slt i32 %iv, 331
|
|
br i1 %range_check, label %loop_latch, label %deopt
|
|
|
|
loop_latch: ; preds = %range_check_block
|
|
%iv_next = add i32 %iv, 1
|
|
%loop_cond = icmp ult i32 %iv_next, 400
|
|
br i1 %loop_cond, label %loop_header, label %exit
|
|
|
|
deopt: ; preds = %range_check_block
|
|
ret void
|
|
}
|
|
|
|
; Similar to test_01, but here the range check is done against 450. No postloop
|
|
; is required.
|
|
|
|
define void @test_02() {
|
|
|
|
; CHECK-LABEL: test_02
|
|
; CHECK-NOT: preloop
|
|
; CHECK-NOT: postloop
|
|
; CHECK: range_check_block: ; preds = %inner_loop
|
|
; CHECK-NEXT: %range_check = icmp slt i32 %iv, 450
|
|
; CHECK-NEXT: br i1 true, label %loop_latch
|
|
; CHECK: loop_latch: ; preds = %range_check_block
|
|
; CHECK-NEXT: %iv_next = add i32 %iv, 1
|
|
; CHECK-NEXT: %loop_cond = icmp ult i32 %iv_next, 400
|
|
; CHECK-NEXT: br i1 %loop_cond, label %loop_header, label %exit
|
|
|
|
entry:
|
|
br label %loop_header
|
|
|
|
loop_header: ; preds = %loop_latch, %entry
|
|
%iv = phi i32 [ 2, %entry ], [ %iv_next, %loop_latch ]
|
|
%iv.prev = phi i32 [ 1, %entry ], [ %iv, %loop_latch ]
|
|
%tmp2 = icmp sgt i32 %iv.prev, -1
|
|
br i1 %tmp2, label %loop_header.split.us, label %exit
|
|
|
|
loop_header.split.us: ; preds = %loop_header
|
|
br label %inner_loop
|
|
|
|
inner_loop: ; preds = %inner_loop, %loop_header.split.us
|
|
%inner_iv = phi i32 [ 1, %loop_header.split.us ], [ %inner_iv_next, %inner_loop ]
|
|
%inner_iv_next = add nuw nsw i32 %inner_iv, 1
|
|
%inner_cond = icmp ult i32 %inner_iv_next, 31
|
|
br i1 %inner_cond, label %inner_loop, label %range_check_block
|
|
|
|
exit: ; preds = %loop_latch, %loop_header
|
|
ret void
|
|
|
|
range_check_block: ; preds = %inner_loop
|
|
%range_check = icmp slt i32 %iv, 450
|
|
br i1 %range_check, label %loop_latch, label %deopt
|
|
|
|
loop_latch: ; preds = %range_check_block
|
|
%iv_next = add i32 %iv, 1
|
|
%loop_cond = icmp ult i32 %iv_next, 400
|
|
br i1 %loop_cond, label %loop_header, label %exit
|
|
|
|
deopt: ; preds = %range_check_block
|
|
ret void
|
|
}
|
|
|
|
; Range check is made against 0, so the safe iteration range is empty. IRCE
|
|
; should not apply.
|
|
|
|
define void @test_03() {
|
|
|
|
; CHECK-LABEL: test_03
|
|
|
|
entry:
|
|
br label %loop_header
|
|
|
|
loop_header: ; preds = %loop_latch, %entry
|
|
%iv = phi i32 [ 2, %entry ], [ %iv_next, %loop_latch ]
|
|
%iv.prev = phi i32 [ 1, %entry ], [ %iv, %loop_latch ]
|
|
%tmp2 = icmp sgt i32 %iv.prev, -1
|
|
br i1 %tmp2, label %loop_header.split.us, label %exit
|
|
|
|
loop_header.split.us: ; preds = %loop_header
|
|
br label %inner_loop
|
|
|
|
inner_loop: ; preds = %inner_loop, %loop_header.split.us
|
|
%inner_iv = phi i32 [ 1, %loop_header.split.us ], [ %inner_iv_next, %inner_loop ]
|
|
%inner_iv_next = add nuw nsw i32 %inner_iv, 1
|
|
%inner_cond = icmp ult i32 %inner_iv_next, 31
|
|
br i1 %inner_cond, label %inner_loop, label %range_check_block
|
|
|
|
exit: ; preds = %loop_latch, %loop_header
|
|
ret void
|
|
|
|
range_check_block: ; preds = %inner_loop
|
|
%range_check = icmp slt i32 %iv, 0
|
|
br i1 %range_check, label %loop_latch, label %deopt
|
|
|
|
loop_latch: ; preds = %range_check_block
|
|
%iv_next = add i32 %iv, 1
|
|
%loop_cond = icmp ult i32 %iv_next, 400
|
|
br i1 %loop_cond, label %loop_header, label %exit
|
|
|
|
deopt: ; preds = %range_check_block
|
|
ret void
|
|
}
|
|
|
|
; We do not know whether %n is positive or negative, so we prohibit IRCE in
|
|
; order to avoid incorrect intersection of signed and unsigned ranges.
|
|
|
|
define void @test_04(i32* %p) {
|
|
|
|
; CHECK-LABEL: test_04
|
|
|
|
entry:
|
|
%n = load i32, i32* %p
|
|
br label %loop_header
|
|
|
|
loop_header: ; preds = %loop_latch, %entry
|
|
%iv = phi i32 [ 2, %entry ], [ %iv_next, %loop_latch ]
|
|
%iv.prev = phi i32 [ 1, %entry ], [ %iv, %loop_latch ]
|
|
%tmp2 = icmp sgt i32 %iv.prev, -1
|
|
br i1 %tmp2, label %loop_header.split.us, label %exit
|
|
|
|
loop_header.split.us: ; preds = %loop_header
|
|
br label %inner_loop
|
|
|
|
inner_loop: ; preds = %inner_loop, %loop_header.split.us
|
|
%inner_iv = phi i32 [ 1, %loop_header.split.us ], [ %inner_iv_next, %inner_loop ]
|
|
%inner_iv_next = add nuw nsw i32 %inner_iv, 1
|
|
%inner_cond = icmp ult i32 %inner_iv_next, 31
|
|
br i1 %inner_cond, label %inner_loop, label %range_check_block
|
|
|
|
exit: ; preds = %loop_latch, %loop_header
|
|
ret void
|
|
|
|
range_check_block: ; preds = %inner_loop
|
|
%range_check = icmp slt i32 %iv, %n
|
|
br i1 %range_check, label %loop_latch, label %deopt
|
|
|
|
loop_latch: ; preds = %range_check_block
|
|
%iv_next = add i32 %iv, 1
|
|
%loop_cond = icmp ult i32 %iv_next, 400
|
|
br i1 %loop_cond, label %loop_header, label %exit
|
|
|
|
deopt: ; preds = %range_check_block
|
|
ret void
|
|
}
|
|
|
|
; Same as test_04, but range guarantees that %n is positive. So we can safely
|
|
; intersect ranges (with insertion of postloop).
|
|
|
|
define void @test_05(i32* %p) {
|
|
|
|
; CHECK-LABEL: test_05
|
|
; CHECK-NOT: preloop
|
|
; CHECK: entry:
|
|
; CHECK-NEXT: %n = load i32, i32* %p, !range !6
|
|
; CHECK-NEXT: [[CMP_1:%[^ ]+]] = icmp ugt i32 %n, 2
|
|
; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP_1]], i32 %n, i32 2
|
|
; CHECK-NEXT: [[CMP_2:%[^ ]+]] = icmp ult i32 2, %exit.mainloop.at
|
|
; CHECK-NEXT: br i1 [[CMP_2]], label %loop_header.preheader, label %main.pseudo.exit
|
|
; CHECK: range_check_block: ; preds = %inner_loop
|
|
; CHECK-NEXT: %range_check = icmp slt i32 %iv, %n
|
|
; CHECK-NEXT: br i1 true, label %loop_latch, label %deopt.loopexit2
|
|
; CHECK: loop_latch: ; preds = %range_check_block
|
|
; CHECK-NEXT: %iv_next = add i32 %iv, 1
|
|
; CHECK-NEXT: %loop_cond = icmp ult i32 %iv_next, 400
|
|
; CHECK-NEXT: [[COND:%[^ ]+]] = icmp ult i32 %iv_next, %exit.mainloop.at
|
|
; CHECK-NEXT: br i1 [[COND]], label %loop_header, label %main.exit.selector
|
|
; CHECK: main.exit.selector: ; preds = %loop_latch
|
|
; CHECK-NEXT: %iv_next.lcssa = phi i32 [ %iv_next, %loop_latch ]
|
|
; CHECK-NEXT: %iv.lcssa = phi i32 [ %iv, %loop_latch ]
|
|
; CHECK-NEXT: [[MES_COND:%[^ ]+]] = icmp ult i32 %iv_next.lcssa, 400
|
|
; CHECK-NEXT: br i1 [[MES_COND]], label %main.pseudo.exit, label %exit
|
|
; CHECK: loop_latch.postloop: ; preds = %range_check_block.postloop
|
|
; CHECK-NEXT: %iv_next.postloop = add i32 %iv.postloop, 1
|
|
; CHECK-NEXT: %loop_cond.postloop = icmp ult i32 %iv_next.postloop, 400
|
|
; CHECK-NEXT: br i1 %loop_cond.postloop, label %loop_header.postloop, label %exit.loopexit
|
|
|
|
entry:
|
|
%n = load i32, i32* %p, !range !0
|
|
br label %loop_header
|
|
|
|
loop_header: ; preds = %loop_latch, %entry
|
|
%iv = phi i32 [ 2, %entry ], [ %iv_next, %loop_latch ]
|
|
%iv.prev = phi i32 [ 1, %entry ], [ %iv, %loop_latch ]
|
|
%tmp2 = icmp sgt i32 %iv.prev, -1
|
|
br i1 %tmp2, label %loop_header.split.us, label %exit
|
|
|
|
loop_header.split.us: ; preds = %loop_header
|
|
br label %inner_loop
|
|
|
|
inner_loop: ; preds = %inner_loop, %loop_header.split.us
|
|
%inner_iv = phi i32 [ 1, %loop_header.split.us ], [ %inner_iv_next, %inner_loop ]
|
|
%inner_iv_next = add nuw nsw i32 %inner_iv, 1
|
|
%inner_cond = icmp ult i32 %inner_iv_next, 31
|
|
br i1 %inner_cond, label %inner_loop, label %range_check_block
|
|
|
|
exit: ; preds = %loop_latch, %loop_header
|
|
ret void
|
|
|
|
range_check_block: ; preds = %inner_loop
|
|
%range_check = icmp slt i32 %iv, %n
|
|
br i1 %range_check, label %loop_latch, label %deopt
|
|
|
|
loop_latch: ; preds = %range_check_block
|
|
%iv_next = add i32 %iv, 1
|
|
%loop_cond = icmp ult i32 %iv_next, 400
|
|
br i1 %loop_cond, label %loop_header, label %exit
|
|
|
|
deopt: ; preds = %range_check_block
|
|
ret void
|
|
}
|
|
|
|
!0 = !{i32 0, i32 50}
|