Home | History | Annotate | Download | only in Thumb
      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