; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc -mtriple=aarch64-none-linux-gnu -o - %s | FileCheck %s ;; Check that the llvm aarch64 backend can handle arrays of ;; structs and vice versa when passed from IR. ;; (this layering is something clang would normally simplify) ;; ;; Some of these examples are not ABI compliant and they're not ;; meant to be. For instance according to the ABI an aggregate ;; with more than 4 members must go in memory. This restriction ;; is applied earlier in the compilation process so here we do ;; see 8 member types in registers. ;; ;; When we have more than 8 members we simply run out of registers ;; and that's what produces the 8 limit here. ;; Plain arrays define [ 0 x double ] @array_0() { ; CHECK-LABEL: array_0: ; CHECK: // %bb.0: ; CHECK-NEXT: ret ret [ 0 x double ] zeroinitializer } define [ 1 x double ] @array_1() { ; CHECK-LABEL: array_1: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: ret ret [ 1 x double ] zeroinitializer } define [ 8 x double ] @array_8() { ; CHECK-LABEL: array_8: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: movi d2, #0000000000000000 ; CHECK-NEXT: movi d3, #0000000000000000 ; CHECK-NEXT: movi d4, #0000000000000000 ; CHECK-NEXT: movi d5, #0000000000000000 ; CHECK-NEXT: movi d6, #0000000000000000 ; CHECK-NEXT: movi d7, #0000000000000000 ; CHECK-NEXT: ret ret [ 8 x double ] zeroinitializer } ;; > 8 items goes on the stack define [ 9 x double ] @array_9() { ; CHECK-LABEL: array_9: ; CHECK: // %bb.0: ; CHECK-NEXT: movi v0.2d, #0000000000000000 ; CHECK-NEXT: str xzr, [x8, #64] ; CHECK-NEXT: stp q0, q0, [x8] ; CHECK-NEXT: stp q0, q0, [x8, #32] ; CHECK-NEXT: ret ret [ 9 x double ] zeroinitializer } ;; Won't use any registers, just checking for assumptions. %T_STRUCT_0M = type { } define %T_STRUCT_0M @struct_zero_fields() { ; CHECK-LABEL: struct_zero_fields: ; CHECK: // %bb.0: ; CHECK-NEXT: ret ret %T_STRUCT_0M zeroinitializer } define [ 1 x %T_STRUCT_0M ] @array_of_struct_zero_fields() { ; CHECK-LABEL: array_of_struct_zero_fields: ; CHECK: // %bb.0: ; CHECK-NEXT: ret ret [ 1 x %T_STRUCT_0M ] zeroinitializer } define [ 2 x %T_STRUCT_0M ] @array_of_struct_zero_fields_in_struct() { ; CHECK-LABEL: array_of_struct_zero_fields_in_struct: ; CHECK: // %bb.0: ; CHECK-NEXT: ret ret [ 2 x %T_STRUCT_0M ] zeroinitializer } %T_STRUCT_1M = type { i32 } define %T_STRUCT_1M @struct_one_field() { ; CHECK-LABEL: struct_one_field: ; CHECK: // %bb.0: ; CHECK-NEXT: mov w0, wzr ; CHECK-NEXT: ret ret %T_STRUCT_1M zeroinitializer } define [ 1 x %T_STRUCT_1M ] @array_of_struct_one_field() { ; CHECK-LABEL: array_of_struct_one_field: ; CHECK: // %bb.0: ; CHECK-NEXT: mov w0, wzr ; CHECK-NEXT: ret ret [ 1 x %T_STRUCT_1M ] zeroinitializer } ;; This one will be a reg block define [ 2 x %T_STRUCT_1M ] @array_of_struct_one_field_2() { ; CHECK-LABEL: array_of_struct_one_field_2: ; CHECK: // %bb.0: ; CHECK-NEXT: mov w0, wzr ; CHECK-NEXT: mov w1, wzr ; CHECK-NEXT: ret ret [ 2 x %T_STRUCT_1M ] zeroinitializer } ;; Different types for each field, will not be put in a reg block %T_STRUCT_DIFFM = type { double, i32 } define %T_STRUCT_DIFFM @struct_different_field_types() { ; CHECK-LABEL: struct_different_field_types: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: mov w0, wzr ; CHECK-NEXT: ret ret %T_STRUCT_DIFFM zeroinitializer } define [ 1 x %T_STRUCT_DIFFM ] @array_of_struct_different_field_types() { ; CHECK-LABEL: array_of_struct_different_field_types: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: mov w0, wzr ; CHECK-NEXT: ret ret [ 1 x %T_STRUCT_DIFFM ] zeroinitializer } define [ 2 x %T_STRUCT_DIFFM ] @array_of_struct_different_field_types_2() { ; CHECK-LABEL: array_of_struct_different_field_types_2: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: mov w0, wzr ; CHECK-NEXT: mov w1, wzr ; CHECK-NEXT: ret ret [ 2 x %T_STRUCT_DIFFM ] zeroinitializer } ;; Each field is the same type, can be put in a reg block %T_STRUCT_SAMEM = type { double, double } ;; Here isn't a block as such, we just allocate two consecutive registers define %T_STRUCT_SAMEM @struct_same_field_types() { ; CHECK-LABEL: struct_same_field_types: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: ret ret %T_STRUCT_SAMEM zeroinitializer } define [ 1 x %T_STRUCT_SAMEM ] @array_of_struct_same_field_types() { ; CHECK-LABEL: array_of_struct_same_field_types: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: ret ret [ 1 x %T_STRUCT_SAMEM ] zeroinitializer } define [ 2 x %T_STRUCT_SAMEM ] @array_of_struct_same_field_types_2() { ; CHECK-LABEL: array_of_struct_same_field_types_2: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: movi d2, #0000000000000000 ; CHECK-NEXT: movi d3, #0000000000000000 ; CHECK-NEXT: ret ret [ 2 x %T_STRUCT_SAMEM ] zeroinitializer } ;; Same field type but integer this time. Put into x registers instead. %T_STRUCT_SAMEM_INT = type { i64, i64 } define %T_STRUCT_SAMEM_INT @struct_same_field_types_int() { ; CHECK-LABEL: struct_same_field_types_int: ; CHECK: // %bb.0: ; CHECK-NEXT: mov x0, xzr ; CHECK-NEXT: mov x1, xzr ; CHECK-NEXT: ret ret %T_STRUCT_SAMEM_INT zeroinitializer } define [ 1 x %T_STRUCT_SAMEM_INT ] @array_of_struct_same_field_types_int() { ; CHECK-LABEL: array_of_struct_same_field_types_int: ; CHECK: // %bb.0: ; CHECK-NEXT: mov x0, xzr ; CHECK-NEXT: mov x1, xzr ; CHECK-NEXT: ret ret [ 1 x %T_STRUCT_SAMEM_INT ] zeroinitializer } define [ 2 x %T_STRUCT_SAMEM_INT ] @array_of_struct_same_field_types_int_2() { ; CHECK-LABEL: array_of_struct_same_field_types_int_2: ; CHECK: // %bb.0: ; CHECK-NEXT: mov x0, xzr ; CHECK-NEXT: mov x1, xzr ; CHECK-NEXT: mov x2, xzr ; CHECK-NEXT: mov x3, xzr ; CHECK-NEXT: ret ret [ 2 x %T_STRUCT_SAMEM_INT ] zeroinitializer } ;; An aggregate of more than 8 items must go in memory. ;; 4x2 struct fields = 8 items so it goes in a block. define [ 4 x %T_STRUCT_SAMEM ] @array_of_struct_8_fields() { ; CHECK-LABEL: array_of_struct_8_fields: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: movi d2, #0000000000000000 ; CHECK-NEXT: movi d3, #0000000000000000 ; CHECK-NEXT: movi d4, #0000000000000000 ; CHECK-NEXT: movi d5, #0000000000000000 ; CHECK-NEXT: movi d6, #0000000000000000 ; CHECK-NEXT: movi d7, #0000000000000000 ; CHECK-NEXT: ret ret [ 4 x %T_STRUCT_SAMEM ] zeroinitializer } ;; 5x2 fields = 10 so it is returned in memory. define [ 5 x %T_STRUCT_SAMEM ] @array_of_struct_in_memory() { ; CHECK-LABEL: array_of_struct_in_memory: ; CHECK: // %bb.0: ; CHECK-NEXT: movi v0.2d, #0000000000000000 ; CHECK-NEXT: stp q0, q0, [x8, #16] ; CHECK-NEXT: stp q0, q0, [x8, #48] ; CHECK-NEXT: str q0, [x8] ; CHECK-NEXT: ret ret [ 5 x %T_STRUCT_SAMEM ] zeroinitializer } ;; A struct whose field is an array. %T_STRUCT_ARRAYM = type { [ 2 x double ]}; define %T_STRUCT_ARRAYM @struct_array_field() { ; CHECK-LABEL: struct_array_field: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: ret ret %T_STRUCT_ARRAYM zeroinitializer } define [ 1 x %T_STRUCT_ARRAYM ] @array_of_struct_array_field() { ; CHECK-LABEL: array_of_struct_array_field: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: ret ret [ 1 x %T_STRUCT_ARRAYM ] zeroinitializer } define [ 2 x %T_STRUCT_ARRAYM ] @array_of_struct_array_field_2() { ; CHECK-LABEL: array_of_struct_array_field_2: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: movi d2, #0000000000000000 ; CHECK-NEXT: movi d3, #0000000000000000 ; CHECK-NEXT: ret ret [ 2 x %T_STRUCT_ARRAYM ] zeroinitializer } ;; All non-aggregate fields must have the same type, all through the ;; overall aggreagate. This is false here because of the i32. %T_NESTED_STRUCT_DIFFM = type { [ 1 x { { double, double } } ], [ 1 x { { double, i32 } } ] }; define %T_NESTED_STRUCT_DIFFM @struct_nested_different_field_types() { ; CHECK-LABEL: struct_nested_different_field_types: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: movi d2, #0000000000000000 ; CHECK-NEXT: mov w0, wzr ; CHECK-NEXT: ret ret %T_NESTED_STRUCT_DIFFM zeroinitializer } define [ 1 x %T_NESTED_STRUCT_DIFFM ] @array_of_struct_nested_different_field_types() { ; CHECK-LABEL: array_of_struct_nested_different_field_types: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: movi d2, #0000000000000000 ; CHECK-NEXT: mov w0, wzr ; CHECK-NEXT: ret ret [ 1 x %T_NESTED_STRUCT_DIFFM ] zeroinitializer } define [ 2 x %T_NESTED_STRUCT_DIFFM ] @array_of_struct_nested_different_field_types_2() { ; CHECK-LABEL: array_of_struct_nested_different_field_types_2: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: movi d2, #0000000000000000 ; CHECK-NEXT: movi d3, #0000000000000000 ; CHECK-NEXT: movi d4, #0000000000000000 ; CHECK-NEXT: movi d5, #0000000000000000 ; CHECK-NEXT: mov w0, wzr ; CHECK-NEXT: mov w1, wzr ; CHECK-NEXT: ret ret [ 2 x %T_NESTED_STRUCT_DIFFM ] zeroinitializer } ;; All fields here are the same type, more nesting to stress the recursive walk. %T_NESTED_STRUCT_SAMEM = type { { { double} }, { [ 2 x { double, double } ] } }; define %T_NESTED_STRUCT_SAMEM @struct_nested_same_field_types() { ; CHECK-LABEL: struct_nested_same_field_types: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: movi d2, #0000000000000000 ; CHECK-NEXT: movi d3, #0000000000000000 ; CHECK-NEXT: movi d4, #0000000000000000 ; CHECK-NEXT: ret ret %T_NESTED_STRUCT_SAMEM zeroinitializer } define [ 1 x %T_NESTED_STRUCT_SAMEM ] @array_of_struct_nested_same_field_types() { ; CHECK-LABEL: array_of_struct_nested_same_field_types: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: movi d2, #0000000000000000 ; CHECK-NEXT: movi d3, #0000000000000000 ; CHECK-NEXT: movi d4, #0000000000000000 ; CHECK-NEXT: ret ret [ 1 x %T_NESTED_STRUCT_SAMEM ] zeroinitializer } ;; 2 x (1 + (2 x 2)) = 10 so this is returned in memory define [ 2 x %T_NESTED_STRUCT_SAMEM ] @array_of_struct_nested_same_field_types_2() { ; CHECK-LABEL: array_of_struct_nested_same_field_types_2: ; CHECK: // %bb.0: ; CHECK-NEXT: movi v0.2d, #0000000000000000 ; CHECK-NEXT: stp q0, q0, [x8, #16] ; CHECK-NEXT: stp q0, q0, [x8, #48] ; CHECK-NEXT: str q0, [x8] ; CHECK-NEXT: ret ret [ 2 x %T_NESTED_STRUCT_SAMEM ] zeroinitializer } ;; Check combinations of call, return and argument passing %T_IN_BLOCK = type [ 2 x { double, { double, double } } ] define %T_IN_BLOCK @return_in_block() { ; CHECK-LABEL: return_in_block: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: movi d2, #0000000000000000 ; CHECK-NEXT: movi d3, #0000000000000000 ; CHECK-NEXT: movi d4, #0000000000000000 ; CHECK-NEXT: movi d5, #0000000000000000 ; CHECK-NEXT: ret ret %T_IN_BLOCK zeroinitializer } @in_block_store = dso_local global %T_IN_BLOCK zeroinitializer, align 8 define void @caller_in_block() { ; CHECK-LABEL: caller_in_block: ; CHECK: // %bb.0: ; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill ; CHECK-NEXT: .cfi_def_cfa_offset 16 ; CHECK-NEXT: .cfi_offset w30, -16 ; CHECK-NEXT: bl return_in_block ; CHECK-NEXT: adrp x8, in_block_store ; CHECK-NEXT: add x8, x8, :lo12:in_block_store ; CHECK-NEXT: str d0, [x8] ; CHECK-NEXT: str d1, [x8, #8] ; CHECK-NEXT: str d2, [x8, #16] ; CHECK-NEXT: str d3, [x8, #24] ; CHECK-NEXT: str d4, [x8, #32] ; CHECK-NEXT: str d5, [x8, #40] ; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %1 = call %T_IN_BLOCK @return_in_block() store %T_IN_BLOCK %1, %T_IN_BLOCK* @in_block_store ret void } define void @callee_in_block(%T_IN_BLOCK %a) { ; CHECK-LABEL: callee_in_block: ; CHECK: // %bb.0: ; CHECK-NEXT: adrp x8, in_block_store ; CHECK-NEXT: add x8, x8, :lo12:in_block_store ; CHECK-NEXT: str d5, [x8, #40] ; CHECK-NEXT: str d4, [x8, #32] ; CHECK-NEXT: str d3, [x8, #24] ; CHECK-NEXT: str d2, [x8, #16] ; CHECK-NEXT: str d1, [x8, #8] ; CHECK-NEXT: str d0, [x8] ; CHECK-NEXT: ret store %T_IN_BLOCK %a, %T_IN_BLOCK* @in_block_store ret void } define void @argument_in_block() { ; CHECK-LABEL: argument_in_block: ; CHECK: // %bb.0: ; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill ; CHECK-NEXT: .cfi_def_cfa_offset 16 ; CHECK-NEXT: .cfi_offset w30, -16 ; CHECK-NEXT: adrp x8, in_block_store ; CHECK-NEXT: add x8, x8, :lo12:in_block_store ; CHECK-NEXT: ldp d4, d5, [x8, #32] ; CHECK-NEXT: ldp d2, d3, [x8, #16] ; CHECK-NEXT: ldp d0, d1, [x8] ; CHECK-NEXT: bl callee_in_block ; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %1 = load %T_IN_BLOCK, %T_IN_BLOCK* @in_block_store call void @callee_in_block(%T_IN_BLOCK %1) ret void } %T_IN_MEMORY = type [ 3 x { double, { double, double } } ] define %T_IN_MEMORY @return_in_memory() { ; CHECK-LABEL: return_in_memory: ; CHECK: // %bb.0: ; CHECK-NEXT: movi v0.2d, #0000000000000000 ; CHECK-NEXT: str xzr, [x8, #64] ; CHECK-NEXT: stp q0, q0, [x8] ; CHECK-NEXT: stp q0, q0, [x8, #32] ; CHECK-NEXT: ret ret %T_IN_MEMORY zeroinitializer } @in_memory_store = dso_local global %T_IN_MEMORY zeroinitializer, align 8 define void @caller_in_memory() { ; CHECK-LABEL: caller_in_memory: ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #96 ; CHECK-NEXT: .cfi_def_cfa_offset 96 ; CHECK-NEXT: str x30, [sp, #80] // 8-byte Folded Spill ; CHECK-NEXT: .cfi_offset w30, -16 ; CHECK-NEXT: add x8, sp, #8 ; CHECK-NEXT: bl return_in_memory ; CHECK-NEXT: ldur q0, [sp, #24] ; CHECK-NEXT: adrp x8, in_memory_store ; CHECK-NEXT: add x8, x8, :lo12:in_memory_store ; CHECK-NEXT: ldur q1, [sp, #8] ; CHECK-NEXT: ldur q2, [sp, #56] ; CHECK-NEXT: ldur q3, [sp, #40] ; CHECK-NEXT: ldr d4, [sp, #72] ; CHECK-NEXT: stp q1, q0, [x8] ; CHECK-NEXT: ldr x30, [sp, #80] // 8-byte Folded Reload ; CHECK-NEXT: stp q3, q2, [x8, #32] ; CHECK-NEXT: str d4, [x8, #64] ; CHECK-NEXT: add sp, sp, #96 ; CHECK-NEXT: ret %1 = call %T_IN_MEMORY @return_in_memory() store %T_IN_MEMORY %1, %T_IN_MEMORY* @in_memory_store ret void } define void @callee_in_memory(%T_IN_MEMORY %a) { ; CHECK-LABEL: callee_in_memory: ; CHECK: // %bb.0: ; CHECK-NEXT: ldr d0, [sp, #64] ; CHECK-NEXT: adrp x8, in_memory_store ; CHECK-NEXT: add x8, x8, :lo12:in_memory_store ; CHECK-NEXT: ldr q3, [sp, #16] ; CHECK-NEXT: ldp q1, q2, [sp, #32] ; CHECK-NEXT: str d0, [x8, #64] ; CHECK-NEXT: ldr q0, [sp] ; CHECK-NEXT: stp q1, q2, [x8, #32] ; CHECK-NEXT: stp q0, q3, [x8] ; CHECK-NEXT: ret store %T_IN_MEMORY %a, %T_IN_MEMORY* @in_memory_store ret void } define void @argument_in_memory() { ; CHECK-LABEL: argument_in_memory: ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #96 ; CHECK-NEXT: .cfi_def_cfa_offset 96 ; CHECK-NEXT: str x30, [sp, #80] // 8-byte Folded Spill ; CHECK-NEXT: .cfi_offset w30, -16 ; CHECK-NEXT: adrp x8, in_memory_store ; CHECK-NEXT: add x8, x8, :lo12:in_memory_store ; CHECK-NEXT: ldp q0, q1, [x8] ; CHECK-NEXT: ldp q2, q3, [x8, #32] ; CHECK-NEXT: stp q0, q1, [sp] ; CHECK-NEXT: ldr d4, [x8, #64] ; CHECK-NEXT: stp q2, q3, [sp, #32] ; CHECK-NEXT: str d4, [sp, #64] ; CHECK-NEXT: bl callee_in_memory ; CHECK-NEXT: ldr x30, [sp, #80] // 8-byte Folded Reload ; CHECK-NEXT: add sp, sp, #96 ; CHECK-NEXT: ret %1 = load %T_IN_MEMORY, %T_IN_MEMORY* @in_memory_store call void @callee_in_memory(%T_IN_MEMORY %1) ret void } %T_NO_BLOCK = type [ 2 x { double, { i32 } } ] define %T_NO_BLOCK @return_no_block() { ; CHECK-LABEL: return_no_block: ; CHECK: // %bb.0: ; CHECK-NEXT: movi d0, #0000000000000000 ; CHECK-NEXT: movi d1, #0000000000000000 ; CHECK-NEXT: mov w0, wzr ; CHECK-NEXT: mov w1, wzr ; CHECK-NEXT: ret ret %T_NO_BLOCK zeroinitializer } @no_block_store = dso_local global %T_NO_BLOCK zeroinitializer, align 8 define void @caller_no_block() { ; CHECK-LABEL: caller_no_block: ; CHECK: // %bb.0: ; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill ; CHECK-NEXT: .cfi_def_cfa_offset 16 ; CHECK-NEXT: .cfi_offset w30, -16 ; CHECK-NEXT: bl return_no_block ; CHECK-NEXT: adrp x8, no_block_store ; CHECK-NEXT: add x8, x8, :lo12:no_block_store ; CHECK-NEXT: str d0, [x8] ; CHECK-NEXT: str w0, [x8, #8] ; CHECK-NEXT: str d1, [x8, #16] ; CHECK-NEXT: str w1, [x8, #24] ; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %1 = call %T_NO_BLOCK @return_no_block() store %T_NO_BLOCK %1, %T_NO_BLOCK* @no_block_store ret void } define void @callee_no_block(%T_NO_BLOCK %a) { ; CHECK-LABEL: callee_no_block: ; CHECK: // %bb.0: ; CHECK-NEXT: adrp x8, no_block_store ; CHECK-NEXT: add x8, x8, :lo12:no_block_store ; CHECK-NEXT: str w1, [x8, #24] ; CHECK-NEXT: str d1, [x8, #16] ; CHECK-NEXT: str w0, [x8, #8] ; CHECK-NEXT: str d0, [x8] ; CHECK-NEXT: ret store %T_NO_BLOCK %a, %T_NO_BLOCK* @no_block_store ret void } define void @argument_no_block() { ; CHECK-LABEL: argument_no_block: ; CHECK: // %bb.0: ; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill ; CHECK-NEXT: .cfi_def_cfa_offset 16 ; CHECK-NEXT: .cfi_offset w30, -16 ; CHECK-NEXT: adrp x8, no_block_store ; CHECK-NEXT: add x8, x8, :lo12:no_block_store ; CHECK-NEXT: ldr w1, [x8, #24] ; CHECK-NEXT: ldr d1, [x8, #16] ; CHECK-NEXT: ldr w0, [x8, #8] ; CHECK-NEXT: ldr d0, [x8] ; CHECK-NEXT: bl callee_no_block ; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %1 = load %T_NO_BLOCK, %T_NO_BLOCK* @no_block_store call void @callee_no_block(%T_NO_BLOCK %1) ret void }