1; RUN: llc -mtriple=thumbv6m-eabi -frame-pointer=none %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 14declare void @llvm.va_start(i8*) 15declare dso_local i32 @g(i32*, i32, i32, i32, i32, i32) local_unnamed_addr 16declare dso_local i32 @f(i32*, i32, i32, i32, %struct.S* byval(%struct.S) align 4) local_unnamed_addr 17declare dso_local i32 @h(i32*, i32*, i32*) local_unnamed_addr 18declare dso_local i32 @u(i32*, i32*, i32*, %struct.S* byval(%struct.S) align 4, %struct.S* byval(%struct.S) 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; } 29define dso_local i32 @test_args_sp(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr { 30entry: 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, #32] 40; CHECK-NEXT: str r3, [sp] 41; Pass `e` on stack 42; CHECK-NEXT: str r0, [sp, #4] 43; CHECK: bl g 44 45; int test_varargs_sp(int a, ...) { 46; int v[4]; 47; __builtin_va_list ap; 48; __builtin_va_start(ap, a); 49; return g(v, a, 0, 0, 0, 0); 50; } 51define dso_local i32 @test_varargs_sp(i32 %a, ...) local_unnamed_addr { 52entry: 53 %v = alloca [4 x i32], align 4 54 %ap = alloca %struct.__va_list, align 4 55 %0 = bitcast [4 x i32]* %v to i8* 56 %1 = bitcast %struct.__va_list* %ap to i8* 57 call void @llvm.va_start(i8* nonnull %1) 58 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0 59 %call = call i32 @g(i32* nonnull %arraydecay, i32 %a, i32 0, i32 0, i32 0, i32 0) 60 ret i32 %call 61} 62; CHECK-LABEL: test_varargs_sp 63; Three incoming varargs in registers 64; CHECK: sub sp, #12 65; CHECK: sub sp, #28 66; Incoming arguments area is accessed via SP 67; CHECK: add r0, sp, #36 68; CHECK: stm r0!, {r1, r2, r3} 69 70; Re-aligned stack, access via FP 71; int test_args_realign(int a, int b, int c, int d, int e) { 72; __attribute__((aligned(16))) int v[4]; 73; return g(v, a, b, c, d, e); 74; } 75; Function Attrs: nounwind 76define dso_local i32 @test_args_realign(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr { 77entry: 78 %v = alloca [4 x i32], align 16 79 %0 = bitcast [4 x i32]* %v to i8* 80 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0 81 %call = call i32 @g(i32* nonnull %arraydecay, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) 82 ret i32 %call 83} 84; CHECK-LABEL: test_args_realign 85; Setup frame pointer 86; CHECK: add r7, sp, #8 87; Align stack 88; CHECK: mov r4, sp 89; CHECK-NEXT: lsrs r4, r4, #4 90; CHECK-NEXT: lsls r4, r4, #4 91; CHECK-NEXT: mov sp, r4 92; Load `e` via FP 93; CHECK: ldr r0, [r7, #8] 94; CHECK-NEXT: str r3, [sp] 95; Pass `e` as argument 96; CHECK-NEXT: str r0, [sp, #4] 97; CHECK: bl g 98 99; int test_varargs_realign(int a, ...) { 100; __attribute__((aligned(16))) int v[4]; 101; __builtin_va_list ap; 102; __builtin_va_start(ap, a); 103; return g(v, a, 0, 0, 0, 0); 104; } 105define dso_local i32 @test_varargs_realign(i32 %a, ...) local_unnamed_addr { 106entry: 107 %v = alloca [4 x i32], align 16 108 %ap = alloca %struct.__va_list, align 4 109 %0 = bitcast [4 x i32]* %v to i8* 110 %1 = bitcast %struct.__va_list* %ap to i8* 111 call void @llvm.va_start(i8* nonnull %1) 112 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0 113 %call = call i32 @g(i32* nonnull %arraydecay, i32 %a, i32 0, i32 0, i32 0, i32 0) 114 ret i32 %call 115} 116; CHECK-LABEL: test_varargs_realign 117; Three incoming register varargs 118; CHECK: sub sp, #12 119; Setup frame pointer 120; CHECK: add r7, sp, #8 121; Align stack 122; CHECK: mov r4, sp 123; CHECK-NEXT: lsrs r4, r4, #4 124; CHECK-NEXT: lsls r4, r4, #4 125; CHECK-NEXT: mov sp, r4 126; Incoming register varargs stored via FP 127; CHECK: mov r0, r7 128; CHECK-NEXT: adds r0, #8 129; CHECK-NEXT: stm r0!, {r1, r2, r3} 130; VLAs present, access via FP 131; int test_args_vla(int a, int b, int c, int d, int e) { 132; int v[a]; 133; return g(v, a, b, c, d, e); 134; } 135define dso_local i32 @test_args_vla(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr { 136entry: 137 %vla = alloca i32, i32 %a, align 4 138 %call = call i32 @g(i32* nonnull %vla, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) 139 ret i32 %call 140} 141; CHECK-LABEL: test_args_vla 142; Setup frame pointer 143; CHECK: add r7, sp, #12 144; Allocate outgoing stack arguments space 145; CHECK: sub sp, #4 146; Load `e` via FP 147; CHECK: ldr r5, [r7, #8] 148; Pass `d` and `e` as arguments 149; CHECK-NEXT: str r3, [sp] 150; CHECK-NEXT: str r5, [sp, #4] 151; CHECK: bl g 152 153; int test_varargs_vla(int a, ...) { 154; int v[a]; 155; __builtin_va_list ap; 156; __builtin_va_start(ap, a); 157; return g(v, a, 0, 0, 0, 0); 158; } 159define dso_local i32 @test_varargs_vla(i32 %a, ...) local_unnamed_addr { 160entry: 161 %ap = alloca %struct.__va_list, align 4 162 %vla = alloca i32, i32 %a, align 4 163 %0 = bitcast %struct.__va_list* %ap to i8* 164 call void @llvm.va_start(i8* nonnull %0) 165 %call = call i32 @g(i32* nonnull %vla, i32 %a, i32 0, i32 0, i32 0, i32 0) 166 ret i32 %call 167} 168; CHECK-LABEL: test_varargs_vla 169; Three incoming register varargs 170; CHECK: sub sp, #12 171; Setup frame pointer 172; CHECK: add r7, sp, #8 173; Register varargs stored via FP 174; CHECK-DAG: str r3, [r7, #16] 175; CHECK-DAG: str r2, [r7, #12] 176; CHECK-DAG: str r1, [r7, #8] 177 178; Moving SP, access via SP 179; int test_args_moving_sp(int a, int b, int c, int d, int e) { 180; int v[4]; 181; return f(v, a, b + c + d, e, s) + h(v, v+1, v+2); 182; } 183define dso_local i32 @test_args_moving_sp(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr { 184entry: 185 %v = alloca [4 x i32], align 4 186 %0 = bitcast [4 x i32]* %v to i8* 187 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0 188 %add = add nsw i32 %c, %b 189 %add1 = add nsw i32 %add, %d 190 %call = call i32 @f(i32* nonnull %arraydecay, i32 %a, i32 %add1, i32 %e, %struct.S* byval(%struct.S) nonnull align 4 @s) 191 %add.ptr = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 1 192 %add.ptr5 = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 2 193 %call6 = call i32 @h(i32* nonnull %arraydecay, i32* nonnull %add.ptr, i32* nonnull %add.ptr5) 194 %add7 = add nsw i32 %call6, %call 195 ret i32 %add7 196} 197; CHECK-LABEL: test_args_moving_sp 198; 20 bytes callee-saved area 199; CHECK: push {r4, r5, r6, r7, lr} 200; 20 bytes locals 201; CHECK: sub sp, #20 202; Setup base pointer 203; CHECK: mov r6, sp 204; Allocate outgoing arguments space 205; CHECK: sub sp, #508 206; CHECK: sub sp, #4 207; Load `e` via BP, 40 = 20 + 20 208; CHECK: ldr r3, [r6, #40] 209; CHECK: bl f 210; Stack restored before next call 211; CHECK-NEXT: add sp, #508 212; CHECK-NEXT: add sp, #4 213; CHECK: bl h 214 215; int test_varargs_moving_sp(int a, ...) { 216; int v[4]; 217; __builtin_va_list ap; 218; __builtin_va_start(ap, a); 219; return f(v, a, 0, 0, s) + h(v, v+1, v+2); 220; } 221define dso_local i32 @test_varargs_moving_sp(i32 %a, ...) local_unnamed_addr { 222entry: 223 %v = alloca [4 x i32], align 4 224 %ap = alloca %struct.__va_list, align 4 225 %0 = bitcast [4 x i32]* %v to i8* 226 %1 = bitcast %struct.__va_list* %ap to i8* 227 call void @llvm.va_start(i8* nonnull %1) 228 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0 229 %call = call i32 @f(i32* nonnull %arraydecay, i32 %a, i32 0, i32 0, %struct.S* byval(%struct.S) nonnull align 4 @s) 230 %add.ptr = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 1 231 %add.ptr5 = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 2 232 %call6 = call i32 @h(i32* nonnull %arraydecay, i32* nonnull %add.ptr, i32* nonnull %add.ptr5) 233 %add = add nsw i32 %call6, %call 234 ret i32 %add 235} 236; CHECK-LABEL: test_varargs_moving_sp 237; Three incoming register varargs 238; CHECK: sub sp, #12 239; 16 bytes callee-saves 240; CHECK: push {r4, r5, r6, lr} 241; 20 bytes locals 242; CHECK: sub sp, #20 243; Incoming varargs stored via BP, 36 = 20 + 16 244; CHECK: mov r0, r6 245; CHECK-NEXT: adds r0, #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; } 259define dso_local i32 @test_local(i32 %n) local_unnamed_addr { 260entry: 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; } 296define dso_local i32 @test_local_realign(i32 %n) local_unnamed_addr { 297entry: 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; } 340define dso_local i32 @test_local_vla(i32 %n) local_unnamed_addr { 341entry: 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; } 381define dso_local i32 @test_local_moving_sp(i32 %n) local_unnamed_addr { 382entry: 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(%struct.S) nonnull align 4 @s, %struct.S* byval(%struct.S) nonnull align 4 @s) 393 %call2 = call i32 @u(i32* nonnull %arraydecay, i32* nonnull %y, i32* nonnull %z, %struct.S* byval(%struct.S) nonnull align 4 @s, %struct.S* byval(%struct.S) 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; Setup BP 401; CHECK: mov r6, sp 402; Outoging arguments 403; CHECK: sub sp, #508 404; CHECK-NEXT: sub sp, #508 405; CHECK-NEXT: sub sp, #8 406; Argument addresses computed relative to BP 407; CHECK: adds r4, r6, #7 408; CHECK-NEXT: adds r4, #13 409; CHECK: adds r1, r6, #7 410; CHECK-NEXT: adds r1, #9 411; CHECK: adds r5, r6, #7 412; CHECK-NEXT: adds r5, #5 413; CHECK: bl u 414; Stack restored before next call 415; CHECK: add sp, #508 416; CHECK-NEXT: add sp, #508 417; CHECK-NEXT: add sp, #8 418; CHECK: bl u 419