; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc -mtriple=thumbv6m-none-eabi < %s | FileCheck %s declare i8* @llvm.returnaddress(i32) ; We don't allocate high registers, so any function not using inline asm will ; only need to save the low registers. define void @low_regs_only() { ; CHECK-LABEL: low_regs_only: ; CHECK: @ %bb.0: @ %entry ; CHECK-NEXT: .save {r4, r5, r6, r7, lr} ; CHECK-NEXT: push {r4, r5, r6, r7, lr} ; CHECK-NEXT: @APP ; CHECK-NEXT: @NO_APP ; CHECK-NEXT: pop {r4, r5, r6, r7, pc} entry: tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7}"() ret void } ; One high reg clobbered, but no low regs, args or returns. We can use an ; argument/return register to help save/restore it. define void @one_high() { ; CHECK-LABEL: one_high: ; CHECK: @ %bb.0: @ %entry ; CHECK-NEXT: mov r3, r8 ; CHECK-NEXT: .save {r8} ; CHECK-NEXT: push {r3} ; CHECK-NEXT: @APP ; CHECK-NEXT: @NO_APP ; CHECK-NEXT: pop {r0} ; CHECK-NEXT: mov r8, r0 ; CHECK-NEXT: bx lr entry: tail call void asm sideeffect "", "~{r8}"() ret void } ; 4 high regs clobbered, but still no low regs, args or returns. We can use all ; 4 arg/return regs for the save/restore. define void @four_high() { ; CHECK-LABEL: four_high: ; CHECK: @ %bb.0: @ %entry ; CHECK-NEXT: mov r3, r11 ; CHECK-NEXT: mov r2, r10 ; CHECK-NEXT: mov r1, r9 ; CHECK-NEXT: mov r0, r8 ; CHECK-NEXT: .save {r8, r9, r10, r11} ; CHECK-NEXT: push {r0, r1, r2, r3} ; CHECK-NEXT: @APP ; CHECK-NEXT: @NO_APP ; CHECK-NEXT: pop {r0, r1, r2, r3} ; CHECK-NEXT: mov r8, r0 ; CHECK-NEXT: mov r9, r1 ; CHECK-NEXT: mov r10, r2 ; CHECK-NEXT: mov r11, r3 ; CHECK-NEXT: bx lr entry: tail call void asm sideeffect "", "~{r8},~{r9},~{r10},~{r11}"() ret void } ; One high and one low register clobbered. lr also gets pushed to simplify the ; return, and r7 to keep the stack aligned. Here, we could use r0-r3, r4, r7 or ; lr to save/restore r8. define void @one_high_one_low() { ; CHECK-LABEL: one_high_one_low: ; CHECK: @ %bb.0: @ %entry ; CHECK-NEXT: .save {r4, r7, lr} ; CHECK-NEXT: push {r4, r7, lr} ; CHECK-NEXT: mov lr, r8 ; CHECK-NEXT: .save {r8} ; CHECK-NEXT: push {lr} ; CHECK-NEXT: @APP ; CHECK-NEXT: @NO_APP ; CHECK-NEXT: pop {r0} ; CHECK-NEXT: mov r8, r0 ; CHECK-NEXT: pop {r4, r7, pc} entry: tail call void asm sideeffect "", "~{r4},~{r8}"() ret void } ; All callee-saved registers clobbered, r4-r7 and lr are not live after the ; first push so can be used for pushing the high registers. define void @four_high_four_low() { ; CHECK-LABEL: four_high_four_low: ; CHECK: @ %bb.0: @ %entry ; CHECK-NEXT: .save {r4, r5, r6, r7, lr} ; CHECK-NEXT: push {r4, r5, r6, r7, lr} ; CHECK-NEXT: mov lr, r11 ; CHECK-NEXT: mov r7, r10 ; CHECK-NEXT: mov r6, r9 ; CHECK-NEXT: mov r5, r8 ; CHECK-NEXT: .save {r8, r9, r10, r11} ; CHECK-NEXT: push {r5, r6, r7, lr} ; CHECK-NEXT: @APP ; CHECK-NEXT: @NO_APP ; CHECK-NEXT: pop {r0, r1, r2, r3} ; CHECK-NEXT: mov r8, r0 ; CHECK-NEXT: mov r9, r1 ; CHECK-NEXT: mov r10, r2 ; CHECK-NEXT: mov r11, r3 ; CHECK-NEXT: pop {r4, r5, r6, r7, pc} entry: tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"() ret void } ; All callee-saved registers clobbered, and frame pointer is requested. r7 now ; cannot be used while saving/restoring the high regs. define void @four_high_four_low_frame_ptr() "frame-pointer"="all" { ; CHECK-LABEL: four_high_four_low_frame_ptr: ; CHECK: @ %bb.0: @ %entry ; CHECK-NEXT: .save {r4, r5, r6, r7, lr} ; CHECK-NEXT: push {r4, r5, r6, r7, lr} ; CHECK-NEXT: .setfp r7, sp, #12 ; CHECK-NEXT: add r7, sp, #12 ; CHECK-NEXT: mov lr, r11 ; CHECK-NEXT: mov r6, r10 ; CHECK-NEXT: mov r5, r9 ; CHECK-NEXT: mov r4, r8 ; CHECK-NEXT: .save {r8, r9, r10, r11} ; CHECK-NEXT: push {r4, r5, r6, lr} ; CHECK-NEXT: @APP ; CHECK-NEXT: @NO_APP ; CHECK-NEXT: pop {r0, r1, r2, r3} ; CHECK-NEXT: mov r8, r0 ; CHECK-NEXT: mov r9, r1 ; CHECK-NEXT: mov r10, r2 ; CHECK-NEXT: mov r11, r3 ; CHECK-NEXT: pop {r4, r5, r6, r7, pc} entry: tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"() ret void } ; All callee-saved registers clobbered, frame pointer is requested and ; llvm.returnaddress used. r7 and lr now cannot be used while saving/restoring ; the high regs. define void @four_high_four_low_frame_ptr_ret_addr() "frame-pointer"="all" { ; CHECK-LABEL: four_high_four_low_frame_ptr_ret_addr: ; CHECK: @ %bb.0: @ %entry ; CHECK-NEXT: .save {r4, r5, r6, r7, lr} ; CHECK-NEXT: push {r4, r5, r6, r7, lr} ; CHECK-NEXT: .setfp r7, sp, #12 ; CHECK-NEXT: add r7, sp, #12 ; CHECK-NEXT: mov r6, r11 ; CHECK-NEXT: mov r5, r10 ; CHECK-NEXT: mov r4, r9 ; CHECK-NEXT: mov r3, r8 ; CHECK-NEXT: .save {r8, r9, r10, r11} ; CHECK-NEXT: push {r3, r4, r5, r6} ; CHECK-NEXT: mov r0, lr ; CHECK-NEXT: @APP ; CHECK-NEXT: @NO_APP ; CHECK-NEXT: pop {r0, r1, r2, r3} ; CHECK-NEXT: mov r8, r0 ; CHECK-NEXT: mov r9, r1 ; CHECK-NEXT: mov r10, r2 ; CHECK-NEXT: mov r11, r3 ; CHECK-NEXT: pop {r4, r5, r6, r7, pc} entry: %a = tail call i8* @llvm.returnaddress(i32 0) tail call void asm sideeffect "", "r,~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"(i8* %a) ret void } ; 4 high regs clobbered, all 4 argument registers used. We push an extra 4 low ; registers, so that we can use them for saving the high regs. define void @four_high_four_arg(i32 %a, i32 %b, i32 %c, i32 %d) { ; CHECK-LABEL: four_high_four_arg: ; CHECK: @ %bb.0: @ %entry ; CHECK-NEXT: .save {r5, r6, r7, lr} ; CHECK-NEXT: push {r5, r6, r7, lr} ; CHECK-NEXT: mov lr, r11 ; CHECK-NEXT: mov r7, r10 ; CHECK-NEXT: mov r6, r9 ; CHECK-NEXT: mov r5, r8 ; CHECK-NEXT: .save {r8, r9, r10, r11} ; CHECK-NEXT: push {r5, r6, r7, lr} ; CHECK-NEXT: @APP ; CHECK-NEXT: @NO_APP ; CHECK-NEXT: pop {r0, r1, r2, r3} ; CHECK-NEXT: mov r8, r0 ; CHECK-NEXT: mov r9, r1 ; CHECK-NEXT: mov r10, r2 ; CHECK-NEXT: mov r11, r3 ; CHECK-NEXT: pop {r5, r6, r7, pc} entry: tail call void asm sideeffect "", "r,r,r,r,~{r8},~{r9},~{r10},~{r11}"(i32 %a, i32 %b, i32 %c, i32 %d) ret void } ; 4 high regs clobbered, all 4 return registers used. We push an extra 4 low ; registers, so that we can use them for restoring the high regs. define <4 x i32> @four_high_four_return() { ; CHECK-LABEL: four_high_four_return: ; CHECK: @ %bb.0: @ %entry ; CHECK-NEXT: .save {r4, r5, r6, r7, lr} ; CHECK-NEXT: push {r4, r5, r6, r7, lr} ; CHECK-NEXT: mov lr, r11 ; CHECK-NEXT: mov r7, r10 ; CHECK-NEXT: mov r6, r9 ; CHECK-NEXT: mov r5, r8 ; CHECK-NEXT: .save {r8, r9, r10, r11} ; CHECK-NEXT: push {r5, r6, r7, lr} ; CHECK-NEXT: @APP ; CHECK-NEXT: @NO_APP ; CHECK-NEXT: movs r0, #1 ; CHECK-NEXT: movs r1, #2 ; CHECK-NEXT: movs r2, #3 ; CHECK-NEXT: movs r3, #4 ; CHECK-NEXT: pop {r4, r5, r6, r7} ; CHECK-NEXT: mov r8, r4 ; CHECK-NEXT: mov r9, r5 ; CHECK-NEXT: mov r10, r6 ; CHECK-NEXT: mov r11, r7 ; CHECK-NEXT: pop {r4, r5, r6, r7, pc} entry: tail call void asm sideeffect "", "~{r8},~{r9},~{r10},~{r11}"() %vecinit = insertelement <4 x i32> undef, i32 1, i32 0 %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1 %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2 %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3 ret <4 x i32> %vecinit13 } ; 4 high regs clobbered, all args & returns used, frame pointer requested and ; llvm.returnaddress called. This leaves us with 3 low registers available (r4, ; r5, r6), with which to save 4 high registers, so we have to use two pushes ; and pops. define <4 x i32> @all_of_the_above(i32 %a, i32 %b, i32 %c, i32 %d) "frame-pointer"="all" { ; CHECK-LABEL: all_of_the_above: ; CHECK: @ %bb.0: @ %entry ; CHECK-NEXT: .save {r4, r5, r6, r7, lr} ; CHECK-NEXT: push {r4, r5, r6, r7, lr} ; CHECK-NEXT: .setfp r7, sp, #12 ; CHECK-NEXT: add r7, sp, #12 ; CHECK-NEXT: mov r6, r11 ; CHECK-NEXT: mov r5, r10 ; CHECK-NEXT: mov r4, r9 ; CHECK-NEXT: .save {r9, r10, r11} ; CHECK-NEXT: push {r4, r5, r6} ; CHECK-NEXT: mov r6, r8 ; CHECK-NEXT: .save {r8} ; CHECK-NEXT: push {r6} ; CHECK-NEXT: .pad #4 ; CHECK-NEXT: sub sp, #4 ; CHECK-NEXT: mov r4, lr ; CHECK-NEXT: str r4, [sp] @ 4-byte Spill ; CHECK-NEXT: @APP ; CHECK-NEXT: @NO_APP ; CHECK-NEXT: movs r1, #2 ; CHECK-NEXT: movs r2, #3 ; CHECK-NEXT: movs r3, #4 ; CHECK-NEXT: ldr r0, [sp] @ 4-byte Reload ; CHECK-NEXT: add sp, #4 ; CHECK-NEXT: pop {r4, r5, r6} ; CHECK-NEXT: mov r8, r4 ; CHECK-NEXT: mov r9, r5 ; CHECK-NEXT: mov r10, r6 ; CHECK-NEXT: pop {r4} ; CHECK-NEXT: mov r11, r4 ; CHECK-NEXT: pop {r4, r5, r6, r7, pc} entry: 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) %e = tail call i8* @llvm.returnaddress(i32 0) %f = ptrtoint i8* %e to i32 %vecinit = insertelement <4 x i32> undef, i32 %f, i32 0 %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1 %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2 %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3 ret <4 x i32> %vecinit13 } ; When a base pointer is being used, we can safely use it for saving/restoring ; the high regs because it is set after the last push, and not used at all in the ; epliogue. We can also use r4 for restoring the registers despite it also being ; used when restoring sp from fp, as that happens before the first pop. define <4 x i32> @base_pointer(i32 %a) { ; CHECK-LABEL: base_pointer: ; CHECK: @ %bb.0: @ %entry ; CHECK-NEXT: .save {r4, r6, r7, lr} ; CHECK-NEXT: push {r4, r6, r7, lr} ; CHECK-NEXT: .setfp r7, sp, #8 ; CHECK-NEXT: add r7, sp, #8 ; CHECK-NEXT: mov lr, r9 ; CHECK-NEXT: mov r6, r8 ; CHECK-NEXT: .save {r8, r9} ; CHECK-NEXT: push {r6, lr} ; CHECK-NEXT: mov r6, sp ; CHECK-NEXT: lsls r0, r0, #2 ; CHECK-NEXT: adds r0, r0, #7 ; CHECK-NEXT: movs r1, #7 ; CHECK-NEXT: bics r0, r1 ; CHECK-NEXT: mov r1, sp ; CHECK-NEXT: subs r0, r1, r0 ; CHECK-NEXT: mov sp, r0 ; CHECK-NEXT: @APP ; CHECK-NEXT: @NO_APP ; CHECK-NEXT: movs r0, #1 ; CHECK-NEXT: movs r1, #2 ; CHECK-NEXT: movs r2, #3 ; CHECK-NEXT: movs r3, #4 ; CHECK-NEXT: subs r4, r7, #7 ; CHECK-NEXT: subs r4, #9 ; CHECK-NEXT: mov sp, r4 ; CHECK-NEXT: pop {r4, r6} ; CHECK-NEXT: mov r8, r4 ; CHECK-NEXT: mov r9, r6 ; CHECK-NEXT: pop {r4, r6, r7, pc} entry: %b = alloca i32, i32 %a call void asm sideeffect "", "r,~{r8},~{r9}"(i32* %b) %vecinit = insertelement <4 x i32> undef, i32 1, i32 0 %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1 %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2 %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3 ret <4 x i32> %vecinit13 }