Files
Odin/core/rexcode/isa/labels.odin
2026-06-14 16:30:18 +01:00

107 lines
3.8 KiB
Odin

package rexcode_isa
// =============================================================================
// LABELS (architecture-independent bookkeeping)
// =============================================================================
//
// The *array-index* label model lives here -- it's genuinely universal:
// every arch's `encode()` consumes a `[]Label_Definition` and `decode()`
// populates one identically. The procedures are parametric over the
// Instruction type (`$T`), so each arch calls them as `isa.label(...)`
// without per-arch re-exports.
// -----------------------------------------------------------------------------
// Array-index model (used by every arch's encode/decode hot path)
// -----------------------------------------------------------------------------
//
// Label_Definition maps a label ID to:
// - the instruction index where it's defined (input to encode), then
// - the byte offset of that instruction (rewritten in place by encode).
Label_Definition :: distinct u32
LABEL_UNDEFINED :: Label_Definition(0xFFFFFFFF)
// Define a label at the current instruction position. Parametric on $T so
// any arch's Instruction type works.
label :: #force_inline proc(labels: ^[dynamic]Label_Definition, instructions: ^[dynamic]$T) -> u32 {
id := u32(len(labels))
append(labels, Label_Definition(len(instructions)))
return id
}
// Reserve a label slot for forward references.
label_forward :: #force_inline proc(labels: ^[dynamic]Label_Definition) -> u32 {
id := u32(len(labels))
append(labels, LABEL_UNDEFINED)
return id
}
// Define a previously reserved (anonymous) label's position. Pairs with
// `label_forward` to support `1: ... jlt <1` / `... jlt 1> ... 1:` style
// forward and backward local labels:
//
// fwd := label_forward(&labels)
// ... encode body that references fwd ...
// label_set_at(&labels, fwd, &instructions)
label_set_at :: #force_inline proc(labels: ^[dynamic]Label_Definition, id: u32, instructions: ^[dynamic]$T) {
labels[id] = Label_Definition(len(instructions))
}
// Label_Map: named labels.
Label_Map :: struct {
labels: [dynamic]Label_Definition,
names: map[string]u32,
}
label_map_init :: #force_inline proc(lm: ^Label_Map, allocator := context.allocator) {
lm.labels = make([dynamic]Label_Definition, allocator)
lm.names = make(map[string]u32, allocator = allocator)
}
label_map_destroy :: #force_inline proc(lm: ^Label_Map) {
delete(lm.labels)
delete(lm.names)
}
// Define a named label at the current instruction position.
label_named :: #force_inline proc(lm: ^Label_Map, name: string, instructions: ^[dynamic]$T) -> u32 {
id := u32(len(lm.labels))
append(&lm.labels, Label_Definition(len(instructions)))
lm.names[name] = id
return id
}
// Reserve a named label for forward reference.
label_reserve :: #force_inline proc(lm: ^Label_Map, name: string) -> u32 {
id := u32(len(lm.labels))
append(&lm.labels, LABEL_UNDEFINED)
lm.names[name] = id
return id
}
// Define a previously reserved named label's position.
label_set :: #force_inline proc(lm: ^Label_Map, name: string, instructions: ^[dynamic]$T) {
lm.labels[lm.names[name]] = Label_Definition(len(instructions))
}
// -----------------------------------------------------------------------------
// Encoder helper: rewrite label_defs from instruction indices to byte
// offsets in place. Called between pass 1 (encoding) and pass 2
// (relocation resolution). Pure bookkeeping, arch-independent.
// -----------------------------------------------------------------------------
rewrite_label_defs_to_offsets :: #force_inline proc(
label_defs: []Label_Definition, inst_offsets: []u32,
) {
for &label in label_defs {
if label != LABEL_UNDEFINED {
inst_idx := u32(label)
if inst_idx < u32(len(inst_offsets)) {
label = Label_Definition(inst_offsets[inst_idx])
} else {
label = LABEL_UNDEFINED
}
}
}
}