; RUN: opt < %s -passes=gvn -enable-load-pre -S | FileCheck %s
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
declare void @llvm.experimental.guard(i1, ...)
; This is a motivating example on why we prohibit hoisting through guards.
; In the bottom block, we check that the index is within bounds and only access
; the element in this case and deoptimize otherwise. If we hoist the load to a
; place above the guard, it will may lead to out-of-bound array access.
define i32 @test_motivation(ptr %p, ptr %q, i1 %C, i32 %index, i32 %len) {
; CHECK-LABEL: @test_motivation(
block1:
%el1 = getelementptr inbounds i32, ptr %q, i32 %index
%el2 = getelementptr inbounds i32, ptr %p, i32 %index
br i1 %C, label %block2, label %block3
block2:
; CHECK: block2:
; CHECK-NEXT: br
; CHECK-NOT: load
; CHECK-NOT: sge
; CHECK-NOT: slt
; CHECK-NOT: and
br label %block4
block3:
store i32 0, ptr %el1
br label %block4
block4:
; CHECK: block4:
; CHECK: %cond1 = icmp sge i32 %index, 0
; CHECK-NEXT: %cond2 = icmp slt i32 %index, %len
; CHECK-NEXT: %in.bounds = and i1 %cond1, %cond2
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %in.bounds)
; CHECK-NEXT: %PRE = load i32, ptr %P2
; CHECK: ret i32 %PRE
%P2 = phi ptr [%el2, %block3], [%el1, %block2]
%cond1 = icmp sge i32 %index, 0
%cond2 = icmp slt i32 %index, %len
%in.bounds = and i1 %cond1, %cond2
call void (i1, ...) @llvm.experimental.guard(i1 %in.bounds) [ "deopt"() ]
%PRE = load i32, ptr %P2
ret i32 %PRE
}
; Guard in load's block that is above the load should prohibit the PRE.
define i32 @test_guard_01(ptr %p, ptr %q, i1 %C, i1 %G) {
; CHECK-LABEL: @test_guard_01(
block1:
br i1 %C, label %block2, label %block3
block2:
; CHECK: block2:
; CHECK-NEXT: br
; CHECK-NOT: load
br label %block4
block3:
store i32 0, ptr %p
br label %block4
block4:
; CHECK: block4:
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %G)
; CHECK-NEXT: load
; CHECK: ret i32
%P2 = phi ptr [%p, %block3], [%q, %block2]
call void (i1, ...) @llvm.experimental.guard(i1 %G) [ "deopt"() ]
%PRE = load i32, ptr %P2
ret i32 %PRE
}
; Guard in load's block that is below the load should not prohibit the PRE.
define i32 @test_guard_02(ptr %p, ptr %q, i1 %C, i1 %G) {
; CHECK-LABEL: @test_guard_02(
block1:
br i1 %C, label %block2, label %block3
block2:
; CHECK: block2:
; CHECK-NEXT: load i32, ptr %q
br label %block4
block3:
store i32 0, ptr %p
br label %block4
block4:
; CHECK: block4:
; CHECK-NEXT: phi i32 [
; CHECK-NEXT: phi ptr [
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %G)
; CHECK-NOT: load
; CHECK: ret i32
%P2 = phi ptr [%p, %block3], [%q, %block2]
%PRE = load i32, ptr %P2
call void (i1, ...) @llvm.experimental.guard(i1 %G) [ "deopt"() ]
ret i32 %PRE
}
; Guard above the load's block should prevent PRE from hoisting through it.
define i32 @test_guard_03(ptr %p, ptr %q, i1 %C, i1 %G) {
; CHECK-LABEL: @test_guard_03(
block1:
br i1 %C, label %block2, label %block3
block2:
; CHECK: block2:
; CHECK-NEXT: br
; CHECK-NOT: load
br label %block4
block3:
store i32 0, ptr %p
br label %block4
block4:
; CHECK: block4:
; CHECK-NEXT: phi ptr
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %G)
; CHECK-NEXT: load
; CHECK-NEXT: ret i32
%P2 = phi ptr [%p, %block3], [%q, %block2]
call void (i1, ...) @llvm.experimental.guard(i1 %G) [ "deopt"() ]
br label %block5
block5:
%PRE = load i32, ptr %P2
ret i32 %PRE
}