Files
clang-p2996/llvm/test/Transforms/GVN/PRE/pre-loop-load.ll
Nikita Popov 79b55e5038 [GVN] Fix test for loop load PRE on alloca (NFC)
This test was not modifying the pointer in the loop, so the loads
just ended up as undef, without relation to loop load PRE.

Pass the alloca to the called function, so the memory is
potentially modified.
2021-08-22 22:28:53 +02:00

942 lines
29 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -basic-aa -enable-load-pre -enable-pre -lcssa -gvn -S < %s | FileCheck %s
declare void @side_effect() nofree
declare i1 @side_effect_cond() nofree
declare void @may_free_memory()
declare i32 @personality_function()
; We can PRE the load from gc-managed memory away from the hot path.
define i32 @test_load_on_cold_path_gc(i32 addrspace(1)* %p) gc "statepoint-example" personality i32 ()* @"personality_function" {
; CHECK-LABEL: @test_load_on_cold_path_gc(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[X_PRE1:%.*]] = load i32, i32 addrspace(1)* [[P:%.*]], align 4
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[X_PRE1]], [[ENTRY:%.*]] ], [ [[X2:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE]] ]
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: call void @may_free_memory()
; CHECK-NEXT: [[X_PRE:%.*]] = load i32, i32 addrspace(1)* [[P]], align 4
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[X2]] = phi i32 [ [[X_PRE]], [[COLD_PATH]] ], [ [[X]], [[HOT_PATH]] ]
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%x = load i32, i32 addrspace(1)* %p
%cond = icmp ne i32 %x, 0
br i1 %cond, label %hot_path, label %cold_path
hot_path:
br label %backedge
cold_path:
call void @may_free_memory()
br label %backedge
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
}
; Do not PRE here because a loop-variant pointer.
define i32 @test_load_on_cold_path_gc_variant_neg(i32 addrspace(1)* addrspace(1)* %pp) gc "statepoint-example" personality i32 ()* @"personality_function" {
; CHECK-LABEL: @test_load_on_cold_path_gc_variant_neg(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[P:%.*]] = load volatile i32 addrspace(1)*, i32 addrspace(1)* addrspace(1)* [[PP:%.*]], align 8
; CHECK-NEXT: [[X:%.*]] = load i32, i32 addrspace(1)* [[P]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: call void @may_free_memory()
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%p = load volatile i32 addrspace(1)*, i32 addrspace(1)* addrspace(1)* %pp
%x = load i32, i32 addrspace(1)* %p
%cond = icmp ne i32 %x, 0
br i1 %cond, label %hot_path, label %cold_path
hot_path:
br label %backedge
cold_path:
call void @may_free_memory()
br label %backedge
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
}
; TODO: We can PRE the load away from the hot path.
define i32 @test_load_on_cold_path(i32* %p) {
; CHECK-LABEL: @test_load_on_cold_path(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: call void @side_effect() #[[ATTR0:[0-9]+]]
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%x = load i32, i32* %p
%cond = icmp ne i32 %x, 0
br i1 %cond, label %hot_path, label %cold_path
hot_path:
br label %backedge
cold_path:
call void @side_effect() nofree
br label %backedge
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
}
; We should NOT PRE here because the function on the cold path may possibly
; free the memory under the pointer.
define i32 @test_load_on_cold_path_may_free_memory_neg(i32* %p) {
; CHECK-LABEL: @test_load_on_cold_path_may_free_memory_neg(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: call void @may_free_memory()
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%x = load i32, i32* %p
%cond = icmp ne i32 %x, 0
br i1 %cond, label %hot_path, label %cold_path
hot_path:
br label %backedge
cold_path:
call void @may_free_memory()
br label %backedge
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
}
declare void @may_modify_or_free_pointer(i32* %p)
; TODO: Despite the fact that the function may free memory in general, it
; cannot free memory allocated by alloca.
define i32 @test_load_on_cold_path_may_free_memory_alloca() {
; CHECK-LABEL: @test_load_on_cold_path_may_free_memory_alloca(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[P:%.*]] = alloca i32, align 4
; CHECK-NEXT: call void @may_modify_or_free_pointer(i32* [[P]])
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: call void @may_modify_or_free_pointer(i32* [[P]])
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
;
entry:
%p = alloca i32
call void @may_modify_or_free_pointer(i32* %p)
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%x = load i32, i32* %p
%cond = icmp ne i32 %x, 0
br i1 %cond, label %hot_path, label %cold_path
hot_path:
br label %backedge
cold_path:
call void @may_modify_or_free_pointer(i32* %p)
br label %backedge
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
}
; PRE here is meaningless, so we should not do it.
define i32 @test_load_on_both_paths(i32* %p) {
; CHECK-LABEL: @test_load_on_both_paths(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%x = load i32, i32* %p
%cond = icmp ne i32 %x, 0
br i1 %cond, label %hot_path, label %cold_path
hot_path:
call void @side_effect() nofree
br label %backedge
cold_path:
call void @side_effect() nofree
br label %backedge
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
}
; We could PRE here, but it doesn't seem very profitable.
define i32 @test_load_on_backedge(i32* %p) {
; CHECK-LABEL: @test_load_on_backedge(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%x = load i32, i32* %p
%cond = icmp ne i32 %x, 0
br i1 %cond, label %hot_path, label %cold_path
hot_path:
br label %backedge
cold_path:
br label %backedge
backedge:
%iv.next = add i32 %iv, %x
call void @side_effect() nofree
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
}
; TODO: We can PRE via splitting of the critical edge in the cold path.
define i32 @test_load_on_exiting_cold_path_01(i32* %p) {
; CHECK-LABEL: @test_load_on_exiting_cold_path_01(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: [[SIDE_COND:%.*]] = call i1 @side_effect_cond() #[[ATTR0]]
; CHECK-NEXT: br i1 [[SIDE_COND]], label [[BACKEDGE]], label [[COLD_EXIT:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
; CHECK: cold_exit:
; CHECK-NEXT: ret i32 -1
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%x = load i32, i32* %p
%cond = icmp ne i32 %x, 0
br i1 %cond, label %hot_path, label %cold_path
hot_path:
br label %backedge
cold_path:
%side_cond = call i1 @side_effect_cond() nofree
br i1 %side_cond, label %backedge, label %cold_exit
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
cold_exit:
ret i32 -1
}
; TODO: We can PRE via splitting of the critical edge in the cold path.
define i32 @test_load_on_exiting_cold_path_02(i32* %p) gc "statepoint-example" personality i32 ()* @personality_function {
; CHECK-LABEL: @test_load_on_exiting_cold_path_02(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: invoke void @side_effect()
; CHECK-NEXT: to label [[BACKEDGE]] unwind label [[COLD_EXIT:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
; CHECK: cold_exit:
; CHECK-NEXT: [[LANDING_PAD:%.*]] = landingpad token
; CHECK-NEXT: cleanup
; CHECK-NEXT: ret i32 -1
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%x = load i32, i32* %p
%cond = icmp ne i32 %x, 0
br i1 %cond, label %hot_path, label %cold_path
hot_path:
br label %backedge
cold_path:
invoke void @side_effect() to label %backedge unwind label %cold_exit
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
cold_exit:
%landing_pad = landingpad token
cleanup
ret i32 -1
}
; Make sure we do not insert load into both cold path & backedge.
define i32 @test_load_on_cold_path_and_backedge(i32* %p) {
; CHECK-LABEL: @test_load_on_cold_path_and_backedge(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%x = load i32, i32* %p
%cond = icmp ne i32 %x, 0
br i1 %cond, label %hot_path, label %cold_path
hot_path:
br label %backedge
cold_path:
call void @side_effect() nofree
br label %backedge
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
call void @side_effect() nofree
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
}
; TODO: We can PRE the load away from the hot path. Make sure we only insert 1 load.
define i32 @test_load_multi_block_cold_path(i32* %p) {
; CHECK-LABEL: @test_load_multi_block_cold_path(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH_1:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path.1:
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%x = load i32, i32* %p
%cond = icmp ne i32 %x, 0
br i1 %cond, label %hot_path, label %cold_path.1
hot_path:
br label %backedge
cold_path.1:
call void @side_effect() nofree
br label %cold_path.2
cold_path.2:
call void @side_effect() nofree
br label %cold_path.3
cold_path.3:
call void @side_effect() nofree
br label %backedge
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
call void @side_effect() nofree
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
}
; TODO: We can PRE via splitting of the critical edge in the cold path. Make sure we only insert 1 load.
define i32 @test_load_on_multi_exiting_cold_path(i32* %p) {
; CHECK-LABEL: @test_load_on_multi_exiting_cold_path(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH_1:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path.1:
; CHECK-NEXT: [[SIDE_COND_1:%.*]] = call i1 @side_effect_cond() #[[ATTR0]]
; CHECK-NEXT: br i1 [[SIDE_COND_1]], label [[COLD_PATH_2:%.*]], label [[COLD_EXIT:%.*]]
; CHECK: cold_path.2:
; CHECK-NEXT: [[SIDE_COND_2:%.*]] = call i1 @side_effect_cond() #[[ATTR0]]
; CHECK-NEXT: br i1 [[SIDE_COND_2]], label [[COLD_PATH_3:%.*]], label [[COLD_EXIT]]
; CHECK: cold_path.3:
; CHECK-NEXT: [[SIDE_COND_3:%.*]] = call i1 @side_effect_cond() #[[ATTR0]]
; CHECK-NEXT: br i1 [[SIDE_COND_3]], label [[BACKEDGE]], label [[COLD_EXIT]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
; CHECK: cold_exit:
; CHECK-NEXT: ret i32 -1
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%x = load i32, i32* %p
%cond = icmp ne i32 %x, 0
br i1 %cond, label %hot_path, label %cold_path.1
hot_path:
br label %backedge
cold_path.1:
%side_cond.1 = call i1 @side_effect_cond() nofree
br i1 %side_cond.1, label %cold_path.2, label %cold_exit
cold_path.2:
%side_cond.2 = call i1 @side_effect_cond() nofree
br i1 %side_cond.2, label %cold_path.3, label %cold_exit
cold_path.3:
%side_cond.3 = call i1 @side_effect_cond() nofree
br i1 %side_cond.3, label %backedge, label %cold_exit
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
cold_exit:
ret i32 -1
}
; TODO: PRE via splittinga backedge in the cold loop. Make sure we don't insert a load into an inner loop.
define i32 @test_inner_loop(i32* %p) {
; CHECK-LABEL: @test_inner_loop(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: br label [[INNER_LOOP:%.*]]
; CHECK: inner_loop:
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: br i1 undef, label [[INNER_LOOP]], label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%x = load i32, i32* %p
%cond = icmp ne i32 %x, 0
br i1 %cond, label %hot_path, label %cold_path
hot_path:
br label %backedge
cold_path:
br label %inner_loop
inner_loop:
call void @side_effect() nofree
br i1 undef, label %inner_loop, label %backedge
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
}
; TODO: We can PRE here, but profitablility depends on frequency of cold blocks. Conservatively, we should not do it unless there is a reason.
define i32 @test_multiple_cold_paths(i32* %p) {
; CHECK-LABEL: @test_multiple_cold_paths(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4
; CHECK-NEXT: [[COND_1:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND_1]], label [[HOT_PATH_1:%.*]], label [[COLD_PATH_1:%.*]]
; CHECK: hot_path.1:
; CHECK-NEXT: br label [[DOM_1:%.*]]
; CHECK: cold_path.1:
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: br label [[DOM_1]]
; CHECK: dom.1:
; CHECK-NEXT: [[COND_2:%.*]] = icmp ne i32 [[X]], 1
; CHECK-NEXT: br i1 [[COND_2]], label [[HOT_PATH_2:%.*]], label [[COLD_PATH_2:%.*]]
; CHECK: hot_path.2:
; CHECK-NEXT: br label [[DOM_2:%.*]]
; CHECK: cold_path.2:
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: br label [[DOM_2]]
; CHECK: dom.2:
; CHECK-NEXT: [[COND_3:%.*]] = icmp ne i32 [[X]], 2
; CHECK-NEXT: br i1 [[COND_3]], label [[HOT_PATH_3:%.*]], label [[COLD_PATH_3:%.*]]
; CHECK: hot_path.3:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path.3:
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%x = load i32, i32* %p
%cond.1 = icmp ne i32 %x, 0
br i1 %cond.1, label %hot_path.1, label %cold_path.1
hot_path.1:
br label %dom.1
cold_path.1:
call void @side_effect() nofree
br label %dom.1
dom.1:
%cond.2 = icmp ne i32 %x, 1
br i1 %cond.2, label %hot_path.2, label %cold_path.2
hot_path.2:
br label %dom.2
cold_path.2:
call void @side_effect() nofree
br label %dom.2
dom.2:
%cond.3 = icmp ne i32 %x, 2
br i1 %cond.3, label %hot_path.3, label %cold_path.3
hot_path.3:
br label %backedge
cold_path.3:
call void @side_effect() nofree
br label %backedge
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
}
; TODO: We can PRE via split of critical edge.
define i32 @test_side_exit_after_merge(i32* %p) {
; CHECK-LABEL: @test_side_exit_after_merge(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: [[COND_1:%.*]] = icmp ne i32 [[IV]], 1
; CHECK-NEXT: br i1 [[COND_1]], label [[DO_CALL:%.*]], label [[SIDE_EXITING:%.*]]
; CHECK: do_call:
; CHECK-NEXT: [[SIDE_COND:%.*]] = call i1 @side_effect_cond() #[[ATTR0]]
; CHECK-NEXT: br label [[SIDE_EXITING]]
; CHECK: side_exiting:
; CHECK-NEXT: [[SIDE_COND_PHI:%.*]] = phi i1 [ [[SIDE_COND]], [[DO_CALL]] ], [ true, [[COLD_PATH]] ]
; CHECK-NEXT: br i1 [[SIDE_COND_PHI]], label [[BACKEDGE]], label [[COLD_EXIT:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
; CHECK: cold_exit:
; CHECK-NEXT: ret i32 -1
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%x = load i32, i32* %p
%cond = icmp ne i32 %x, 0
br i1 %cond, label %hot_path, label %cold_path
hot_path:
br label %backedge
cold_path:
%cond.1 = icmp ne i32 %iv, 1
br i1 %cond.1, label %do_call, label %side_exiting
do_call:
%side_cond = call i1 @side_effect_cond() nofree
br label %side_exiting
side_exiting:
%side_cond_phi = phi i1 [%side_cond, %do_call], [true, %cold_path]
br i1 %side_cond_phi, label %backedge, label %cold_exit
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
cold_exit:
ret i32 -1
}
declare void @llvm.experimental.guard(i1, ...)
define i32 @test_guard_1(i32* %p, i32 %g) {
; CHECK-LABEL: @test_guard_1(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[GUARD_COND:%.*]] = icmp ne i32 [[IV]], [[G:%.*]]
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[GUARD_COND]]) [ "deopt"() ]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp ult i32 [[X]], 100
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%guard_cond = icmp ne i32 %iv, %g
call void(i1, ...) @llvm.experimental.guard(i1 %guard_cond) [ "deopt"() ]
%x = load i32, i32* %p
%cond = icmp ult i32 %x, 100
br i1 %cond, label %hot_path, label %cold_path
hot_path:
br label %backedge
cold_path:
call void @side_effect() nofree
br label %backedge
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
}
define i32 @test_guard_2(i32* %p, i32 %g) {
; CHECK-LABEL: @test_guard_2(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[GUARD_COND:%.*]] = icmp ne i32 [[IV]], [[G:%.*]]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[GUARD_COND]]) [ "deopt"() ]
; CHECK-NEXT: [[COND:%.*]] = icmp ult i32 [[X]], 100
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret i32 [[X]]
;
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry], [%iv.next, %backedge]
%guard_cond = icmp ne i32 %iv, %g
%x = load i32, i32* %p
call void(i1, ...) @llvm.experimental.guard(i1 %guard_cond) [ "deopt"() ]
%cond = icmp ult i32 %x, 100
br i1 %cond, label %hot_path, label %cold_path
hot_path:
br label %backedge
cold_path:
call void @side_effect() nofree
br label %backedge
backedge:
%iv.next = add i32 %iv, %x
%loop.cond = icmp ult i32 %iv.next, 1000
br i1 %loop.cond, label %loop, label %exit
exit:
ret i32 %x
}