xref: /llvm-project/llvm/test/CodeGen/X86/statepoint-call-lowering.ll (revision 0ad92c0cbb34a6e24a9a32f03f3ddeb2114b378e)
1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2; RUN: llc -verify-machineinstrs < %s | FileCheck %s
3; This file contains a collection of basic tests to ensure we didn't
4; screw up normal call lowering when there are no deopt or gc arguments.
5
6target datalayout = "e-i64:64-f80:128-n8:16:32:64-S128"
7target triple = "x86_64-pc-linux-gnu"
8
9%struct = type { i64, i64 }
10
11declare zeroext i1 @return_i1()
12declare zeroext i32 @return_i32()
13declare ptr @return_i32ptr()
14declare float @return_float()
15declare %struct @return_struct()
16declare void @varargf(i32, ...)
17
18define i1 @test_i1_return() gc "statepoint-example" {
19; CHECK-LABEL: test_i1_return:
20; CHECK:       # %bb.0: # %entry
21; CHECK-NEXT:    pushq %rax
22; CHECK-NEXT:    .cfi_def_cfa_offset 16
23; CHECK-NEXT:    callq return_i1@PLT
24; CHECK-NEXT:  .Ltmp0:
25; CHECK-NEXT:    popq %rcx
26; CHECK-NEXT:    .cfi_def_cfa_offset 8
27; CHECK-NEXT:    retq
28; This is just checking that a i1 gets lowered normally when there's no extra
29; state arguments to the statepoint
30entry:
31  %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(i1 ()) @return_i1, i32 0, i32 0, i32 0, i32 0)
32  %call1 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token)
33  ret i1 %call1
34}
35
36define i32 @test_i32_return() gc "statepoint-example" {
37; CHECK-LABEL: test_i32_return:
38; CHECK:       # %bb.0: # %entry
39; CHECK-NEXT:    pushq %rax
40; CHECK-NEXT:    .cfi_def_cfa_offset 16
41; CHECK-NEXT:    callq return_i32@PLT
42; CHECK-NEXT:  .Ltmp1:
43; CHECK-NEXT:    popq %rcx
44; CHECK-NEXT:    .cfi_def_cfa_offset 8
45; CHECK-NEXT:    retq
46entry:
47  %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(i32 ()) @return_i32, i32 0, i32 0, i32 0, i32 0)
48  %call1 = call zeroext i32 @llvm.experimental.gc.result.i32(token %safepoint_token)
49  ret i32 %call1
50}
51
52define ptr @test_i32ptr_return() gc "statepoint-example" {
53; CHECK-LABEL: test_i32ptr_return:
54; CHECK:       # %bb.0: # %entry
55; CHECK-NEXT:    pushq %rax
56; CHECK-NEXT:    .cfi_def_cfa_offset 16
57; CHECK-NEXT:    callq return_i32ptr@PLT
58; CHECK-NEXT:  .Ltmp2:
59; CHECK-NEXT:    popq %rcx
60; CHECK-NEXT:    .cfi_def_cfa_offset 8
61; CHECK-NEXT:    retq
62entry:
63  %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(ptr ()) @return_i32ptr, i32 0, i32 0, i32 0, i32 0)
64  %call1 = call ptr @llvm.experimental.gc.result.p0(token %safepoint_token)
65  ret ptr %call1
66}
67
68define float @test_float_return() gc "statepoint-example" {
69; CHECK-LABEL: test_float_return:
70; CHECK:       # %bb.0: # %entry
71; CHECK-NEXT:    pushq %rax
72; CHECK-NEXT:    .cfi_def_cfa_offset 16
73; CHECK-NEXT:    callq return_float@PLT
74; CHECK-NEXT:  .Ltmp3:
75; CHECK-NEXT:    popq %rax
76; CHECK-NEXT:    .cfi_def_cfa_offset 8
77; CHECK-NEXT:    retq
78entry:
79  %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(float ()) @return_float, i32 0, i32 0, i32 0, i32 0)
80  %call1 = call float @llvm.experimental.gc.result.f32(token %safepoint_token)
81  ret float %call1
82}
83
84define %struct @test_struct_return() gc "statepoint-example" {
85; CHECK-LABEL: test_struct_return:
86; CHECK:       # %bb.0: # %entry
87; CHECK-NEXT:    pushq %rax
88; CHECK-NEXT:    .cfi_def_cfa_offset 16
89; CHECK-NEXT:    callq return_struct@PLT
90; CHECK-NEXT:  .Ltmp4:
91; CHECK-NEXT:    popq %rcx
92; CHECK-NEXT:    .cfi_def_cfa_offset 8
93; CHECK-NEXT:    retq
94entry:
95  %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(%struct ()) @return_struct, i32 0, i32 0, i32 0, i32 0)
96  %call1 = call %struct @llvm.experimental.gc.result.struct(token %safepoint_token)
97  ret %struct %call1
98}
99
100define i1 @test_relocate(ptr addrspace(1) %a) gc "statepoint-example" {
101; CHECK-LABEL: test_relocate:
102; CHECK:       # %bb.0: # %entry
103; CHECK-NEXT:    pushq %rax
104; CHECK-NEXT:    .cfi_def_cfa_offset 16
105; CHECK-NEXT:    movq %rdi, (%rsp)
106; CHECK-NEXT:    callq return_i1@PLT
107; CHECK-NEXT:  .Ltmp5:
108; CHECK-NEXT:    popq %rcx
109; CHECK-NEXT:    .cfi_def_cfa_offset 8
110; CHECK-NEXT:    retq
111; Check that an ununsed relocate has no code-generation impact
112entry:
113  %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(i1 ()) @return_i1, i32 0, i32 0, i32 0, i32 0) ["gc-live" (ptr addrspace(1) %a)]
114  %call1 = call ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token %safepoint_token,  i32 0, i32 0)
115  %call2 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token)
116  ret i1 %call2
117}
118
119define void @test_void_vararg() gc "statepoint-example" {
120; CHECK-LABEL: test_void_vararg:
121; CHECK:       # %bb.0: # %entry
122; CHECK-NEXT:    pushq %rax
123; CHECK-NEXT:    .cfi_def_cfa_offset 16
124; CHECK-NEXT:    movl $42, %edi
125; CHECK-NEXT:    movl $43, %esi
126; CHECK-NEXT:    callq varargf@PLT
127; CHECK-NEXT:  .Ltmp6:
128; CHECK-NEXT:    popq %rax
129; CHECK-NEXT:    .cfi_def_cfa_offset 8
130; CHECK-NEXT:    retq
131; Check a statepoint wrapping a *ptr returning vararg function works
132entry:
133  %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(void (i32, ...)) @varargf, i32 2, i32 0, i32 42, i32 43, i32 0, i32 0)
134  ;; if we try to use the result from a statepoint wrapping a
135  ;; non-void-returning varargf, we will experience a crash.
136  ret void
137}
138
139define i1 @test_i1_return_patchable() gc "statepoint-example" {
140; CHECK-LABEL: test_i1_return_patchable:
141; CHECK:       # %bb.0: # %entry
142; CHECK-NEXT:    pushq %rax
143; CHECK-NEXT:    .cfi_def_cfa_offset 16
144; CHECK-NEXT:    nopl (%rax)
145; CHECK-NEXT:  .Ltmp7:
146; CHECK-NEXT:    popq %rcx
147; CHECK-NEXT:    .cfi_def_cfa_offset 8
148; CHECK-NEXT:    retq
149; A patchable variant of test_i1_return
150entry:
151  %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 3, ptr elementtype(i1 ()) null, i32 0, i32 0, i32 0, i32 0)
152  %call1 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token)
153  ret i1 %call1
154}
155
156declare void @consume(ptr addrspace(1) %obj)
157
158define i1 @test_cross_bb(ptr addrspace(1) %a, i1 %external_cond) gc "statepoint-example" {
159; CHECK-LABEL: test_cross_bb:
160; CHECK:       # %bb.0: # %entry
161; CHECK-NEXT:    pushq %rbx
162; CHECK-NEXT:    .cfi_def_cfa_offset 16
163; CHECK-NEXT:    subq $16, %rsp
164; CHECK-NEXT:    .cfi_def_cfa_offset 32
165; CHECK-NEXT:    .cfi_offset %rbx, -16
166; CHECK-NEXT:    movl %esi, %ebx
167; CHECK-NEXT:    movq %rdi, {{[0-9]+}}(%rsp)
168; CHECK-NEXT:    callq return_i1@PLT
169; CHECK-NEXT:  .Ltmp8:
170; CHECK-NEXT:    testb $1, %bl
171; CHECK-NEXT:    je .LBB8_2
172; CHECK-NEXT:  # %bb.1: # %left
173; CHECK-NEXT:    movq {{[0-9]+}}(%rsp), %rdi
174; CHECK-NEXT:    movl %eax, %ebx
175; CHECK-NEXT:    callq consume@PLT
176; CHECK-NEXT:    movl %ebx, %eax
177; CHECK-NEXT:    jmp .LBB8_3
178; CHECK-NEXT:  .LBB8_2: # %right
179; CHECK-NEXT:    movb $1, %al
180; CHECK-NEXT:  .LBB8_3: # %right
181; CHECK-NEXT:    addq $16, %rsp
182; CHECK-NEXT:    .cfi_def_cfa_offset 16
183; CHECK-NEXT:    popq %rbx
184; CHECK-NEXT:    .cfi_def_cfa_offset 8
185; CHECK-NEXT:    retq
186entry:
187  %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(i1 ()) @return_i1, i32 0, i32 0, i32 0, i32 0) ["gc-live" (ptr addrspace(1) %a)]
188  br i1 %external_cond, label %left, label %right
189
190left:
191  %call1 = call ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token %safepoint_token,  i32 0, i32 0)
192  %call2 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token)
193  call void @consume(ptr addrspace(1) %call1)
194  ret i1 %call2
195
196right:
197  ret i1 true
198}
199
200%struct2 = type { i64, i64, i64 }
201
202declare void @consume_attributes(i32, ptr nest, i32, ptr byval(%struct2))
203
204define void @test_attributes(ptr byval(%struct2) %s) gc "statepoint-example" {
205; CHECK-LABEL: test_attributes:
206; CHECK:       # %bb.0: # %entry
207; CHECK-NEXT:    pushq %rax
208; CHECK-NEXT:    .cfi_def_cfa_offset 16
209; CHECK-NEXT:    subq $8, %rsp
210; CHECK-NEXT:    .cfi_adjust_cfa_offset 8
211; CHECK-NEXT:    movq {{[0-9]+}}(%rsp), %rax
212; CHECK-NEXT:    movq {{[0-9]+}}(%rsp), %rcx
213; CHECK-NEXT:    movq {{[0-9]+}}(%rsp), %rdx
214; CHECK-NEXT:    movl $42, %edi
215; CHECK-NEXT:    xorl %r10d, %r10d
216; CHECK-NEXT:    movl $17, %esi
217; CHECK-NEXT:    pushq %rax
218; CHECK-NEXT:    .cfi_adjust_cfa_offset 8
219; CHECK-NEXT:    pushq %rdx
220; CHECK-NEXT:    .cfi_adjust_cfa_offset 8
221; CHECK-NEXT:    pushq %rcx
222; CHECK-NEXT:    .cfi_adjust_cfa_offset 8
223; CHECK-NEXT:    callq consume_attributes@PLT
224; CHECK-NEXT:  .Ltmp9:
225; CHECK-NEXT:    addq $32, %rsp
226; CHECK-NEXT:    .cfi_adjust_cfa_offset -32
227; CHECK-NEXT:    popq %rax
228; CHECK-NEXT:    .cfi_def_cfa_offset 8
229; CHECK-NEXT:    retq
230entry:
231; Check that arguments with attributes are lowered correctly.
232; We call a function that has a nest argument and a byval argument.
233  %statepoint_token = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(void (i32, ptr, i32, ptr)) @consume_attributes, i32 4, i32 0, i32 42, ptr nest null, i32 17, ptr byval(%struct2) %s, i32 0, i32 0)
234  ret void
235}
236
237declare signext i1 @signext_return_i1()
238
239; Check that the generated code takes the zeroext and signext return attributes
240; on the GC result into account. The attribute in the return position allows the
241; caller to assume that the callee did the extension already.
242
243define i8 @test_signext_return(ptr) gc "statepoint-example" {
244; CHECK-LABEL: test_signext_return:
245; CHECK:       # %bb.0: # %entry
246; CHECK-NEXT:    pushq %rax
247; CHECK-NEXT:    .cfi_def_cfa_offset 16
248; CHECK-NEXT:    callq signext_return_i1@PLT
249; CHECK-NEXT:  .Ltmp10:
250; CHECK-NEXT:    popq %rcx
251; CHECK-NEXT:    .cfi_def_cfa_offset 8
252; CHECK-NEXT:    retq
253entry:
254  %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(i1 ()) @signext_return_i1, i32 0, i32 0, i32 0, i32 0)
255  %call1 = call signext i1 @llvm.experimental.gc.result.i1(token %safepoint_token)
256  %ext = sext i1 %call1 to i8
257  ret i8 %ext
258}
259
260define i8 @test_zeroext_return() gc "statepoint-example" {
261; CHECK-LABEL: test_zeroext_return:
262; CHECK:       # %bb.0: # %entry
263; CHECK-NEXT:    pushq %rax
264; CHECK-NEXT:    .cfi_def_cfa_offset 16
265; CHECK-NEXT:    callq return_i1@PLT
266; CHECK-NEXT:  .Ltmp11:
267; CHECK-NEXT:    popq %rcx
268; CHECK-NEXT:    .cfi_def_cfa_offset 8
269; CHECK-NEXT:    retq
270entry:
271  %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(i1 ()) @return_i1, i32 0, i32 0, i32 0, i32 0)
272  %call1 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token)
273  %ext = zext i1 %call1 to i8
274  ret i8 %ext
275}
276
277define signext i1 @test_noext_signext_return() gc "statepoint-example" {
278; CHECK-LABEL: test_noext_signext_return:
279; CHECK:       # %bb.0: # %entry
280; CHECK-NEXT:    pushq %rax
281; CHECK-NEXT:    .cfi_def_cfa_offset 16
282; CHECK-NEXT:    callq return_i1@PLT
283; CHECK-NEXT:  .Ltmp12:
284; CHECK-NEXT:    andb $1, %al
285; CHECK-NEXT:    negb %al
286; CHECK-NEXT:    popq %rcx
287; CHECK-NEXT:    .cfi_def_cfa_offset 8
288; CHECK-NEXT:    retq
289entry:
290  %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(i1 ()) @return_i1, i32 0, i32 0, i32 0, i32 0)
291  %call1 = call i1 @llvm.experimental.gc.result.i1(token %safepoint_token)
292  ret i1 %call1
293}
294
295declare token @llvm.experimental.gc.statepoint.p0(i64, i32, ptr, i32, i32, ...)
296declare i1 @llvm.experimental.gc.result.i1(token)
297
298declare i32 @llvm.experimental.gc.result.i32(token)
299
300declare ptr @llvm.experimental.gc.result.p0(token)
301
302declare float @llvm.experimental.gc.result.f32(token)
303
304declare %struct @llvm.experimental.gc.result.struct(token)
305
306
307
308declare ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token, i32, i32)
309