1 ; RUN: llc -mtriple=thumbv6m-eabi -disable-fp-elim=false %s -o - | FileCheck %s 2 3 ; struct S { int x[128]; } s; 4 ; int f(int *, int, int, int, struct S); 5 ; int g(int *, int, int, int, int, int); 6 ; int h(int *, int *, int *); 7 ; int u(int *, int *, int *, struct S, struct S); 8 9 %struct.S = type { [128 x i32] } 10 %struct.__va_list = type { i8* } 11 12 @s = common dso_local global %struct.S zeroinitializer, align 4 13 14 declare void @llvm.va_start(i8*) 15 declare dso_local i32 @g(i32*, i32, i32, i32, i32, i32) local_unnamed_addr 16 declare dso_local i32 @f(i32*, i32, i32, i32, %struct.S* byval align 4) local_unnamed_addr 17 declare dso_local i32 @h(i32*, i32*, i32*) local_unnamed_addr 18 declare dso_local i32 @u(i32*, i32*, i32*, %struct.S* byval align 4, %struct.S* byval align 4) local_unnamed_addr 19 20 ; 21 ; Test access to arguments, passed on stack (including varargs) 22 ; 23 24 ; Usual case, access via SP 25 ; int test_args_sp(int a, int b, int c, int d, int e) { 26 ; int v[4]; 27 ; return g(v, a, b, c, d, e); 28 ; } 29 define dso_local i32 @test_args_sp(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr { 30 entry: 31 %v = alloca [4 x i32], align 4 32 %0 = bitcast [4 x i32]* %v to i8* 33 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0 34 %call = call i32 @g(i32* nonnull %arraydecay, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) 35 ret i32 %call 36 } 37 ; CHECK-LABEL: test_args_sp 38 ; Load `e` 39 ; CHECK: ldr r0, [sp, #40] 40 ; CHECK-NEXT: mov r5, sp 41 ; CHECK-NEXT: str r3, [r5] 42 ; Pass `e` on stack 43 ; CHECK-NEXT: str r0, [r5, #4] 44 ; CHECK: bl g 45 46 ; int test_varargs_sp(int a, ...) { 47 ; int v[4]; 48 ; __builtin_va_list ap; 49 ; __builtin_va_start(ap, a); 50 ; return g(v, a, 0, 0, 0, 0); 51 ; } 52 define dso_local i32 @test_varargs_sp(i32 %a, ...) local_unnamed_addr { 53 entry: 54 %v = alloca [4 x i32], align 4 55 %ap = alloca %struct.__va_list, align 4 56 %0 = bitcast [4 x i32]* %v to i8* 57 %1 = bitcast %struct.__va_list* %ap to i8* 58 call void @llvm.va_start(i8* nonnull %1) 59 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0 60 %call = call i32 @g(i32* nonnull %arraydecay, i32 %a, i32 0, i32 0, i32 0, i32 0) 61 ret i32 %call 62 } 63 ; CHECK-LABEL: test_varargs_sp 64 ; Three incoming varargs in registers 65 ; CHECK: sub sp, #12 66 ; CHECK: sub sp, #28 67 ; Incoming arguments area is accessed via SP 68 ; CHECK: add r0, sp, #36 69 ; CHECK: stm r0!, {r1, r2, r3} 70 71 ; Re-aligned stack, access via FP 72 ; int test_args_realign(int a, int b, int c, int d, int e) { 73 ; __attribute__((aligned(16))) int v[4]; 74 ; return g(v, a, b, c, d, e); 75 ; } 76 ; Function Attrs: nounwind 77 define dso_local i32 @test_args_realign(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr { 78 entry: 79 %v = alloca [4 x i32], align 16 80 %0 = bitcast [4 x i32]* %v to i8* 81 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0 82 %call = call i32 @g(i32* nonnull %arraydecay, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) 83 ret i32 %call 84 } 85 ; CHECK-LABEL: test_args_realign 86 ; Setup frame pointer 87 ; CHECK: add r7, sp, #8 88 ; Align stack 89 ; CHECK: mov r4, sp 90 ; CHECK-NEXT: lsrs r4, r4, #4 91 ; CHECK-NEXT: lsls r4, r4, #4 92 ; CHECK-NEXT: mov sp, r4 93 ; Load `e` via FP 94 ; CHECK: ldr r0, [r7, #8] 95 ; CHECK-NEXT: mov r5, sp 96 ; CHECK-NEXT: str r3, [r5] 97 ; Pass `e` as argument 98 ; CHECK-NEXT: str r0, [r5, #4] 99 ; CHECK: bl g 100 101 ; int test_varargs_realign(int a, ...) { 102 ; __attribute__((aligned(16))) int v[4]; 103 ; __builtin_va_list ap; 104 ; __builtin_va_start(ap, a); 105 ; return g(v, a, 0, 0, 0, 0); 106 ; } 107 define dso_local i32 @test_varargs_realign(i32 %a, ...) local_unnamed_addr { 108 entry: 109 %v = alloca [4 x i32], align 16 110 %ap = alloca %struct.__va_list, align 4 111 %0 = bitcast [4 x i32]* %v to i8* 112 %1 = bitcast %struct.__va_list* %ap to i8* 113 call void @llvm.va_start(i8* nonnull %1) 114 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0 115 %call = call i32 @g(i32* nonnull %arraydecay, i32 %a, i32 0, i32 0, i32 0, i32 0) 116 ret i32 %call 117 } 118 ; CHECK-LABEL: test_varargs_realign 119 ; Three incoming register varargs 120 ; CHECK: sub sp, #12 121 ; Setup frame pointer 122 ; CHECK: add r7, sp, #8 123 ; Align stack 124 ; CHECK: mov r4, sp 125 ; CHECK-NEXT: lsrs r4, r4, #4 126 ; CHECK-NEXT: lsls r4, r4, #4 127 ; CHECK-NEXT: mov sp, r4 128 ; Incoming register varargs stored via FP 129 ; CHECK: str r3, [r7, #16] 130 ; CHECK-NEXT: str r2, [r7, #12] 131 ; CHECK-NEXT: str r1, [r7, #8] 132 133 ; VLAs present, access via FP 134 ; int test_args_vla(int a, int b, int c, int d, int e) { 135 ; int v[a]; 136 ; return g(v, a, b, c, d, e); 137 ; } 138 define dso_local i32 @test_args_vla(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr { 139 entry: 140 %vla = alloca i32, i32 %a, align 4 141 %call = call i32 @g(i32* nonnull %vla, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) 142 ret i32 %call 143 } 144 ; CHECK-LABEL: test_args_vla 145 ; Setup frame pointer 146 ; CHECK: add r7, sp, #12 147 ; Allocate outgoing stack arguments space 148 ; CHECK: sub sp, #4 149 ; Load `e` via FP 150 ; CHECK: ldr r5, [r7, #8] 151 ; CHECK-NEXT: mov r0, sp 152 ; Pass `d` and `e` as arguments 153 ; CHECK-NEXT: stm r0!, {r3, r5} 154 ; CHECK: bl g 155 156 ; int test_varargs_vla(int a, ...) { 157 ; int v[a]; 158 ; __builtin_va_list ap; 159 ; __builtin_va_start(ap, a); 160 ; return g(v, a, 0, 0, 0, 0); 161 ; } 162 define dso_local i32 @test_varargs_vla(i32 %a, ...) local_unnamed_addr { 163 entry: 164 %ap = alloca %struct.__va_list, align 4 165 %vla = alloca i32, i32 %a, align 4 166 %0 = bitcast %struct.__va_list* %ap to i8* 167 call void @llvm.va_start(i8* nonnull %0) 168 %call = call i32 @g(i32* nonnull %vla, i32 %a, i32 0, i32 0, i32 0, i32 0) 169 ret i32 %call 170 } 171 ; CHECK-LABEL: test_varargs_vla 172 ; Three incoming register varargs 173 ; CHECK: sub sp, #12 174 ; Setup frame pointer 175 ; CHECK: add r7, sp, #8 176 ; Register varargs stored via FP 177 ; CHECK: str r3, [r7, #16] 178 ; CHECK-NEXT: str r2, [r7, #12] 179 ; CHECK-NEXT: str r1, [r7, #8] 180 181 ; Moving SP, access via SP 182 ; int test_args_moving_sp(int a, int b, int c, int d, int e) { 183 ; int v[4]; 184 ; return f(v, a, b + c + d, e, s) + h(v, v+1, v+2); 185 ; } 186 define dso_local i32 @test_args_moving_sp(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr { 187 entry: 188 %v = alloca [4 x i32], align 4 189 %0 = bitcast [4 x i32]* %v to i8* 190 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0 191 %add = add nsw i32 %c, %b 192 %add1 = add nsw i32 %add, %d 193 %call = call i32 @f(i32* nonnull %arraydecay, i32 %a, i32 %add1, i32 %e, %struct.S* byval nonnull align 4 @s) 194 %add.ptr = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 1 195 %add.ptr5 = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 2 196 %call6 = call i32 @h(i32* nonnull %arraydecay, i32* nonnull %add.ptr, i32* nonnull %add.ptr5) 197 %add7 = add nsw i32 %call6, %call 198 ret i32 %add7 199 } 200 ; CHECK-LABEL: test_args_moving_sp 201 ; 20 bytes callee-saved area 202 ; CHECK: push {r4, r5, r6, r7, lr} 203 ; 20 bytes locals 204 ; CHECK: sub sp, #20 205 ; Allocate outgoing arguments space 206 ; CHECK: sub sp, #508 207 ; CHECK: sub sp, #4 208 ; Load `e` via SP, 552 = 512 + 20 + 20 209 ; CHECK: ldr r3, [sp, #552] 210 ; CHECK: bl f 211 ; Stack restored before next call 212 ; CHECK-NEXT: add sp, #508 213 ; CHECK-NEXT: add sp, #4 214 ; CHECK: bl h 215 216 ; int test_varargs_moving_sp(int a, ...) { 217 ; int v[4]; 218 ; __builtin_va_list ap; 219 ; __builtin_va_start(ap, a); 220 ; return f(v, a, 0, 0, s) + h(v, v+1, v+2); 221 ; } 222 define dso_local i32 @test_varargs_moving_sp(i32 %a, ...) local_unnamed_addr { 223 entry: 224 %v = alloca [4 x i32], align 4 225 %ap = alloca %struct.__va_list, align 4 226 %0 = bitcast [4 x i32]* %v to i8* 227 %1 = bitcast %struct.__va_list* %ap to i8* 228 call void @llvm.va_start(i8* nonnull %1) 229 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0 230 %call = call i32 @f(i32* nonnull %arraydecay, i32 %a, i32 0, i32 0, %struct.S* byval nonnull align 4 @s) 231 %add.ptr = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 1 232 %add.ptr5 = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 2 233 %call6 = call i32 @h(i32* nonnull %arraydecay, i32* nonnull %add.ptr, i32* nonnull %add.ptr5) 234 %add = add nsw i32 %call6, %call 235 ret i32 %add 236 } 237 ; CHECK-LABEL: test_varargs_moving_sp 238 ; Three incoming register varargs 239 ; CHECK: sub sp, #12 240 ; 16 bytes callee-saves 241 ; CHECK: push {r4, r5, r7, lr} 242 ; 20 bytes locals 243 ; CHECK: sub sp, #20 244 ; Incoming varargs stored via SP, 36 = 20 + 16 245 ; CHECK: add r0, sp, #36 246 ; CHECK-NEXT: stm r0!, {r1, r2, r3} 247 248 ; 249 ; Access to locals 250 ; 251 252 ; Usual case, access via SP. 253 ; int test_local(int n) { 254 ; int v[4]; 255 ; int x, y, z; 256 ; h(&x, &y, &z); 257 ; return g(v, x, y, z, 0, 0); 258 ; } 259 define dso_local i32 @test_local(i32 %n) local_unnamed_addr { 260 entry: 261 %v = alloca [4 x i32], align 4 262 %x = alloca i32, align 4 263 %y = alloca i32, align 4 264 %z = alloca i32, align 4 265 %0 = bitcast [4 x i32]* %v to i8* 266 %1 = bitcast i32* %x to i8* 267 %2 = bitcast i32* %y to i8* 268 %3 = bitcast i32* %z to i8* 269 %call = call i32 @h(i32* nonnull %x, i32* nonnull %y, i32* nonnull %z) 270 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0 271 %4 = load i32, i32* %x, align 4 272 %5 = load i32, i32* %y, align 4 273 %6 = load i32, i32* %z, align 4 274 %call1 = call i32 @g(i32* nonnull %arraydecay, i32 %4, i32 %5, i32 %6, i32 0, i32 0) 275 ret i32 %call1 276 } 277 ; CHECK-LABEL: test_local 278 ; Arguments to `h` relative to SP 279 ; CHECK: add r0, sp, #20 280 ; CHECK-NEXT: add r1, sp, #16 281 ; CHECK-NEXT: add r2, sp, #12 282 ; CHECK-NEXT: bl h 283 ; Load `x`, `y`, and `z` via SP 284 ; CHECK: ldr r1, [sp, #20] 285 ; CHECK-NEXT: ldr r2, [sp, #16] 286 ; CHECK-NEXT: ldr r3, [sp, #12] 287 ; CHECK: bl g 288 289 ; Re-aligned stack, access via SP. 290 ; int test_local_realign(int n) { 291 ; __attribute__((aligned(16))) int v[4]; 292 ; int x, y, z; 293 ; h(&x, &y, &z); 294 ; return g(v, x, y, z, 0, 0); 295 ; } 296 define dso_local i32 @test_local_realign(i32 %n) local_unnamed_addr { 297 entry: 298 %v = alloca [4 x i32], align 16 299 %x = alloca i32, align 4 300 %y = alloca i32, align 4 301 %z = alloca i32, align 4 302 %0 = bitcast [4 x i32]* %v to i8* 303 %1 = bitcast i32* %x to i8* 304 %2 = bitcast i32* %y to i8* 305 %3 = bitcast i32* %z to i8* 306 %call = call i32 @h(i32* nonnull %x, i32* nonnull %y, i32* nonnull %z) 307 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0 308 %4 = load i32, i32* %x, align 4 309 %5 = load i32, i32* %y, align 4 310 %6 = load i32, i32* %z, align 4 311 %call1 = call i32 @g(i32* nonnull %arraydecay, i32 %4, i32 %5, i32 %6, i32 0, i32 0) 312 ret i32 %call1 313 } 314 ; CHECK-LABEL: test_local_realign 315 ; Setup frame pointer 316 ; CHECK: add r7, sp, #8 317 ; Re-align stack 318 ; CHECK: mov r4, sp 319 ; CHECK-NEXT: lsrs r4, r4, #4 320 ; CHECK-NEXT: lsls r4, r4, #4 321 ; CHECK-NEXT: mov sp, r4 322 ; Arguments to `h` computed relative to SP 323 ; CHECK: add r0, sp, #28 324 ; CHECK-NEXT: add r1, sp, #24 325 ; CHECK-NEXT: add r2, sp, #20 326 ; CHECK-NEXT: bl h 327 ; Load `x`, `y`, and `z` via SP for passing to `g` 328 ; CHECK: ldr r1, [sp, #28] 329 ; CHECK-NEXT: ldr r2, [sp, #24] 330 ; CHECK-NEXT: ldr r3, [sp, #20] 331 ; CHECK: bl g 332 333 ; VLAs, access via BP. 334 ; int test_local_vla(int n) { 335 ; int v[n]; 336 ; int x, y, z; 337 ; h(&x, &y, &z); 338 ; return g(v, x, y, z, 0, 0); 339 ; } 340 define dso_local i32 @test_local_vla(i32 %n) local_unnamed_addr { 341 entry: 342 %x = alloca i32, align 4 343 %y = alloca i32, align 4 344 %z = alloca i32, align 4 345 %vla = alloca i32, i32 %n, align 4 346 %0 = bitcast i32* %x to i8* 347 %1 = bitcast i32* %y to i8* 348 %2 = bitcast i32* %z to i8* 349 %call = call i32 @h(i32* nonnull %x, i32* nonnull %y, i32* nonnull %z) 350 %3 = load i32, i32* %x, align 4 351 %4 = load i32, i32* %y, align 4 352 %5 = load i32, i32* %z, align 4 353 %call1 = call i32 @g(i32* nonnull %vla, i32 %3, i32 %4, i32 %5, i32 0, i32 0) 354 ret i32 %call1 355 } 356 ; CHECK-LABEL: test_local_vla 357 ; Setup frame pointer 358 ; CHECK: add r7, sp, #12 359 ; Setup base pointer 360 ; CHECK: mov r6, sp 361 ; CHECK: mov r5, r6 362 ; Arguments to `h` compute relative to BP 363 ; CHECK: adds r0, r6, #7 364 ; CHECK-NEXT: adds r0, #1 365 ; CHECK-NEXT: adds r1, r6, #4 366 ; CHECK-NEXT: mov r2, r6 367 ; CHECK-NEXT: bl h 368 ; Load `x`, `y`, `z` via BP (r5 should still have the value of r6 from the move 369 ; above) 370 ; CHECK: ldr r3, [r5] 371 ; CHECK-NEXT: ldr r2, [r5, #4] 372 ; CHECK-NEXT: ldr r1, [r5, #8] 373 ; CHECK: bl g 374 375 ; Moving SP, access via SP. 376 ; int test_local_moving_sp(int n) { 377 ; int v[4]; 378 ; int x, y, z; 379 ; return u(v, &x, &y, s, s) + u(v, &y, &z, s, s); 380 ; } 381 define dso_local i32 @test_local_moving_sp(i32 %n) local_unnamed_addr { 382 entry: 383 %v = alloca [4 x i32], align 4 384 %x = alloca i32, align 4 385 %y = alloca i32, align 4 386 %z = alloca i32, align 4 387 %0 = bitcast [4 x i32]* %v to i8* 388 %1 = bitcast i32* %x to i8* 389 %2 = bitcast i32* %y to i8* 390 %3 = bitcast i32* %z to i8* 391 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0 392 %call = call i32 @u(i32* nonnull %arraydecay, i32* nonnull %x, i32* nonnull %y, %struct.S* byval nonnull align 4 @s, %struct.S* byval nonnull align 4 @s) 393 %call2 = call i32 @u(i32* nonnull %arraydecay, i32* nonnull %y, i32* nonnull %z, %struct.S* byval nonnull align 4 @s, %struct.S* byval nonnull align 4 @s) 394 %add = add nsw i32 %call2, %call 395 ret i32 %add 396 } 397 ; CHECK-LABEL: test_local_moving_sp 398 ; Locals area 399 ; CHECK: sub sp, #36 400 ; Outoging arguments 401 ; CHECK: sub sp, #508 402 ; CHECK-NEXT: sub sp, #508 403 ; CHECK-NEXT: sub sp, #8 404 ; Argument addresses computed relative to SP 405 ; CHECK: add r4, sp, #1020 406 ; CHECK-NEXT: adds r4, #24 407 ; CHECK: add r1, sp, #1020 408 ; CHECK-NEXT: adds r1, #20 409 ; CHECK: add r5, sp, #1020 410 ; CHECK-NEXT: adds r5, #16 411 ; CHECK: bl u 412 ; Stack restored before next call 413 ; CHECK: add sp, #508 414 ; CHECK-NEXT: add sp, #508 415 ; CHECK-NEXT: add sp, #8 416 ; CHECK: bl u 417