[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