xref: /llvm-project/llvm/test/CodeGen/AArch64/argument-blocks-array-of-struct.ll (revision f71ad19c04763b858e53e0e291698b769b7c2bfc)
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:    mov w0, wzr
287; CHECK-NEXT:    movi d2, #0000000000000000
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:    mov w0, wzr
298; CHECK-NEXT:    movi d2, #0000000000000000
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:    mov w0, wzr
309; CHECK-NEXT:    movi d2, #0000000000000000
310; CHECK-NEXT:    movi d3, #0000000000000000
311; CHECK-NEXT:    mov w1, wzr
312; CHECK-NEXT:    movi d4, #0000000000000000
313; CHECK-NEXT:    movi d5, #0000000000000000
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:    stp d0, d1, [x8]
389; CHECK-NEXT:    stp d2, d3, [x8, #16]
390; CHECK-NEXT:    stp d4, d5, [x8, #32]
391; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
392; CHECK-NEXT:    ret
393  %1 = call %T_IN_BLOCK @return_in_block()
394  store %T_IN_BLOCK %1, ptr @in_block_store
395  ret void
396}
397
398define void @callee_in_block(%T_IN_BLOCK %a) {
399; CHECK-LABEL: callee_in_block:
400; CHECK:       // %bb.0:
401; CHECK-NEXT:    adrp x8, in_block_store
402; CHECK-NEXT:    add x8, x8, :lo12:in_block_store
403; CHECK-NEXT:    stp d4, d5, [x8, #32]
404; CHECK-NEXT:    stp d2, d3, [x8, #16]
405; CHECK-NEXT:    stp d0, d1, [x8]
406; CHECK-NEXT:    ret
407  store %T_IN_BLOCK %a, ptr @in_block_store
408  ret void
409}
410
411define void @argument_in_block() {
412; CHECK-LABEL: argument_in_block:
413; CHECK:       // %bb.0:
414; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
415; CHECK-NEXT:    .cfi_def_cfa_offset 16
416; CHECK-NEXT:    .cfi_offset w30, -16
417; CHECK-NEXT:    adrp x8, in_block_store
418; CHECK-NEXT:    add x8, x8, :lo12:in_block_store
419; CHECK-NEXT:    ldp d4, d5, [x8, #32]
420; CHECK-NEXT:    ldp d2, d3, [x8, #16]
421; CHECK-NEXT:    ldp d0, d1, [x8]
422; CHECK-NEXT:    bl callee_in_block
423; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
424; CHECK-NEXT:    ret
425  %1 = load %T_IN_BLOCK, ptr @in_block_store
426  call void @callee_in_block(%T_IN_BLOCK %1)
427  ret void
428}
429
430%T_IN_MEMORY = type [ 3 x { double, { double, double } } ]
431
432define %T_IN_MEMORY @return_in_memory() {
433; CHECK-LABEL: return_in_memory:
434; CHECK:       // %bb.0:
435; CHECK-NEXT:    movi v0.2d, #0000000000000000
436; CHECK-NEXT:    str xzr, [x8, #64]
437; CHECK-NEXT:    stp q0, q0, [x8]
438; CHECK-NEXT:    stp q0, q0, [x8, #32]
439; CHECK-NEXT:    ret
440  ret %T_IN_MEMORY zeroinitializer
441}
442
443@in_memory_store = dso_local global %T_IN_MEMORY zeroinitializer, align 8
444
445define void @caller_in_memory() {
446; CHECK-LABEL: caller_in_memory:
447; CHECK:       // %bb.0:
448; CHECK-NEXT:    sub sp, sp, #96
449; CHECK-NEXT:    str x30, [sp, #80] // 8-byte Folded Spill
450; CHECK-NEXT:    .cfi_def_cfa_offset 96
451; CHECK-NEXT:    .cfi_offset w30, -16
452; CHECK-NEXT:    add x8, sp, #8
453; CHECK-NEXT:    bl return_in_memory
454; CHECK-NEXT:    ldur q0, [sp, #24]
455; CHECK-NEXT:    ldur q1, [sp, #8]
456; CHECK-NEXT:    adrp x8, in_memory_store
457; CHECK-NEXT:    add x8, x8, :lo12:in_memory_store
458; CHECK-NEXT:    ldr d2, [sp, #72]
459; CHECK-NEXT:    ldur q3, [sp, #56]
460; CHECK-NEXT:    ldur q4, [sp, #40]
461; CHECK-NEXT:    ldr x30, [sp, #80] // 8-byte Folded Reload
462; CHECK-NEXT:    stp q1, q0, [x8]
463; CHECK-NEXT:    str d2, [x8, #64]
464; CHECK-NEXT:    stp q4, q3, [x8, #32]
465; CHECK-NEXT:    add sp, sp, #96
466; CHECK-NEXT:    ret
467  %1 = call %T_IN_MEMORY @return_in_memory()
468  store %T_IN_MEMORY %1, ptr @in_memory_store
469  ret void
470}
471
472define void @callee_in_memory(%T_IN_MEMORY %a) {
473; CHECK-LABEL: callee_in_memory:
474; CHECK:       // %bb.0:
475; CHECK-NEXT:    ldp q1, q2, [sp, #32]
476; CHECK-NEXT:    adrp x8, in_memory_store
477; CHECK-NEXT:    add x8, x8, :lo12:in_memory_store
478; CHECK-NEXT:    ldr d0, [sp, #64]
479; CHECK-NEXT:    str d0, [x8, #64]
480; CHECK-NEXT:    ldr q0, [sp, #16]
481; CHECK-NEXT:    str q2, [x8, #48]
482; CHECK-NEXT:    ldr q2, [sp]
483; CHECK-NEXT:    stp q0, q1, [x8, #16]
484; CHECK-NEXT:    str q2, [x8]
485; CHECK-NEXT:    ret
486  store %T_IN_MEMORY %a, ptr @in_memory_store
487  ret void
488}
489
490define void @argument_in_memory() {
491; CHECK-LABEL: argument_in_memory:
492; CHECK:       // %bb.0:
493; CHECK-NEXT:    sub sp, sp, #96
494; CHECK-NEXT:    str x30, [sp, #80] // 8-byte Folded Spill
495; CHECK-NEXT:    .cfi_def_cfa_offset 96
496; CHECK-NEXT:    .cfi_offset w30, -16
497; CHECK-NEXT:    adrp x8, in_memory_store
498; CHECK-NEXT:    add x8, x8, :lo12:in_memory_store
499; CHECK-NEXT:    ldp q0, q1, [x8]
500; CHECK-NEXT:    ldr d4, [x8, #64]
501; CHECK-NEXT:    ldp q2, q3, [x8, #32]
502; CHECK-NEXT:    str d4, [sp, #64]
503; CHECK-NEXT:    stp q0, q1, [sp]
504; CHECK-NEXT:    stp q2, q3, [sp, #32]
505; CHECK-NEXT:    bl callee_in_memory
506; CHECK-NEXT:    ldr x30, [sp, #80] // 8-byte Folded Reload
507; CHECK-NEXT:    add sp, sp, #96
508; CHECK-NEXT:    ret
509  %1 = load %T_IN_MEMORY, ptr @in_memory_store
510  call void @callee_in_memory(%T_IN_MEMORY %1)
511  ret void
512}
513
514%T_NO_BLOCK = type [ 2 x { double, { i32 } } ]
515
516define %T_NO_BLOCK @return_no_block() {
517; CHECK-LABEL: return_no_block:
518; CHECK:       // %bb.0:
519; CHECK-NEXT:    movi d0, #0000000000000000
520; CHECK-NEXT:    movi d1, #0000000000000000
521; CHECK-NEXT:    mov w0, wzr
522; CHECK-NEXT:    mov w1, wzr
523; CHECK-NEXT:    ret
524  ret %T_NO_BLOCK zeroinitializer
525}
526
527@no_block_store = dso_local global %T_NO_BLOCK zeroinitializer, align 8
528
529define void @caller_no_block() {
530; CHECK-LABEL: caller_no_block:
531; CHECK:       // %bb.0:
532; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
533; CHECK-NEXT:    .cfi_def_cfa_offset 16
534; CHECK-NEXT:    .cfi_offset w30, -16
535; CHECK-NEXT:    bl return_no_block
536; CHECK-NEXT:    adrp x8, no_block_store
537; CHECK-NEXT:    add x8, x8, :lo12:no_block_store
538; CHECK-NEXT:    str d0, [x8]
539; CHECK-NEXT:    str w0, [x8, #8]
540; CHECK-NEXT:    str d1, [x8, #16]
541; CHECK-NEXT:    str w1, [x8, #24]
542; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
543; CHECK-NEXT:    ret
544  %1 = call %T_NO_BLOCK @return_no_block()
545  store %T_NO_BLOCK %1, ptr @no_block_store
546  ret void
547}
548
549define void @callee_no_block(%T_NO_BLOCK %a) {
550; CHECK-LABEL: callee_no_block:
551; CHECK:       // %bb.0:
552; CHECK-NEXT:    adrp x8, no_block_store
553; CHECK-NEXT:    add x8, x8, :lo12:no_block_store
554; CHECK-NEXT:    str w1, [x8, #24]
555; CHECK-NEXT:    str d1, [x8, #16]
556; CHECK-NEXT:    str w0, [x8, #8]
557; CHECK-NEXT:    str d0, [x8]
558; CHECK-NEXT:    ret
559  store %T_NO_BLOCK %a, ptr @no_block_store
560  ret void
561}
562
563define void @argument_no_block() {
564; CHECK-LABEL: argument_no_block:
565; CHECK:       // %bb.0:
566; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
567; CHECK-NEXT:    .cfi_def_cfa_offset 16
568; CHECK-NEXT:    .cfi_offset w30, -16
569; CHECK-NEXT:    adrp x8, no_block_store
570; CHECK-NEXT:    add x8, x8, :lo12:no_block_store
571; CHECK-NEXT:    ldr w1, [x8, #24]
572; CHECK-NEXT:    ldr d1, [x8, #16]
573; CHECK-NEXT:    ldr w0, [x8, #8]
574; CHECK-NEXT:    ldr d0, [x8]
575; CHECK-NEXT:    bl callee_no_block
576; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
577; CHECK-NEXT:    ret
578  %1 = load %T_NO_BLOCK, ptr @no_block_store
579  call void @callee_no_block(%T_NO_BLOCK %1)
580  ret void
581}
582