This patch allows PRE of the following type of loads: ``` preheader: br label %loop loop: br i1 ..., label %merge, label %clobber clobber: call foo() // Clobbers %p br label %merge merge: ... br i1 ..., label %loop, label %exit ``` Into ``` preheader: %x0 = load %p br label %loop loop: %x.pre = phi(x0, x2) br i1 ..., label %merge, label %clobber clobber: call foo() // Clobbers %p %x1 = load %p br label %merge merge: x2 = phi(x.pre, x1) ... br i1 ..., label %loop, label %exit ``` So instead of loading from %p on every iteration, we load only when the actual clobber happens. The typical pattern which it is trying to address is: hot loop, with all code inlined and provably having no side effects, and some side-effecting calls on cold path. The worst overhead from it is, if we always take clobber block, we make 1 more load overall (in preheader). It only matters if loop has very few iteration. If clobber block is not taken at least once, the transform is neutral or profitable. There are several improvements prospect open up: - We can sometimes be smarter in loop-exiting blocks via split of critical edges; - If we have block frequency info, we can handle multiple clobbers. The only obstacle now is that we don't know if their sum is colder than the header. Differential Revision: https://reviews.llvm.org/D99926 Reviewed By: reames
937 lines
29 KiB
LLVM
937 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
|
|
}
|
|
|
|
; 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_free_memory()
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: br i1 undef, 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: br i1 false, label [[BACKEDGE_LOOP_CRIT_EDGE:%.*]], label [[EXIT:%.*]]
|
|
; CHECK: backedge.loop_crit_edge:
|
|
; CHECK-NEXT: br label [[LOOP]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret i32 undef
|
|
;
|
|
entry:
|
|
%p = alloca i32
|
|
call void @may_free_memory()
|
|
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
|
|
}
|
|
|
|
|
|
; 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
|
|
}
|