xref: /llvm-project/llvm/test/CodeGen/AArch64/argument-blocks-array-of-struct.ll (revision 572fc7d2fd14b768b58e697015b18c46cafb421c)
1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2; RUN: llc -mtriple=aarch64-none-linux-gnu -o - %s | FileCheck %s
3
4;; Check that the llvm aarch64 backend can handle arrays of
5;; structs and vice versa when passed from IR.
6;; (this layering is something clang would normally simplify)
7;;
8;; Some of these examples are not ABI compliant and they're not
9;; meant to be. For instance according to the ABI an aggregate
10;; with more than 4 members must go in memory. This restriction
11;; is applied earlier in the compilation process so here we do
12;; see 8 member types in registers.
13;;
14;; When we have more than 8 members we simply run out of registers
15;; and that's what produces the 8 limit here.
16
17;; Plain arrays
18
19define [ 0 x double ] @array_0() {
20; CHECK-LABEL: array_0:
21; CHECK:       // %bb.0:
22; CHECK-NEXT:    ret
23  ret [ 0 x double ] zeroinitializer
24}
25
26define [ 1 x double ] @array_1() {
27; CHECK-LABEL: array_1:
28; CHECK:       // %bb.0:
29; CHECK-NEXT:    movi d0, #0000000000000000
30; CHECK-NEXT:    ret
31  ret [ 1 x double ] zeroinitializer
32}
33
34define [ 8 x double ] @array_8() {
35; CHECK-LABEL: array_8:
36; CHECK:       // %bb.0:
37; CHECK-NEXT:    movi d0, #0000000000000000
38; CHECK-NEXT:    movi d1, #0000000000000000
39; CHECK-NEXT:    movi d2, #0000000000000000
40; CHECK-NEXT:    movi d3, #0000000000000000
41; CHECK-NEXT:    movi d4, #0000000000000000
42; CHECK-NEXT:    movi d5, #0000000000000000
43; CHECK-NEXT:    movi d6, #0000000000000000
44; CHECK-NEXT:    movi d7, #0000000000000000
45; CHECK-NEXT:    ret
46  ret [ 8 x double ] zeroinitializer
47}
48
49;; > 8 items goes on the stack
50
51define [ 9 x double ] @array_9() {
52; CHECK-LABEL: array_9:
53; CHECK:       // %bb.0:
54; CHECK-NEXT:    movi v0.2d, #0000000000000000
55; CHECK-NEXT:    str xzr, [x8, #64]
56; CHECK-NEXT:    stp q0, q0, [x8]
57; CHECK-NEXT:    stp q0, q0, [x8, #32]
58; CHECK-NEXT:    ret
59  ret [ 9 x double ] zeroinitializer
60}
61
62;; Won't use any registers, just checking for assumptions.
63%T_STRUCT_0M = type { }
64
65define %T_STRUCT_0M @struct_zero_fields() {
66; CHECK-LABEL: struct_zero_fields:
67; CHECK:       // %bb.0:
68; CHECK-NEXT:    ret
69  ret %T_STRUCT_0M zeroinitializer
70}
71
72define [ 1 x %T_STRUCT_0M ] @array_of_struct_zero_fields() {
73; CHECK-LABEL: array_of_struct_zero_fields:
74; CHECK:       // %bb.0:
75; CHECK-NEXT:    ret
76  ret [ 1 x %T_STRUCT_0M ] zeroinitializer
77}
78
79define [ 2 x %T_STRUCT_0M ] @array_of_struct_zero_fields_in_struct() {
80; CHECK-LABEL: array_of_struct_zero_fields_in_struct:
81; CHECK:       // %bb.0:
82; CHECK-NEXT:    ret
83  ret [ 2 x %T_STRUCT_0M ] zeroinitializer
84}
85
86%T_STRUCT_1M = type { i32 }
87
88define %T_STRUCT_1M @struct_one_field() {
89; CHECK-LABEL: struct_one_field:
90; CHECK:       // %bb.0:
91; CHECK-NEXT:    mov w0, wzr
92; CHECK-NEXT:    ret
93  ret %T_STRUCT_1M zeroinitializer
94}
95
96define [ 1 x %T_STRUCT_1M ] @array_of_struct_one_field() {
97; CHECK-LABEL: array_of_struct_one_field:
98; CHECK:       // %bb.0:
99; CHECK-NEXT:    mov w0, wzr
100; CHECK-NEXT:    ret
101  ret [ 1 x %T_STRUCT_1M ] zeroinitializer
102}
103
104;; This one will be a reg block
105define [ 2 x %T_STRUCT_1M ] @array_of_struct_one_field_2() {
106; CHECK-LABEL: array_of_struct_one_field_2:
107; CHECK:       // %bb.0:
108; CHECK-NEXT:    mov w0, wzr
109; CHECK-NEXT:    mov w1, wzr
110; CHECK-NEXT:    ret
111  ret [ 2 x %T_STRUCT_1M ] zeroinitializer
112}
113
114;; Different types for each field, will not be put in a reg block
115%T_STRUCT_DIFFM = type { double, i32 }
116
117define %T_STRUCT_DIFFM @struct_different_field_types() {
118; CHECK-LABEL: struct_different_field_types:
119; CHECK:       // %bb.0:
120; CHECK-NEXT:    movi d0, #0000000000000000
121; CHECK-NEXT:    mov w0, wzr
122; CHECK-NEXT:    ret
123  ret %T_STRUCT_DIFFM zeroinitializer
124}
125
126define [ 1 x %T_STRUCT_DIFFM ] @array_of_struct_different_field_types() {
127; CHECK-LABEL: array_of_struct_different_field_types:
128; CHECK:       // %bb.0:
129; CHECK-NEXT:    movi d0, #0000000000000000
130; CHECK-NEXT:    mov w0, wzr
131; CHECK-NEXT:    ret
132  ret [ 1 x %T_STRUCT_DIFFM ] zeroinitializer
133}
134
135define [ 2 x %T_STRUCT_DIFFM ] @array_of_struct_different_field_types_2() {
136; CHECK-LABEL: array_of_struct_different_field_types_2:
137; CHECK:       // %bb.0:
138; CHECK-NEXT:    movi d0, #0000000000000000
139; CHECK-NEXT:    movi d1, #0000000000000000
140; CHECK-NEXT:    mov w0, wzr
141; CHECK-NEXT:    mov w1, wzr
142; CHECK-NEXT:    ret
143  ret [ 2 x %T_STRUCT_DIFFM ] zeroinitializer
144}
145
146;; Each field is the same type, can be put in a reg block
147%T_STRUCT_SAMEM = type { double, double }
148
149;; Here isn't a block as such, we just allocate two consecutive registers
150define %T_STRUCT_SAMEM @struct_same_field_types() {
151; CHECK-LABEL: struct_same_field_types:
152; CHECK:       // %bb.0:
153; CHECK-NEXT:    movi d0, #0000000000000000
154; CHECK-NEXT:    movi d1, #0000000000000000
155; CHECK-NEXT:    ret
156  ret %T_STRUCT_SAMEM zeroinitializer
157}
158
159define [ 1 x %T_STRUCT_SAMEM ] @array_of_struct_same_field_types() {
160; CHECK-LABEL: array_of_struct_same_field_types:
161; CHECK:       // %bb.0:
162; CHECK-NEXT:    movi d0, #0000000000000000
163; CHECK-NEXT:    movi d1, #0000000000000000
164; CHECK-NEXT:    ret
165  ret [ 1 x %T_STRUCT_SAMEM ] zeroinitializer
166}
167
168define [ 2 x %T_STRUCT_SAMEM ] @array_of_struct_same_field_types_2() {
169; CHECK-LABEL: array_of_struct_same_field_types_2:
170; CHECK:       // %bb.0:
171; CHECK-NEXT:    movi d0, #0000000000000000
172; CHECK-NEXT:    movi d1, #0000000000000000
173; CHECK-NEXT:    movi d2, #0000000000000000
174; CHECK-NEXT:    movi d3, #0000000000000000
175; CHECK-NEXT:    ret
176  ret [ 2 x %T_STRUCT_SAMEM ] zeroinitializer
177}
178
179;; Same field type but integer this time. Put into x registers instead.
180%T_STRUCT_SAMEM_INT = type { i64, i64 }
181
182define %T_STRUCT_SAMEM_INT @struct_same_field_types_int() {
183; CHECK-LABEL: struct_same_field_types_int:
184; CHECK:       // %bb.0:
185; CHECK-NEXT:    mov x0, xzr
186; CHECK-NEXT:    mov x1, xzr
187; CHECK-NEXT:    ret
188  ret %T_STRUCT_SAMEM_INT zeroinitializer
189}
190
191define [ 1 x %T_STRUCT_SAMEM_INT ] @array_of_struct_same_field_types_int() {
192; CHECK-LABEL: array_of_struct_same_field_types_int:
193; CHECK:       // %bb.0:
194; CHECK-NEXT:    mov x0, xzr
195; CHECK-NEXT:    mov x1, xzr
196; CHECK-NEXT:    ret
197  ret [ 1 x %T_STRUCT_SAMEM_INT ] zeroinitializer
198}
199
200define [ 2 x %T_STRUCT_SAMEM_INT ] @array_of_struct_same_field_types_int_2() {
201; CHECK-LABEL: array_of_struct_same_field_types_int_2:
202; CHECK:       // %bb.0:
203; CHECK-NEXT:    mov x0, xzr
204; CHECK-NEXT:    mov x1, xzr
205; CHECK-NEXT:    mov x2, xzr
206; CHECK-NEXT:    mov x3, xzr
207; CHECK-NEXT:    ret
208  ret [ 2 x %T_STRUCT_SAMEM_INT ] zeroinitializer
209}
210
211;; An aggregate of more than 8 items must go in memory.
212;; 4x2 struct fields = 8 items so it goes in a block.
213
214define [ 4 x %T_STRUCT_SAMEM ] @array_of_struct_8_fields() {
215; CHECK-LABEL: array_of_struct_8_fields:
216; CHECK:       // %bb.0:
217; CHECK-NEXT:    movi d0, #0000000000000000
218; CHECK-NEXT:    movi d1, #0000000000000000
219; CHECK-NEXT:    movi d2, #0000000000000000
220; CHECK-NEXT:    movi d3, #0000000000000000
221; CHECK-NEXT:    movi d4, #0000000000000000
222; CHECK-NEXT:    movi d5, #0000000000000000
223; CHECK-NEXT:    movi d6, #0000000000000000
224; CHECK-NEXT:    movi d7, #0000000000000000
225; CHECK-NEXT:    ret
226  ret [ 4 x %T_STRUCT_SAMEM ] zeroinitializer
227}
228
229;; 5x2 fields = 10 so it is returned in memory.
230
231define [ 5 x %T_STRUCT_SAMEM ] @array_of_struct_in_memory() {
232; CHECK-LABEL: array_of_struct_in_memory:
233; CHECK:       // %bb.0:
234; CHECK-NEXT:    movi v0.2d, #0000000000000000
235; CHECK-NEXT:    stp q0, q0, [x8, #16]
236; CHECK-NEXT:    stp q0, q0, [x8, #48]
237; CHECK-NEXT:    str q0, [x8]
238; CHECK-NEXT:    ret
239  ret [ 5 x %T_STRUCT_SAMEM ] zeroinitializer
240}
241
242;; A struct whose field is an array.
243%T_STRUCT_ARRAYM = type { [ 2 x double ]};
244
245define %T_STRUCT_ARRAYM @struct_array_field() {
246; CHECK-LABEL: struct_array_field:
247; CHECK:       // %bb.0:
248; CHECK-NEXT:    movi d0, #0000000000000000
249; CHECK-NEXT:    movi d1, #0000000000000000
250; CHECK-NEXT:    ret
251  ret %T_STRUCT_ARRAYM zeroinitializer
252}
253
254define [ 1 x %T_STRUCT_ARRAYM ] @array_of_struct_array_field() {
255; CHECK-LABEL: array_of_struct_array_field:
256; CHECK:       // %bb.0:
257; CHECK-NEXT:    movi d0, #0000000000000000
258; CHECK-NEXT:    movi d1, #0000000000000000
259; CHECK-NEXT:    ret
260  ret [ 1 x %T_STRUCT_ARRAYM ] zeroinitializer
261}
262
263define [ 2 x %T_STRUCT_ARRAYM ] @array_of_struct_array_field_2() {
264; CHECK-LABEL: array_of_struct_array_field_2:
265; CHECK:       // %bb.0:
266; CHECK-NEXT:    movi d0, #0000000000000000
267; CHECK-NEXT:    movi d1, #0000000000000000
268; CHECK-NEXT:    movi d2, #0000000000000000
269; CHECK-NEXT:    movi d3, #0000000000000000
270; CHECK-NEXT:    ret
271  ret [ 2 x %T_STRUCT_ARRAYM ] zeroinitializer
272}
273
274;; All non-aggregate fields must have the same type, all through the
275;; overall aggreagate. This is false here because of the i32.
276%T_NESTED_STRUCT_DIFFM = type {
277  [ 1 x { { double, double } } ],
278  [ 1 x { { double, i32 } } ]
279};
280
281define %T_NESTED_STRUCT_DIFFM @struct_nested_different_field_types() {
282; CHECK-LABEL: struct_nested_different_field_types:
283; CHECK:       // %bb.0:
284; CHECK-NEXT:    movi d0, #0000000000000000
285; CHECK-NEXT:    movi d1, #0000000000000000
286; CHECK-NEXT:    movi d2, #0000000000000000
287; CHECK-NEXT:    mov w0, wzr
288; CHECK-NEXT:    ret
289  ret %T_NESTED_STRUCT_DIFFM zeroinitializer
290}
291
292define [ 1 x %T_NESTED_STRUCT_DIFFM ] @array_of_struct_nested_different_field_types() {
293; CHECK-LABEL: array_of_struct_nested_different_field_types:
294; CHECK:       // %bb.0:
295; CHECK-NEXT:    movi d0, #0000000000000000
296; CHECK-NEXT:    movi d1, #0000000000000000
297; CHECK-NEXT:    movi d2, #0000000000000000
298; CHECK-NEXT:    mov w0, wzr
299; CHECK-NEXT:    ret
300  ret [ 1 x %T_NESTED_STRUCT_DIFFM ] zeroinitializer
301}
302
303define [ 2 x %T_NESTED_STRUCT_DIFFM ] @array_of_struct_nested_different_field_types_2() {
304; CHECK-LABEL: array_of_struct_nested_different_field_types_2:
305; CHECK:       // %bb.0:
306; CHECK-NEXT:    movi d0, #0000000000000000
307; CHECK-NEXT:    movi d1, #0000000000000000
308; CHECK-NEXT:    movi d2, #0000000000000000
309; CHECK-NEXT:    movi d3, #0000000000000000
310; CHECK-NEXT:    movi d4, #0000000000000000
311; CHECK-NEXT:    movi d5, #0000000000000000
312; CHECK-NEXT:    mov w0, wzr
313; CHECK-NEXT:    mov w1, wzr
314; CHECK-NEXT:    ret
315  ret [ 2 x %T_NESTED_STRUCT_DIFFM ] zeroinitializer
316}
317
318;; All fields here are the same type, more nesting to stress the recursive walk.
319%T_NESTED_STRUCT_SAMEM = type {
320  { { double} },
321  { [ 2 x { double, double } ] }
322};
323
324define %T_NESTED_STRUCT_SAMEM @struct_nested_same_field_types() {
325; CHECK-LABEL: struct_nested_same_field_types:
326; CHECK:       // %bb.0:
327; CHECK-NEXT:    movi d0, #0000000000000000
328; CHECK-NEXT:    movi d1, #0000000000000000
329; CHECK-NEXT:    movi d2, #0000000000000000
330; CHECK-NEXT:    movi d3, #0000000000000000
331; CHECK-NEXT:    movi d4, #0000000000000000
332; CHECK-NEXT:    ret
333  ret %T_NESTED_STRUCT_SAMEM zeroinitializer
334}
335
336define [ 1 x %T_NESTED_STRUCT_SAMEM ] @array_of_struct_nested_same_field_types() {
337; CHECK-LABEL: array_of_struct_nested_same_field_types:
338; CHECK:       // %bb.0:
339; CHECK-NEXT:    movi d0, #0000000000000000
340; CHECK-NEXT:    movi d1, #0000000000000000
341; CHECK-NEXT:    movi d2, #0000000000000000
342; CHECK-NEXT:    movi d3, #0000000000000000
343; CHECK-NEXT:    movi d4, #0000000000000000
344; CHECK-NEXT:    ret
345  ret [ 1 x %T_NESTED_STRUCT_SAMEM ] zeroinitializer
346}
347
348;; 2 x (1 + (2 x 2)) = 10 so this is returned in memory
349define [ 2 x %T_NESTED_STRUCT_SAMEM ] @array_of_struct_nested_same_field_types_2() {
350; CHECK-LABEL: array_of_struct_nested_same_field_types_2:
351; CHECK:       // %bb.0:
352; CHECK-NEXT:    movi v0.2d, #0000000000000000
353; CHECK-NEXT:    stp q0, q0, [x8, #16]
354; CHECK-NEXT:    stp q0, q0, [x8, #48]
355; CHECK-NEXT:    str q0, [x8]
356; CHECK-NEXT:    ret
357  ret [ 2 x %T_NESTED_STRUCT_SAMEM ] zeroinitializer
358}
359
360;; Check combinations of call, return and argument passing
361
362%T_IN_BLOCK = type [ 2 x { double, { double, double } } ]
363
364define %T_IN_BLOCK @return_in_block() {
365; CHECK-LABEL: return_in_block:
366; CHECK:       // %bb.0:
367; CHECK-NEXT:    movi d0, #0000000000000000
368; CHECK-NEXT:    movi d1, #0000000000000000
369; CHECK-NEXT:    movi d2, #0000000000000000
370; CHECK-NEXT:    movi d3, #0000000000000000
371; CHECK-NEXT:    movi d4, #0000000000000000
372; CHECK-NEXT:    movi d5, #0000000000000000
373; CHECK-NEXT:    ret
374  ret %T_IN_BLOCK zeroinitializer
375}
376
377@in_block_store = dso_local global %T_IN_BLOCK zeroinitializer, align 8
378
379define void @caller_in_block() {
380; CHECK-LABEL: caller_in_block:
381; CHECK:       // %bb.0:
382; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
383; CHECK-NEXT:    .cfi_def_cfa_offset 16
384; CHECK-NEXT:    .cfi_offset w30, -16
385; CHECK-NEXT:    bl return_in_block
386; CHECK-NEXT:    adrp x8, in_block_store
387; CHECK-NEXT:    add x8, x8, :lo12:in_block_store
388; CHECK-NEXT:    str d0, [x8]
389; CHECK-NEXT:    str d1, [x8, #8]
390; CHECK-NEXT:    str d2, [x8, #16]
391; CHECK-NEXT:    str d3, [x8, #24]
392; CHECK-NEXT:    str d4, [x8, #32]
393; CHECK-NEXT:    str d5, [x8, #40]
394; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
395; CHECK-NEXT:    ret
396  %1 = call %T_IN_BLOCK @return_in_block()
397  store %T_IN_BLOCK %1, %T_IN_BLOCK* @in_block_store
398  ret void
399}
400
401define void @callee_in_block(%T_IN_BLOCK %a) {
402; CHECK-LABEL: callee_in_block:
403; CHECK:       // %bb.0:
404; CHECK-NEXT:    adrp x8, in_block_store
405; CHECK-NEXT:    add x8, x8, :lo12:in_block_store
406; CHECK-NEXT:    str d5, [x8, #40]
407; CHECK-NEXT:    str d4, [x8, #32]
408; CHECK-NEXT:    str d3, [x8, #24]
409; CHECK-NEXT:    str d2, [x8, #16]
410; CHECK-NEXT:    str d1, [x8, #8]
411; CHECK-NEXT:    str d0, [x8]
412; CHECK-NEXT:    ret
413  store %T_IN_BLOCK %a, %T_IN_BLOCK* @in_block_store
414  ret void
415}
416
417define void @argument_in_block() {
418; CHECK-LABEL: argument_in_block:
419; CHECK:       // %bb.0:
420; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
421; CHECK-NEXT:    .cfi_def_cfa_offset 16
422; CHECK-NEXT:    .cfi_offset w30, -16
423; CHECK-NEXT:    adrp x8, in_block_store
424; CHECK-NEXT:    add x8, x8, :lo12:in_block_store
425; CHECK-NEXT:    ldp d4, d5, [x8, #32]
426; CHECK-NEXT:    ldp d2, d3, [x8, #16]
427; CHECK-NEXT:    ldp d0, d1, [x8]
428; CHECK-NEXT:    bl callee_in_block
429; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
430; CHECK-NEXT:    ret
431  %1 = load %T_IN_BLOCK, %T_IN_BLOCK* @in_block_store
432  call void @callee_in_block(%T_IN_BLOCK %1)
433  ret void
434}
435
436%T_IN_MEMORY = type [ 3 x { double, { double, double } } ]
437
438define %T_IN_MEMORY @return_in_memory() {
439; CHECK-LABEL: return_in_memory:
440; CHECK:       // %bb.0:
441; CHECK-NEXT:    movi v0.2d, #0000000000000000
442; CHECK-NEXT:    str xzr, [x8, #64]
443; CHECK-NEXT:    stp q0, q0, [x8]
444; CHECK-NEXT:    stp q0, q0, [x8, #32]
445; CHECK-NEXT:    ret
446  ret %T_IN_MEMORY zeroinitializer
447}
448
449@in_memory_store = dso_local global %T_IN_MEMORY zeroinitializer, align 8
450
451define void @caller_in_memory() {
452; CHECK-LABEL: caller_in_memory:
453; CHECK:       // %bb.0:
454; CHECK-NEXT:    sub sp, sp, #96
455; CHECK-NEXT:    .cfi_def_cfa_offset 96
456; CHECK-NEXT:    str x30, [sp, #80] // 8-byte Folded Spill
457; CHECK-NEXT:    .cfi_offset w30, -16
458; CHECK-NEXT:    add x8, sp, #8
459; CHECK-NEXT:    bl return_in_memory
460; CHECK-NEXT:    ldur q0, [sp, #24]
461; CHECK-NEXT:    adrp x8, in_memory_store
462; CHECK-NEXT:    add x8, x8, :lo12:in_memory_store
463; CHECK-NEXT:    ldur q1, [sp, #8]
464; CHECK-NEXT:    ldur q2, [sp, #56]
465; CHECK-NEXT:    ldur q3, [sp, #40]
466; CHECK-NEXT:    ldr d4, [sp, #72]
467; CHECK-NEXT:    stp q1, q0, [x8]
468; CHECK-NEXT:    ldr x30, [sp, #80] // 8-byte Folded Reload
469; CHECK-NEXT:    stp q3, q2, [x8, #32]
470; CHECK-NEXT:    str d4, [x8, #64]
471; CHECK-NEXT:    add sp, sp, #96
472; CHECK-NEXT:    ret
473  %1 = call %T_IN_MEMORY @return_in_memory()
474  store %T_IN_MEMORY %1, %T_IN_MEMORY* @in_memory_store
475  ret void
476}
477
478define void @callee_in_memory(%T_IN_MEMORY %a) {
479; CHECK-LABEL: callee_in_memory:
480; CHECK:       // %bb.0:
481; CHECK-NEXT:    ldr d0, [sp, #64]
482; CHECK-NEXT:    adrp x8, in_memory_store
483; CHECK-NEXT:    add x8, x8, :lo12:in_memory_store
484; CHECK-NEXT:    ldr q3, [sp, #16]
485; CHECK-NEXT:    ldp q1, q2, [sp, #32]
486; CHECK-NEXT:    str d0, [x8, #64]
487; CHECK-NEXT:    ldr q0, [sp]
488; CHECK-NEXT:    stp q1, q2, [x8, #32]
489; CHECK-NEXT:    stp q0, q3, [x8]
490; CHECK-NEXT:    ret
491  store %T_IN_MEMORY %a, %T_IN_MEMORY* @in_memory_store
492  ret void
493}
494
495define void @argument_in_memory() {
496; CHECK-LABEL: argument_in_memory:
497; CHECK:       // %bb.0:
498; CHECK-NEXT:    sub sp, sp, #96
499; CHECK-NEXT:    .cfi_def_cfa_offset 96
500; CHECK-NEXT:    str x30, [sp, #80] // 8-byte Folded Spill
501; CHECK-NEXT:    .cfi_offset w30, -16
502; CHECK-NEXT:    adrp x8, in_memory_store
503; CHECK-NEXT:    add x8, x8, :lo12:in_memory_store
504; CHECK-NEXT:    ldp q0, q1, [x8]
505; CHECK-NEXT:    ldp q2, q3, [x8, #32]
506; CHECK-NEXT:    stp q0, q1, [sp]
507; CHECK-NEXT:    ldr d4, [x8, #64]
508; CHECK-NEXT:    stp q2, q3, [sp, #32]
509; CHECK-NEXT:    str d4, [sp, #64]
510; CHECK-NEXT:    bl callee_in_memory
511; CHECK-NEXT:    ldr x30, [sp, #80] // 8-byte Folded Reload
512; CHECK-NEXT:    add sp, sp, #96
513; CHECK-NEXT:    ret
514  %1 = load %T_IN_MEMORY, %T_IN_MEMORY* @in_memory_store
515  call void @callee_in_memory(%T_IN_MEMORY %1)
516  ret void
517}
518
519%T_NO_BLOCK = type [ 2 x { double, { i32 } } ]
520
521define %T_NO_BLOCK @return_no_block() {
522; CHECK-LABEL: return_no_block:
523; CHECK:       // %bb.0:
524; CHECK-NEXT:    movi d0, #0000000000000000
525; CHECK-NEXT:    movi d1, #0000000000000000
526; CHECK-NEXT:    mov w0, wzr
527; CHECK-NEXT:    mov w1, wzr
528; CHECK-NEXT:    ret
529  ret %T_NO_BLOCK zeroinitializer
530}
531
532@no_block_store = dso_local global %T_NO_BLOCK zeroinitializer, align 8
533
534define void @caller_no_block() {
535; CHECK-LABEL: caller_no_block:
536; CHECK:       // %bb.0:
537; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
538; CHECK-NEXT:    .cfi_def_cfa_offset 16
539; CHECK-NEXT:    .cfi_offset w30, -16
540; CHECK-NEXT:    bl return_no_block
541; CHECK-NEXT:    adrp x8, no_block_store
542; CHECK-NEXT:    add x8, x8, :lo12:no_block_store
543; CHECK-NEXT:    str d0, [x8]
544; CHECK-NEXT:    str w0, [x8, #8]
545; CHECK-NEXT:    str d1, [x8, #16]
546; CHECK-NEXT:    str w1, [x8, #24]
547; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
548; CHECK-NEXT:    ret
549  %1 = call %T_NO_BLOCK @return_no_block()
550  store %T_NO_BLOCK %1, %T_NO_BLOCK* @no_block_store
551  ret void
552}
553
554define void @callee_no_block(%T_NO_BLOCK %a) {
555; CHECK-LABEL: callee_no_block:
556; CHECK:       // %bb.0:
557; CHECK-NEXT:    adrp x8, no_block_store
558; CHECK-NEXT:    add x8, x8, :lo12:no_block_store
559; CHECK-NEXT:    str w1, [x8, #24]
560; CHECK-NEXT:    str d1, [x8, #16]
561; CHECK-NEXT:    str w0, [x8, #8]
562; CHECK-NEXT:    str d0, [x8]
563; CHECK-NEXT:    ret
564  store %T_NO_BLOCK %a, %T_NO_BLOCK* @no_block_store
565  ret void
566}
567
568define void @argument_no_block() {
569; CHECK-LABEL: argument_no_block:
570; CHECK:       // %bb.0:
571; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
572; CHECK-NEXT:    .cfi_def_cfa_offset 16
573; CHECK-NEXT:    .cfi_offset w30, -16
574; CHECK-NEXT:    adrp x8, no_block_store
575; CHECK-NEXT:    add x8, x8, :lo12:no_block_store
576; CHECK-NEXT:    ldr w1, [x8, #24]
577; CHECK-NEXT:    ldr d1, [x8, #16]
578; CHECK-NEXT:    ldr w0, [x8, #8]
579; CHECK-NEXT:    ldr d0, [x8]
580; CHECK-NEXT:    bl callee_no_block
581; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
582; CHECK-NEXT:    ret
583  %1 = load %T_NO_BLOCK, %T_NO_BLOCK* @no_block_store
584  call void @callee_no_block(%T_NO_BLOCK %1)
585  ret void
586}
587