1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
; RUN: opt < %s -basic-aa -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(i32* %p, i32* %q, i1 %C, i32 %index, i32 %len) {
; CHECK-LABEL: @test_motivation(
block1:
%el1 = getelementptr inbounds i32, i32* %q, i32 %index
%el2 = getelementptr inbounds i32, i32* %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, i32* %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, i32* %P2
; CHECK: ret i32 %PRE
%P2 = phi i32* [%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, i32* %P2
ret i32 %PRE
}
; Guard in load's block that is above the load should prohibit the PRE.
define i32 @test_guard_01(i32* %p, i32* %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, i32* %p
br label %block4
block4:
; CHECK: block4:
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %G)
; CHECK-NEXT: load
; CHECK: ret i32
%P2 = phi i32* [%p, %block3], [%q, %block2]
call void (i1, ...) @llvm.experimental.guard(i1 %G) [ "deopt"() ]
%PRE = load i32, i32* %P2
ret i32 %PRE
}
; Guard in load's block that is below the load should not prohibit the PRE.
define i32 @test_guard_02(i32* %p, i32* %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, i32* %q
br label %block4
block3:
store i32 0, i32* %p
br label %block4
block4:
; CHECK: block4:
; CHECK-NEXT: phi i32 [
; CHECK-NEXT: phi i32* [
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %G)
; CHECK-NOT: load
; CHECK: ret i32
%P2 = phi i32* [%p, %block3], [%q, %block2]
%PRE = load i32, i32* %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(i32* %p, i32* %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, i32* %p
br label %block4
block4:
; CHECK: block4:
; CHECK-NEXT: phi i32*
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %G)
; CHECK-NEXT: load
; CHECK-NEXT: ret i32
%P2 = phi i32* [%p, %block3], [%q, %block2]
call void (i1, ...) @llvm.experimental.guard(i1 %G) [ "deopt"() ]
br label %block5
block5:
%PRE = load i32, i32* %P2
ret i32 %PRE
}
|