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