[llvm-branch-commits] [llvm] d4d778b - [DebugInfo][WebAssembly] Anchor stack locals to the frame base (#211826)

Tobias Hieta via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sat Jul 25 08:34:58 PDT 2026


Author: Jonas Devlieghere
Date: 2026-07-25T17:34:45+02:00
New Revision: d4d778b2532dba74d3a257ac74cb427bbadfb803

URL: https://github.com/llvm/llvm-project/commit/d4d778b2532dba74d3a257ac74cb427bbadfb803
DIFF: https://github.com/llvm/llvm-project/commit/d4d778b2532dba74d3a257ac74cb427bbadfb803.diff

LOG: [DebugInfo][WebAssembly] Anchor stack locals to the frame base (#211826)

WebAssembly's stack pointer is not a register that can be described in
DWARF. When a function has no virtual frame base, because its stack
pointer is never explicitly referenced (e.g. a function whose only local
is dead), the frame base falls back to the __stack_pointer global and
the frame register is the physical SP.

addMachineReg failed for that register, so addMachineRegExpression
dropped the base and the caller emitted the frame offset with nothing
under it: a bare DW_OP_plus_uconst that underflows the DWARF stack when
the location is evaluated.

```
  DW_AT_frame_base (DW_OP_WASM_location 0x3 0x0, DW_OP_stack_value)
    DW_AT_location  (DW_OP_plus_uconst 0xc)   ;; before
    DW_AT_location  (DW_OP_fbreg +12)         ;; after
```

A frame-relative location does not need the frame register to be
describable on its own, since it is anchored through DW_AT_frame_base
and DW_OP_fbreg. Take the same path already used for a virtual frame
register so a physical one without a DWARF number is handled too.

Assisted-by: Claude

PS: I also updated the frame-base fallback comment in
WebAssemblyFrameLowering: reads now work at the innermost frame, and the
remaining limitation is that the __stack_pointer global does not
describe outer frames.

(cherry picked from commit d3f58f9efd3e91824bc496e6fac80bad6d0db8f2)

Added: 
    llvm/test/DebugInfo/WebAssembly/dbg-fbreg-global-frame-base.ll

Modified: 
    llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
    llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
index ef83a877ad37c..d2a8bc569fa62 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
@@ -122,6 +122,13 @@ bool DwarfExpression::addMachineReg(const TargetRegisterInfo &TRI,
     return true;
   }
 
+  // The frame register is referenced through DW_OP_fbreg relative to
+  // DW_AT_frame_base, so it needs no DWARF register number of its own.
+  if (isFrameRegister(TRI, MachineReg)) {
+    DwarfRegs.push_back(Register::createRegister(-1, nullptr));
+    return true;
+  }
+
   // Walk up the super-register chain until we find a valid number.
   // For example, EAX on x86_64 is a 32-bit fragment of RAX with offset 0.
   for (MCPhysReg SR : TRI.superregs(MachineReg)) {

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
index 50f820086c5a6..442d75f1884f0 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
@@ -413,8 +413,9 @@ WebAssemblyFrameLowering::getDwarfFrameBase(const MachineFunction &MF) const {
     // that this code is not reached in that case, but assert here to be sure.
     assert(!MF.getSubtarget<WebAssemblySubtarget>().hasLibcallThreadContext());
 
-    // TODO: This should work on a breakpoint at a function with no frame,
-    // but probably won't work for traversing up the stack.
+    // The __stack_pointer global holds only the innermost frame's stack
+    // pointer, so this frame base is correct only for the innermost frame.
+    // TODO: Describe outer frames, which need a per-frame stack pointer value.
     Loc.Location.WasmLoc = {WebAssembly::TI_GLOBAL_RELOC, 0};
   }
   return Loc;

diff  --git a/llvm/test/DebugInfo/WebAssembly/dbg-fbreg-global-frame-base.ll b/llvm/test/DebugInfo/WebAssembly/dbg-fbreg-global-frame-base.ll
new file mode 100644
index 0000000000000..c736274466d53
--- /dev/null
+++ b/llvm/test/DebugInfo/WebAssembly/dbg-fbreg-global-frame-base.ll
@@ -0,0 +1,39 @@
+;; A function whose stack pointer is never referenced has no virtual frame base
+;; local, so its frame base is the __stack_pointer global. A stack local must
+;; still be described relative to that frame base with DW_OP_fbreg: the stack
+;; pointer is not a DWARF register, but a frame-relative location needs no
+;; register of its own. Otherwise the base is lost and the offset underflows
+;; the DWARF stack when the location is evaluated.
+
+; RUN: llc < %s -filetype=obj | llvm-dwarfdump - | FileCheck %s
+
+; CHECK:      DW_TAG_subprogram
+; CHECK:        DW_AT_frame_base ({{.*}}0x3 0x0, DW_OP_stack_value)
+; CHECK:        DW_TAG_variable
+; CHECK-NEXT:     DW_AT_location (DW_OP_fbreg +12)
+; CHECK-NEXT:     DW_AT_name ("t1")
+
+target triple = "wasm32-unknown-unknown"
+
+define hidden i32 @main() #0 !dbg !6 {
+  %1 = alloca i32, align 4
+    #dbg_declare(ptr %1, !11, !DIExpression(), !13)
+  ret i32 0, !dbg !13
+}
+
+attributes #0 = { noinline nounwind optnone }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "t.c", directory: "/")
+!2 = !{i32 7, !"Dwarf Version", i32 4}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!6 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !10)
+!7 = !DISubroutineType(types: !8)
+!8 = !{!9}
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!10 = !{!11}
+!11 = !DILocalVariable(name: "t1", scope: !6, file: !1, line: 1, type: !9)
+!13 = !DILocation(line: 1, column: 1, scope: !6)


        


More information about the llvm-branch-commits mailing list