commit 30d33c176ededa9a33f7bcd0e8c13473ff10ff46
parent cb415ed9fc863e0bb9281b4aa2526d8ef01e4cb0
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 10 Jun 2026 06:00:01 -0700
cleanup wave: dedups (B.5 register_at, F.4 dist path-join, F.3 LEB128)
B.5: collapse the three near-identical *_register_at_public wrappers (x64/aa64/
rv64 arch.c) onto a shared arch_register_at_public(idx, out, iter_get) inline in
src/arch/arch.h; each arch's register_at is now a one-line delegation.
F.4: drop the duplicate join_tree_path in src/dist/cas.c; its one caller
(dist_cas_materialize_tree) now uses the identical dist_cas_join_path.
F.3 (LEB128 half): extract form_uleb_inline / form_sleb_inline (raw-buffer
LEB128, byte-identical to the Buf* form_uleb/form_sleb) into debug_form.c and
use them for the DWARF exprloc DW_OP_regx / DW_OP_fbreg operands in debug_emit.c,
replacing two hand-rolled encode loops. (The dwarf-reader attr-skip consolidation
stays on the backlog.)
Verified: test-debug, test-dwarf, test-dist, test-cg-api, test-toy (1394/0/33).
Diffstat:
9 files changed, 60 insertions(+), 57 deletions(-)
diff --git a/doc/plan/TODO.md b/doc/plan/TODO.md
@@ -127,9 +127,11 @@ Add new deferred fixes below as they are discovered.
flag-parse blocks were assessed and are not byte-identical — different option structs.)
- **F.2 — ELF strtab builder.** `objbb_append_str`'s dedup strtab builder is reimplemented
as `StrBuilder`/`strtab_add` ~4× in `src/obj/elf/link.c`. Use the shared builder.
-- **F.3 — LEB128 + dwarf attr-skip.** LEB128 encoders are forked in `src/debug/debug_emit.c`;
- the `dw_skip_die_attrs` loop is inlined ~6× in the dwarf reader. One encoder + one skip
- helper.
+- **F.3 — dwarf attr-skip.** The `dw_skip_die_attrs` loop is inlined ~6× in the dwarf
+ reader (`src/debug/dwarf_open.c` / `dwarf_die.c`); collapse the pure-skip sites onto
+ the existing helper (leave the read+skip two-pass sites alone). (The LEB128 fork is
+ done: `form_uleb_inline`/`form_sleb_inline` in `debug_form.c` now back the exprloc
+ builder in `debug_emit.c`.)
- **F.4 — path-join triplicated** (2 copies in one `src/dist/` file). (The `ctx->diag`
varargs wrapper part of this finding is done — now `kit_ctx_diagf` in `core/diag`.)
- **G.4 — RV disassembler dispatches sub-format by `strcmp`** on the mnemonic display
diff --git a/src/arch/aa64/arch.c b/src/arch/aa64/arch.c
@@ -18,12 +18,7 @@ extern const ArchDwarfOps aa64_dwarf_ops;
extern const ArchAsmOps aa64_asm_ops;
static int aa64_register_at_public(uint32_t idx, KitArchReg* out) {
- const char* nm = NULL;
- int rc;
- if (!out) return 1;
- rc = aa64_register_iter_get(idx, &out->dwarf_idx, &nm);
- if (rc == 0) out->name = kit_slice_cstr(nm);
- return rc;
+ return arch_register_at_public(idx, out, aa64_register_iter_get);
}
static void aa64_wr_u64_target(Compiler* c, u8* p, u64 v) {
diff --git a/src/arch/arch.h b/src/arch/arch.h
@@ -11,6 +11,21 @@
#include "core/core.h"
#include "obj/obj.h"
+/* Generic ArchImpl.register_at body: adapt a per-arch register_iter_get (which
+ * yields a DWARF index + a C-string name) into the public KitArchReg out
+ * struct. Each arch's register_at is a one-line wrapper delegating here with
+ * its own iter_get, so the conversion lives in exactly one place. */
+static inline int arch_register_at_public(
+ uint32_t idx, KitArchReg* out,
+ int (*iter_get)(uint32_t, uint32_t*, const char**)) {
+ const char* nm = NULL;
+ int rc;
+ if (!out) return 1;
+ rc = iter_get(idx, &out->dwarf_idx, &nm);
+ if (rc == 0) out->name = kit_slice_cstr(nm);
+ return rc;
+}
+
typedef struct AsmDriver AsmDriver;
typedef struct ArchAsm ArchAsm;
diff --git a/src/arch/riscv/arch.c b/src/arch/riscv/arch.c
@@ -22,12 +22,7 @@ extern const LinkArchDesc link_arch_rv32;
extern const ArchDbgOps rv32_dbg_ops;
static int rv64_register_at_public(uint32_t idx, KitArchReg* out) {
- const char* nm = NULL;
- int rc;
- if (!out) return 1;
- rc = rv64_register_iter_get(idx, &out->dwarf_idx, &nm);
- if (rc == 0) out->name = kit_slice_cstr(nm);
- return rc;
+ return arch_register_at_public(idx, out, rv64_register_iter_get);
}
static SrcLoc rv64_no_loc(void) {
diff --git a/src/arch/x64/arch.c b/src/arch/x64/arch.c
@@ -102,12 +102,7 @@ static void x64_target_feature_defaults(const Target* target, u64* words,
}
static int x64_register_at_public(uint32_t idx, KitArchReg* out) {
- const char* nm = NULL;
- int rc;
- if (!out) return 1;
- rc = x64_register_iter_get(idx, &out->dwarf_idx, &nm);
- if (rc == 0) out->name = kit_slice_cstr(nm);
- return rc;
+ return arch_register_at_public(idx, out, x64_register_iter_get);
}
static CgTarget* x64_backend_make(Compiler* c, ObjBuilder* o,
diff --git a/src/debug/debug_emit.c b/src/debug/debug_emit.c
@@ -534,31 +534,14 @@ static void emit_var_loc_exprloc(EmitCtx* e, Buf* b, DebugVarLoc loc) {
if (loc.v.reg < 32) {
expr[n++] = (u8)(DW_OP_reg0 + loc.v.reg);
} else {
- u64 v = loc.v.reg;
expr[n++] = DW_OP_regx;
- while (v >= 0x80) {
- expr[n++] = (u8)((v & 0x7f) | 0x80);
- v >>= 7;
- }
- expr[n++] = (u8)v;
+ n += form_uleb_inline(&expr[n], loc.v.reg);
}
break;
- case DVL_FRAME: {
- i64 v = loc.v.frame_ofs;
- int more = 1;
+ case DVL_FRAME:
expr[n++] = DW_OP_fbreg;
- while (more) {
- u8 byte = (u8)(v & 0x7f);
- v >>= 7;
- if ((v == 0 && (byte & 0x40) == 0) || (v == -1 && (byte & 0x40) != 0)) {
- more = 0;
- } else {
- byte |= 0x80;
- }
- expr[n++] = byte;
- }
+ n += form_sleb_inline(&expr[n], loc.v.frame_ofs);
break;
- }
case DVL_GLOBAL: {
/* DW_OP_addr <ptr_size>: relocation against the symbol. We can't
* place a section reloc inside an exprloc body without computing
diff --git a/src/debug/debug_form.c b/src/debug/debug_form.c
@@ -83,3 +83,34 @@ size_t form_sleb_size(i64 v) {
}
return n;
}
+
+/* LEB128 encoders into a raw fixed-size buffer (vs the Buf* form_uleb/form_sleb),
+ * for expression-byte builders that stage into a stack array. Caller guarantees
+ * room (<=10 bytes for u64, <=10 for i64). Returns the number of bytes written. */
+u32 form_uleb_inline(u8* buf, u64 v) {
+ u32 n = 0;
+ for (;;) {
+ u8 byte = (u8)(v & 0x7f);
+ v >>= 7;
+ if (v == 0) {
+ buf[n++] = byte;
+ return n;
+ }
+ buf[n++] = (u8)(byte | 0x80);
+ }
+}
+
+u32 form_sleb_inline(u8* buf, i64 v) {
+ u32 n = 0;
+ int more = 1;
+ while (more) {
+ u8 byte = (u8)(v & 0x7f);
+ v >>= 7; /* arithmetic shift */
+ if ((v == 0 && (byte & 0x40) == 0) || (v == -1 && (byte & 0x40) != 0))
+ more = 0;
+ else
+ byte |= 0x80;
+ buf[n++] = byte;
+ }
+ return n;
+}
diff --git a/src/debug/debug_internal.h b/src/debug/debug_internal.h
@@ -232,6 +232,8 @@ void form_uleb(Buf*, u64);
void form_sleb(Buf*, i64);
size_t form_uleb_size(u64);
size_t form_sleb_size(i64);
+u32 form_uleb_inline(u8* buf, u64 v);
+u32 form_sleb_inline(u8* buf, i64 v);
/* Abbrev pool ops (debug_abbrev.c). Teardown is abbrev_fini_heap (declared
* in debug_abbrev.c) — it needs the heap to free the per-abbrev attr arrays. */
diff --git a/src/dist/cas.c b/src/dist/cas.c
@@ -190,21 +190,6 @@ int dist_cas_get_tree(DistCas* cas, const uint8_t tree[DIST_BLAKE2B_LEN],
return DIST_ERR;
}
-static int join_tree_path(char* out, size_t cap, const char* dst,
- const char* rel) {
- size_t dl, rl;
- int needs_slash;
- if (!out || !cap || !dst || !rel) return DIST_ERR;
- dl = strlen(dst);
- rl = strlen(rel);
- needs_slash = dl > 0 && dst[dl - 1u] != '/';
- if (dl + (needs_slash ? 1u : 0u) + rl + 1u > cap) return DIST_ERR;
- memcpy(out, dst, dl);
- if (needs_slash) out[dl++] = '/';
- memcpy(out + dl, rel, rl);
- out[dl + rl] = '\0';
- return DIST_OK;
-}
int dist_cas_materialize_tree(DistCas* cas, const DistTree* tree,
const char* dst) {
@@ -232,7 +217,7 @@ int dist_cas_materialize_tree(DistCas* cas, const DistTree* tree,
goto entry_out;
if (bi.size != e->size || memcmp(bi.root, e->root, DIST_BLAKE2B_LEN) != 0)
goto entry_out;
- if (join_tree_path(outpath, sizeof outpath, dst, e->path) != DIST_OK)
+ if (dist_cas_join_path(outpath, sizeof outpath, dst, e->path) != DIST_OK)
goto entry_out;
if (parent_dir(parent, sizeof parent, outpath) != DIST_OK) goto entry_out;
if (cas->host.mkdir_p && cas->host.mkdir_p(cas->host.user, parent) != 0)