These intrinsics, not the icmp+select are the canonical form nowadays, so we might as well directly emit them. This should not cause any regressions, but if it does, then then they would needed to be fixed regardless. Note that this doesn't deal with `SCEVExpander::isHighCostExpansion()`, but that is a pessimization, not a correctness issue. Additionally, the non-intrinsic form has issues with undef, see https://reviews.llvm.org/D88287#2587863
287 lines
12 KiB
LLVM
287 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>,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, align 4, !range !
|
|
; CHECK-NEXT: %exit.mainloop.at = call i32 @llvm.umax.i32(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}
|