From badf2af2986749442d9016165ca5a3634b265527 Mon Sep 17 00:00:00 2001 From: agra Date: Wed, 3 Jun 2026 13:52:38 +0300 Subject: [PATCH] docs(debugger): point DWARF/Frame wiring at backend/llvm helpers (A9.2) Refresh the debugging architecture reference for the A7.2 relocation: DWARF emission lives in src/backend/llvm/debug.zig (DebugInfo) and the interned Frame / tag-name tables in src/backend/llvm/reflection.zig (Reflection); emit_llvm.zig is the orchestrator that owns LLVMEmitter and dispatches to them. Behavior is unchanged; only the file-and-function map, the 'what's emitted' home, and the debugEnabled() owner are corrected. --- docs/debugger.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/docs/debugger.md b/docs/debugger.md index 6c66467..fd3b56b 100644 --- a/docs/debugger.md +++ b/docs/debugger.md @@ -105,10 +105,12 @@ niladic, span-stamped IR op** — the same pattern as `is_comptime` / `interp_print_frames`. It carries **no operands and no global reference**; each backend derives the frame from its own context: -- **`emit_llvm`:** resolves the op's `span` + current function → - `{file, line, col, func}` (reusing the source map wired in for DWARF), - **interns and builds the `Frame` global in `emit_llvm`** (the same - mechanism as the tag-name table), then emits `call sx_trace_push(ptr)`. +- **`emit_llvm` (the `.trace_frame` arm):** resolves the op's `span` + + current function → `{file, line, col, func}` (reusing the source map + wired in for DWARF), **interns and builds the `Frame` global** in + [`src/backend/llvm/reflection.zig`](../src/backend/llvm/reflection.zig) + (the same mechanism, in the same file, as the tag-name table), then emits + `call sx_trace_push(ptr)`. - **`interp`:** pushes the packed `(func_id, ir_offset)` from its own execution context. @@ -118,8 +120,9 @@ alternative — an op carrying a `GlobalId` to an IR-level `Frame` global — would make the global visible to the interpreter (forcing comptime onto the pointer-deref path) and fatten the lowerer; **do not do this.** -`Frame` is defined **once** in sx (`trace.sx`/std); `emit_llvm` builds the -interned global off that `TypeId` through the normal struct-emission path, +`Frame` is defined **once** in sx (`trace.sx`/std); the reflection builder +(`src/backend/llvm/reflection.zig`) builds the interned global off that +`TypeId` through the normal struct-emission path, never a bespoke byte layout (which would risk the "8-bytes-assumed" clobber class of bug). `file`/`func` strings are interned into a shared pool so a path shared by N push sites is stored once — the table stays @@ -193,8 +196,9 @@ stripped without affecting traces. ### What's emitted -In [`src/ir/emit_llvm.zig`](../src/ir/emit_llvm.zig), gated on the same -debug opt levels + a wired source map (`setDebugContext`): +In [`src/backend/llvm/debug.zig`](../src/backend/llvm/debug.zig) (the +`DebugInfo` helper, driven from `emit_llvm`'s `emit()` pipeline), gated on +the same debug opt levels + a wired source map (`setDebugContext`): - one `DICompileUnit` + `DIFile` on the main file, - a `DISubprogram` per emitted function (`LLVMSetSubprogram`), @@ -237,7 +241,9 @@ both the trace path and the DWARF path. Items marked ✅ exist today; |---|---| | [`src/core.zig`](../src/core.zig) | `Compilation`: owns `import_sources` (file→source map), constructs the emitter, calls `setDebugContext` + `emit`; re-enters the interpreter for `#run`/post-link | | [`src/ir/lower.zig`](../src/ir/lower.zig) | AST→IR. Stamps `Inst.span`; emits push/clear at failure/absorb sites; `tracesEnabled` gate; declares the `sx_trace_*` externs | -| [`src/ir/emit_llvm.zig`](../src/ir/emit_llvm.zig) | IR→LLVM. Builds the interned `Frame` table; lowers the push op to a pointer push; emits all DWARF metadata | +| [`src/ir/emit_llvm.zig`](../src/ir/emit_llvm.zig) | IR→LLVM orchestrator. Owns `LLVMEmitter` + the source map (`setDebugContext`); dispatches the push op and the DWARF passes to the helpers below | +| [`src/backend/llvm/reflection.zig`](../src/backend/llvm/reflection.zig) | `Reflection`: builds the interned `Frame` table + the tag-name / type-name tables; lowers the push op to a pointer push | +| [`src/backend/llvm/debug.zig`](../src/backend/llvm/debug.zig) | `DebugInfo`: builds all DWARF metadata (compile unit, per-function subprograms, per-instruction `DILocation`) | | [`src/ir/interp.zig`](../src/ir/interp.zig) | Comptime IR interpreter. Lowers the push op to a packed `(func_id, offset)`; resolves comptime frames | | [`src/errors.zig`](../src/errors.zig) | `SourceLoc.compute(source, offset) → {line, col}`; the `import_sources` map type | | [`src/ir/inst.zig`](../src/ir/inst.zig) | `Inst.span`, `Function.source_file`, the `Op` union (home of the trace-push op) | @@ -330,8 +336,8 @@ the failable-`main` wrapper, whose `ret` path in `emit_llvm` ### The gate: one switch, two consumers -`Lowering.tracesEnabled()` (lower.zig) and `LLVMEmitter.debugEnabled()` -(emit_llvm) both reduce to `opt_level == .none or .less`. The `Frame` +`Lowering.tracesEnabled()` (lower.zig) and `DebugInfo.debugEnabled()` +(backend/llvm/debug.zig) both reduce to `opt_level == .none or .less`. The `Frame` table + push/clear ride `tracesEnabled`; DWARF rides `debugEnabled`. Release (`-O2`/`-O3`) emits neither. `sx run` defaults to `-O0` (both on); `sx ir`/`sx asm` default to `-O2` (both off) — which is why the `.ir`