mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-12-11 13:37:07 +00:00
c10031a2ab
We can preserve make.implicit metadata in the split block if it is guaranteed that after following the branch we always reach the block where processing of null case happens, which is equivalent to "initial condition must execute if the loop is entered". Differential Revision: https://reviews.llvm.org/D84925 Reviewed By: asbirlea
316 lines
12 KiB
LLVM
316 lines
12 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt -enable-nontrivial-unswitch=true -simple-loop-unswitch -S < %s | FileCheck %s
|
|
; RUN: opt -enable-nontrivial-unswitch=true -passes='loop(unswitch),verify<loops>' -S < %s | FileCheck %s
|
|
|
|
declare void @may_exit()
|
|
declare void @throw_npe()
|
|
|
|
; It is illegal to preserve make_implicit notion of the condition being
|
|
; unswitched because we may exit loop before we reach the condition, so
|
|
; there is no guarantee that following implicit branch always means getting
|
|
; to throw_npe block.
|
|
define i32 @test_should_drop_make_implicit(i32* %p1, i32* %p2) {
|
|
; CHECK-LABEL: @test_should_drop_make_implicit(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[NULL_CHECK:%.*]] = icmp eq i32* [[P2:%.*]], null
|
|
; CHECK-NOT: !make.implicit
|
|
; CHECK-NEXT: br i1 [[NULL_CHECK]], label [[ENTRY_SPLIT_US:%.*]], label [[ENTRY_SPLIT:%.*]]
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label [[LOOP_US:%.*]]
|
|
; CHECK: loop.us:
|
|
; CHECK-NEXT: [[IV_US:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT_US]] ]
|
|
; CHECK-NEXT: [[X_US:%.*]] = load i32, i32* [[P1:%.*]], align 4
|
|
; CHECK-NEXT: [[SIDE_EXIT_COND_US:%.*]] = icmp eq i32 [[X_US]], 0
|
|
; CHECK-NEXT: br i1 [[SIDE_EXIT_COND_US]], label [[SIDE_EXIT_SPLIT_US:%.*]], label [[NULL_CHECK_BLOCK_US:%.*]]
|
|
; CHECK: null_check_block.us:
|
|
; CHECK-NEXT: br label [[THROW_NPE_SPLIT_US:%.*]]
|
|
; CHECK: side_exit.split.us:
|
|
; CHECK-NEXT: br label [[SIDE_EXIT:%.*]]
|
|
; CHECK: throw_npe.split.us:
|
|
; CHECK-NEXT: br label [[THROW_NPE:%.*]]
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
|
|
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P1]], align 4
|
|
; CHECK-NEXT: [[SIDE_EXIT_COND:%.*]] = icmp eq i32 [[X]], 0
|
|
; CHECK-NEXT: br i1 [[SIDE_EXIT_COND]], label [[SIDE_EXIT_SPLIT:%.*]], label [[NULL_CHECK_BLOCK:%.*]]
|
|
; CHECK: null_check_block:
|
|
; CHECK-NEXT: br label [[BACKEDGE]]
|
|
; CHECK: backedge:
|
|
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
|
|
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp slt i32 [[IV_NEXT]], 10000
|
|
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
|
|
; CHECK: side_exit.split:
|
|
; CHECK-NEXT: br label [[SIDE_EXIT]]
|
|
; CHECK: side_exit:
|
|
; CHECK-NEXT: ret i32 0
|
|
; CHECK: throw_npe:
|
|
; CHECK-NEXT: call void @throw_npe()
|
|
; CHECK-NEXT: unreachable
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: [[X_LCSSA2:%.*]] = phi i32 [ [[X]], [[BACKEDGE]] ]
|
|
; CHECK-NEXT: ret i32 [[X_LCSSA2]]
|
|
;
|
|
entry:
|
|
%null_check = icmp eq i32* %p2, null
|
|
br label %loop
|
|
loop:
|
|
%iv = phi i32 [0, %entry], [%iv.next, %backedge]
|
|
%x = load i32, i32* %p1
|
|
%side_exit_cond = icmp eq i32 %x, 0
|
|
br i1 %side_exit_cond, label %side_exit, label %null_check_block
|
|
|
|
null_check_block:
|
|
br i1 %null_check, label %throw_npe, label %backedge, !make.implicit !0
|
|
|
|
backedge:
|
|
%iv.next = add i32 %iv,1
|
|
%loop_cond = icmp slt i32 %iv.next, 10000
|
|
br i1 %loop_cond, label %loop, label %exit
|
|
|
|
side_exit:
|
|
ret i32 0
|
|
|
|
throw_npe:
|
|
call void @throw_npe()
|
|
unreachable
|
|
|
|
exit:
|
|
ret i32 %x
|
|
}
|
|
|
|
; Here make.implicit notion may be preserved because we always get to throw_npe
|
|
; after following true branch. This is a trivial unswitch.
|
|
define i32 @test_may_keep_make_implicit_trivial(i32* %p1, i32* %p2) {
|
|
; CHECK-LABEL: @test_may_keep_make_implicit_trivial(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[NULL_CHECK:%.*]] = icmp eq i32* [[P2:%.*]], null
|
|
; CHECK-NEXT: br i1 [[NULL_CHECK]], label [[THROW_NPE:%.*]], label [[ENTRY_SPLIT:%.*]], !make.implicit !0
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
|
|
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P1:%.*]], align 4
|
|
; CHECK-NEXT: [[SIDE_EXIT_COND:%.*]] = icmp eq i32 [[X]], 0
|
|
; CHECK-NEXT: br label [[SIDE_EXIT_BLOCK:%.*]]
|
|
; CHECK: side_exit_block:
|
|
; CHECK-NEXT: br i1 [[SIDE_EXIT_COND]], label [[SIDE_EXIT:%.*]], label [[BACKEDGE]]
|
|
; CHECK: backedge:
|
|
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
|
|
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp slt i32 [[IV_NEXT]], 10000
|
|
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
|
|
; CHECK: side_exit:
|
|
; CHECK-NEXT: ret i32 0
|
|
; CHECK: throw_npe:
|
|
; CHECK-NEXT: call void @throw_npe()
|
|
; CHECK-NEXT: unreachable
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: [[X_LCSSA2:%.*]] = phi i32 [ [[X]], [[BACKEDGE]] ]
|
|
; CHECK-NEXT: ret i32 [[X_LCSSA2]]
|
|
;
|
|
entry:
|
|
%null_check = icmp eq i32* %p2, null
|
|
br label %loop
|
|
loop:
|
|
%iv = phi i32 [0, %entry], [%iv.next, %backedge]
|
|
%x = load i32, i32* %p1
|
|
%side_exit_cond = icmp eq i32 %x, 0
|
|
br i1 %null_check, label %throw_npe, label %side_exit_block, !make.implicit !0
|
|
|
|
side_exit_block:
|
|
br i1 %side_exit_cond, label %side_exit, label %backedge
|
|
|
|
backedge:
|
|
%iv.next = add i32 %iv,1
|
|
%loop_cond = icmp slt i32 %iv.next, 10000
|
|
br i1 %loop_cond, label %loop, label %exit
|
|
|
|
side_exit:
|
|
ret i32 0
|
|
|
|
throw_npe:
|
|
call void @throw_npe()
|
|
unreachable
|
|
|
|
exit:
|
|
ret i32 %x
|
|
}
|
|
|
|
define i32 @test_may_keep_make_implicit_non_trivial(i32* %p1, i32* %p2) {
|
|
; CHECK-LABEL: @test_may_keep_make_implicit_non_trivial(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[NULL_CHECK:%.*]] = icmp eq i32* [[P2:%.*]], null
|
|
; CHECK-NEXT: br i1 [[NULL_CHECK]], label [[ENTRY_SPLIT_US:%.*]], label [[ENTRY_SPLIT:%.*]], !make.implicit !0
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label [[LOOP_US:%.*]]
|
|
; CHECK: loop.us:
|
|
; CHECK-NEXT: [[IV_US:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT_US]] ]
|
|
; CHECK-NEXT: [[X_US:%.*]] = load i32, i32* [[P1:%.*]], align 4
|
|
; CHECK-NEXT: [[INNER_BLOCK_COND_US:%.*]] = icmp eq i32 [[X_US]], 0
|
|
; CHECK-NEXT: br i1 [[INNER_BLOCK_COND_US]], label [[INNER_BLOCK_US:%.*]], label [[NULL_CHECK_BLOCK_US:%.*]]
|
|
; CHECK: inner_block.us:
|
|
; CHECK-NEXT: br label [[NULL_CHECK_BLOCK_US]]
|
|
; CHECK: null_check_block.us:
|
|
; CHECK-NEXT: br label [[THROW_NPE_SPLIT_US:%.*]]
|
|
; CHECK: throw_npe.split.us:
|
|
; CHECK-NEXT: br label [[THROW_NPE:%.*]]
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
|
|
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P1]], align 4
|
|
; CHECK-NEXT: [[INNER_BLOCK_COND:%.*]] = icmp eq i32 [[X]], 0
|
|
; CHECK-NEXT: br i1 [[INNER_BLOCK_COND]], label [[INNER_BLOCK:%.*]], label [[NULL_CHECK_BLOCK:%.*]]
|
|
; CHECK: inner_block:
|
|
; CHECK-NEXT: br label [[NULL_CHECK_BLOCK]]
|
|
; CHECK: null_check_block:
|
|
; CHECK-NEXT: br label [[BACKEDGE]]
|
|
; CHECK: backedge:
|
|
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
|
|
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp slt i32 [[IV_NEXT]], 10000
|
|
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
|
|
; CHECK: throw_npe:
|
|
; CHECK-NEXT: call void @throw_npe()
|
|
; CHECK-NEXT: unreachable
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: [[X_LCSSA1:%.*]] = phi i32 [ [[X]], [[BACKEDGE]] ]
|
|
; CHECK-NEXT: ret i32 [[X_LCSSA1]]
|
|
;
|
|
entry:
|
|
%null_check = icmp eq i32* %p2, null
|
|
br label %loop
|
|
loop:
|
|
%iv = phi i32 [0, %entry], [%iv.next, %backedge]
|
|
%x = load i32, i32* %p1
|
|
%inner_block_cond = icmp eq i32 %x, 0
|
|
br i1 %inner_block_cond, label %inner_block, label %null_check_block
|
|
|
|
inner_block:
|
|
br label %null_check_block
|
|
|
|
null_check_block:
|
|
br i1 %null_check, label %throw_npe, label %backedge, !make.implicit !0
|
|
|
|
backedge:
|
|
%iv.next = add i32 %iv,1
|
|
%loop_cond = icmp slt i32 %iv.next, 10000
|
|
br i1 %loop_cond, label %loop, label %exit
|
|
|
|
throw_npe:
|
|
call void @throw_npe()
|
|
unreachable
|
|
|
|
exit:
|
|
ret i32 %x
|
|
}
|
|
|
|
; Here make.implicit notion should be dropped because of exiting call.
|
|
define i32 @test_should_drop_make_implicit_exiting_call(i32* %p1, i32* %p2) {
|
|
; CHECK-LABEL: @test_should_drop_make_implicit_exiting_call(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[NULL_CHECK:%.*]] = icmp eq i32* [[P2:%.*]], null
|
|
; CHECK-NOT: !make.implicit
|
|
; CHECK-NEXT: br i1 [[NULL_CHECK]], label [[ENTRY_SPLIT_US:%.*]], label [[ENTRY_SPLIT:%.*]]
|
|
; CHECK: entry.split.us:
|
|
; CHECK-NEXT: br label [[LOOP_US:%.*]]
|
|
; CHECK: loop.us:
|
|
; CHECK-NEXT: [[IV_US:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT_US]] ]
|
|
; CHECK-NEXT: call void @may_exit()
|
|
; CHECK-NEXT: [[X_US:%.*]] = load i32, i32* [[P1:%.*]], align 4
|
|
; CHECK-NEXT: [[SIDE_EXIT_COND_US:%.*]] = icmp eq i32 [[X_US]], 0
|
|
; CHECK-NEXT: br label [[THROW_NPE_SPLIT_US:%.*]]
|
|
; CHECK: throw_npe.split.us:
|
|
; CHECK-NEXT: br label [[THROW_NPE:%.*]]
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
|
|
; CHECK-NEXT: call void @may_exit()
|
|
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P1]], align 4
|
|
; CHECK-NEXT: [[SIDE_EXIT_COND:%.*]] = icmp eq i32 [[X]], 0
|
|
; CHECK-NEXT: br label [[BACKEDGE]]
|
|
; CHECK: backedge:
|
|
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
|
|
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp slt i32 [[IV_NEXT]], 10000
|
|
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
|
|
; CHECK: throw_npe:
|
|
; CHECK-NEXT: call void @throw_npe()
|
|
; CHECK-NEXT: unreachable
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: [[X_LCSSA1:%.*]] = phi i32 [ [[X]], [[BACKEDGE]] ]
|
|
; CHECK-NEXT: ret i32 [[X_LCSSA1]]
|
|
;
|
|
entry:
|
|
%null_check = icmp eq i32* %p2, null
|
|
br label %loop
|
|
loop:
|
|
%iv = phi i32 [0, %entry], [%iv.next, %backedge]
|
|
call void @may_exit()
|
|
%x = load i32, i32* %p1
|
|
%side_exit_cond = icmp eq i32 %x, 0
|
|
br i1 %null_check, label %throw_npe, label %backedge, !make.implicit !0
|
|
|
|
backedge:
|
|
%iv.next = add i32 %iv,1
|
|
%loop_cond = icmp slt i32 %iv.next, 10000
|
|
br i1 %loop_cond, label %loop, label %exit
|
|
|
|
throw_npe:
|
|
call void @throw_npe()
|
|
unreachable
|
|
|
|
exit:
|
|
ret i32 %x
|
|
}
|
|
|
|
; Here exiting call goes after the null check, so make.implicit may be preserved.
|
|
define i32 @test_may_keep_make_implicit_exiting_call(i32* %p1, i32* %p2) {
|
|
; CHECK-LABEL: @test_may_keep_make_implicit_exiting_call(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[NULL_CHECK:%.*]] = icmp eq i32* [[P2:%.*]], null
|
|
; CHECK-NEXT: br i1 [[NULL_CHECK]], label [[THROW_NPE:%.*]], label [[ENTRY_SPLIT:%.*]], !make.implicit !0
|
|
; CHECK: entry.split:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY_SPLIT]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
|
|
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P1:%.*]], align 4
|
|
; CHECK-NEXT: [[SIDE_EXIT_COND:%.*]] = icmp eq i32 [[X]], 0
|
|
; CHECK-NEXT: br label [[BACKEDGE]]
|
|
; CHECK: backedge:
|
|
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
|
|
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp slt i32 [[IV_NEXT]], 10000
|
|
; CHECK-NEXT: call void @may_exit()
|
|
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
|
|
; CHECK: throw_npe:
|
|
; CHECK-NEXT: call void @throw_npe()
|
|
; CHECK-NEXT: unreachable
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: [[X_LCSSA1:%.*]] = phi i32 [ [[X]], [[BACKEDGE]] ]
|
|
; CHECK-NEXT: ret i32 [[X_LCSSA1]]
|
|
;
|
|
entry:
|
|
%null_check = icmp eq i32* %p2, null
|
|
br label %loop
|
|
loop:
|
|
%iv = phi i32 [0, %entry], [%iv.next, %backedge]
|
|
%x = load i32, i32* %p1
|
|
%side_exit_cond = icmp eq i32 %x, 0
|
|
br i1 %null_check, label %throw_npe, label %backedge, !make.implicit !0
|
|
|
|
backedge:
|
|
%iv.next = add i32 %iv,1
|
|
%loop_cond = icmp slt i32 %iv.next, 10000
|
|
call void @may_exit()
|
|
br i1 %loop_cond, label %loop, label %exit
|
|
|
|
throw_npe:
|
|
call void @throw_npe()
|
|
unreachable
|
|
|
|
exit:
|
|
ret i32 %x
|
|
}
|
|
|
|
!0 = !{}
|