From 8c8d2e54aa7942e83507eb6a7cf0a04a57a389c5 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sun, 26 Jul 2026 14:40:57 -0400 Subject: [PATCH] remove cruft --- scripts/passes/annotation.lua | 2 +- scripts/passes/dwarf_injection.lua | 94 +++++++++++++++++++----------- scripts/passes/emission_model.lua | 2 - 3 files changed, 62 insertions(+), 36 deletions(-) diff --git a/scripts/passes/annotation.lua b/scripts/passes/annotation.lua index 3ebbe7a..70cd109 100644 --- a/scripts/passes/annotation.lua +++ b/scripts/passes/annotation.lua @@ -328,7 +328,7 @@ local function check_skip_marker(marker, _pipe_ctx, findings) local kind = marker.marker_kind local line = marker.marker_line - -- Tasks 6+7 left `scan.debug_skip_markers` with production records for `atom_dbg_skip` only; other identifiers take the walker's unrelated branch. + -- Left `scan.debug_skip_markers` with production records for `atom_dbg_skip` only; other identifiers take the walker's unrelated branch. if marker.has_parens then findings.errors[#findings.errors + 1] = { diff --git a/scripts/passes/dwarf_injection.lua b/scripts/passes/dwarf_injection.lua index b59ecbb..6d36af0 100644 --- a/scripts/passes/dwarf_injection.lua +++ b/scripts/passes/dwarf_injection.lua @@ -159,9 +159,8 @@ local DW_AT_decl_line = 0x3B -- DWARF5 §7.7.1: DW_AT_decl_line -- Both live in the existing gcc-generated line unit; their 1-based indices are stable across rebuilds because the include order -- in code/gte_hello/hello_gte.c determines the unit's file table. -- gcc only adds a file to the line table when it has actual line-number entries; --- headers that are pure macros/typedefs (dsl.h, memory.h, math.h, mips.h, gp.h, gte.h, etc.) --- never appear. lottes_tape.h is the FIRST include that emits line entries --- (MipsAtomComp_ declarations), so it is the FIRST entry after the primary file. +-- headers that are pure macros/typedefs (dsl.h, memory.h, math.h, mips.h, gp.h, gte.h, etc.) never appear. +-- lottes_tape.h is the FIRST include that emits line entries (MipsAtomComp_ declarations), so it is the FIRST entry after the primary file. local PROVENANCE_BASENAME_TO_FILE_INDEX = { ["hello_gte_tape.c"] = ATOM_SOURCE_FILE_INDEX, -- = 11 ["lottes_tape.h"] = 2, @@ -423,13 +422,13 @@ end --- - Extended opcode marker byte = 0. --- - Extended opcodes: marker byte + ULEB128 size + sub_opcode + payload. --- ---- Statement-state rules (atom_dbg_step_ux_20260725 — per-row policy): +--- Statement-state rules: --- * A marked whole atom emits one opaque is_stmt=false range row and no nested component rows; its subprogram symbol/range remains available. --- * Per-row policy at every other PC: --- - Call-site row of any invocation's first word: is_stmt = true (unconditional; `want_call = true`). --- - Body row of any invocation (first or subsequent): is_stmt = not inv.debug_skip (`want_body = not inv.debug_skip`). --- - RAW word (no containing invocation): is_stmt = true (unconditional). ---- * The previous per-word `marked_idx` ancestor walk (Tasks 6+7) and the GDB 12 zero-instruction-prologue duplicate row at atom entry are DELETED; the new +--- * The previous per-word `marked_idx` ancestor walk and the GDB 12 zero-instruction-prologue duplicate row at atom entry are DELETED; the new --- first-word emission IS the entry statement. --- * Whole-atom suppression wins over component markers; no nested inversion. --- @param atom table -- {name, addr, size_bytes, words, entries, invocations, debug_skip, word_events} @@ -472,20 +471,16 @@ local function build_atom_sequence(atom) }) end - -- Per-word invocation ancestry (atom_dbg_step_ux_20260725): - -- * `innermost_idx[idx]` = deepest active invocation at idx (nil for RAW words). Used to pick body_lines[k] for body rows. - -- * `ancestry_idx[idx]` = full active ancestry at idx, ordered outermost-first (widest range first; innermost = last entry). - -- Consumed at the first word of every invocation (atom entry + every `idx == inv.start_pos + 1`). - -- Drives the nested-display rule: the outer invocation's call-site + body_lines[1] rows are - -- re-emitted at the inner's first word PC so the debugger displays the outer body line (not - -- the inner body line) when stepping into the inner. PROBLEM B fix. + -- Per-word invocation ancestry: + -- * `innermost_idx[idx]`: Deepest active invocation at idx (nil for RAW words). Used to pick body_lines[k] for body rows. + -- * `ancestry_idx[idx]`: Full active ancestry at idx, ordered outermost-first (widest range first; innermost = last entry). + -- Consumed at the first word of every invocation (atom entry + every `idx == inv.start_pos + 1`). + -- Drives the nested-display rule: the outer invocation's call-site + body_lines[1] rows are + -- re-emitted at the inner's first word PC so the debugger displays the outer body line + -- (not the inner body line) when stepping into the inner. PROBLEM B fix. -- - -- `start_pos` / `end_pos` are 0-based emitted-word positions stamped at construction/close time by - -- `duffle.emit_invoke_begin` / `duffle.emit_invoke_end`; missing values are a corpus-plumbing bug, so we let the index - -- expression fail loud with arithmetic-on-nil rather than silently producing `0+1=1` for a missing start_pos. - -- - -- The previous per-word `marked_idx` array (Tasks 6+7) is DELETED: a marked invocation's body rows are now driven - -- by the per-invocation `inv.debug_skip` predicate at the emit site, not by a precomputed ancestry flag. + -- `start_pos` / `end_pos` are 0-based emitted-word positions stamped at construction/close time by `duffle.emit_invoke_begin` / `duffle.emit_invoke_end`; + -- Missing values are a corpus-plumbing bug, so we let the index expression fail loud with arithmetic-on-nil rather than silently producing `0+1=1` for a missing start_pos. local invs = atom.invocations or {} local innermost_idx = {} local ancestry_idx = {} @@ -508,6 +503,36 @@ local function build_atom_sequence(atom) end end + -- atom_dbg_step_ux_20260725 (gdb 12.1 fix): per-invocation `body_first_line` cache. + -- Maps invocation_id -> the line in the PARENT'S source where the body's FIRST CONTENT lives. + -- * If the invocation's body has any NESTED invocations (parent_id == top_inv.id), the body's + -- first content is the call_line of the earliest nested invocation (by start_pos). + -- * Otherwise (only RAW words in the body), it's the line of the first raw word = body_lines[1]. + -- This is the value the multi-row PC's body_lines[1] row must reference for source-order display: + -- `anc.body_lines[1]` is the line of the FIRST WORD (which for an outer whose body starts with a + -- nested expansion is inside the inner's expansion = wrong for display purposes); `anc.body_first_line` + -- is the body's first content line in the parent's source (= correct for display). + local body_first_line_of = {} + for _, top_inv in ipairs(invs) do + local earliest_nested_call_line = nil + local earliest_nested_start_pos = nil + for _, cand in ipairs(invs) do + if cand.parent_id == top_inv.id and cand.call_line ~= nil then + if earliest_nested_start_pos == nil or cand.start_pos < earliest_nested_start_pos then + earliest_nested_start_pos = cand.start_pos + earliest_nested_call_line = cand.call_line + end + end + end + if earliest_nested_call_line ~= nil then + body_first_line_of[top_inv.id] = earliest_nested_call_line + elseif top_inv.body_lines and top_inv.body_lines[1] ~= nil then + body_first_line_of[top_inv.id] = top_inv.body_lines[1] + else + body_first_line_of[top_inv.id] = 0 + end + end + local parts = { set_address(atom.addr), -- 7 bytes: marker + size + sub + addr; PC := atom.addr } @@ -546,15 +571,10 @@ local function build_atom_sequence(atom) local entry_1 = atom.entries[1] local entry_1_ancestry = ancestry_idx[1] - -- atom_dbg_step_ux_20260725: the call-site row at atom entry is ALWAYS is_stmt=true - -- (PROBLEM A fix). The previous `not marked_idx[1]` predicate suppressed this row when any - -- ancestor was marked; that behavior made source-level stepping skip past the call line entirely. - -- The new contract is `want_call = true` unconditionally. - -- - -- If atom entry 1 starts inside an invocation, walk the ancestry and emit a call-site row + (when - -- applicable) a body_lines[1] row for every active ancestor. For a non-nested invocation this is - -- just the one pair; for nested invocations this emits the outer call-site + body_lines[1] rows - -- BEFORE the inner pair so the debugger displays the outer body line at the inner's first word + -- If atom entry 1 starts inside an invocation, walk the ancestry and emit a call-site row + (when applicable) + -- a body_lines[1] row for every active ancestor. For a non-nested invocation this is just the one pair; + -- for nested invocations this emits the outer call-site + body_lines[1] rows BEFORE the inner pair so the debugger displays + -- the outer body line at the inner's first word -- (PROBLEM B fix). -- -- A marked OUTERMOST ancestor's body_lines[1] row is suppressed at this PC (the existing full-skip @@ -568,6 +588,9 @@ local function build_atom_sequence(atom) -- Atom starts in an invocation. Walk the ancestry outermost-first. -- Each ancestor emits one call-site row (statement) and one body_lines[1] row -- (statement iff unmarked; suppressed for marked outermost). + -- The body_lines[1] row references body_first_line_of[anc.id] (= the body's first content + -- line in the parent's source), NOT anc.body_lines[1] (= the line of the first WORD, + -- which is wrong when the outer's body starts with a nested call). for ai, anc in ipairs(entry_1_ancestry) do assert(anc.body_lines, "missing body_lines: emitter did not run emission-model") assert(anc.body_lines[1] ~= nil @@ -577,7 +600,7 @@ local function build_atom_sequence(atom) emit_row(resolve_provenance_file_index(anc.call_path), anc.call_line, true) local is_outermost = (ai == 1) if not (is_outermost and anc.debug_skip) then - emit_row(resolve_provenance_file_index(anc.def_path), anc.body_lines[1], not anc.debug_skip) + emit_row(resolve_provenance_file_index(anc.def_path), body_first_line_of[anc.id] or anc.body_lines[1], not anc.debug_skip) end end end @@ -601,6 +624,11 @@ local function build_atom_sequence(atom) -- This re-emits the outer ancestor's call-site + body rows at the inner's first word PC -- for debugger context: source-level stepping now shows the outer body line (not the -- inner body line) when stepping into the inner. PROBLEM B fix. + -- The body_lines[1] row references body_first_line_of[anc.id] (= the body's first content + -- line in the parent's source), NOT anc.body_lines[1] (= the line of the first WORD, + -- which is wrong when the outer's body starts with a nested call: gdb 12.1 picks the + -- displayed line as the LAST row at the same PC in byte-stream order, so the disc=1 row's + -- value matters for what's shown when stepping into the nested case). local ancestry = ancestry_idx[idx] for ai, anc in ipairs(ancestry) do assert(anc.body_lines, "missing body_lines: emitter did not run emission-model") @@ -612,7 +640,7 @@ local function build_atom_sequence(atom) emit_row(resolve_provenance_file_index(anc.call_path), anc.call_line, true) local is_outermost = (ai == 1) if not (is_outermost and anc.debug_skip) then - emit_row(resolve_provenance_file_index(anc.def_path), anc.body_lines[1], not anc.debug_skip) + emit_row(resolve_provenance_file_index(anc.def_path), body_first_line_of[anc.id] or anc.body_lines[1], not anc.debug_skip) end end elseif inv then @@ -652,7 +680,7 @@ end --- The atom table is built entirely from in-memory state. Disk source-map and provenance text artifacts are diagnostic outputs, not semantic inputs; --- the DWARF injection pass must remain correct regardless of their on-disk content. --- ---- Skip-state ownership (Tasks 6+7): +--- Skip-state ownership: --- * Whole-atom skip is read from the `atom.debug_skip` field (stamped by `scan_source.run` when the bare `atom_dbg_skip` marker --- immediately precedes the declaration; `corpus.atoms_by_name` exposes it directly). --- * Invocation skip is read from `atom.paths.invocations[*].debug_skip` (stamped by `duffle.emit_invoke_begin` @@ -713,7 +741,7 @@ local function build_atom_table(corpus, addrs) -- Using `start_word` would shift every `words_into` lookup by the marker count and break the call-site + body row pairing at the first word of every invocation. atom.invocations = invocations_proj for _, inv in ipairs(atom.invocations) do - -- Tasks 6+7: `debug_skip` flag is already stamped by `duffle.emit_invoke_begin` from `corpus.components[name].debug_skip`. + -- `debug_skip` flag is already stamped by `duffle.emit_invoke_begin` from `corpus.components[name].debug_skip`. -- Normalize to boolean for downstream dispatch. A missing value is a corpus-plumbing bug; the fail-loud error was raised at the construction site. inv.debug_skip = inv.debug_skip == true assert(type(inv.start_pos) == "number" @@ -1960,11 +1988,11 @@ local function build_inserted_children(main_cu_offset, main_cu_end_excl, atom_ta -- Per-component invocation inlined_subroutine instances. -- Each invocation covers a contiguous .word range [start_pos, end_pos] within the atom; we compute the PC range from -- atom start + .word offsets × MIPS_BYTES_PER_WORD. `inv.call_file` resolves to the line-unit file index, preserving call-site attribution across source files. - -- Tasks 6+7: a skipped invocation suppresses its synthetic inline frame entirely so source-level `step` cannot descend into it; the per-PC-range non-statement rows in .debug_line (emitted above) provide full skip semantics. + -- A skipped invocation suppresses its synthetic inline frame entirely so source-level `step` cannot descend into it; the per-PC-range non-statement rows in .debug_line (emitted above) provide full skip semantics. if atom.invocations and not atom.debug_skip then for _, inv in ipairs(atom.invocations) do if inv.debug_skip then - -- Tasks 6+7: this invocation emits no inlined_subroutine DIE; the whole-PC-range non-statement rows in .debug_line provide full skip semantics. + -- This invocation emits no inlined_subroutine DIE; the whole-PC-range non-statement rows in .debug_line provide full skip semantics. -- Stepping from the preceding atom statement lands at the first unskipped row after this invocation's range. else local inv_low = atom.addr + inv.start_pos * MIPS_BYTES_PER_WORD diff --git a/scripts/passes/emission_model.lua b/scripts/passes/emission_model.lua index 8d927d6..0905e49 100644 --- a/scripts/passes/emission_model.lua +++ b/scripts/passes/emission_model.lua @@ -157,8 +157,6 @@ local function project_atom(atom_record, src, corpus) local body = atom_record.body or "" local wc = corpus.word_counts or {} local cbi = corpus.component_body_index or {} - -- Stamp invocation-level `debug_skip` during construction (Task 5 of atom_component_skip_semantics_20260725). - -- I found the declaration metadata in `corpus.components`; the walker forwards that registry to `emit_invoke_begin` in `duffle.lua`. -- That construction site stamps `invocation.debug_skip` while appending each record to `proj.invocations`. local proj = duffle.project_emission(body, cbi, wc, corpus.components) local paths = {