| ; 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 | |
| } |