[llvm] [AVR] Implement selection of 'ISD::RETURNADDR' and 'ISD::FRAMEADDR' (PR #222909)
Ben Shi via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 17 17:49:10 PDT 2026
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/222909
>From e7e3fc1234e4e5829d96489c16922a88101d0c50 Mon Sep 17 00:00:00 2001
From: Ben Shi <bennshi at tencent.com>
Date: Fri, 11 Sep 2026 16:53:54 +0800
Subject: [PATCH 1/3] [AVR] Implement selection of 'ISD::FRAMEADDR'
---
llvm/lib/Target/AVR/AVRFrameLowering.cpp | 7 ++-
llvm/lib/Target/AVR/AVRISelLowering.cpp | 26 +++++++++
llvm/lib/Target/AVR/AVRISelLowering.h | 1 +
llvm/test/CodeGen/AVR/frameaddress.ll | 74 ++++++++++++++++++++++++
4 files changed, 106 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/CodeGen/AVR/frameaddress.ll
diff --git a/llvm/lib/Target/AVR/AVRFrameLowering.cpp b/llvm/lib/Target/AVR/AVRFrameLowering.cpp
index 5f16f04f7bc6c..3374a441c519e 100644
--- a/llvm/lib/Target/AVR/AVRFrameLowering.cpp
+++ b/llvm/lib/Target/AVR/AVRFrameLowering.cpp
@@ -245,15 +245,18 @@ StackOffset AVRFrameLowering::getFrameIndexReference(const MachineFunction &MF,
// - a register has been spilled
// - has allocas
// - input arguments are passed using the stack
+// - has variable sized objects
+// - the frame address is taken (llvm.frameaddress)
//
// Notice that strictly this is not a frame pointer because it contains SP after
// frame allocation instead of having the original SP in function entry.
bool AVRFrameLowering::hasFPImpl(const MachineFunction &MF) const {
+ const MachineFrameInfo &MFI = MF.getFrameInfo();
const AVRMachineFunctionInfo *FuncInfo = MF.getInfo<AVRMachineFunctionInfo>();
return (FuncInfo->getHasSpills() || FuncInfo->getHasAllocas() ||
- FuncInfo->getHasStackArgs() ||
- MF.getFrameInfo().hasVarSizedObjects());
+ FuncInfo->getHasStackArgs() || MFI.hasVarSizedObjects() ||
+ MFI.isFrameAddressTaken());
}
bool AVRFrameLowering::spillCalleeSavedRegisters(
diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp
index 8fd1c34af9d7b..06f7d588aa8f4 100644
--- a/llvm/lib/Target/AVR/AVRISelLowering.cpp
+++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp
@@ -50,6 +50,7 @@ AVRTargetLowering::AVRTargetLowering(const AVRTargetMachine &TM,
setOperationAction(ISD::GlobalAddress, MVT::i16, Custom);
setOperationAction(ISD::BlockAddress, MVT::i16, Custom);
+ setOperationAction(ISD::FRAMEADDR, MVT::i16, Custom);
setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand);
@@ -961,11 +962,36 @@ SDValue AVRTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
return LowerDivRem(Op, DAG);
case ISD::INLINEASM:
return LowerINLINEASM(Op, DAG);
+ case ISD::FRAMEADDR:
+ return LowerFRAMEADDR(Op, DAG);
}
return SDValue();
}
+SDValue AVRTargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const {
+ // The frame pointer (Y = r29:r28) is set up to contain the stack pointer
+ // *after* the frame has been allocated, i.e. Y == SP_entry - StackSize,
+ // which means it points to the lowest address of the frame: it is really a
+ // frame *base* rather than the canonical frame address. The slot holding the
+ // caller's Y therefore sits at a function dependent offset (the frame size)
+ // above it. Walking the frame chain is only possible for the current frame.
+ //
+ // This is also what avr-gcc returns for __builtin_frame_address(0).
+ if (Op.getConstantOperandVal(0) > 0)
+ // Use the legalizer's default expansion, which is to return 0 (what this
+ // function is documented to do).
+ return SDValue();
+
+ MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo();
+ MFI.setFrameAddressIsTaken(true);
+
+ // Note that AVRRegisterInfo::getFrameRegister returns R28, which is only
+ // the low half of the frame pointer: use the full 16-bit register pair.
+ return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), AVR::R29R28,
+ Op.getValueType());
+}
+
/// Replace a node with an illegal result type
/// with a new node built out of custom code.
void AVRTargetLowering::ReplaceNodeResults(SDNode *N,
diff --git a/llvm/lib/Target/AVR/AVRISelLowering.h b/llvm/lib/Target/AVR/AVRISelLowering.h
index 2ae22b2d8af6c..601c4871338db 100644
--- a/llvm/lib/Target/AVR/AVRISelLowering.h
+++ b/llvm/lib/Target/AVR/AVRISelLowering.h
@@ -108,6 +108,7 @@ class AVRTargetLowering : public TargetLowering {
SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerSETCC(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const;
+ SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const;
bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF,
bool isVarArg,
diff --git a/llvm/test/CodeGen/AVR/frameaddress.ll b/llvm/test/CodeGen/AVR/frameaddress.ll
new file mode 100644
index 0000000000000..8fcf519b5bd62
--- /dev/null
+++ b/llvm/test/CodeGen/AVR/frameaddress.ll
@@ -0,0 +1,74 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=avr | FileCheck %s
+
+declare ptr @llvm.frameaddress.p0(i32 immarg)
+declare void @use(ptr)
+
+; The frame pointer is Y (r29:r28), which is set up to contain the stack
+; pointer after the frame has been allocated.
+define ptr @frameaddress_0() {
+; CHECK-LABEL: frameaddress_0:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: push r28
+; CHECK-NEXT: push r29
+; CHECK-NEXT: in r28, 61
+; CHECK-NEXT: in r29, 62
+; CHECK-NEXT: mov r24, r28
+; CHECK-NEXT: mov r25, r29
+; CHECK-NEXT: pop r29
+; CHECK-NEXT: pop r28
+; CHECK-NEXT: ret
+ %1 = call ptr @llvm.frameaddress.p0(i32 0)
+ ret ptr %1
+}
+
+; Walking the frame chain is not possible, because the slot holding the
+; caller's Y is at a function dependent offset above Y. Return zero instead.
+define ptr @frameaddress_1() {
+; CHECK-LABEL: frameaddress_1:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: ldi r24, 0
+; CHECK-NEXT: ldi r25, 0
+; CHECK-NEXT: ret
+ %1 = call ptr @llvm.frameaddress.p0(i32 1)
+ ret ptr %1
+}
+
+; Local objects are addressed relative to Y, the same value that is returned
+; by llvm.frameaddress(0).
+define ptr @frameaddress_alloca() {
+; CHECK-LABEL: frameaddress_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: mov r24, r28
+; CHECK-NEXT: mov r25, r29
+; 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
+ %x = alloca i8, i32 4
+ store i8 42, ptr %x
+ call void @use(ptr %x)
+ %1 = call ptr @llvm.frameaddress.p0(i32 0)
+ ret ptr %1
+}
>From ebf21bc81e0fa5b697e1684bbdff1f523f817c7a Mon Sep 17 00:00:00 2001
From: Ben Shi <bennshi at tencent.com>
Date: Fri, 11 Sep 2026 18:08:19 +0800
Subject: [PATCH 2/3] [AVR] Implement selection of 'ISD::RETURNADDR'
---
llvm/lib/Target/AVR/AVRFrameLowering.cpp | 6 +-
llvm/lib/Target/AVR/AVRISelLowering.cpp | 59 ++++++++++
llvm/lib/Target/AVR/AVRISelLowering.h | 1 +
llvm/test/CodeGen/AVR/returnaddress.ll | 131 +++++++++++++++++++++++
4 files changed, 196 insertions(+), 1 deletion(-)
create mode 100644 llvm/test/CodeGen/AVR/returnaddress.ll
diff --git a/llvm/lib/Target/AVR/AVRFrameLowering.cpp b/llvm/lib/Target/AVR/AVRFrameLowering.cpp
index 3374a441c519e..6573deb272f4a 100644
--- a/llvm/lib/Target/AVR/AVRFrameLowering.cpp
+++ b/llvm/lib/Target/AVR/AVRFrameLowering.cpp
@@ -247,6 +247,7 @@ StackOffset AVRFrameLowering::getFrameIndexReference(const MachineFunction &MF,
// - input arguments are passed using the stack
// - has variable sized objects
// - the frame address is taken (llvm.frameaddress)
+// - the return address is taken (llvm.returnaddress)
//
// Notice that strictly this is not a frame pointer because it contains SP after
// frame allocation instead of having the original SP in function entry.
@@ -254,9 +255,12 @@ bool AVRFrameLowering::hasFPImpl(const MachineFunction &MF) const {
const MachineFrameInfo &MFI = MF.getFrameInfo();
const AVRMachineFunctionInfo *FuncInfo = MF.getInfo<AVRMachineFunctionInfo>();
+ // Note that reading the return address requires a frame index, and that frame
+ // indexes are always referenced through Y (see
+ // AVRRegisterInfo::eliminateFrameIndex).
return (FuncInfo->getHasSpills() || FuncInfo->getHasAllocas() ||
FuncInfo->getHasStackArgs() || MFI.hasVarSizedObjects() ||
- MFI.isFrameAddressTaken());
+ MFI.isFrameAddressTaken() || MFI.isReturnAddressTaken());
}
bool AVRFrameLowering::spillCalleeSavedRegisters(
diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp
index 06f7d588aa8f4..9c3e04e681c54 100644
--- a/llvm/lib/Target/AVR/AVRISelLowering.cpp
+++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp
@@ -51,6 +51,7 @@ AVRTargetLowering::AVRTargetLowering(const AVRTargetMachine &TM,
setOperationAction(ISD::GlobalAddress, MVT::i16, Custom);
setOperationAction(ISD::BlockAddress, MVT::i16, Custom);
setOperationAction(ISD::FRAMEADDR, MVT::i16, Custom);
+ setOperationAction(ISD::RETURNADDR, MVT::i16, Custom);
setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand);
@@ -964,6 +965,8 @@ SDValue AVRTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
return LowerINLINEASM(Op, DAG);
case ISD::FRAMEADDR:
return LowerFRAMEADDR(Op, DAG);
+ case ISD::RETURNADDR:
+ return LowerRETURNADDR(Op, DAG);
}
return SDValue();
@@ -992,6 +995,62 @@ SDValue AVRTargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const {
Op.getValueType());
}
+SDValue AVRTargetLowering::LowerRETURNADDR(SDValue Op,
+ SelectionDAG &DAG) const {
+ // AVR has no link register: the return address is pushed onto the stack by
+ // the call instruction. The stack pointer always points at the first free
+ // byte, so the pushed return address ends up right below the local area (the
+ // incoming stack arguments), 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 avr-gcc has to swap the two bytes it reads for
+ // __builtin_return_address(0).
+ //
+ // 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. This is also what avr-gcc
+ // does for __builtin_return_address(1), which returns zero.
+ if (Op.getConstantOperandVal(0) > 0)
+ // Use the legalizer's default expansion, which is to return 0 (what this
+ // function is documented to do).
+ return SDValue();
+
+ MachineFunction &MF = DAG.getMachineFunction();
+ MachineFrameInfo &MFI = MF.getFrameInfo();
+ MFI.setReturnAddressIsTaken(true);
+
+ SDLoc DL(Op);
+ EVT PtrVT = getPointerTy(DAG.getDataLayout());
+
+ // A call pushes as many bytes as the program counter is wide: three on
+ // devices with a 22-bit PC (the ones providing EIJMP/EICALL), two elsewhere.
+ // Only the two low bytes of the return address are returned, so on the former
+ // they are found one byte higher up.
+ int PCWidth = Subtarget.hasEIJMPCALL() ? 3 : 2;
+
+ // A fixed object at offset N is located at SP_entry + N + 3, so the two low
+ // bytes of the return address are the fixed object at offset PCWidth - 4.
+ // Note that a frame index can only be referenced through Y, so taking the
+ // return address forces the function to have a frame pointer (see
+ // AVRFrameLowering::hasFPImpl).
+ int FI = MFI.CreateFixedObject(2, PCWidth - 4, true);
+ SDValue Addr = DAG.getFrameIndex(FI, PtrVT);
+
+ // Load the two bytes separately and put them in the right halves of the
+ // result, which is cheaper than loading a word and byte swapping it.
+ SDValue Hi = DAG.getLoad(MVT::i8, DL, DAG.getEntryNode(), Addr,
+ MachinePointerInfo::getFixedStack(MF, FI));
+ SDValue Lo =
+ DAG.getLoad(MVT::i8, DL, DAG.getEntryNode(),
+ DAG.getObjectPtrOffset(DL, Addr, TypeSize::getFixed(1)),
+ MachinePointerInfo::getFixedStack(MF, FI, 1));
+
+ SDValue Res = DAG.getTargetInsertSubreg(AVR::sub_lo, DL, MVT::i16,
+ DAG.getUNDEF(MVT::i16), Lo);
+ return DAG.getTargetInsertSubreg(AVR::sub_hi, DL, MVT::i16, Res, Hi);
+}
+
/// Replace a node with an illegal result type
/// with a new node built out of custom code.
void AVRTargetLowering::ReplaceNodeResults(SDNode *N,
diff --git a/llvm/lib/Target/AVR/AVRISelLowering.h b/llvm/lib/Target/AVR/AVRISelLowering.h
index 601c4871338db..ae99f654aba00 100644
--- a/llvm/lib/Target/AVR/AVRISelLowering.h
+++ b/llvm/lib/Target/AVR/AVRISelLowering.h
@@ -109,6 +109,7 @@ class AVRTargetLowering : public TargetLowering {
SDValue LowerSETCC(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const;
+ SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const;
bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF,
bool isVarArg,
diff --git a/llvm/test/CodeGen/AVR/returnaddress.ll b/llvm/test/CodeGen/AVR/returnaddress.ll
new file mode 100644
index 0000000000000..634b11fca92c3
--- /dev/null
+++ b/llvm/test/CodeGen/AVR/returnaddress.ll
@@ -0,0 +1,131 @@
+; 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
+}
>From 24a4fd15cb7a4622411aa8085a12c229180333da Mon Sep 17 00:00:00 2001
From: Ben Shi <bennshi at tencent.com>
Date: Fri, 18 Sep 2026 08:48:24 +0800
Subject: [PATCH 3/3] Add more comments
---
llvm/lib/Target/AVR/AVRISelLowering.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp
index 9c3e04e681c54..79ed56679120b 100644
--- a/llvm/lib/Target/AVR/AVRISelLowering.cpp
+++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp
@@ -987,6 +987,8 @@ SDValue AVRTargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const {
return SDValue();
MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo();
+ // Mark frame address as taken, so that during frame lowering we're forced to
+ // emit frame pointer register (R29R28) we rely on here.
MFI.setFrameAddressIsTaken(true);
// Note that AVRRegisterInfo::getFrameRegister returns R28, which is only
@@ -1018,6 +1020,8 @@ SDValue AVRTargetLowering::LowerRETURNADDR(SDValue Op,
MachineFunction &MF = DAG.getMachineFunction();
MachineFrameInfo &MFI = MF.getFrameInfo();
+ // Mark return address as taken, so that during frame lowering we're forced to
+ // emit frame pointer register (R29R28) we rely on here.
MFI.setReturnAddressIsTaken(true);
SDLoc DL(Op);
More information about the llvm-commits
mailing list