blob: 634b11fca92c3762f251efe3a5b7bc323feaff09 [file]
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
; RUN: llc < %s -mtriple=avr | FileCheck %s
; RUN: llc < %s -mtriple=avr -mcpu=atmega2560 | FileCheck %s --check-prefix=AVR6
declare ptr @llvm.returnaddress.p0(i32 immarg)
declare void @use(ptr)
; AVR has no link register: the return address is pushed onto the stack by the
; call instruction. The stack pointer points at the first free byte, so the
; pushed return address ends up right below the local area, which starts at
; SP_entry + 3.
;
; The return address is pushed most significant byte first, hence it is stored
; big-endian: [SP_entry + 1] is the high byte and [SP_entry + 2] the low byte.
; This is why the two bytes have to be swapped (avr-gcc does the same).
define ptr @returnaddress_0() {
; CHECK-LABEL: returnaddress_0:
; CHECK: ; %bb.0:
; CHECK-NEXT: push r28
; CHECK-NEXT: push r29
; CHECK-NEXT: in r28, 61
; CHECK-NEXT: in r29, 62
; CHECK-NEXT: ldd r24, Y+4
; CHECK-NEXT: ldd r25, Y+3
; CHECK-NEXT: pop r29
; CHECK-NEXT: pop r28
; CHECK-NEXT: ret
;
; AVR6-LABEL: returnaddress_0:
; AVR6: ; %bb.0:
; AVR6-NEXT: push r28
; AVR6-NEXT: push r29
; AVR6-NEXT: in r28, 61
; AVR6-NEXT: in r29, 62
; AVR6-NEXT: ldd r24, Y+5
; AVR6-NEXT: ldd r25, Y+4
; AVR6-NEXT: pop r29
; AVR6-NEXT: pop r28
; AVR6-NEXT: ret
; Devices with a 22-bit program counter push one byte more, so the two low
; bytes of the return address are found one byte higher up.
%1 = call ptr @llvm.returnaddress.p0(i32 0)
ret ptr %1
}
; Walking the frame chain is not possible, because the distance between the
; frame base and the slot holding the caller's return address depends on the
; caller's frame size, which is not known here. Return zero instead.
define ptr @returnaddress_1() {
; CHECK-LABEL: returnaddress_1:
; CHECK: ; %bb.0:
; CHECK-NEXT: ldi r24, 0
; CHECK-NEXT: ldi r25, 0
; CHECK-NEXT: ret
;
; AVR6-LABEL: returnaddress_1:
; AVR6: ; %bb.0:
; AVR6-NEXT: ldi r24, 0
; AVR6-NEXT: ldi r25, 0
; AVR6-NEXT: ret
%1 = call ptr @llvm.returnaddress.p0(i32 1)
ret ptr %1
}
; The return address is always the two bytes just below the incoming stack
; arguments, so its offset relative to Y grows with the frame size.
define ptr @returnaddress_alloca() {
; CHECK-LABEL: returnaddress_alloca:
; CHECK: ; %bb.0:
; CHECK-NEXT: push r28
; CHECK-NEXT: push r29
; CHECK-NEXT: in r28, 61
; CHECK-NEXT: in r29, 62
; CHECK-NEXT: sbiw r28, 4
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: cli
; CHECK-NEXT: out 62, r29
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: out 61, r28
; CHECK-NEXT: ldi r24, 42
; CHECK-NEXT: std Y+1, r24
; CHECK-NEXT: mov r24, r28
; CHECK-NEXT: mov r25, r29
; CHECK-NEXT: adiw r24, 1
; CHECK-NEXT: rcall use
; CHECK-NEXT: ldd r24, Y+8
; CHECK-NEXT: ldd r25, Y+7
; CHECK-NEXT: adiw r28, 4
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: cli
; CHECK-NEXT: out 62, r29
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: out 61, r28
; CHECK-NEXT: pop r29
; CHECK-NEXT: pop r28
; CHECK-NEXT: ret
;
; AVR6-LABEL: returnaddress_alloca:
; AVR6: ; %bb.0:
; AVR6-NEXT: push r28
; AVR6-NEXT: push r29
; AVR6-NEXT: in r28, 61
; AVR6-NEXT: in r29, 62
; AVR6-NEXT: sbiw r28, 4
; AVR6-NEXT: in r0, 63
; AVR6-NEXT: cli
; AVR6-NEXT: out 62, r29
; AVR6-NEXT: out 63, r0
; AVR6-NEXT: out 61, r28
; AVR6-NEXT: ldi r24, 42
; AVR6-NEXT: std Y+1, r24
; AVR6-NEXT: movw r24, r28
; AVR6-NEXT: adiw r24, 1
; AVR6-NEXT: call use
; AVR6-NEXT: ldd r24, Y+9
; AVR6-NEXT: ldd r25, Y+8
; AVR6-NEXT: adiw r28, 4
; AVR6-NEXT: in r0, 63
; AVR6-NEXT: cli
; AVR6-NEXT: out 62, r29
; AVR6-NEXT: out 63, r0
; AVR6-NEXT: out 61, r28
; AVR6-NEXT: pop r29
; AVR6-NEXT: pop r28
; AVR6-NEXT: ret
%x = alloca i8, i32 4
store i8 42, ptr %x
call void @use(ptr %x)
%1 = call ptr @llvm.returnaddress.p0(i32 0)
ret ptr %1
}