llvm-mirror/test/Transforms/SimpleLoopUnswitch/implicit-null-checks.ll
Max Kazantsev c10031a2ab [SimpleLoopUnswitch] Preserve make.implicit in non-trivial unswitch if legal
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
2020-07-31 11:38:43 +07:00

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 = !{}