1; RUN: opt -S -rewrite-statepoints-for-gc < %s | FileCheck %s
2; RUN: opt -S -passes=rewrite-statepoints-for-gc < %s | FileCheck %s
3
4target datalayout = "e-ni:1:6"
5
6; constants don't get relocated.
7@G = addrspace(1) global i8 5
8
9declare void @foo()
10
11define i8 @test() gc "statepoint-example" {
12; CHECK-LABEL: @test
13; CHECK: gc.statepoint
14; CHECK-NEXT: load i8, i8 addrspace(1)* inttoptr (i64 15 to i8 addrspace(1)*)
15; Mostly just here to show reasonable code test can come from.
16entry:
17  call void @foo() [ "deopt"() ]
18  %res = load i8, i8 addrspace(1)* inttoptr (i64 15 to i8 addrspace(1)*)
19  ret i8 %res
20}
21
22define i8 @test2(i8 addrspace(1)* %p) gc "statepoint-example" {
23; CHECK-LABEL: @test2
24; CHECK: gc.statepoint
25; CHECK-NEXT: gc.relocate
26; CHECK-NEXT: icmp
27; Globals don't move and thus don't get relocated
28entry:
29  call void @foo() [ "deopt"() ]
30  %cmp = icmp eq i8 addrspace(1)* %p, null
31  br i1 %cmp, label %taken, label %not_taken
32
33taken:                                            ; preds = %not_taken, %entry
34  ret i8 0
35
36not_taken:                                        ; preds = %entry
37  %cmp2 = icmp ne i8 addrspace(1)* %p, null
38  br i1 %cmp2, label %taken, label %dead
39
40dead:                                             ; preds = %not_taken
41  %addr = getelementptr i8, i8 addrspace(1)* %p, i32 15
42  %res = load i8, i8 addrspace(1)* %addr
43  ret i8 %res
44}
45
46define i8 @test3(i1 %always_true) gc "statepoint-example" {
47; CHECK-LABEL: @test3
48; CHECK: gc.statepoint
49; CHECK-NEXT: load i8, i8 addrspace(1)* @G
50entry:
51  call void @foo() [ "deopt"() ]
52  %res = load i8, i8 addrspace(1)* @G, align 1
53  ret i8 %res
54}
55
56; Even for source languages without constant references, we can
57; see constants can show up along paths where the value is dead.
58; This is particular relevant when computing bases of PHIs.
59define i8 addrspace(1)* @test4(i8 addrspace(1)* %p) gc "statepoint-example" {
60; CHECK-LABEL: @test4
61entry:
62  %is_null = icmp eq i8 addrspace(1)* %p, null
63  br i1 %is_null, label %split, label %join
64
65split:
66  call void @foo()
67  %arg_value_addr.i = getelementptr inbounds i8, i8 addrspace(1)* %p, i64 8
68  %arg_value_addr_casted.i = bitcast i8 addrspace(1)* %arg_value_addr.i to i8 addrspace(1)* addrspace(1)*
69  br label %join
70
71join:
72; CHECK-LABEL: join
73; CHECK: %addr2.base =
74  %addr2 = phi i8 addrspace(1)* addrspace(1)* [ %arg_value_addr_casted.i, %split ], [ inttoptr (i64 8 to i8 addrspace(1)* addrspace(1)*), %entry ]
75  ;; NOTE: This particular example can be jump-threaded, but in general,
76  ;; we can't, and have to deal with the resulting IR.
77  br i1 %is_null, label %early-exit, label %use
78
79early-exit:
80  ret i8 addrspace(1)* null
81
82use:
83; CHECK-LABEL: use:
84; CHECK: gc.statepoint
85; CHECK: gc.relocate
86  call void @foo()
87  %res = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %addr2, align 1
88  ret i8 addrspace(1)* %res
89}
90
91; Globals don't move and thus don't get relocated
92define i8 addrspace(1)* @test5(i1 %always_true) gc "statepoint-example" {
93; CHECK-LABEL: @test5
94; CHECK: gc.statepoint
95; CHECK-NEXT: %res = extractelement <2 x i8 addrspace(1)*> <i8 addrspace(1)* @G, i8 addrspace(1)* @G>, i32 0
96entry:
97  call void @foo()
98  %res = extractelement <2 x i8 addrspace(1)*> <i8 addrspace(1)* @G, i8 addrspace(1)* @G>, i32 0
99  ret i8 addrspace(1)* %res
100}
101
102define i8 addrspace(1)* @test6(i64 %arg) gc "statepoint-example" {
103entry:
104  ; Don't fail any assertions and don't record null as a live value
105  ; CHECK-LABEL: test6
106  ; CHECK: gc.statepoint
107  ; CHECK-NOT: call {{.*}}gc.relocate
108  %load_addr = getelementptr i8, i8 addrspace(1)* null, i64 %arg
109  call void @foo() [ "deopt"() ]
110  ret i8 addrspace(1)* %load_addr
111}
112
113define i8 addrspace(1)* @test7(i64 %arg) gc "statepoint-example" {
114entry:
115  ; Same as test7 but use regular constant instead of a null
116  ; CHECK-LABEL: test7
117  ; CHECK: gc.statepoint
118  ; CHECK-NOT: call {{.*}}gc.relocate
119  %load_addr = getelementptr i8, i8 addrspace(1)* inttoptr (i64 15 to i8 addrspace(1)*), i64 %arg
120  call void @foo() [ "deopt"() ]
121  ret i8 addrspace(1)* %load_addr
122}
123
124define i8 @test8(i8 addrspace(1)* %p) gc "statepoint-example" {
125; Checks that base( phi(gep null, oop) ) = phi(null, base(oop)) and that we
126; correctly relocate this value
127; CHECK-LABEL: @test8
128entry:
129  %is_null = icmp eq i8 addrspace(1)* %p, null
130  br i1 %is_null, label %null.crit-edge, label %not-null
131
132not-null:
133  %load_addr = getelementptr inbounds i8, i8 addrspace(1)* %p, i64 8
134  br label %join
135
136null.crit-edge:
137  %load_addr.const = getelementptr inbounds i8, i8 addrspace(1)* null, i64 8
138  br label %join
139
140join:
141  %addr = phi i8 addrspace(1)* [ %load_addr, %not-null ], [%load_addr.const, %null.crit-edge]
142  ; CHECK: %addr.base = phi i8 addrspace(1)*
143  ; CHECK-DAG: [ %p, %not-null ]
144  ; CHECK-DAG: [ null, %null.crit-edge ]
145  ; CHECK: gc.statepoint
146  call void @foo() [ "deopt"() ]
147  ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr.base)
148  ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr)
149  br i1 %is_null, label %early-exit, label %use
150
151early-exit:
152  ret i8 0
153
154use:
155  %res = load i8, i8 addrspace(1)* %addr, align 1
156  ret i8 %res
157}
158
159define i8 @test9(i8 addrspace(1)* %p) gc "statepoint-example" {
160; Checks that base( phi(inttoptr, oop) ) = phi(null, base(oop)) and that we
161; correctly relocate this value
162; CHECK-LABEL: @test9
163entry:
164  %is_null = icmp eq i8 addrspace(1)* %p, null
165  br i1 %is_null, label %null.crit-edge, label %not-null
166
167not-null:
168  %load_addr = getelementptr inbounds i8, i8 addrspace(1)* %p, i64 8
169  br label %join
170
171null.crit-edge:
172  br label %join
173
174join:
175  %addr = phi i8 addrspace(1)* [ %load_addr, %not-null ], [inttoptr (i64 8 to i8 addrspace(1)*), %null.crit-edge]
176  ; CHECK: %addr.base = phi i8 addrspace(1)*
177  ; CHECK-DAG: [ %p, %not-null ]
178  ; CHECK-DAG: [ null, %null.crit-edge ]
179  ; CHECK: gc.statepoint
180  call void @foo() [ "deopt"() ]
181  ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr.base)
182  ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr)
183  br i1 %is_null, label %early-exit, label %use
184
185early-exit:
186  ret i8 0
187
188use:
189  %res = load i8, i8 addrspace(1)* %addr, align 1
190  ret i8 %res
191}
192
193define i8 @test10(i8 addrspace(1)* %p) gc "statepoint-example" {
194; Checks that base( phi(const gep, oop) ) = phi(null, base(oop)) and that we
195; correctly relocate this value
196; CHECK-LABEL: @test10
197entry:
198  %is_null = icmp eq i8 addrspace(1)* %p, null
199  br i1 %is_null, label %null.crit-edge, label %not-null
200
201not-null:
202  %load_addr = getelementptr inbounds i8, i8 addrspace(1)* %p, i64 8
203  br label %join
204
205null.crit-edge:
206  br label %join
207
208join:
209  %addr = phi i8 addrspace(1)* [ %load_addr, %not-null ], [getelementptr (i8, i8 addrspace(1)* null, i64 8), %null.crit-edge]
210  ; CHECK: %addr.base = phi i8 addrspace(1)*
211  ; CHECK-DAG: [ %p, %not-null ]
212  ; CHECK-DAG: [ null, %null.crit-edge ]
213  ; CHECK: gc.statepoint
214  call void @foo() [ "deopt"() ]
215  ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr.base)
216  ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr)
217  br i1 %is_null, label %early-exit, label %use
218
219early-exit:
220  ret i8 0
221
222use:
223  %res = load i8, i8 addrspace(1)* %addr, align 1
224  ret i8 %res
225}
226
227define i32 addrspace(1)* @test11(i1 %c) gc "statepoint-example" {
228; CHECK-LABEL: @test11
229; Checks that base( select(const1, const2) ) == null and that we don't record
230; such value in the oop map
231entry:
232  %val = select i1 %c, i32 addrspace(1)* inttoptr (i64 8 to i32 addrspace(1)*), i32 addrspace(1)* inttoptr (i64 15 to i32 addrspace(1)*)
233  ; CHECK: gc.statepoint
234  ; CHECK-NOT: call {{.*}}gc.relocate
235  call void @foo() [ "deopt"() ]
236  ret i32 addrspace(1)* %val
237}
238
239
240define <2 x i32 addrspace(1)*> @test12(i1 %c) gc "statepoint-example" {
241; CHECK-LABEL: @test12
242; Same as test11 but with vectors
243entry:
244  %val = select i1 %c, <2 x i32 addrspace(1)*> <i32 addrspace(1)* inttoptr (i64 5 to i32 addrspace(1)*),
245                                                i32 addrspace(1)* inttoptr (i64 15 to i32 addrspace(1)*)>,
246                       <2 x i32 addrspace(1)*> <i32 addrspace(1)* inttoptr (i64 30 to i32 addrspace(1)*),
247                                                i32 addrspace(1)* inttoptr (i64 60 to i32 addrspace(1)*)>
248  ; CHECK: gc.statepoint
249  ; CHECK-NOT: call {{.*}}gc.relocate
250  call void @foo() [ "deopt"() ]
251  ret <2 x i32 addrspace(1)*> %val
252}
253
254define <2 x i32 addrspace(1)*> @test13(i1 %c, <2 x i32 addrspace(1)*> %ptr) gc "statepoint-example" {
255; CHECK-LABEL: @test13
256; Similar to test8, test9 and test10 but with vectors
257entry:
258  %val = select i1 %c, <2 x i32 addrspace(1)*> %ptr,
259                       <2 x i32 addrspace(1)*> <i32 addrspace(1)* inttoptr (i64 30 to i32 addrspace(1)*), i32 addrspace(1)* inttoptr (i64 60 to i32 addrspace(1)*)>
260  ; CHECK: %val.base = select i1 %c, <2 x i32 addrspace(1)*> %ptr, <2 x i32 addrspace(1)*> zeroinitializer, !is_base_value !0
261  ; CHECK: gc.statepoint
262  call void @foo() [ "deopt"() ]
263  ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%val.base, %val.base)
264  ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%val.base, %val)
265  ret <2 x i32 addrspace(1)*> %val
266}
267