callee_save.ll 10.9 KB
; 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
}