[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