xref: /llvm-project/llvm/test/CodeGen/AArch64/arm64-windows-calls.ll (revision f71ad19c04763b858e53e0e291698b769b7c2bfc)
1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
2; FIXME: Add tests for global-isel/fast-isel.
3
4; RUN: llc < %s -mtriple=arm64-windows | FileCheck %s
5
6; Returns <= 8 bytes should be in X0.
7%struct.S1 = type { i32, i32 }
8define dso_local i64 @"?f1"() {
9entry:
10; CHECK-LABEL: f1
11; CHECK-DAG: str xzr, [sp, #8]
12; CHECK-DAG: mov x0, xzr
13
14  %retval = alloca %struct.S1, align 4
15  store i32 0, ptr %retval, align 4
16  %b = getelementptr inbounds %struct.S1, ptr %retval, i32 0, i32 1
17  store i32 0, ptr %b, align 4
18  %0 = load i64, ptr %retval, align 4
19  ret i64 %0
20}
21
22; Returns <= 16 bytes should be in X0/X1.
23%struct.S2 = type { i32, i32, i32, i32 }
24define dso_local [2 x i64] @"?f2"() {
25entry:
26; FIXME: Missed optimization, the entire SP push/pop could be removed
27; CHECK-LABEL: f2
28; CHECK:         sub     sp, sp, #16
29; CHECK-NEXT:    .seh_stackalloc 16
30; CHECK-NEXT:    .seh_endprologue
31; CHECK-DAG:     stp     xzr, xzr, [sp]
32; CHECK-DAG:     mov     x0, xzr
33; CHECK-DAG:     mov     x1, xzr
34; CHECK:         .seh_startepilogue
35; CHECK-NEXT:    add     sp, sp, #16
36
37  %retval = alloca %struct.S2, align 4
38  store i32 0, ptr %retval, align 4
39  %b = getelementptr inbounds %struct.S2, ptr %retval, i32 0, i32 1
40  store i32 0, ptr %b, align 4
41  %c = getelementptr inbounds %struct.S2, ptr %retval, i32 0, i32 2
42  store i32 0, ptr %c, align 4
43  %d = getelementptr inbounds %struct.S2, ptr %retval, i32 0, i32 3
44  store i32 0, ptr %d, align 4
45  %0 = load [2 x i64], ptr %retval, align 4
46  ret [2 x i64] %0
47}
48
49; Arguments > 16 bytes should be passed in X8.
50%struct.S3 = type { i32, i32, i32, i32, i32 }
51define dso_local void @"?f3"(ptr noalias sret(%struct.S3) %agg.result) {
52entry:
53; CHECK-LABEL: f3
54; CHECK: stp xzr, xzr, [x8]
55; CHECK: str wzr, [x8, #16]
56
57  store i32 0, ptr %agg.result, align 4
58  %b = getelementptr inbounds %struct.S3, ptr %agg.result, i32 0, i32 1
59  store i32 0, ptr %b, align 4
60  %c = getelementptr inbounds %struct.S3, ptr %agg.result, i32 0, i32 2
61  store i32 0, ptr %c, align 4
62  %d = getelementptr inbounds %struct.S3, ptr %agg.result, i32 0, i32 3
63  store i32 0, ptr %d, align 4
64  %e = getelementptr inbounds %struct.S3, ptr %agg.result, i32 0, i32 4
65  store i32 0, ptr %e, align 4
66  ret void
67}
68
69; InReg arguments to non-instance methods must be passed in X0 and returns in
70; X0.
71%class.B = type { i32 }
72define dso_local void @"?f4"(ptr inreg noalias nocapture sret(%class.B) %agg.result) {
73entry:
74; CHECK-LABEL: f4
75; CHECK: mov w8, #1
76; CHECK: str w8, [x0]
77  store i32 1, ptr %agg.result, align 4
78  ret void
79}
80
81; InReg arguments to instance methods must be passed in X1 and returns in X0.
82%class.C = type { i8 }
83%class.A = type { i8 }
84
85define dso_local void @"?inst@C"(ptr %this, ptr inreg noalias sret(%class.A) %agg.result) {
86entry:
87; CHECK-LABEL: inst@C
88; CHECK-DAG: mov x0, x1
89; CHECK-DAG: str x8, [sp, #8]
90
91  %this.addr = alloca ptr, align 8
92  store ptr %this, ptr %this.addr, align 8
93  %this1 = load ptr, ptr %this.addr, align 8
94  ret void
95}
96
97; The following tests correspond to tests in
98; clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp
99
100; Pod is a trivial HFA
101%struct.Pod = type { [2 x double] }
102; Not an aggregate according to C++14 spec => not HFA according to MSVC
103%struct.NotCXX14Aggregate  = type { %struct.Pod }
104; NotPod is a C++14 aggregate. But not HFA, because it contains
105; NotCXX14Aggregate (which itself is not HFA because it's not a C++14
106; aggregate).
107%struct.NotPod = type { %struct.NotCXX14Aggregate }
108
109define dso_local %struct.Pod @copy_pod(ptr %x) {
110; CHECK-LABEL: copy_pod:
111; CHECK:       // %bb.0:
112; CHECK-NEXT:    ldp d0, d1, [x0]
113; CHECK-NEXT:    ret
114  %x1 = load %struct.Pod, ptr %x, align 8
115  ret %struct.Pod %x1
116}
117
118declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg)
119
120define dso_local void @copy_notcxx14aggregate(ptr inreg noalias sret(%struct.NotCXX14Aggregate) align 8 %agg.result, ptr %x) {
121; CHECK-LABEL: copy_notcxx14aggregate:
122; CHECK:       // %bb.0:
123; CHECK-NEXT:    ldr q0, [x1]
124; CHECK-NEXT:    str q0, [x0]
125; CHECK-NEXT:    ret
126  call void @llvm.memcpy.p0.p0.i64(ptr align 8 %agg.result, ptr align 8 %x, i64 16, i1 false)
127  ret void
128}
129
130define dso_local [2 x i64] @copy_notpod(ptr %x) {
131; CHECK-LABEL: copy_notpod:
132; CHECK:       // %bb.0:
133; CHECK-NEXT:    ldp x8, x1, [x0]
134; CHECK-NEXT:    mov x0, x8
135; CHECK-NEXT:    ret
136  %x2 = load [2 x i64], ptr %x
137  ret [2 x i64] %x2
138}
139
140@Pod = external global %struct.Pod
141
142define void @call_copy_pod() {
143; CHECK-LABEL: call_copy_pod:
144; CHECK:       .seh_proc call_copy_pod
145; CHECK-NEXT:  // %bb.0:
146; CHECK-NEXT:    str x19, [sp, #-16]! // 8-byte Folded Spill
147; CHECK-NEXT:    .seh_save_reg_x x19, 16
148; CHECK-NEXT:    str x30, [sp, #8] // 8-byte Folded Spill
149; CHECK-NEXT:    .seh_save_reg x30, 8
150; CHECK-NEXT:    .seh_endprologue
151; CHECK-NEXT:    adrp x19, Pod
152; CHECK-NEXT:    add x19, x19, :lo12:Pod
153; CHECK-NEXT:    mov x0, x19
154; CHECK-NEXT:    bl copy_pod
155; CHECK-NEXT:    stp d0, d1, [x19]
156; CHECK-NEXT:    .seh_startepilogue
157; CHECK-NEXT:    ldr x30, [sp, #8] // 8-byte Folded Reload
158; CHECK-NEXT:    .seh_save_reg x30, 8
159; CHECK-NEXT:    ldr x19, [sp], #16 // 8-byte Folded Reload
160; CHECK-NEXT:    .seh_save_reg_x x19, 16
161; CHECK-NEXT:    .seh_endepilogue
162; CHECK-NEXT:    ret
163; CHECK-NEXT:    .seh_endfunclet
164; CHECK-NEXT:    .seh_endproc
165  %x = call %struct.Pod @copy_pod(ptr @Pod)
166  store %struct.Pod %x, ptr @Pod
167  ret void
168}
169
170@NotCXX14Aggregate = external global %struct.NotCXX14Aggregate
171
172define void @call_copy_notcxx14aggregate() {
173; CHECK-LABEL: call_copy_notcxx14aggregate:
174; CHECK:       .seh_proc call_copy_notcxx14aggregate
175; CHECK-NEXT:  // %bb.0:
176; CHECK-NEXT:    sub sp, sp, #32
177; CHECK-NEXT:    .seh_stackalloc 32
178; CHECK-NEXT:    str x19, [sp, #16] // 8-byte Folded Spill
179; CHECK-NEXT:    .seh_save_reg x19, 16
180; CHECK-NEXT:    str x30, [sp, #24] // 8-byte Folded Spill
181; CHECK-NEXT:    .seh_save_reg x30, 24
182; CHECK-NEXT:    .seh_endprologue
183; CHECK-NEXT:    adrp x19, NotCXX14Aggregate
184; CHECK-NEXT:    add x19, x19, :lo12:NotCXX14Aggregate
185; CHECK-NEXT:    mov x0, sp
186; CHECK-NEXT:    mov x1, x19
187; CHECK-NEXT:    bl copy_notcxx14aggregate
188; CHECK-NEXT:    ldp d0, d1, [sp]
189; CHECK-NEXT:    stp d0, d1, [x19]
190; CHECK-NEXT:    .seh_startepilogue
191; CHECK-NEXT:    ldr x30, [sp, #24] // 8-byte Folded Reload
192; CHECK-NEXT:    .seh_save_reg x30, 24
193; CHECK-NEXT:    ldr x19, [sp, #16] // 8-byte Folded Reload
194; CHECK-NEXT:    .seh_save_reg x19, 16
195; CHECK-NEXT:    add sp, sp, #32
196; CHECK-NEXT:    .seh_stackalloc 32
197; CHECK-NEXT:    .seh_endepilogue
198; CHECK-NEXT:    ret
199; CHECK-NEXT:    .seh_endfunclet
200; CHECK-NEXT:    .seh_endproc
201  %x = alloca %struct.NotCXX14Aggregate
202  call void @copy_notcxx14aggregate(ptr %x, ptr @NotCXX14Aggregate)
203  %x1 = load %struct.NotCXX14Aggregate, ptr %x
204  store %struct.NotCXX14Aggregate %x1, ptr @NotCXX14Aggregate
205  ret void
206}
207
208@NotPod = external global %struct.NotPod
209
210define void @call_copy_notpod() {
211; CHECK-LABEL: call_copy_notpod:
212; CHECK:       .seh_proc call_copy_notpod
213; CHECK-NEXT:  // %bb.0:
214; CHECK-NEXT:    str x19, [sp, #-16]! // 8-byte Folded Spill
215; CHECK-NEXT:    .seh_save_reg_x x19, 16
216; CHECK-NEXT:    str x30, [sp, #8] // 8-byte Folded Spill
217; CHECK-NEXT:    .seh_save_reg x30, 8
218; CHECK-NEXT:    .seh_endprologue
219; CHECK-NEXT:    adrp x19, NotPod
220; CHECK-NEXT:    add x19, x19, :lo12:NotPod
221; CHECK-NEXT:    mov x0, x19
222; CHECK-NEXT:    bl copy_notpod
223; CHECK-NEXT:    stp x0, x1, [x19]
224; CHECK-NEXT:    .seh_startepilogue
225; CHECK-NEXT:    ldr x30, [sp, #8] // 8-byte Folded Reload
226; CHECK-NEXT:    .seh_save_reg x30, 8
227; CHECK-NEXT:    ldr x19, [sp], #16 // 8-byte Folded Reload
228; CHECK-NEXT:    .seh_save_reg_x x19, 16
229; CHECK-NEXT:    .seh_endepilogue
230; CHECK-NEXT:    ret
231; CHECK-NEXT:    .seh_endfunclet
232; CHECK-NEXT:    .seh_endproc
233  %x = call [2 x i64] @copy_notpod(ptr @NotPod)
234  store [2 x i64] %x, ptr @NotPod
235  ret void
236}
237
238; We shouldn't return the argument
239; when it has only inreg attribute
240define i64 @foobar(ptr inreg %0) {
241; CHECK-LABEL: foobar:
242; CHECK:       // %bb.0: // %entry
243; CHECK-NEXT:    ldr x0, [x0]
244; CHECK-NEXT:    ret
245entry:
246  %1 = load i64, ptr %0
247  ret i64 %1
248}
249