commit 0f62395863b90231877320b7f7c8f669f8023133
parent 71f06498a9a5b3f6eafdd71b49d3bed19615bc6a
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 10 Jun 2026 07:01:25 -0700
cleanup: F.3 dwarf attr-skip dedup + G.4 RV disasm encoding dispatch
F.3: collapse the 6 pure-skip attr loops in dwarf_die.c onto the existing
dw_skip_die_attrs helper (read+skip two-pass sites in dwarf_open.c left
alone). No behavior change.
G.4: rv64_decode_flags now derives the CR-format control-flow flags from
the instruction encoding (rs2==0 && rd/rs1!=0 picks c.jr/c.jalr; bit[12]
picks the linking c.jalr) instead of strcmp on the mnemonic string. Adds
decode_compressed_cr_flags to rv64_decode_test pinning c.jr/c.jalr/c.mv/
c.add flags.
test-isa (rv64_decode 95 checks/0 fail), test-dwarf, test-debug, test-asm
all green.
Diffstat:
4 files changed, 64 insertions(+), 47 deletions(-)
diff --git a/doc/plan/TODO.md b/doc/plan/TODO.md
@@ -114,13 +114,6 @@ Add new deferred fixes below as they are discovered.
(`cc_default_obj_path_for_name`, `cc_dep_default_target`, `build_default_obj_name`); its
home is the driver util layer (`driver/`), not `link_inputs.c`. (The link pipeline +
flag-parse blocks were assessed and are not byte-identical — different option structs.)
-- **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`.)
-- **G.4 — RV disassembler dispatches sub-format by `strcmp`** on the mnemonic display
- string (`src/arch/riscv/disasm.c`) rather than an encoding tag.
- **I.1 — C parser shadow stack** is a 3-array struct-of-arrays
(`lang/c/parse/cg_adapter.c:53-141`) whose dup/swap/rot3/grow shuffle 3 lanes in
lockstep; collapse into one `PcgSlot{type;flags;aux;}` array. (Touches the parser↔CG
diff --git a/src/arch/riscv/disasm.c b/src/arch/riscv/disasm.c
@@ -177,13 +177,20 @@ static u16 rv64_decode_flags(const Rv64InsnDesc* desc, u32 word) {
if (rd == RV_ZERO && rs1 == RV_RA) flags |= KIT_DECODE_RET;
break;
}
- case RV64_FMT_CR:
- if (slice_eq_cstr(desc->mnemonic, "c.jr") ||
- slice_eq_cstr(desc->mnemonic, "c.jalr")) {
+ case RV64_FMT_CR: {
+ /* CR holds c.jr/c.jalr (rs2==0, rd/rs1!=0) and c.mv/c.add (rs2!=0).
+ * Dispatch on the encoding rather than the mnemonic string: only the
+ * jr/jalr pair is a control transfer, and bit[12] selects c.jalr (which
+ * links ra, so it is a call). */
+ u32 hw = word & 0xffffu;
+ u32 rs2 = (hw >> 2) & 0x1fu; /* bits[6:2] */
+ u32 rd_rs1 = (hw >> 7) & 0x1fu; /* bits[11:7] */
+ if (rs2 == 0u && rd_rs1 != 0u) {
flags |= KIT_DECODE_TERMINATOR | KIT_DECODE_BRANCH;
- if (slice_eq_cstr(desc->mnemonic, "c.jalr")) flags |= KIT_DECODE_CALL;
+ if (((hw >> 12) & 1u) != 0u) flags |= KIT_DECODE_CALL; /* c.jalr */
}
break;
+ }
case RV64_FMT_SYSTEM:
if (word == rv_ecall() || word == rv_ebreak())
flags |= KIT_DECODE_TERMINATOR | KIT_DECODE_TRAP;
diff --git a/src/debug/dwarf_die.c b/src/debug/dwarf_die.c
@@ -189,18 +189,10 @@ static void walk_for_subs(KitDebugInfo* d, u32 cu_idx, u32* off) {
}
} else if (die.abbrev->has_children) {
/* Skip attrs, then descend. */
- u32 i;
- for (i = 0; i < die.abbrev->nattrs; ++i) {
- DwAbbrevAttr* aa = &die.abbrev->attrs[i];
- dw_skip_form(d, cu, aa->form, aa->implicit_const, off);
- }
+ dw_skip_die_attrs(d, cu, &die, off);
walk_for_subs(d, cu_idx, off);
} else {
- u32 i;
- for (i = 0; i < die.abbrev->nattrs; ++i) {
- DwAbbrevAttr* aa = &die.abbrev->attrs[i];
- dw_skip_form(d, cu, aa->form, aa->implicit_const, off);
- }
+ dw_skip_die_attrs(d, cu, &die, off);
}
}
}
@@ -216,13 +208,7 @@ void dw_build_subs(KitDebugInfo* d) {
DwDie root;
if (!dw_read_die(d, cu, &off, &root)) continue;
/* Skip root attrs */
- {
- u32 j;
- for (j = 0; j < root.abbrev->nattrs; ++j) {
- DwAbbrevAttr* aa = &root.abbrev->attrs[j];
- dw_skip_form(d, cu, aa->form, aa->implicit_const, &off);
- }
- }
+ dw_skip_die_attrs(d, cu, &root, &off);
if (root.abbrev->has_children) walk_for_subs(d, i, &off);
}
}
@@ -320,11 +306,7 @@ static void walk_subprog_body(LocalCtx* x, u32* off, u64 scope_lo, u64 scope_hi,
walk_subprog_body(x, off, lo, hi, die.die_off, 1);
}
} else {
- u32 i;
- for (i = 0; i < die.abbrev->nattrs; ++i) {
- DwAbbrevAttr* aa = &die.abbrev->attrs[i];
- dw_skip_form(d, cu, aa->form, aa->implicit_const, off);
- }
+ dw_skip_die_attrs(d, cu, &die, off);
if (die.abbrev->has_children)
walk_subprog_body(x, off, scope_lo, scope_hi, scope_die_off, has_scope);
}
@@ -343,13 +325,7 @@ void dw_build_locals(KitDebugInfo* d, DwSubprog* sp) {
if (!dw_read_die(d, cu, &off, &die)) return;
if (!die.abbrev || !die.abbrev->has_children) return;
/* Skip subprog attrs */
- {
- u32 i;
- for (i = 0; i < die.abbrev->nattrs; ++i) {
- DwAbbrevAttr* aa = &die.abbrev->attrs[i];
- dw_skip_form(d, cu, aa->form, aa->implicit_const, &off);
- }
- }
+ dw_skip_die_attrs(d, cu, &die, &off);
memset(&x, 0, sizeof(x));
x.d = d;
x.cu_idx = sp->cu_idx;
@@ -371,13 +347,7 @@ void dw_build_globals(KitDebugInfo* d) {
u32 off = cu->die_start_off;
DwDie root;
if (!dw_read_die(d, cu, &off, &root)) continue;
- {
- u32 j;
- for (j = 0; j < root.abbrev->nattrs; ++j) {
- DwAbbrevAttr* aa = &root.abbrev->attrs[j];
- dw_skip_form(d, cu, aa->form, aa->implicit_const, &off);
- }
- }
+ dw_skip_die_attrs(d, cu, &root, &off);
if (!root.abbrev->has_children) continue;
/* Walk only top-level children of the CU; collect DW_TAG_variable. */
for (;;) {
diff --git a/test/arch/rv64_decode_test.c b/test/arch/rv64_decode_test.c
@@ -37,6 +37,11 @@ static void put32(unsigned char* b, size_t off, unsigned v) {
b[off + 3] = (unsigned char)(v >> 24);
}
+static void put16(unsigned char* b, size_t off, unsigned v) {
+ b[off + 0] = (unsigned char)v;
+ b[off + 1] = (unsigned char)(v >> 8);
+}
+
static void decode_addi(KitCompiler* pub) {
Compiler* c = (Compiler*)pub;
unsigned char bytes[4];
@@ -171,6 +176,47 @@ static void csr_pseudos_match_full_form(KitCompiler* pub) {
}
}
+/* The CR compressed format packs c.jr/c.jalr (rs2==0, rd/rs1!=0) and
+ * c.mv/c.add (rs2!=0). rv64_decode_flags derives the control-flow flags from
+ * the encoding (bit[12] picks the linking c.jalr) rather than the mnemonic
+ * string; pin that decode here so the dispatch stays encoding-driven. */
+static unsigned cr_word(unsigned bit12, unsigned rd_rs1, unsigned rs2) {
+ return (4u << 13) | ((bit12 & 1u) << 12) | ((rd_rs1 & 0x1fu) << 7) |
+ ((rs2 & 0x1fu) << 2) | 2u;
+}
+
+static void expect_cr_flags(KitCompiler* pub, unsigned word, u16 want_set,
+ u16 want_clear, const char* what) {
+ Compiler* c = (Compiler*)pub;
+ unsigned char bytes[4];
+ KitDecodedInsn insn;
+ KitStatus st;
+ memset(bytes, 0, sizeof(bytes));
+ put16(bytes, 0, word);
+ memset(&insn, 0, sizeof(insn));
+ st = arch_decode_one(c, bytes, sizeof(bytes), 0x7000, &insn);
+ EXPECT(st == KIT_OK, "decode_one(%s) status %d", what, (int)st);
+ EXPECT(insn.nbytes == 2, "%s nbytes = %u (want 2)", what,
+ (unsigned)insn.nbytes);
+ EXPECT((insn.flags & want_set) == want_set,
+ "%s flags 0x%x missing 0x%x", what, (unsigned)insn.flags,
+ (unsigned)want_set);
+ EXPECT((insn.flags & want_clear) == 0, "%s flags 0x%x should clear 0x%x",
+ what, (unsigned)insn.flags, (unsigned)want_clear);
+}
+
+static void decode_compressed_cr_flags(KitCompiler* pub) {
+ u16 br = (u16)(KIT_DECODE_TERMINATOR | KIT_DECODE_BRANCH);
+ u16 ctrl = (u16)(KIT_DECODE_TERMINATOR | KIT_DECODE_BRANCH | KIT_DECODE_CALL);
+ /* c.jr ra: branch terminator, not a call. */
+ expect_cr_flags(pub, cr_word(0, RV_RA, 0), br, (u16)KIT_DECODE_CALL, "c.jr");
+ /* c.jalr ra: linking branch -> call. */
+ expect_cr_flags(pub, cr_word(1, RV_RA, 0), ctrl, 0, "c.jalr");
+ /* c.mv / c.add (rs2!=0): not control transfers. */
+ expect_cr_flags(pub, cr_word(0, RV_A0, RV_A1), 0, ctrl, "c.mv");
+ expect_cr_flags(pub, cr_word(1, RV_A0, RV_A1), 0, ctrl, "c.add");
+}
+
int main(void) {
KitCompiler* c;
kit_unit_init(&g_u);
@@ -179,6 +225,7 @@ int main(void) {
decode_block_stops_at_ecall(c);
format_decoded_record(c);
csr_pseudos_match_full_form(c);
+ decode_compressed_cr_flags(c);
kit_compiler_free(c);
kit_unit_summary(&g_u, "rv64_decode_test");
return kit_unit_status(&g_u);