1 ; RUN: llc -mtriple=thumbv6m-none-eabi < %s | FileCheck %s 2 3 declare i8* @llvm.returnaddress(i32) 4 5 ; We don't allocate high registers, so any function not using inline asm will 6 ; only need to save the low registers. 7 define void @low_regs_only() { 8 ; CHECK-LABEL: low_regs_only: 9 entry: 10 ; CHECK: push {r4, r5, r6, r7, lr} 11 tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7}"() 12 ; CHECK: pop {r4, r5, r6, r7, pc} 13 ret void 14 } 15 16 ; One high reg clobbered, but no low regs, args or returns. We can use an 17 ; argument/return register to help save/restore it. 18 define void @one_high() { 19 ; CHECK-LABEL: one_high: 20 entry: 21 ; CHECK: mov [[SAVEREG:r[0-3]]], r8 22 ; CHECK: push {[[SAVEREG]]} 23 tail call void asm sideeffect "", "~{r8}"() 24 ; CHECK: pop {[[RESTOREREG:r[0-3]]]} 25 ; CHECK: mov r8, [[RESTOREREG]] 26 ret void 27 } 28 29 ; 4 high regs clobbered, but still no low regs, args or returns. We can use all 30 ; 4 arg/return regs for the save/restore. 31 define void @four_high() { 32 ; CHECK-LABEL: four_high: 33 entry: 34 ; CHECK: mov r3, r11 35 ; CHECK: mov r2, r10 36 ; CHECK: mov r1, r9 37 ; CHECK: mov r0, r8 38 ; CHECK: push {r0, r1, r2, r3} 39 tail call void asm sideeffect "", "~{r8},~{r9},~{r10},~{r11}"() 40 ; CHECK: pop {r0, r1, r2, r3} 41 ; CHECK: mov r8, r0 42 ; CHECK: mov r9, r1 43 ; CHECK: mov r10, r2 44 ; CHECK: mov r11, r3 45 ret void 46 } 47 48 ; One high and one low register clobbered. lr also gets pushed to simplify the 49 ; return, and r7 to keep the stack aligned. Here, we could use r0-r3, r4, r7 or 50 ; lr to save/restore r8. 51 define void @one_high_one_low() { 52 ; CHECK-LABEL: one_high_one_low: 53 entry: 54 ; CHECK: push {r4, r7, lr} 55 ; CHECK: mov [[SAVEREG:r0|r1|r2|r3|r4|r7|lr]], r8 56 ; CHECK: push {[[SAVEREG]]} 57 tail call void asm sideeffect "", "~{r4},~{r8}"() 58 ; CHECK: pop {[[RESTOREREG:r0|r1|r2|r3|r4|r7]]} 59 ; CHECK: mov r8, [[RESTOREREG]] 60 ; CHECK: pop {r4, r7, pc} 61 ret void 62 } 63 64 ; All callee-saved registers clobbered, r4-r7 and lr are not live after the 65 ; first push so can be used for pushing the high registers. 66 define void @four_high_four_low() { 67 ; CHECK-LABEL: four_high_four_low: 68 entry: 69 ; CHECK: push {r4, r5, r6, r7, lr} 70 ; CHECK: mov lr, r11 71 ; CHECK: mov r7, r10 72 ; CHECK: mov r6, r9 73 ; CHECK: mov r5, r8 74 ; CHECK: push {r5, r6, r7, lr} 75 tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"() 76 ; CHECK: pop {r0, r1, r2, r3} 77 ; CHECK: mov r8, r0 78 ; CHECK: mov r9, r1 79 ; CHECK: mov r10, r2 80 ; CHECK: mov r11, r3 81 ; CHECK: pop {r4, r5, r6, r7, pc} 82 ret void 83 } 84 85 86 ; All callee-saved registers clobbered, and frame pointer is requested. r7 now 87 ; cannot be used while saving/restoring the high regs. 88 define void @four_high_four_low_frame_ptr() "no-frame-pointer-elim"="true" { 89 ; CHECK-LABEL: four_high_four_low_frame_ptr: 90 entry: 91 ; CHECK: push {r4, r5, r6, r7, lr} 92 ; CHECK: add r7, sp, #12 93 ; CHECK: mov lr, r11 94 ; CHECK: mov r6, r10 95 ; CHECK: mov r5, r9 96 ; CHECK: mov r4, r8 97 ; CHECK: push {r4, r5, r6, lr} 98 tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"() 99 ; CHECK: pop {r0, r1, r2, r3} 100 ; CHECK: mov r8, r0 101 ; CHECK: mov r9, r1 102 ; CHECK: mov r10, r2 103 ; CHECK: mov r11, r3 104 ; CHECK: pop {r4, r5, r6, r7, pc} 105 ret void 106 } 107 108 ; All callee-saved registers clobbered, frame pointer is requested and 109 ; llvm.returnaddress used. r7 and lr now cannot be used while saving/restoring 110 ; the high regs. 111 define void @four_high_four_low_frame_ptr_ret_addr() "no-frame-pointer-elim"="true" { 112 ; CHECK-LABEL: four_high_four_low_frame_ptr_ret_addr: 113 entry: 114 ; CHECK: push {r4, r5, r6, r7, lr} 115 ; CHECK: mov r6, r11 116 ; CHECK: mov r5, r10 117 ; CHECK: mov r4, r9 118 ; CHECK: mov r3, r8 119 ; CHECK: push {r3, r4, r5, r6} 120 %a = tail call i8* @llvm.returnaddress(i32 0) 121 tail call void asm sideeffect "", "r,~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"(i8* %a) 122 ; CHECK: pop {r0, r1, r2, r3} 123 ; CHECK: mov r8, r0 124 ; CHECK: mov r9, r1 125 ; CHECK: mov r10, r2 126 ; CHECK: mov r11, r3 127 ; CHECK: pop {r4, r5, r6, r7, pc} 128 ret void 129 } 130 131 ; 4 high regs clobbered, all 4 argument registers used. We push an extra 4 low 132 ; registers, so that we can use them for saving the high regs. 133 define void @four_high_four_arg(i32 %a, i32 %b, i32 %c, i32 %d) { 134 ; CHECK-LABEL: four_high_four_arg: 135 entry: 136 ; CHECK: push {r5, r6, r7, lr} 137 ; CHECK: mov lr, r11 138 ; CHECK: mov r7, r10 139 ; CHECK: mov r6, r9 140 ; CHECK: mov r5, r8 141 ; CHECK: push {r5, r6, r7, lr} 142 tail call void asm sideeffect "", "r,r,r,r,~{r8},~{r9},~{r10},~{r11}"(i32 %a, i32 %b, i32 %c, i32 %d) 143 ; CHECK: pop {r0, r1, r2, r3} 144 ; CHECK: mov r8, r0 145 ; CHECK: mov r9, r1 146 ; CHECK: mov r10, r2 147 ; CHECK: mov r11, r3 148 ; CHECK: pop {r5, r6, r7, pc} 149 ret void 150 } 151 152 ; 4 high regs clobbered, all 4 return registers used. We push an extra 4 low 153 ; registers, so that we can use them for restoring the high regs. 154 define <4 x i32> @four_high_four_return() { 155 ; CHECK-LABEL: four_high_four_return: 156 entry: 157 ; CHECK: push {r4, r5, r6, r7, lr} 158 ; CHECK: mov lr, r11 159 ; CHECK: mov r7, r10 160 ; CHECK: mov r6, r9 161 ; CHECK: mov r5, r8 162 ; CHECK: push {r5, r6, r7, lr} 163 tail call void asm sideeffect "", "~{r8},~{r9},~{r10},~{r11}"() 164 %vecinit = insertelement <4 x i32> undef, i32 1, i32 0 165 %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1 166 %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2 167 %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3 168 ; CHECK: pop {r4, r5, r6, r7} 169 ; CHECK: mov r8, r4 170 ; CHECK: mov r9, r5 171 ; CHECK: mov r10, r6 172 ; CHECK: mov r11, r7 173 ; CHECK: pop {r4, r5, r6, r7, pc} 174 ret <4 x i32> %vecinit13 175 } 176 177 ; 4 high regs clobbered, all args & returns used, frame pointer requested and 178 ; llvm.returnaddress called. This leaves us with 3 low registers available (r4, 179 ; r5, r6), with which to save 4 high registers, so we have to use two pushes 180 ; and pops. 181 define <4 x i32> @all_of_the_above(i32 %a, i32 %b, i32 %c, i32 %d) "no-frame-pointer-elim"="true" { 182 ; CHECK-LABEL: all_of_the_above 183 entry: 184 ; CHECK: push {r4, r5, r6, r7, lr} 185 ; CHECK: add r7, sp, #12 186 ; CHECK: mov r6, r11 187 ; CHECK: mov r5, r10 188 ; CHECK: mov r4, r9 189 ; CHECK: push {r4, r5, r6} 190 ; CHECK: mov r6, r8 191 ; CHECK: push {r6} 192 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) 193 %e = tail call i8* @llvm.returnaddress(i32 0) 194 %f = ptrtoint i8* %e to i32 195 %vecinit = insertelement <4 x i32> undef, i32 %f, i32 0 196 %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1 197 %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2 198 %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3 199 ; CHECK: pop {r4, r5, r6} 200 ; CHECK: mov r8, r4 201 ; CHECK: mov r9, r5 202 ; CHECK: mov r10, r6 203 ; CHECK: pop {r4} 204 ; CHECK: mov r11, r4 205 ; CHECK: pop {r4, r5, r6, r7, pc} 206 ret <4 x i32> %vecinit13 207 } 208 209 ; When a base pointer is being used, we can safely use it for saving/restoring 210 ; the high regs because it is set after the last push, and not used at all in the 211 ; epliogue. We can also use r4 for restoring the registers despite it also being 212 ; used when restoring sp from fp, as that happens before the first pop. 213 define <4 x i32> @base_pointer(i32 %a) { 214 ; CHECK-LABEL: base_pointer: 215 entry: 216 ; CHECK: push {r4, r6, r7, lr} 217 ; CHECK: add r7, sp, #8 218 ; CHECK: mov lr, r9 219 ; CHECK: mov r6, r8 220 ; CHECK: push {r6, lr} 221 ; CHECK: mov r6, sp 222 %b = alloca i32, i32 %a 223 call void asm sideeffect "", "r,~{r8},~{r9}"(i32* %b) 224 %vecinit = insertelement <4 x i32> undef, i32 1, i32 0 225 %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1 226 %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2 227 %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3 228 ; CHECK: subs r4, r7, #7 229 ; CHECK: subs r4, #9 230 ; CHECK: mov sp, r4 231 ; CHECK: pop {r4, r6} 232 ; CHECK: mov r8, r4 233 ; CHECK: mov r9, r6 234 ; CHECK: pop {r4, r6, r7, pc} 235 ret <4 x i32> %vecinit13 236 } 237