Files
Odin/core/rexcode/isa/label_infer.odin
Flāvius a4f08f8307 Load rexcode encode/decode tables from committed binary blobs
Each ISA's hand-written ENCODING_TABLE (the single source of truth) now lives
in a per-arch tablegen/ metaprogram that flattens it and serializes committed
binary blobs; the library #loads those into @(rodata) at compile time rather
than compiling a table body. No arch keeps encoding_table.odin or
decoding_tables.odin -- only a generated tables.odin loader and tables/*.bin.

* Two-stage, type-checked pipeline: tablegen Stage A emits human-readable
  generated Odin, which compiles and serializes the blobs in Stage B.
* encode() goes through encoding_forms(m); decoders are unchanged apart from
  x86's flattened 2-D index. Decode tables are byte-identical to the old ones.
* build.lua: a LuaJIT driver for the metaprograms, validations, and tests,
  with cross-platform gating and a clear report.
* Docs refreshed; the obsolete forward-looking plan in cross_arch_design.md
  trimmed to what was actually built.
* Attribution headers added to all rexcode source files; the generators emit
  them so generated files keep them.
2026-06-15 07:43:29 -04:00

76 lines
2.6 KiB
Odin

// rexcode · Brendan Punsky (dotbmp@github), original author
package rexcode_isa
// =============================================================================
// LABEL INFERENCE (decoder helper, architecture-independent)
// =============================================================================
//
// Pass 2 of decode: turn the list of branch targets discovered during
// instruction decoding into label definitions, reusing label IDs from a
// caller-supplied relocations array when available (so symbolic names
// from the encoder survive the round trip).
// Branch target discovered while decoding. Each branch operand a
// per-arch decoder produces (RELATIVE-kind operand with a known target
// offset) becomes one of these for pass 2.
Branch_Target :: struct {
inst_idx: u32, // which decoded instruction has the branch
op_idx: u8, // which operand of that instruction is the target
target: u32, // absolute byte offset of the branch target
}
// Resolve branch targets into label definitions.
//
// For each branch target inside the decoded region (target < decoded_end):
// - if a label is already defined at that offset, do nothing;
// - else if `relocs` provides a label_id for that offset (named label
// carried over from the encode side), use that ID;
// - else create a fresh label ID and append it.
//
// label_defs may grow.
//
// Parametric over the caller's relocation type `$R`: the body only reads
// `.offset` and `.label_id`, so any per-arch `Relocation` struct with
// those fields works. Monomorphizes at the call site with no overhead.
infer_labels_from_branches :: proc(
branches: []Branch_Target,
decoded_end: u32,
label_defs: ^[dynamic]Label_Definition,
relocs: []$R,
) {
offset_to_label: map[u32]u32
defer delete(offset_to_label)
for _, id in label_defs {
if label_defs[id] != LABEL_UNDEFINED {
offset_to_label[u32(label_defs[id])] = u32(id)
}
}
relocation_offset_to_label: map[u32]u32
defer delete(relocation_offset_to_label)
for relocation in relocs {
relocation_offset_to_label[relocation.offset] = relocation.label_id
}
for branch in branches {
if branch.target >= decoded_end {
continue
}
if branch.target in offset_to_label {
continue
}
if reloc_id, ok := relocation_offset_to_label[branch.target]; ok {
for u32(len(label_defs)) <= reloc_id {
append(label_defs, LABEL_UNDEFINED)
}
label_defs[reloc_id] = Label_Definition(branch.target)
offset_to_label[branch.target] = reloc_id
} else {
new_id := u32(len(label_defs))
append(label_defs, Label_Definition(branch.target))
offset_to_label[branch.target] = new_id
}
}
}