1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py 2; RUN: llc -mtriple=thumbv6m-none-eabi < %s | FileCheck %s 3 4declare ptr @llvm.returnaddress(i32) 5 6; We don't allocate high registers, so any function not using inline asm will 7; only need to save the low registers. 8define void @low_regs_only() { 9; CHECK-LABEL: low_regs_only: 10; CHECK: @ %bb.0: @ %entry 11; CHECK-NEXT: .save {r4, r5, r6, r7, lr} 12; CHECK-NEXT: push {r4, r5, r6, r7, lr} 13; CHECK-NEXT: @APP 14; CHECK-NEXT: @NO_APP 15; CHECK-NEXT: pop {r4, r5, r6, r7, pc} 16entry: 17 tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7}"() 18 ret void 19} 20 21; One high reg clobbered, but no low regs, args or returns. We can use an 22; argument/return register to help save/restore it. 23define void @one_high() { 24; CHECK-LABEL: one_high: 25; CHECK: @ %bb.0: @ %entry 26; CHECK-NEXT: mov r3, r8 27; CHECK-NEXT: .save {r8} 28; CHECK-NEXT: push {r3} 29; CHECK-NEXT: @APP 30; CHECK-NEXT: @NO_APP 31; CHECK-NEXT: pop {r0} 32; CHECK-NEXT: mov r8, r0 33; CHECK-NEXT: bx lr 34entry: 35 tail call void asm sideeffect "", "~{r8}"() 36 ret void 37} 38 39; 4 high regs clobbered, but still no low regs, args or returns. We can use all 40; 4 arg/return regs for the save/restore. 41define void @four_high() { 42; CHECK-LABEL: four_high: 43; CHECK: @ %bb.0: @ %entry 44; CHECK-NEXT: mov r3, r11 45; CHECK-NEXT: mov r2, r10 46; CHECK-NEXT: mov r1, r9 47; CHECK-NEXT: mov r0, r8 48; CHECK-NEXT: .save {r8, r9, r10, r11} 49; CHECK-NEXT: push {r0, r1, r2, r3} 50; CHECK-NEXT: @APP 51; CHECK-NEXT: @NO_APP 52; CHECK-NEXT: pop {r0, r1, r2, r3} 53; CHECK-NEXT: mov r8, r0 54; CHECK-NEXT: mov r9, r1 55; CHECK-NEXT: mov r10, r2 56; CHECK-NEXT: mov r11, r3 57; CHECK-NEXT: bx lr 58entry: 59 tail call void asm sideeffect "", "~{r8},~{r9},~{r10},~{r11}"() 60 ret void 61} 62 63; One high and one low register clobbered. lr also gets pushed to simplify the 64; return, and r7 to keep the stack aligned. Here, we could use r0-r3, r4, r7 or 65; lr to save/restore r8. 66define void @one_high_one_low() { 67; CHECK-LABEL: one_high_one_low: 68; CHECK: @ %bb.0: @ %entry 69; CHECK-NEXT: .save {r4, r7, lr} 70; CHECK-NEXT: push {r4, r7, lr} 71; CHECK-NEXT: mov lr, r8 72; CHECK-NEXT: .save {r8} 73; CHECK-NEXT: push {lr} 74; CHECK-NEXT: @APP 75; CHECK-NEXT: @NO_APP 76; CHECK-NEXT: pop {r0} 77; CHECK-NEXT: mov r8, r0 78; CHECK-NEXT: pop {r4, r7, pc} 79entry: 80 tail call void asm sideeffect "", "~{r4},~{r8}"() 81 ret void 82} 83 84; All callee-saved registers clobbered, r4-r7 and lr are not live after the 85; first push so can be used for pushing the high registers. 86define void @four_high_four_low() { 87; CHECK-LABEL: four_high_four_low: 88; CHECK: @ %bb.0: @ %entry 89; CHECK-NEXT: .save {r4, r5, r6, r7, lr} 90; CHECK-NEXT: push {r4, r5, r6, r7, lr} 91; CHECK-NEXT: mov lr, r11 92; CHECK-NEXT: mov r7, r10 93; CHECK-NEXT: mov r6, r9 94; CHECK-NEXT: mov r5, r8 95; CHECK-NEXT: .save {r8, r9, r10, r11} 96; CHECK-NEXT: push {r5, r6, r7, lr} 97; CHECK-NEXT: @APP 98; CHECK-NEXT: @NO_APP 99; CHECK-NEXT: pop {r0, r1, r2, r3} 100; CHECK-NEXT: mov r8, r0 101; CHECK-NEXT: mov r9, r1 102; CHECK-NEXT: mov r10, r2 103; CHECK-NEXT: mov r11, r3 104; CHECK-NEXT: pop {r4, r5, r6, r7, pc} 105entry: 106 tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"() 107 ret void 108} 109 110 111; All callee-saved registers clobbered, and frame pointer is requested. r7 now 112; cannot be used while saving/restoring the high regs. 113define void @four_high_four_low_frame_ptr() "frame-pointer"="all" { 114; CHECK-LABEL: four_high_four_low_frame_ptr: 115; CHECK: @ %bb.0: @ %entry 116; CHECK-NEXT: .save {r4, r5, r6, r7, lr} 117; CHECK-NEXT: push {r4, r5, r6, r7, lr} 118; CHECK-NEXT: .setfp r7, sp, #12 119; CHECK-NEXT: add r7, sp, #12 120; CHECK-NEXT: mov lr, r11 121; CHECK-NEXT: mov r6, r10 122; CHECK-NEXT: mov r5, r9 123; CHECK-NEXT: mov r4, r8 124; CHECK-NEXT: .save {r8, r9, r10, r11} 125; CHECK-NEXT: push {r4, r5, r6, lr} 126; CHECK-NEXT: @APP 127; CHECK-NEXT: @NO_APP 128; CHECK-NEXT: pop {r0, r1, r2, r3} 129; CHECK-NEXT: mov r8, r0 130; CHECK-NEXT: mov r9, r1 131; CHECK-NEXT: mov r10, r2 132; CHECK-NEXT: mov r11, r3 133; CHECK-NEXT: pop {r4, r5, r6, r7, pc} 134entry: 135 tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"() 136 ret void 137} 138 139; All callee-saved registers clobbered, frame pointer is requested and 140; llvm.returnaddress used. r7 and lr now cannot be used while saving/restoring 141; the high regs. 142define void @four_high_four_low_frame_ptr_ret_addr() "frame-pointer"="all" { 143; CHECK-LABEL: four_high_four_low_frame_ptr_ret_addr: 144; CHECK: @ %bb.0: @ %entry 145; CHECK-NEXT: .save {r4, r5, r6, r7, lr} 146; CHECK-NEXT: push {r4, r5, r6, r7, lr} 147; CHECK-NEXT: .setfp r7, sp, #12 148; CHECK-NEXT: add r7, sp, #12 149; CHECK-NEXT: mov r6, r11 150; CHECK-NEXT: mov r5, r10 151; CHECK-NEXT: mov r4, r9 152; CHECK-NEXT: mov r3, r8 153; CHECK-NEXT: .save {r8, r9, r10, r11} 154; CHECK-NEXT: push {r3, r4, r5, r6} 155; CHECK-NEXT: mov r0, lr 156; CHECK-NEXT: @APP 157; CHECK-NEXT: @NO_APP 158; CHECK-NEXT: pop {r0, r1, r2, r3} 159; CHECK-NEXT: mov r8, r0 160; CHECK-NEXT: mov r9, r1 161; CHECK-NEXT: mov r10, r2 162; CHECK-NEXT: mov r11, r3 163; CHECK-NEXT: pop {r4, r5, r6, r7, pc} 164entry: 165 %a = tail call ptr @llvm.returnaddress(i32 0) 166 tail call void asm sideeffect "", "r,~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"(ptr %a) 167 ret void 168} 169 170; 4 high regs clobbered, all 4 argument registers used. We push an extra 4 low 171; registers, so that we can use them for saving the high regs. 172define void @four_high_four_arg(i32 %a, i32 %b, i32 %c, i32 %d) { 173; CHECK-LABEL: four_high_four_arg: 174; CHECK: @ %bb.0: @ %entry 175; CHECK-NEXT: .save {r5, r6, r7, lr} 176; CHECK-NEXT: push {r5, r6, r7, lr} 177; CHECK-NEXT: mov lr, r11 178; CHECK-NEXT: mov r7, r10 179; CHECK-NEXT: mov r6, r9 180; CHECK-NEXT: mov r5, r8 181; CHECK-NEXT: .save {r8, r9, r10, r11} 182; CHECK-NEXT: push {r5, r6, r7, lr} 183; CHECK-NEXT: @APP 184; CHECK-NEXT: @NO_APP 185; CHECK-NEXT: pop {r0, r1, r2, r3} 186; CHECK-NEXT: mov r8, r0 187; CHECK-NEXT: mov r9, r1 188; CHECK-NEXT: mov r10, r2 189; CHECK-NEXT: mov r11, r3 190; CHECK-NEXT: pop {r5, r6, r7, pc} 191entry: 192 tail call void asm sideeffect "", "r,r,r,r,~{r8},~{r9},~{r10},~{r11}"(i32 %a, i32 %b, i32 %c, i32 %d) 193 ret void 194} 195 196; 4 high regs clobbered, all 4 return registers used. We push an extra 4 low 197; registers, so that we can use them for restoring the high regs. 198define <4 x i32> @four_high_four_return() { 199; CHECK-LABEL: four_high_four_return: 200; CHECK: @ %bb.0: @ %entry 201; CHECK-NEXT: .save {r4, r5, r6, r7, lr} 202; CHECK-NEXT: push {r4, r5, r6, r7, lr} 203; CHECK-NEXT: mov lr, r11 204; CHECK-NEXT: mov r7, r10 205; CHECK-NEXT: mov r6, r9 206; CHECK-NEXT: mov r5, r8 207; CHECK-NEXT: .save {r8, r9, r10, r11} 208; CHECK-NEXT: push {r5, r6, r7, lr} 209; CHECK-NEXT: @APP 210; CHECK-NEXT: @NO_APP 211; CHECK-NEXT: movs r0, #1 212; CHECK-NEXT: movs r1, #2 213; CHECK-NEXT: movs r2, #3 214; CHECK-NEXT: movs r3, #4 215; CHECK-NEXT: pop {r4, r5, r6, r7} 216; CHECK-NEXT: mov r8, r4 217; CHECK-NEXT: mov r9, r5 218; CHECK-NEXT: mov r10, r6 219; CHECK-NEXT: mov r11, r7 220; CHECK-NEXT: pop {r4, r5, r6, r7, pc} 221entry: 222 tail call void asm sideeffect "", "~{r8},~{r9},~{r10},~{r11}"() 223 %vecinit = insertelement <4 x i32> undef, i32 1, i32 0 224 %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1 225 %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2 226 %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3 227 ret <4 x i32> %vecinit13 228} 229 230; 4 high regs clobbered, all args & returns used, frame pointer requested and 231; llvm.returnaddress called. This leaves us with 3 low registers available (r4, 232; r5, r6), with which to save 4 high registers, so we have to use two pushes 233; and pops. 234define <4 x i32> @all_of_the_above(i32 %a, i32 %b, i32 %c, i32 %d) "frame-pointer"="all" { 235; CHECK-LABEL: all_of_the_above: 236; CHECK: @ %bb.0: @ %entry 237; CHECK-NEXT: .save {r4, r5, r6, r7, lr} 238; CHECK-NEXT: push {r4, r5, r6, r7, lr} 239; CHECK-NEXT: .setfp r7, sp, #12 240; CHECK-NEXT: add r7, sp, #12 241; CHECK-NEXT: mov r6, r11 242; CHECK-NEXT: mov r5, r10 243; CHECK-NEXT: mov r4, r9 244; CHECK-NEXT: .save {r9, r10, r11} 245; CHECK-NEXT: push {r4, r5, r6} 246; CHECK-NEXT: mov r6, r8 247; CHECK-NEXT: .save {r8} 248; CHECK-NEXT: push {r6} 249; CHECK-NEXT: .pad #4 250; CHECK-NEXT: sub sp, #4 251; CHECK-NEXT: mov r4, lr 252; CHECK-NEXT: str r4, [sp] @ 4-byte Spill 253; CHECK-NEXT: @APP 254; CHECK-NEXT: @NO_APP 255; CHECK-NEXT: movs r1, #2 256; CHECK-NEXT: movs r2, #3 257; CHECK-NEXT: movs r3, #4 258; CHECK-NEXT: ldr r0, [sp] @ 4-byte Reload 259; CHECK-NEXT: add sp, #4 260; CHECK-NEXT: pop {r4, r5, r6} 261; CHECK-NEXT: mov r8, r4 262; CHECK-NEXT: mov r9, r5 263; CHECK-NEXT: mov r10, r6 264; CHECK-NEXT: pop {r4} 265; CHECK-NEXT: mov r11, r4 266; CHECK-NEXT: pop {r4, r5, r6, r7, pc} 267entry: 268 tail call void asm sideeffect "", "r,r,r,r,~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"(i32 %a, i32 %b, i32 %c, i32 %d) 269 %e = tail call ptr @llvm.returnaddress(i32 0) 270 %f = ptrtoint ptr %e to i32 271 %vecinit = insertelement <4 x i32> undef, i32 %f, i32 0 272 %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1 273 %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2 274 %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3 275 ret <4 x i32> %vecinit13 276} 277 278; When a base pointer is being used, we can safely use it for saving/restoring 279; the high regs because it is set after the last push, and not used at all in the 280; epliogue. We can also use r4 for restoring the registers despite it also being 281; used when restoring sp from fp, as that happens before the first pop. 282define <4 x i32> @base_pointer(i32 %a) { 283; CHECK-LABEL: base_pointer: 284; CHECK: @ %bb.0: @ %entry 285; CHECK-NEXT: .save {r4, r6, r7, lr} 286; CHECK-NEXT: push {r4, r6, r7, lr} 287; CHECK-NEXT: .setfp r7, sp, #8 288; CHECK-NEXT: add r7, sp, #8 289; CHECK-NEXT: mov lr, r9 290; CHECK-NEXT: mov r6, r8 291; CHECK-NEXT: .save {r8, r9} 292; CHECK-NEXT: push {r6, lr} 293; CHECK-NEXT: mov r6, sp 294; CHECK-NEXT: lsls r0, r0, #2 295; CHECK-NEXT: adds r0, r0, #7 296; CHECK-NEXT: movs r1, #7 297; CHECK-NEXT: bics r0, r1 298; CHECK-NEXT: mov r1, sp 299; CHECK-NEXT: subs r0, r1, r0 300; CHECK-NEXT: mov sp, r0 301; CHECK-NEXT: @APP 302; CHECK-NEXT: @NO_APP 303; CHECK-NEXT: movs r0, #1 304; CHECK-NEXT: movs r1, #2 305; CHECK-NEXT: movs r2, #3 306; CHECK-NEXT: movs r3, #4 307; CHECK-NEXT: subs r6, r7, #7 308; CHECK-NEXT: subs r6, #9 309; CHECK-NEXT: mov sp, r6 310; CHECK-NEXT: pop {r4, r6} 311; CHECK-NEXT: mov r8, r4 312; CHECK-NEXT: mov r9, r6 313; CHECK-NEXT: pop {r4, r6, r7, pc} 314entry: 315 %b = alloca i32, i32 %a 316 call void asm sideeffect "", "r,~{r8},~{r9}"(ptr %b) 317 %vecinit = insertelement <4 x i32> undef, i32 1, i32 0 318 %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1 319 %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2 320 %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3 321 ret <4 x i32> %vecinit13 322} 323