1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-02-01 05:01:59 +01:00
llvm-mirror/test/Transforms/IRCE/range_intersect_miscompile.ll
Max Kazantsev 307d95073b [IRCE] Relax restriction on collected range checks
In IRCE, we have a very old legacy check that works when we collect comparisons that we
treat as range checks. It ensures that the value against which the indvar is compared is
loop invariant and is also positive.

This latter condition remained there since the times when IRCE was only able to handle
signed latch comparison. As the optimization evolved, it now learned how to intersect
signed or unsigned ranges, and this logic has no reliance on the fact that the right border
of each range should be positive.

The old implementation of this non-negativity check was also naive enough and just looked
into ranges (while most of other IRCE logic tries to use power of SCEV implications), so this
check did not allow to deal with the most simple case that looks like follows:

  int size; // not known non-negative
  int length; //known non-negative;
  i = 0;
  if (size != 0) {
    do {
      range_check(i < size);
      range_check(i < length);
    ++i;
    } while (i < size)
  }

In this case, even if from some dominating conditions IRCE could parse loop
structure, it could only remove the range check against `length` and simply
ignored the check against `size`.

In this patch we remove this obsolete check. It will allow IRCE to pick comparison
against `size` as a potential range check and then let Range Intersection logic
decide whether it is OK to eliminate it or not.

Differential Revision: https://reviews.llvm.org/D45362
Reviewed By: samparker

llvm-svn: 329547
2018-04-09 06:01:22 +00:00

288 lines
12 KiB
LLVM

; RUN: opt -verify-loop-info -irce-print-changed-loops -irce -S < %s 2>&1 | FileCheck %s
; RUN: opt -verify-loop-info -irce-print-changed-loops -passes='require<branch-prob>,loop(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-LABEL: irce: in function test_03: constrained Loop at depth 1 containing:
; CHECK-LABEL: irce: in function test_04: constrained Loop at depth 1 containing:
; 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 to the inner loop. The condition %tmp2 can be eliminated.
define void @test_03() {
; CHECK-LABEL: test_03
; CHECK-NOT: preloop
; CHECK-NOT: postloop
; CHECK: %tmp2 = icmp sgt i32 %iv.prev, -1
; CHECK-NEXT: br i1 true, label %loop_header.split.us, label %exit
; CHECK: range_check_block:
; CHECK-NEXT: %range_check = icmp slt i32 %iv, 0
; CHECK-NEXT: br i1 %range_check, label %loop_latch, label %deopt
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 can also properly eliminate range check against %n which is not always
; known positive.
define void @test_04(i32* %p) {
; CHECK-LABEL: test_04
; CHECK: entry
; CHECK-NOT: preloop
; CHECK: %tmp2 = icmp sgt i32 %iv.prev, -1
; CHECK-NEXT: br i1 true, label %loop_header.split.us, label %exit
; CHECK: range_check_block:
; CHECK-NEXT: %range_check = icmp slt i32 %iv, %n
; CHECK-NEXT: br i1 true, label %loop_latch, label %deopt
; CHECK: postloop:
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 !
; 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}