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