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 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
; RUN: opt < %s -aa-pipeline=basic-aa,globals-aa -passes='require<globals-aa>,gvn' -S | FileCheck %s
;
; This tests the safe no-alias conclusions of GMR -- when there is
; a non-escaping global as one indentified underlying object and some pointer
; that would inherently have escaped any other function as the other underlying
; pointer of an alias query.
@g1 = internal global i32 0
define i32 @test1(ptr %param) {
; Ensure that we can fold a store to a load of a global across a store to
; a parameter when the global is non-escaping.
;
; CHECK-LABEL: @test1(
; CHECK: store i32 42, ptr @g1
; CHECK-NOT: load i32
; CHECK: ret i32 42
entry:
store i32 42, ptr @g1
store i32 7, ptr %param
%v = load i32, ptr @g1
ret i32 %v
}
@g1_tls = internal thread_local global i32 0
define i32 @test1_tls(ptr %param) {
; Ensure that we can fold a store to a load of a global across a store to
; a parameter when the global is non-escaping.
;
; CHECK-LABEL: define i32 @test1_tls(
; CHECK-SAME: ptr [[PARAM:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[P:%.*]] = call ptr @llvm.threadlocal.address.p0(ptr @g1_tls)
; CHECK-NEXT: store i32 42, ptr [[P]], align 4
; CHECK-NEXT: store i32 7, ptr [[PARAM]], align 4
; CHECK-NEXT: ret i32 42
;
entry:
%p = call ptr @llvm.threadlocal.address(ptr @g1_tls)
store i32 42, ptr %p
store i32 7, ptr %param
%p2 = call ptr @llvm.threadlocal.address(ptr @g1_tls)
%v = load i32, ptr %p2
ret i32 %v
}
define ptr @test1_tls_noopt(ptr %coro, ptr %param) presplitcoroutine {
; CHECK-LABEL: define ptr @test1_tls_noopt(
; CHECK-SAME: ptr [[CORO:%.*]], ptr [[PARAM:%.*]]) #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[P:%.*]] = call ptr @llvm.threadlocal.address.p0(ptr @g1_tls)
; CHECK-NEXT: store i32 42, ptr [[P]], align 4
; CHECK-NEXT: [[TMP0:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false)
; CHECK-NEXT: switch i8 [[TMP0]], label [[SUSPEND:%.*]] [
; CHECK-NEXT: i8 0, label [[RESUME:%.*]]
; CHECK-NEXT: i8 1, label [[SUSPEND]]
; CHECK-NEXT: ]
; CHECK: resume:
; CHECK-NEXT: [[P2:%.*]] = call ptr @llvm.threadlocal.address.p0(ptr @g1_tls)
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[P2]], align 4
; CHECK-NEXT: store i32 [[V]], ptr [[PARAM]], align 4
; CHECK-NEXT: ret ptr [[CORO]]
; CHECK: suspend:
; CHECK-NEXT: [[TMP1:%.*]] = call i1 @llvm.coro.end(ptr [[CORO]], i1 false, token none)
; CHECK-NEXT: ret ptr [[CORO]]
;
entry:
%p = call ptr @llvm.threadlocal.address(ptr @g1_tls)
store i32 42, ptr %p
%0 = call i8 @llvm.coro.suspend(token none, i1 false)
switch i8 %0, label %suspend [i8 0, label %resume
i8 1, label %suspend]
resume:
%p2 = call ptr @llvm.threadlocal.address(ptr @g1_tls)
%v = load i32, ptr %p2
store i32 %v, ptr %param, align 4
ret ptr %coro
suspend:
call i1 @llvm.coro.end(ptr %coro, i1 0, token none)
ret ptr %coro
}
declare ptr @f()
define i32 @test2() {
; Ensure that we can fold a store to a load of a global across a store to
; the pointer returned by a function call. Since the global could not escape,
; this function cannot be returning its address.
;
; CHECK-LABEL: @test2(
; CHECK: store i32 42, ptr @g1
; CHECK-NOT: load i32
; CHECK: ret i32 42
entry:
%ptr = call ptr @f() readnone
store i32 42, ptr @g1
store i32 7, ptr %ptr
%v = load i32, ptr @g1
ret i32 %v
}
@g2 = external global ptr
define i32 @test3() {
; Ensure that we can fold a store to a load of a global across a store to
; the pointer loaded from that global. Because the global does not escape, it
; cannot alias a pointer loaded out of a global.
;
; CHECK-LABEL: @test3(
; CHECK: store i32 42, ptr @g1
; CHECK: store i32 7, ptr
; CHECK-NOT: load i32
; CHECK: ret i32 42
entry:
store i32 42, ptr @g1
%ptr1 = load ptr, ptr @g2
store i32 7, ptr %ptr1
%v = load i32, ptr @g1
ret i32 %v
}
@g3 = internal global i32 1
@g4 = internal global [10 x ptr] zeroinitializer
define i32 @test4(ptr %param, i32 %n, i1 %c1, i1 %c2, i1 %c3) {
; Ensure that we can fold a store to a load of a global across a store to
; the pointer loaded from that global even when the load is behind PHIs and
; selects, and there is a mixture of a load and another global or argument.
; Note that we can't eliminate the load here because it is used in a PHI and
; GVN doesn't try to do real DCE. The store is still forwarded by GVN though.
;
; CHECK-LABEL: @test4(
; CHECK: store i32 42, ptr @g1
; CHECK: store i32 7, ptr
; CHECK: ret i32 42
entry:
%call = call ptr @f()
store i32 42, ptr @g1
%ptr1 = load ptr, ptr @g2
%ptr2 = select i1 %c1, ptr %ptr1, ptr %param
%ptr3 = select i1 %c3, ptr %ptr2, ptr @g3
br label %loop
loop:
%iv = phi i32 [ 0, %entry ], [ %inc, %loop ]
%ptr = phi ptr [ %ptr3, %entry ], [ %ptr5, %loop ]
store i32 7, ptr %ptr
%ptr4 = load ptr, ptr getelementptr ([10 x ptr], ptr @g4, i32 0, i32 1)
%ptr5 = select i1 %c2, ptr %ptr4, ptr %call
%inc = add i32 %iv, 1
%test = icmp slt i32 %inc, %n
br i1 %test, label %loop, label %exit
exit:
%v = load i32, ptr @g1
ret i32 %v
}
define i32 @test5(ptr %param) {
; Ensure that we can fold a store to a load of a global across a store to
; a parameter that has been dereferenced when the global is non-escaping.
;
; CHECK-LABEL: @test5(
; CHECK: %p = load ptr
; CHECK: store i32 42, ptr @g1
; CHECK-NOT: load i32
; CHECK: ret i32 42
entry:
%p = load ptr, ptr %param
store i32 42, ptr @g1
store i32 7, ptr %p
%v = load i32, ptr @g1
ret i32 %v
}
|