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 i32* @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
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, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 0, 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
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, i32 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i32f(i64 0, i32 0, 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 i32* @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
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, i32* ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_p0i32f(i64 0, i32 0, i32* ()* @return_i32ptr, i32 0, i32 0, i32 0, i32 0)
64  %call1 = call i32* @llvm.experimental.gc.result.p0i32(token %safepoint_token)
65  ret i32* %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
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, float ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_f32f(i64 0, i32 0, 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
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, %struct ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_structf(i64 0, i32 0, %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(i32 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
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, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 0, i1 ()* @return_i1, i32 0, i32 0, i32 0, i32 0) ["gc-live" (i32 addrspace(1)* %a)]
114  %call1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(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
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 *void* returning vararg function works
132entry:
133  %safepoint_token = tail call token (i64, i32, void (i32, ...)*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidi32varargf(i64 0, i32 0, 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, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 3, 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(i32 addrspace(1)* %obj)
157
158define i1 @test_cross_bb(i32 addrspace(1)* %a, i1 %external_cond) gc "statepoint-example" {
159; CHECK-LABEL: test_cross_bb:
160; CHECK:       # %bb.0: # %entry
161; CHECK-NEXT:    pushq %rbp
162; CHECK-NEXT:    .cfi_def_cfa_offset 16
163; CHECK-NEXT:    pushq %rbx
164; CHECK-NEXT:    .cfi_def_cfa_offset 24
165; CHECK-NEXT:    pushq %rax
166; CHECK-NEXT:    .cfi_def_cfa_offset 32
167; CHECK-NEXT:    .cfi_offset %rbx, -24
168; CHECK-NEXT:    .cfi_offset %rbp, -16
169; CHECK-NEXT:    movl %esi, %ebp
170; CHECK-NEXT:    movq %rdi, (%rsp)
171; CHECK-NEXT:    callq return_i1
172; CHECK-NEXT:  .Ltmp8:
173; CHECK-NEXT:    testb $1, %bpl
174; CHECK-NEXT:    je .LBB8_2
175; CHECK-NEXT:  # %bb.1: # %left
176; CHECK-NEXT:    movl %eax, %ebx
177; CHECK-NEXT:    movq (%rsp), %rdi
178; CHECK-NEXT:    callq consume
179; CHECK-NEXT:    movl %ebx, %eax
180; CHECK-NEXT:    jmp .LBB8_3
181; CHECK-NEXT:  .LBB8_2: # %right
182; CHECK-NEXT:    movb $1, %al
183; CHECK-NEXT:  .LBB8_3: # %right
184; CHECK-NEXT:    addq $8, %rsp
185; CHECK-NEXT:    .cfi_def_cfa_offset 24
186; CHECK-NEXT:    popq %rbx
187; CHECK-NEXT:    .cfi_def_cfa_offset 16
188; CHECK-NEXT:    popq %rbp
189; CHECK-NEXT:    .cfi_def_cfa_offset 8
190; CHECK-NEXT:    retq
191entry:
192  %safepoint_token = tail call token (i64, i32, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 0, i1 ()* @return_i1, i32 0, i32 0, i32 0, i32 0) ["gc-live" (i32 addrspace(1)* %a)]
193  br i1 %external_cond, label %left, label %right
194
195left:
196  %call1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token,  i32 0, i32 0)
197  %call2 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token)
198  call void @consume(i32 addrspace(1)* %call1)
199  ret i1 %call2
200
201right:
202  ret i1 true
203}
204
205%struct2 = type { i64, i64, i64 }
206
207declare void @consume_attributes(i32, i8* nest, i32, %struct2* byval(%struct2))
208
209define void @test_attributes(%struct2* byval(%struct2) %s) gc "statepoint-example" {
210; CHECK-LABEL: test_attributes:
211; CHECK:       # %bb.0: # %entry
212; CHECK-NEXT:    pushq %rax
213; CHECK-NEXT:    .cfi_def_cfa_offset 16
214; CHECK-NEXT:    subq $8, %rsp
215; CHECK-NEXT:    .cfi_adjust_cfa_offset 8
216; CHECK-NEXT:    movq {{[0-9]+}}(%rsp), %rax
217; CHECK-NEXT:    movq {{[0-9]+}}(%rsp), %rcx
218; CHECK-NEXT:    movq {{[0-9]+}}(%rsp), %rdx
219; CHECK-NEXT:    movl $42, %edi
220; CHECK-NEXT:    xorl %r10d, %r10d
221; CHECK-NEXT:    movl $17, %esi
222; CHECK-NEXT:    pushq %rax
223; CHECK-NEXT:    .cfi_adjust_cfa_offset 8
224; CHECK-NEXT:    pushq %rdx
225; CHECK-NEXT:    .cfi_adjust_cfa_offset 8
226; CHECK-NEXT:    pushq %rcx
227; CHECK-NEXT:    .cfi_adjust_cfa_offset 8
228; CHECK-NEXT:    callq consume_attributes
229; CHECK-NEXT:  .Ltmp9:
230; CHECK-NEXT:    addq $32, %rsp
231; CHECK-NEXT:    .cfi_adjust_cfa_offset -32
232; CHECK-NEXT:    popq %rax
233; CHECK-NEXT:    .cfi_def_cfa_offset 8
234; CHECK-NEXT:    retq
235entry:
236; Check that arguments with attributes are lowered correctly.
237; We call a function that has a nest argument and a byval argument.
238  %statepoint_token = call token (i64, i32, void (i32, i8*, i32, %struct2*)*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidi32p0i8i32p0s_struct2sf(i64 0, i32 0, void (i32, i8*, i32, %struct2*)* @consume_attributes, i32 4, i32 0, i32 42, i8* nest null, i32 17, %struct2* byval(%struct2) %s, i32 0, i32 0)
239  ret void
240}
241
242declare token @llvm.experimental.gc.statepoint.p0f_i1f(i64, i32, i1 ()*, i32, i32, ...)
243declare i1 @llvm.experimental.gc.result.i1(token)
244
245declare token @llvm.experimental.gc.statepoint.p0f_i32f(i64, i32, i32 ()*, i32, i32, ...)
246declare i32 @llvm.experimental.gc.result.i32(token)
247
248declare token @llvm.experimental.gc.statepoint.p0f_p0i32f(i64, i32, i32* ()*, i32, i32, ...)
249declare i32* @llvm.experimental.gc.result.p0i32(token)
250
251declare token @llvm.experimental.gc.statepoint.p0f_f32f(i64, i32, float ()*, i32, i32, ...)
252declare float @llvm.experimental.gc.result.f32(token)
253
254declare token @llvm.experimental.gc.statepoint.p0f_structf(i64, i32, %struct ()*, i32, i32, ...)
255declare %struct @llvm.experimental.gc.result.struct(token)
256
257declare token @llvm.experimental.gc.statepoint.p0f_isVoidi32varargf(i64, i32, void (i32, ...)*, i32, i32, ...)
258
259declare token @llvm.experimental.gc.statepoint.p0f_isVoidi32p0i8i32p0s_struct2sf(i64, i32, void (i32, i8*, i32, %struct2*)*, i32, i32, ...)
260
261declare i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token, i32, i32)
262