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