[llvm] [AVR] Fix a crash when generating debug information for frame pointer (PR #227285)

Ben Shi via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 29 05:06:11 PDT 2026


https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/227285

fixes https://github.com/llvm/llvm-project/issues/227220

>From 071c61e5b9aec39f3ca3b0ca96b435a91c35d0fe Mon Sep 17 00:00:00 2001
From: Ben Shi <bennshi at tencent.com>
Date: Tue, 29 Sep 2026 20:01:52 +0800
Subject: [PATCH] [AVR] Fix a crash when generating debug information for frame
 pointer

fixes https://github.com/llvm/llvm-project/issues/227220
---
 llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp |   7 +-
 llvm/lib/Target/AVR/AVRFrameLowering.cpp     |   6 +
 llvm/lib/Target/AVR/AVRRegisterInfo.cpp      |   7 +-
 llvm/test/DebugInfo/AVR/frame-index-fbreg.ll | 109 +++++++++++++++++++
 llvm/test/DebugInfo/AVR/lit.local.cfg        |   2 +
 5 files changed, 124 insertions(+), 7 deletions(-)
 create mode 100644 llvm/test/DebugInfo/AVR/frame-index-fbreg.ll
 create mode 100644 llvm/test/DebugInfo/AVR/lit.local.cfg

diff --git a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
index f97e7a8ed08b2..5d04d6f54adc0 100644
--- a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
+++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
@@ -2627,10 +2627,15 @@ template <> bool AVRExpandPseudo::expand<AVR::FRMSP>(Block &MBB, BlockIt MBBI) {
   Register ASOPointer = MI.getOperand(0).getReg();
   int64_t ASOAlignment = MI.getOperand(1).getImm();
 
+  // Note: `FrameReg` is an out-param here - passing `ASOPointer` instead would
+  // overwrite the register we are supposed to write the aligned stack address
+  // into.
+  Register FrameReg;
+
   StackOffset ASOOffset =
       MF.getSubtarget<AVRSubtarget>()
           .getFrameLowering()
-          ->getFrameIndexReference(MF, AFI->AlignedStackObjectIdx, ASOPointer);
+          ->getFrameIndexReference(MF, AFI->AlignedStackObjectIdx, FrameReg);
 
   TII->copyPhysReg(MBB, MI, DL, ASOPointer, AVR::R29R28, false, false, false);
 
diff --git a/llvm/lib/Target/AVR/AVRFrameLowering.cpp b/llvm/lib/Target/AVR/AVRFrameLowering.cpp
index 6573deb272f4a..30bfa0937d320 100644
--- a/llvm/lib/Target/AVR/AVRFrameLowering.cpp
+++ b/llvm/lib/Target/AVR/AVRFrameLowering.cpp
@@ -24,6 +24,7 @@
 #include "llvm/CodeGen/MachineInstrBuilder.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
 #include "llvm/CodeGen/TargetFrameLowering.h"
+#include "llvm/CodeGen/TargetRegisterInfo.h"
 #include "llvm/Support/ErrorHandling.h"
 
 namespace llvm {
@@ -236,6 +237,11 @@ StackOffset AVRFrameLowering::getFrameIndexReference(const MachineFunction &MF,
     llvm_unreachable("Unsupported stack!");
   }
 
+  // Frame indices are addressed relative to the frame register (see
+  // AVRRegisterInfo::eliminateFrameIndex); callers such as the DWARF
+  // debug info emitter rely on it being reported back here.
+  FrameReg = MF.getSubtarget().getRegisterInfo()->getFrameRegister(MF);
+
   return StackOffset::getFixed(Offset);
 }
 
diff --git a/llvm/lib/Target/AVR/AVRRegisterInfo.cpp b/llvm/lib/Target/AVR/AVRRegisterInfo.cpp
index 836faf8edfe19..b461e27658b09 100644
--- a/llvm/lib/Target/AVR/AVRRegisterInfo.cpp
+++ b/llvm/lib/Target/AVR/AVRRegisterInfo.cpp
@@ -260,12 +260,7 @@ bool AVRRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
 
 Register AVRRegisterInfo::getFrameRegister(const MachineFunction &MF) const {
   const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering();
-  if (TFI->hasFP(MF)) {
-    // The Y pointer register
-    return AVR::R28;
-  }
-
-  return AVR::SP;
+  return TFI->hasFP(MF) ? AVR::R29R28 : AVR::SP;
 }
 
 void AVRRegisterInfo::splitReg(Register Reg, Register &LoReg,
diff --git a/llvm/test/DebugInfo/AVR/frame-index-fbreg.ll b/llvm/test/DebugInfo/AVR/frame-index-fbreg.ll
new file mode 100644
index 0000000000000..15261d5a2284e
--- /dev/null
+++ b/llvm/test/DebugInfo/AVR/frame-index-fbreg.ll
@@ -0,0 +1,109 @@
+; RUN: llc -mtriple=avr -O0 -filetype=obj -o %t %s
+; RUN: llvm-dwarfdump --debug-info %t | FileCheck %s
+; RUN: llvm-dwarfdump --verify %t
+;
+; RUN: llc -mtriple=avr -O0 -dwarf-version=4 -filetype=obj -o %t4 %s
+; RUN: llvm-dwarfdump --debug-info %t4 | FileCheck %s --check-prefix=V4
+;
+; The DW_OP_fbreg offsets must match the Y-relative addressing used by the
+; generated code (see the ASM checks below).
+; RUN: llc -mtriple=avr -O0 -o - %s | FileCheck %s --check-prefix=ASM
+
+; Generated with:
+;
+;   clang --target=avr -O0 -g -S -emit-llvm
+;
+; from the following source:
+;
+;   int add(int a, int b) {
+;     int c = a + b;
+;     return c;
+;   }
+;
+; At -O0 every variable lives in a stack slot, so its debug location is
+; described by a frame index (Loc::MMI). Emitting such a location requires
+; AVRFrameLowering::getFrameIndexReference() to report back the register the
+; frame index is addressed through -- the Y pointer (R29R28). When that
+; out-parameter was left unset, DwarfExpression::addMachineReg() saw a
+; non-physical register and hit the llvm_unreachable in
+; TargetRegisterInfo::getDwarfRegNumForVirtReg(), crashing the compiler.
+
+; CHECK:      DW_TAG_subprogram
+; CHECK:        DW_AT_frame_base{{.*}}(DW_OP_reg28 R29R28)
+; CHECK:        DW_AT_name{{.*}}("add")
+
+; CHECK:      DW_TAG_formal_parameter
+; CHECK:        DW_AT_location{{.*}}(DW_OP_fbreg +5)
+; CHECK:        DW_AT_name{{.*}}("a")
+
+; CHECK:      DW_TAG_formal_parameter
+; CHECK:        DW_AT_location{{.*}}(DW_OP_fbreg +3)
+; CHECK:        DW_AT_name{{.*}}("b")
+
+; CHECK:      DW_TAG_variable
+; CHECK:        DW_AT_location{{.*}}(DW_OP_fbreg +1)
+; CHECK:        DW_AT_name{{.*}}("c")
+
+; V4: DW_AT_frame_base{{.*}}(DW_OP_reg28 R29R28)
+; V4: DW_AT_location{{.*}}(DW_OP_fbreg +5)
+; V4: DW_AT_location{{.*}}(DW_OP_fbreg +3)
+; V4: DW_AT_location{{.*}}(DW_OP_fbreg +1)
+
+; The prologue points Y (r29:r28) at the frame and allocates 6 bytes.
+; ASM:      in r28, 61
+; ASM:      sbiw r28, 6
+
+; The stack slot of each variable, matching the DW_OP_fbreg offsets above.
+; ASM:      std Y+5, r24
+; ASM:      std Y+3, r22
+; ASM:      std Y+1, r24
+; ASM:      ldd r24, Y+1
+
+target datalayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8:16-a:8"
+target triple = "avr"
+
+define dso_local i16 @add(i16 noundef %a, i16 noundef %b) addrspace(1) #0 !dbg !7 {
+entry:
+  %a.addr = alloca i16, align 1
+  %b.addr = alloca i16, align 1
+  %c = alloca i16, align 1
+  store i16 %a, ptr %a.addr, align 1
+    #dbg_declare(ptr %a.addr, !12, !DIExpression(), !13)
+  store i16 %b, ptr %b.addr, align 1
+    #dbg_declare(ptr %b.addr, !14, !DIExpression(), !15)
+    #dbg_declare(ptr %c, !16, !DIExpression(), !17)
+  %0 = load i16, ptr %a.addr, align 1, !dbg !18
+  %1 = load i16, ptr %b.addr, align 1, !dbg !19
+  %add = add nsw i16 %0, %1, !dbg !20
+  store i16 %add, ptr %c, align 1, !dbg !17
+  %2 = load i16, ptr %c, align 1, !dbg !21
+  ret i16 %2, !dbg !22
+}
+
+attributes #0 = { noinline nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "avr-dwarf.c", directory: "")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 2}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+!7 = distinct !DISubprogram(name: "add", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !11)
+!8 = !DISubroutineType(types: !9)
+!9 = !{!10, !10, !10}
+!10 = !DIBasicType(name: "int", size: 16, encoding: DW_ATE_signed)
+!11 = !{}
+!12 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 1, type: !10)
+!13 = !DILocation(line: 1, column: 13, scope: !7)
+!14 = !DILocalVariable(name: "b", arg: 2, scope: !7, file: !1, line: 1, type: !10)
+!15 = !DILocation(line: 1, column: 20, scope: !7)
+!16 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 2, type: !10)
+!17 = !DILocation(line: 2, column: 7, scope: !7)
+!18 = !DILocation(line: 2, column: 11, scope: !7)
+!19 = !DILocation(line: 2, column: 15, scope: !7)
+!20 = !DILocation(line: 2, column: 13, scope: !7)
+!21 = !DILocation(line: 3, column: 10, scope: !7)
+!22 = !DILocation(line: 3, column: 3, scope: !7)
diff --git a/llvm/test/DebugInfo/AVR/lit.local.cfg b/llvm/test/DebugInfo/AVR/lit.local.cfg
new file mode 100644
index 0000000000000..1724fb233c169
--- /dev/null
+++ b/llvm/test/DebugInfo/AVR/lit.local.cfg
@@ -0,0 +1,2 @@
+if not "AVR" in config.root.targets:
+    config.unsupported = True



More information about the llvm-commits mailing list