mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-20 00:52:33 +00:00
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.
76 lines
2.6 KiB
Odin
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
|
|
}
|
|
}
|
|
}
|