Merge pull request #7362 from odin-lang/bill/rexcode

`core:rexcode` improvements
This commit is contained in:
gingerBill
2026-08-20 10:54:07 +02:00
committed by GitHub
43 changed files with 1728 additions and 1605 deletions

View File

@@ -284,7 +284,11 @@ end
local function do_test(isa)
local ok, out = run(odin_run(pkg(isa, "tests")))
local fails = out:match("([1-9]%d* failed)")
-- Match case-INSENSITIVELY: the x86 harness prints "5 FAILED" where the others print "5 failed",
-- so a lowercase-only pattern silently exempted the largest suite in the tree from its own gate.
-- (The harness now also exits non-zero, which is the check that should have been load-bearing all
-- along -- this one is the backstop for a suite that forgets to.)
local fails = out:lower():match("([1-9]%d* failed)")
if not ok or fails then return false, (fails or "test run failed").."\n"..out:sub(-400) end
local cases = out:match("(%d+ cases? validated)")
if not cases then

View File

@@ -142,9 +142,9 @@ x86.label_set(&lm, "done", &instructions)
result := x86.encode(instructions[:], lm.labels[:], code[:], &relocs, &errors)
// Printer wants idname; Label_Map stores nameid, so invert once.
id_to_name := make(map[u32]string, len(lm.names), context.temp_allocator)
for name, id in lm.names { id_to_name[id] = name }
x86.print(decoded_insts[:], decoded_info[:], lm.labels[:], label_names = &id_to_name)
names := make(x86.Label_Names, len(lm.names), context.temp_allocator) // BYTE OFFSET -> name
for name, id in lm.names { names[x86.Label_Offset(u32(lm.labels[id]))] = name }
x86.print(decoded_insts[:], decoded_info[:], lm.labels[:], label_names = &names)
```
## Driver script (`build.lua`)

View File

@@ -54,6 +54,8 @@ import "core:rexcode/isa"
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition
Label_Offset :: isa.Label_Offset
Label_Names :: isa.Label_Names
LABEL_UNDEFINED :: isa.LABEL_UNDEFINED
Label_Map :: isa.Label_Map

View File

@@ -70,7 +70,7 @@ sbprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
opts := options
if opts == nil {
@@ -78,13 +78,11 @@ sbprint :: proc(
opts = &defaults
}
offset_to_label: map[u32]u32
defer delete(offset_to_label)
for ld, id in label_defs {
if ld != LABEL_UNDEFINED {
offset_to_label[u32(ld)] = u32(id)
}
}
// Display-side label naming: numbers in ADDRESS order (independent of the internal ids'
// allocation order), caller names keyed by byte offset (isa.Label_Display).
display: isa.Label_Display
isa.label_display_init(&display, label_defs, label_names)
defer isa.label_display_destroy(&display)
for i in 0..<len(instructions) {
inst := &instructions[i]
@@ -93,16 +91,10 @@ sbprint :: proc(
offset = inst_info[i].offset
}
// Label preceding this instruction (if any)
if id, ok := offset_to_label[offset]; ok {
if label_names != nil {
if name, has := label_names[id]; has {
strings.write_string(sb, name)
strings.write_string(sb, ":\n")
}
} else {
fmt.sbprintf(sb, "L%d:\n", id)
}
// A displayable label at this offset — a definition, or a caller-named offset
if isa.label_display_at(&display, offset) {
isa.label_display_write(&display, sb, offset, opts.label_prefix)
strings.write_string(sb, ":\n")
}
if opts.show_offsets {
@@ -138,7 +130,7 @@ sbprint :: proc(
strings.write_string(sb, " ")
for k in 0..<inst.operand_count {
if k > 0 { strings.write_string(sb, ", ") }
write_operand(sb, &inst.ops[k], inst, offset, &offset_to_label, label_names, opts)
write_operand(sb, &inst.ops[k], inst, offset, &display, opts)
}
}
strings.write_string(sb, "\n")
@@ -269,7 +261,7 @@ sbprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sbprint(sb, instructions, inst_info, label_defs, tokens, options, label_names)
strings.write_byte(sb, '\n')
@@ -281,7 +273,7 @@ sbprintln :: proc(
print :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -290,7 +282,7 @@ print :: proc(
println :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -299,7 +291,7 @@ println :: proc(
aprint :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -309,7 +301,7 @@ aprint :: proc(
aprintln :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -319,7 +311,7 @@ aprintln :: proc(
tprint :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -328,7 +320,7 @@ tprint :: proc(
tprintln :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -338,7 +330,7 @@ tprintln :: proc(
bprint :: proc(
buf: []u8,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -348,7 +340,7 @@ bprint :: proc(
bprintln :: proc(
buf: []u8,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -358,7 +350,7 @@ bprintln :: proc(
fprint :: proc(
fd: ^os.File,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -368,7 +360,7 @@ fprint :: proc(
fprintln :: proc(
fd: ^os.File,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -378,7 +370,7 @@ fprintln :: proc(
wprint :: proc(
w: io.Writer,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -388,7 +380,7 @@ wprint :: proc(
wprintln :: proc(
w: io.Writer,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -460,8 +452,7 @@ write_operand :: proc(
op: ^Operand,
inst: ^Instruction,
offset: u32,
offset_to_label: ^map[u32]u32,
label_names: ^map[u32]string,
display: ^isa.Label_Display,
opts: ^Print_Options,
) {
switch op.kind {
@@ -482,14 +473,8 @@ write_operand :: proc(
case .RELATIVE:
// Resolve to label if possible
target := u32(i64(offset) + op.relative)
if id, ok := offset_to_label[target]; ok {
if label_names != nil {
if name, has := label_names[id]; has {
strings.write_string(sb, name)
return
}
}
fmt.sbprintf(sb, "L%d", id)
if isa.label_display_at(display, target) {
isa.label_display_write(display, sb, target, opts.label_prefix)
} else {
// raw absolute
fmt.sbprintf(sb, "0x%x", target)

View File

@@ -40,6 +40,8 @@ import "core:rexcode/isa"
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition
Label_Offset :: isa.Label_Offset
Label_Names :: isa.Label_Names
LABEL_UNDEFINED :: isa.LABEL_UNDEFINED
Label_Map :: isa.Label_Map

View File

@@ -79,7 +79,7 @@ sbprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
opts := options
if opts == nil {
@@ -87,13 +87,11 @@ sbprint :: proc(
opts = &defaults
}
offset_to_label: map[u32]u32
defer delete(offset_to_label)
for ld, id in label_defs {
if ld != LABEL_UNDEFINED {
offset_to_label[u32(ld)] = u32(id)
}
}
// Display-side label naming: numbers in ADDRESS order (independent of the internal ids'
// allocation order), caller names keyed by byte offset (isa.Label_Display).
display: isa.Label_Display
isa.label_display_init(&display, label_defs, label_names)
defer isa.label_display_destroy(&display)
for i in 0..<len(instructions) {
inst := &instructions[i]
@@ -102,8 +100,9 @@ sbprint :: proc(
offset = inst_info[i].offset
}
if label_id, has := offset_to_label[offset]; has {
write_label(sb, label_id, label_names, opts)
// A displayable label at this offset — a definition, or a caller-named offset?
if isa.label_display_at(&display, offset) {
isa.label_display_write(&display, sb, offset, opts.label_prefix)
strings.write_byte(sb, ':')
strings.write_string(sb, opts.separator)
}
@@ -130,7 +129,7 @@ sbprint :: proc(
strings.write_byte(sb, ',')
if opts.space_after_comma { strings.write_byte(sb, ' ') }
}
write_operand(sb, &inst.ops[slot], offset_to_label, label_names, opts)
write_operand(sb, &inst.ops[slot], &display, opts)
}
}
strings.write_string(sb, opts.separator)
@@ -144,7 +143,7 @@ sbprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sbprint(sb, instructions, inst_info, label_defs, tokens, options, label_names)
strings.write_byte(sb, '\n')
@@ -156,7 +155,7 @@ sbprintln :: proc(
print :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -165,7 +164,7 @@ print :: proc(
println :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -174,7 +173,7 @@ println :: proc(
aprint :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -184,7 +183,7 @@ aprint :: proc(
aprintln :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -194,7 +193,7 @@ aprintln :: proc(
tprint :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -203,7 +202,7 @@ tprint :: proc(
tprintln :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -213,7 +212,7 @@ tprintln :: proc(
bprint :: proc(
buf: []u8,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -223,7 +222,7 @@ bprint :: proc(
bprintln :: proc(
buf: []u8,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -233,7 +232,7 @@ bprintln :: proc(
fprint :: proc(
fd: ^os.File,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -243,7 +242,7 @@ fprint :: proc(
fprintln :: proc(
fd: ^os.File,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -253,7 +252,7 @@ fprintln :: proc(
wprint :: proc(
w: io.Writer,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -263,7 +262,7 @@ wprint :: proc(
wprintln :: proc(
w: io.Writer,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -390,11 +389,10 @@ write_register :: proc(sb: ^strings.Builder, r: Register, uppercase: bool) {
@(private="file")
write_operand :: proc(
sb: ^strings.Builder,
op: ^Operand,
offset_to_label: map[u32]u32,
label_names: ^map[u32]string,
opts: ^Print_Options,
sb: ^strings.Builder,
op: ^Operand,
display: ^isa.Label_Display,
opts: ^Print_Options,
) {
switch op.kind {
case .NONE:
@@ -449,8 +447,8 @@ write_operand :: proc(
case .RELATIVE:
target := u32(op.relative)
if id, has := offset_to_label[target]; has {
write_label(sb, id, label_names, opts)
if isa.label_display_at(display, target) {
isa.label_display_write(display, sb, target, opts.label_prefix)
} else {
isa.print_hex(sb, u64(target), opts)
}
@@ -512,23 +510,6 @@ write_memory :: proc(sb: ^strings.Builder, m: Memory, opts: ^Print_Options) {
}
}
@(private="file")
write_label :: proc(
sb: ^strings.Builder,
label_id: u32,
label_names: ^map[u32]string,
opts: ^Print_Options,
) {
if label_names != nil {
if name, has := label_names^[label_id]; has {
strings.write_string(sb, name)
return
}
}
strings.write_string(sb, opts.label_prefix)
write_decimal_u32(sb, label_id)
}
@(private="file")
write_decimal_u32 :: proc(sb: ^strings.Builder, v: u32) {
if v == 0 { strings.write_byte(sb, '0'); return }

View File

@@ -57,6 +57,7 @@ decode :: proc(
label_defs: ^[dynamic]Label_Definition,
errors: ^[dynamic]Error,
endianness: Endianness = .BIG,
features: Feature_Set = FEATURES_ALL,
) -> (byte_count: u32, ok: bool) {
n_bytes := u32(len(data))
if n_bytes & 3 != 0 {
@@ -73,7 +74,7 @@ decode :: proc(
inst: Instruction
info: Instruction_Info
entry_idx := decode_one_inline(word, byte_count, &inst, &info)
entry_idx := decode_one_inline(word, byte_count, &inst, &info, features)
if entry_idx < 0 {
append(errors, Error{inst_idx = byte_count, code = .INVALID_OPCODE})
@@ -114,7 +115,7 @@ decode :: proc(
@(private="file")
decode_one_inline :: #force_inline proc "contextless" (
word: u32, pc: u32, inst: ^Instruction, info: ^Instruction_Info,
word: u32, pc: u32, inst: ^Instruction, info: ^Instruction_Info, features: Feature_Set,
) -> int {
primary := (word >> 26) & 0x3F
@@ -135,6 +136,10 @@ decode_one_inline :: #force_inline proc "contextless" (
matched_idx := -1
for i in 0..<cnt {
e := &DECODE_ENTRIES[base + i]
// Skip an entry belonging to a disabled ISA variant, so a shared opcode resolves to the
// enabled instruction (e.g. opcode 0x37 -> `LD` when VFPU_PSP is not in `features`). Entries
// keep their most-specific-mask-first order, so the first ENABLED match is still the right one.
if e.feature not_in features { continue }
if (word & e.mask) == e.bits {
matched_idx = base + i
break

View File

@@ -38,6 +38,8 @@ import "core:rexcode/isa"
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition
Label_Offset :: isa.Label_Offset
Label_Names :: isa.Label_Names
LABEL_UNDEFINED :: isa.LABEL_UNDEFINED
Label_Map :: isa.Label_Map
// Relocation and Relocation_Type live in reloc.odin (per-arch by design).
@@ -77,6 +79,20 @@ Feature :: enum u8 {
VFPU_PSP, // PSP Allegrex Vector FPU
}
// A set of enabled ISA features, used to disambiguate the several MIPS variants that share the same
// encoding tables. Different targets reuse the same primary opcodes for different instructions — most
// notably opcode 0x37 is `LD` on 64-bit MIPS but the PSP Allegrex `vfim.s` (VFPU) — so a decoder that
// wants only one variant passes the matching set and the others are skipped. `decode` defaults to
// `FEATURES_ALL`, preserving the universal-table behavior for callers that don't care.
Feature_Set :: distinct bit_set[Feature; u32]
FEATURES_ALL :: ~Feature_Set{}
// The NEC VR4300 (Nintendo 64) and other classic 64-bit MIPS III cores: the MIPS I/II/III integer ISA
// plus COP0 (system control) and the COP1 FPU. Excludes MIPS IV+/R6 and every console-specific COP2
// extension (PS1 GTE, PS2 MMI/VU, PSP VFPU), so e.g. opcode 0x37 decodes as `LD`, not `vfim.s`.
FEATURES_MIPS_III :: Feature_Set{.MIPS_I, .MIPS_II, .MIPS_III, .COP0, .FPU}
Encoding_Flags :: bit_field u8 {
delay_slot: bool | 1, // branch with a one-instruction delay slot
likely: bool | 1, // *L variants: nullify delay slot if not taken

View File

@@ -138,7 +138,7 @@ Mnemonic :: enum u16 {
JIC, JIALC,
// R6 mul/div (replaces MULT/MULTU/DIV/DIVU; results in single GPR)
MUH, MULU, MUHU, MOD, MODU,
MUL_R6, MUH, MULU, MUHU, DIV_R6, MOD, DIVU_R6, MODU,
DMUL_R6, DMUH, DMULU, DMUHU,
DDIV_R6, DMOD, DDIVU_R6, DMODU,

View File

@@ -74,7 +74,7 @@ sbprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
opts := options
if opts == nil {
@@ -83,14 +83,11 @@ sbprint :: proc(
opts = &defaults
}
// Offset -> label_id index (single linear pass over label_defs).
offset_to_label: map[u32]u32
defer delete(offset_to_label)
for ld, id in label_defs {
if ld != LABEL_UNDEFINED {
offset_to_label[u32(ld)] = u32(id)
}
}
// Display-side label naming: numbers in ADDRESS order (independent of the internal ids'
// allocation order), caller names keyed by byte offset (isa.Label_Display).
display: isa.Label_Display
isa.label_display_init(&display, label_defs, label_names)
defer isa.label_display_destroy(&display)
for i in 0..<len(instructions) {
inst := &instructions[i]
@@ -99,9 +96,9 @@ sbprint :: proc(
offset = inst_info[i].offset
}
// Label at this offset?
if label_id, has := offset_to_label[offset]; has {
write_label(sb, label_id, label_names, opts)
// A displayable label at this offset — a definition, or a caller-named offset?
if isa.label_display_at(&display, offset) {
isa.label_display_write(&display, sb, offset, opts.label_prefix)
strings.write_byte(sb, ':')
strings.write_string(sb, opts.separator)
}
@@ -123,7 +120,7 @@ sbprint :: proc(
strings.write_byte(sb, ' ')
}
}
write_operand(sb, &inst.ops[slot], offset_to_label, label_names, opts)
write_operand(sb, &inst.ops[slot], &display, opts)
}
}
strings.write_string(sb, opts.separator)
@@ -137,7 +134,7 @@ sbprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sbprint(sb, instructions, inst_info, label_defs, tokens, options, label_names)
strings.write_byte(sb, '\n')
@@ -153,7 +150,7 @@ print :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -166,7 +163,7 @@ println :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -179,7 +176,7 @@ aprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -193,7 +190,7 @@ aprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -207,7 +204,7 @@ tprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -220,7 +217,7 @@ tprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -234,7 +231,7 @@ bprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -248,7 +245,7 @@ bprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -262,7 +259,7 @@ fprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -276,7 +273,7 @@ fprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -290,7 +287,7 @@ wprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -304,7 +301,7 @@ wprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -404,11 +401,10 @@ write_register :: proc(sb: ^strings.Builder, r: Register, uppercase: bool) {
@(private="file")
write_operand :: proc(
sb: ^strings.Builder,
op: ^Operand,
offset_to_label: map[u32]u32,
label_names: ^map[u32]string,
opts: ^Print_Options,
sb: ^strings.Builder,
op: ^Operand,
display: ^isa.Label_Display,
opts: ^Print_Options,
) {
switch op.kind {
case .NONE:
@@ -428,8 +424,8 @@ write_operand :: proc(
case .RELATIVE:
target := u32(op.relative)
if id, has := offset_to_label[target]; has {
write_label(sb, id, label_names, opts)
if isa.label_display_at(display, target) {
isa.label_display_write(display, sb, target, opts.label_prefix)
} else {
// No label discovered at this target -- fall back to absolute hex.
isa.print_hex(sb, u64(target), opts)
@@ -437,23 +433,6 @@ write_operand :: proc(
}
}
@(private="file")
write_label :: proc(
sb: ^strings.Builder,
label_id: u32,
label_names: ^map[u32]string,
opts: ^Print_Options,
) {
if label_names != nil {
if name, has := label_names^[label_id]; has {
strings.write_string(sb, name)
return
}
}
strings.write_string(sb, opts.label_prefix)
write_decimal_u32(sb, label_id)
}
@(private="file")
write_decimal_u32 :: proc(sb: ^strings.Builder, v: u32) {
if v == 0 {

View File

@@ -753,10 +753,13 @@ ENCODING_TABLE := #partial [Mnemonic][]Encoding{
// R6 mul/div: reuse SPECIAL functs 0x18-0x1F (which were MULT/MULTU/
// DIV/DIVU in pre-R6) with shamt distinguishing low half (0x02) from
// high half (0x03). Results land in rd, not HI/LO.
.MUL_R6 = { {.MUL_R6, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x00000098, 0xFC0007FF, .MIPS32_R6, {}} },
.MUH = { {.MUH, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x000000D8, 0xFC0007FF, .MIPS32_R6, {}} },
.MULU = { {.MULU, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x00000099, 0xFC0007FF, .MIPS32_R6, {}} },
.MUHU = { {.MUHU, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x000000D9, 0xFC0007FF, .MIPS32_R6, {}} },
.DIV_R6 = { {.DIV_R6, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x0000009A, 0xFC0007FF, .MIPS32_R6, {}} },
.MOD = { {.MOD, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x000000DA, 0xFC0007FF, .MIPS32_R6, {}} },
.DIVU_R6 = { {.DIVU_R6, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x0000009B, 0xFC0007FF, .MIPS32_R6, {}} },
.MODU = { {.MODU, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x000000DB, 0xFC0007FF, .MIPS32_R6, {}} },
// 64-bit R6 mul/div (functs 0x1C-0x1F).
.DMUL_R6 = { {.DMUL_R6, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x0000009C, 0xFC0007FF, .MIPS64_R6, {only_64=true}} },

View File

@@ -8,7 +8,7 @@ package rexcode_mips_generated
import lib "../.."
@(rodata)
DECODE_ENTRIES := [1026]lib.Decode_Entry{
DECODE_ENTRIES := [1029]lib.Decode_Entry{
{ .NOP, {.NONE,.NONE,.NONE,.NONE}, {.NONE,.NONE,.NONE,.NONE}, 0x00000000, 0xFFFFFFFF, .MIPS_I, {} },
{ .SSNOP, {.NONE,.NONE,.NONE,.NONE}, {.NONE,.NONE,.NONE,.NONE}, 0x00000040, 0xFFFFFFFF, .MIPS32_R1, {} },
{ .EHB, {.NONE,.NONE,.NONE,.NONE}, {.NONE,.NONE,.NONE,.NONE}, 0x000000C0, 0xFFFFFFFF, .MIPS32_R2, {} },
@@ -41,13 +41,16 @@ DECODE_ENTRIES := [1026]lib.Decode_Entry{
{ .DROTRV, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RT,.RS,.NONE}, 0x00000056, 0xFC0007FF, .MIPS64_R2, {only_64=true} },
{ .DSRAV, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RT,.RS,.NONE}, 0x00000017, 0xFC0007FF, .MIPS_III, {only_64=true} },
{ .MULT, {.GPR,.GPR,.NONE,.NONE}, {.RS,.RT,.NONE,.NONE}, 0x00000018, 0xFC00FFFF, .MIPS_I, {writes_hilo=true} },
{ .MUL_R6, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x00000098, 0xFC0007FF, .MIPS32_R6, {} },
{ .MUH, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x000000D8, 0xFC0007FF, .MIPS32_R6, {} },
{ .MULTU, {.GPR,.GPR,.NONE,.NONE}, {.RS,.RT,.NONE,.NONE}, 0x00000019, 0xFC00FFFF, .MIPS_I, {writes_hilo=true} },
{ .MULU, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x00000099, 0xFC0007FF, .MIPS32_R6, {} },
{ .MUHU, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x000000D9, 0xFC0007FF, .MIPS32_R6, {} },
{ .DIV, {.GPR,.GPR,.NONE,.NONE}, {.RS,.RT,.NONE,.NONE}, 0x0000001A, 0xFC00FFFF, .MIPS_I, {writes_hilo=true} },
{ .DIV_R6, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x0000009A, 0xFC0007FF, .MIPS32_R6, {} },
{ .MOD, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x000000DA, 0xFC0007FF, .MIPS32_R6, {} },
{ .DIVU, {.GPR,.GPR,.NONE,.NONE}, {.RS,.RT,.NONE,.NONE}, 0x0000001B, 0xFC00FFFF, .MIPS_I, {writes_hilo=true} },
{ .DIVU_R6, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x0000009B, 0xFC0007FF, .MIPS32_R6, {} },
{ .MODU, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x000000DB, 0xFC0007FF, .MIPS32_R6, {} },
{ .DMULT, {.GPR,.GPR,.NONE,.NONE}, {.RS,.RT,.NONE,.NONE}, 0x0000001C, 0xFC00FFFF, .MIPS_III, {only_64=true, writes_hilo=true} },
{ .DMUL_R6, {.GPR,.GPR,.GPR,.NONE}, {.RD,.RS,.RT,.NONE}, 0x0000009C, 0xFC0007FF, .MIPS64_R6, {only_64=true} },
@@ -1039,70 +1042,70 @@ DECODE_ENTRIES := [1026]lib.Decode_Entry{
@(rodata)
DECODE_INDEX_PRIMARY := [64]lib.Decode_Index{
0x00 = { 0, 84},
0x01 = { 84, 20},
0x02 = { 104, 1},
0x03 = { 105, 1},
0x04 = { 106, 1},
0x05 = { 107, 1},
0x06 = { 108, 2},
0x07 = { 110, 2},
0x08 = { 112, 2},
0x09 = { 114, 1},
0x0A = { 115, 1},
0x0B = { 116, 1},
0x0C = { 117, 1},
0x0D = { 118, 1},
0x0E = { 119, 1},
0x0F = { 120, 2},
0x10 = { 122, 17},
0x11 = { 139, 146},
0x12 = { 285, 36},
0x13 = { 321, 17},
0x14 = { 338, 1},
0x15 = { 339, 1},
0x16 = { 340, 4},
0x17 = { 344, 4},
0x18 = { 348, 14},
0x19 = { 362, 15},
0x1A = { 377, 1},
0x1B = { 378, 13},
0x1C = { 391, 115},
0x1D = { 506, 1},
0x1E = { 507, 230},
0x1F = { 737, 141},
0x20 = { 878, 1},
0x21 = { 879, 1},
0x22 = { 880, 1},
0x23 = { 881, 1},
0x24 = { 882, 1},
0x25 = { 883, 1},
0x26 = { 884, 1},
0x27 = { 885, 1},
0x28 = { 886, 1},
0x29 = { 887, 1},
0x2A = { 888, 1},
0x2B = { 889, 1},
0x2C = { 890, 1},
0x2D = { 891, 1},
0x2E = { 892, 1},
0x2F = { 893, 1},
0x30 = { 894, 1},
0x31 = { 895, 1},
0x32 = { 896, 3},
0x33 = { 899, 1},
0x34 = { 900, 63},
0x35 = { 963, 3},
0x36 = { 966, 5},
0x37 = { 971, 6},
0x38 = { 977, 1},
0x39 = { 978, 1},
0x3A = { 979, 3},
0x3B = { 982, 5},
0x3C = { 987, 27},
0x3D = {1014, 3},
0x3E = {1017, 5},
0x3F = {1022, 4},
0x00 = { 0, 87},
0x01 = { 87, 20},
0x02 = { 107, 1},
0x03 = { 108, 1},
0x04 = { 109, 1},
0x05 = { 110, 1},
0x06 = { 111, 2},
0x07 = { 113, 2},
0x08 = { 115, 2},
0x09 = { 117, 1},
0x0A = { 118, 1},
0x0B = { 119, 1},
0x0C = { 120, 1},
0x0D = { 121, 1},
0x0E = { 122, 1},
0x0F = { 123, 2},
0x10 = { 125, 17},
0x11 = { 142, 146},
0x12 = { 288, 36},
0x13 = { 324, 17},
0x14 = { 341, 1},
0x15 = { 342, 1},
0x16 = { 343, 4},
0x17 = { 347, 4},
0x18 = { 351, 14},
0x19 = { 365, 15},
0x1A = { 380, 1},
0x1B = { 381, 13},
0x1C = { 394, 115},
0x1D = { 509, 1},
0x1E = { 510, 230},
0x1F = { 740, 141},
0x20 = { 881, 1},
0x21 = { 882, 1},
0x22 = { 883, 1},
0x23 = { 884, 1},
0x24 = { 885, 1},
0x25 = { 886, 1},
0x26 = { 887, 1},
0x27 = { 888, 1},
0x28 = { 889, 1},
0x29 = { 890, 1},
0x2A = { 891, 1},
0x2B = { 892, 1},
0x2C = { 893, 1},
0x2D = { 894, 1},
0x2E = { 895, 1},
0x2F = { 896, 1},
0x30 = { 897, 1},
0x31 = { 898, 1},
0x32 = { 899, 3},
0x33 = { 902, 1},
0x34 = { 903, 63},
0x35 = { 966, 3},
0x36 = { 969, 5},
0x37 = { 974, 6},
0x38 = { 980, 1},
0x39 = { 981, 1},
0x3A = { 982, 3},
0x3B = { 985, 5},
0x3C = { 990, 27},
0x3D = {1017, 3},
0x3E = {1020, 5},
0x3F = {1025, 4},
}
@(rodata)
@@ -1130,154 +1133,154 @@ DECODE_INDEX_SPECIAL := [64]lib.Decode_Index{
0x15 = { 27, 1},
0x16 = { 28, 2},
0x17 = { 30, 1},
0x18 = { 31, 2},
0x19 = { 33, 3},
0x1A = { 36, 2},
0x1B = { 38, 2},
0x1C = { 40, 3},
0x1D = { 43, 3},
0x1E = { 46, 3},
0x1F = { 49, 3},
0x20 = { 52, 1},
0x21 = { 53, 1},
0x22 = { 54, 1},
0x23 = { 55, 1},
0x24 = { 56, 1},
0x25 = { 57, 1},
0x26 = { 58, 1},
0x27 = { 59, 1},
0x28 = { 60, 1},
0x29 = { 61, 1},
0x2A = { 62, 1},
0x2B = { 63, 1},
0x2C = { 64, 1},
0x2D = { 65, 1},
0x2E = { 66, 1},
0x2F = { 67, 1},
0x30 = { 68, 1},
0x31 = { 69, 1},
0x32 = { 70, 1},
0x33 = { 71, 1},
0x34 = { 72, 1},
0x35 = { 73, 1},
0x36 = { 74, 1},
0x37 = { 75, 1},
0x38 = { 76, 1},
0x3A = { 77, 2},
0x3B = { 79, 1},
0x3C = { 80, 1},
0x3E = { 81, 2},
0x3F = { 83, 1},
0x18 = { 31, 3},
0x19 = { 34, 3},
0x1A = { 37, 3},
0x1B = { 40, 3},
0x1C = { 43, 3},
0x1D = { 46, 3},
0x1E = { 49, 3},
0x1F = { 52, 3},
0x20 = { 55, 1},
0x21 = { 56, 1},
0x22 = { 57, 1},
0x23 = { 58, 1},
0x24 = { 59, 1},
0x25 = { 60, 1},
0x26 = { 61, 1},
0x27 = { 62, 1},
0x28 = { 63, 1},
0x29 = { 64, 1},
0x2A = { 65, 1},
0x2B = { 66, 1},
0x2C = { 67, 1},
0x2D = { 68, 1},
0x2E = { 69, 1},
0x2F = { 70, 1},
0x30 = { 71, 1},
0x31 = { 72, 1},
0x32 = { 73, 1},
0x33 = { 74, 1},
0x34 = { 75, 1},
0x35 = { 76, 1},
0x36 = { 77, 1},
0x37 = { 78, 1},
0x38 = { 79, 1},
0x3A = { 80, 2},
0x3B = { 82, 1},
0x3C = { 83, 1},
0x3E = { 84, 2},
0x3F = { 86, 1},
}
@(rodata)
DECODE_INDEX_REGIMM := [32]lib.Decode_Index{
0x00 = { 84, 1},
0x01 = { 85, 1},
0x02 = { 86, 1},
0x03 = { 87, 1},
0x06 = { 88, 1},
0x08 = { 89, 1},
0x09 = { 90, 1},
0x0A = { 91, 1},
0x0B = { 92, 1},
0x0C = { 93, 1},
0x0E = { 94, 1},
0x10 = { 95, 1},
0x11 = { 96, 1},
0x12 = { 97, 1},
0x13 = { 98, 1},
0x17 = { 99, 1},
0x18 = { 100, 1},
0x19 = { 101, 1},
0x1C = { 102, 1},
0x1E = { 103, 1},
0x00 = { 87, 1},
0x01 = { 88, 1},
0x02 = { 89, 1},
0x03 = { 90, 1},
0x06 = { 91, 1},
0x08 = { 92, 1},
0x09 = { 93, 1},
0x0A = { 94, 1},
0x0B = { 95, 1},
0x0C = { 96, 1},
0x0E = { 97, 1},
0x10 = { 98, 1},
0x11 = { 99, 1},
0x12 = { 100, 1},
0x13 = { 101, 1},
0x17 = { 102, 1},
0x18 = { 103, 1},
0x19 = { 104, 1},
0x1C = { 105, 1},
0x1E = { 106, 1},
}
@(rodata)
DECODE_INDEX_COP1 := [32]lib.Decode_Index{
0x00 = { 139, 1},
0x01 = { 140, 1},
0x02 = { 141, 1},
0x03 = { 142, 1},
0x04 = { 143, 1},
0x05 = { 144, 1},
0x06 = { 145, 1},
0x07 = { 146, 1},
0x08 = { 147, 4},
0x09 = { 151, 1},
0x0B = { 152, 1},
0x0D = { 153, 1},
0x0F = { 154, 1},
0x10 = { 155, 42},
0x11 = { 197, 42},
0x14 = { 239, 4},
0x15 = { 243, 2},
0x16 = { 245, 32},
0x18 = { 277, 1},
0x19 = { 278, 1},
0x1A = { 279, 1},
0x1B = { 280, 1},
0x1C = { 281, 1},
0x1D = { 282, 1},
0x1E = { 283, 1},
0x1F = { 284, 1},
0x00 = { 142, 1},
0x01 = { 143, 1},
0x02 = { 144, 1},
0x03 = { 145, 1},
0x04 = { 146, 1},
0x05 = { 147, 1},
0x06 = { 148, 1},
0x07 = { 149, 1},
0x08 = { 150, 4},
0x09 = { 154, 1},
0x0B = { 155, 1},
0x0D = { 156, 1},
0x0F = { 157, 1},
0x10 = { 158, 42},
0x11 = { 200, 42},
0x14 = { 242, 4},
0x15 = { 246, 2},
0x16 = { 248, 32},
0x18 = { 280, 1},
0x19 = { 281, 1},
0x1A = { 282, 1},
0x1B = { 283, 1},
0x1C = { 284, 1},
0x1D = { 285, 1},
0x1E = { 286, 1},
0x1F = { 287, 1},
}
@(rodata)
DECODE_INDEX_SPECIAL2 := [64]lib.Decode_Index{
0x00 = { 391, 2},
0x01 = { 393, 2},
0x02 = { 395, 1},
0x04 = { 396, 3},
0x05 = { 399, 2},
0x08 = { 401, 25},
0x09 = { 426, 26},
0x10 = { 452, 1},
0x11 = { 453, 1},
0x12 = { 454, 1},
0x13 = { 455, 1},
0x18 = { 456, 1},
0x19 = { 457, 1},
0x1A = { 458, 1},
0x1B = { 459, 1},
0x20 = { 460, 2},
0x21 = { 462, 2},
0x24 = { 464, 2},
0x25 = { 466, 2},
0x28 = { 468, 17},
0x29 = { 485, 8},
0x30 = { 493, 5},
0x31 = { 498, 1},
0x34 = { 499, 1},
0x36 = { 500, 1},
0x37 = { 501, 1},
0x3C = { 502, 1},
0x3E = { 503, 1},
0x3F = { 504, 2},
0x00 = { 394, 2},
0x01 = { 396, 2},
0x02 = { 398, 1},
0x04 = { 399, 3},
0x05 = { 402, 2},
0x08 = { 404, 25},
0x09 = { 429, 26},
0x10 = { 455, 1},
0x11 = { 456, 1},
0x12 = { 457, 1},
0x13 = { 458, 1},
0x18 = { 459, 1},
0x19 = { 460, 1},
0x1A = { 461, 1},
0x1B = { 462, 1},
0x20 = { 463, 2},
0x21 = { 465, 2},
0x24 = { 467, 2},
0x25 = { 469, 2},
0x28 = { 471, 17},
0x29 = { 488, 8},
0x30 = { 496, 5},
0x31 = { 501, 1},
0x34 = { 502, 1},
0x36 = { 503, 1},
0x37 = { 504, 1},
0x3C = { 505, 1},
0x3E = { 506, 1},
0x3F = { 507, 2},
}
@(rodata)
DECODE_INDEX_SPECIAL3 := [64]lib.Decode_Index{
0x00 = { 737, 2},
0x01 = { 739, 1},
0x02 = { 740, 1},
0x03 = { 741, 1},
0x04 = { 742, 1},
0x05 = { 743, 1},
0x06 = { 744, 1},
0x07 = { 745, 1},
0x0A = { 746, 3},
0x0C = { 749, 1},
0x0F = { 750, 8},
0x10 = { 758, 22},
0x11 = { 780, 15},
0x12 = { 795, 17},
0x13 = { 812, 22},
0x20 = { 834, 5},
0x24 = { 839, 4},
0x30 = { 843, 17},
0x38 = { 860, 17},
0x3B = { 877, 1},
0x00 = { 740, 2},
0x01 = { 742, 1},
0x02 = { 743, 1},
0x03 = { 744, 1},
0x04 = { 745, 1},
0x05 = { 746, 1},
0x06 = { 747, 1},
0x07 = { 748, 1},
0x0A = { 749, 3},
0x0C = { 752, 1},
0x0F = { 753, 8},
0x10 = { 761, 22},
0x11 = { 783, 15},
0x12 = { 798, 17},
0x13 = { 815, 22},
0x20 = { 837, 5},
0x24 = { 842, 4},
0x30 = { 846, 17},
0x38 = { 863, 17},
0x3B = { 880, 1},
}

File diff suppressed because it is too large Load Diff

View File

@@ -337,6 +337,33 @@ run_decoder_tests :: proc() {
true)
}
// ---- 5. Feature disambiguation: opcode 0x37 is `LD` (MIPS III) OR the PSP `vfim.s` (VFPU) --------
// The table is universal, so the shared opcode needs a feature set to resolve. `LD $ra, 0($sp)` must
// decode as LD under a MIPS III profile, but the PSP VFPU entry wins under the all-features default.
{
clear(&relocs); clear(&errors)
for i in 0..<len(code) { code[i] = 0 }
ebyte_count, _ := mips.encode(
[]mips.Instruction{mips.inst_r_m(.LD, mips.RA, mips.mem(mips.SP, 0))},
nil, code[:], &relocs, &errors)
dcheck_int ("disamb: encoded bytes", int(ebyte_count), 4)
decode_one :: proc(code: []u8, features: mips.Feature_Set) -> mips.Mnemonic {
insts: [dynamic]mips.Instruction
info: [dynamic]mips.Instruction_Info
labels: [dynamic]mips.Label_Definition
errs: [dynamic]mips.Error
defer { delete(insts); delete(info); delete(labels); delete(errs) }
mips.decode(code, nil, &insts, &info, &labels, &errs, .BIG, features)
return len(insts) > 0 ? insts[0].mnemonic : .INVALID
}
dcheck_mnem("disamb: MIPS_III -> LD", decode_one(code[:ebyte_count], mips.FEATURES_MIPS_III), .LD)
// The universal default lets the PSP VFPU entry take 0x37 — so it is specifically NOT LD, which is
// exactly why a MIPS III consumer must pass the profile.
dcheck_bool("disamb: ALL default != LD",
decode_one(code[:ebyte_count], mips.FEATURES_ALL) != .LD, true)
}
fmt.println()
fmt.printfln("==> decoder: %d passed, %d failed", dpasses, dfailures)
if dfailures > 0 { os.exit(1) }

View File

@@ -173,9 +173,9 @@ run_printer_tests :: proc() {
clear(&errors)
mips.decode(code[:byte_count], nil, &dec_insts, &dec_info, &dec_labels, &errors)
names: map[u32]string
names: mips.Label_Names // keyed by BYTE OFFSET
defer delete(names)
names[0] = "loop"
names[mips.Label_Offset(0)] = "loop"
out := mips.aprint(dec_insts[:], dec_info[:], dec_labels[:],
nil, nil, &names, context.temp_allocator)
pcheck("Named label",

View File

@@ -30,6 +30,8 @@ import "core:rexcode/isa"
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition
Label_Offset :: isa.Label_Offset
Label_Names :: isa.Label_Names
LABEL_UNDEFINED :: isa.LABEL_UNDEFINED
Label_Map :: isa.Label_Map
// Relocation and Relocation_Type live in reloc.odin (per-arch by design).

View File

@@ -61,7 +61,7 @@ sbprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
opts := options
if opts == nil {
@@ -69,13 +69,11 @@ sbprint :: proc(
opts = &defaults
}
offset_to_label: map[u32]u32
defer delete(offset_to_label)
for ld, id in label_defs {
if ld != LABEL_UNDEFINED {
offset_to_label[u32(ld)] = u32(id)
}
}
// Display-side label naming: numbers in ADDRESS order (independent of the internal ids'
// allocation order), caller names keyed by byte offset (isa.Label_Display).
display: isa.Label_Display
isa.label_display_init(&display, label_defs, label_names)
defer isa.label_display_destroy(&display)
for i in 0..<len(instructions) {
inst := &instructions[i]
@@ -84,8 +82,9 @@ sbprint :: proc(
offset = inst_info[i].offset
}
if label_id, has := offset_to_label[offset]; has {
write_label(sb, label_id, label_names, opts)
// A displayable label at this offset — a definition, or a caller-named offset?
if isa.label_display_at(&display, offset) {
isa.label_display_write(&display, sb, offset, opts.label_prefix)
strings.write_byte(sb, ':')
strings.write_string(sb, opts.separator)
}
@@ -107,7 +106,7 @@ sbprint :: proc(
strings.write_byte(sb, ' ')
}
}
write_operand(sb, &inst.ops[slot], offset_to_label, label_names, opts)
write_operand(sb, &inst.ops[slot], &display, opts)
}
}
strings.write_string(sb, opts.separator)
@@ -121,7 +120,7 @@ sbprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sbprint(sb, instructions, inst_info, label_defs, tokens, options, label_names)
strings.write_byte(sb, '\n')
@@ -133,7 +132,7 @@ sbprintln :: proc(
print :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -142,7 +141,7 @@ print :: proc(
println :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -151,7 +150,7 @@ println :: proc(
aprint :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -161,7 +160,7 @@ aprint :: proc(
aprintln :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -171,7 +170,7 @@ aprintln :: proc(
tprint :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -180,7 +179,7 @@ tprint :: proc(
tprintln :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -190,7 +189,7 @@ tprintln :: proc(
bprint :: proc(
buf: []u8,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -200,7 +199,7 @@ bprint :: proc(
bprintln :: proc(
buf: []u8,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -210,7 +209,7 @@ bprintln :: proc(
fprint :: proc(
fd: ^os.File,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -220,7 +219,7 @@ fprint :: proc(
fprintln :: proc(
fd: ^os.File,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -230,7 +229,7 @@ fprintln :: proc(
wprint :: proc(
w: io.Writer,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -240,7 +239,7 @@ wprint :: proc(
wprintln :: proc(
w: io.Writer,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -277,9 +276,8 @@ write_mnemonic :: proc(sb: ^strings.Builder, m: Mnemonic, uppercase: bool) {
write_operand :: proc(
sb: ^strings.Builder,
op: ^Operand,
offset_to_label: map[u32]u32,
label_names: ^map[u32]string,
opts: ^Print_Options,
display: ^isa.Label_Display,
opts: ^Print_Options,
) {
switch op.kind {
case .NONE:
@@ -297,8 +295,8 @@ write_operand :: proc(
case .RELATIVE:
target := u32(op.relative)
if id, has := offset_to_label[target]; has {
write_label(sb, id, label_names, opts)
if isa.label_display_at(display, target) {
isa.label_display_write(display, sb, target, opts.label_prefix)
} else {
// Fall back to absolute hex when the target isn't a known label.
// Use the operand size to decide width: size=1 -> $XX, size=2 -> $XXXX.
@@ -379,23 +377,6 @@ write_hex_value :: proc(sb: ^strings.Builder, v: u64, size: u8) {
}
}
@(private="file")
write_label :: proc(
sb: ^strings.Builder,
label_id: u32,
label_names: ^map[u32]string,
opts: ^Print_Options,
) {
if label_names != nil {
if name, has := label_names^[label_id]; has {
strings.write_string(sb, name)
return
}
}
strings.write_string(sb, opts.label_prefix)
write_decimal_u32(sb, label_id)
}
@(private="file")
write_decimal_u32 :: proc(sb: ^strings.Builder, v: u32) {
if v == 0 { strings.write_byte(sb, '0'); return }

View File

@@ -248,9 +248,9 @@ run_pipeline_tests :: proc() {
m.decode(code[:byte_count], nil,
&d_insts, &d_info, &d_labels, &errors, cpu = .CMOS_65C02)
names: map[u32]string
names: m.Label_Names // keyed by BYTE OFFSET
defer delete(names)
names[0] = "start"
names[m.Label_Offset(0)] = "start"
text := m.aprint(d_insts[:], d_info[:], d_labels[:],
nil, nil, &names, context.temp_allocator)

View File

@@ -33,6 +33,8 @@ import "core:rexcode/isa"
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition
Label_Offset :: isa.Label_Offset
Label_Names :: isa.Label_Names
LABEL_UNDEFINED :: isa.LABEL_UNDEFINED
Label_Map :: isa.Label_Map

View File

@@ -60,7 +60,7 @@ sbprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
opts := options
if opts == nil {
@@ -68,13 +68,11 @@ sbprint :: proc(
opts = &defaults
}
offset_to_label: map[u32]u32
defer delete(offset_to_label)
for ld, id in label_defs {
if ld != LABEL_UNDEFINED {
offset_to_label[u32(ld)] = u32(id)
}
}
// Display-side label naming: numbers in ADDRESS order (independent of the internal ids'
// allocation order), caller names keyed by byte offset (isa.Label_Display).
display: isa.Label_Display
isa.label_display_init(&display, label_defs, label_names)
defer isa.label_display_destroy(&display)
for i in 0..<len(instructions) {
inst := &instructions[i]
@@ -83,8 +81,9 @@ sbprint :: proc(
offset = inst_info[i].offset
}
if label_id, has := offset_to_label[offset]; has {
write_label(sb, label_id, label_names, opts)
// A displayable label at this offset — a definition, or a caller-named offset?
if isa.label_display_at(&display, offset) {
isa.label_display_write(&display, sb, offset, opts.label_prefix)
strings.write_byte(sb, ':')
strings.write_string(sb, opts.separator)
}
@@ -113,7 +112,7 @@ sbprint :: proc(
strings.write_byte(sb, '#')
write_hex_value(sb, u64(inst.ops[slot].immediate), 1)
} else {
write_operand(sb, &inst.ops[slot], offset_to_label, label_names, opts)
write_operand(sb, &inst.ops[slot], &display, opts)
}
}
}
@@ -128,7 +127,7 @@ sbprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sbprint(sb, instructions, inst_info, label_defs, tokens, options, label_names)
strings.write_byte(sb, '\n')
@@ -140,7 +139,7 @@ sbprintln :: proc(
print :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -149,7 +148,7 @@ print :: proc(
println :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -158,7 +157,7 @@ println :: proc(
aprint :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -168,7 +167,7 @@ aprint :: proc(
aprintln :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -178,7 +177,7 @@ aprintln :: proc(
tprint :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -187,7 +186,7 @@ tprint :: proc(
tprintln :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -197,7 +196,7 @@ tprintln :: proc(
bprint :: proc(
buf: []u8,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -207,7 +206,7 @@ bprint :: proc(
bprintln :: proc(
buf: []u8,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -217,7 +216,7 @@ bprintln :: proc(
fprint :: proc(
fd: ^os.File,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -227,7 +226,7 @@ fprint :: proc(
fprintln :: proc(
fd: ^os.File,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -237,7 +236,7 @@ fprintln :: proc(
wprint :: proc(
w: io.Writer,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -247,7 +246,7 @@ wprint :: proc(
wprintln :: proc(
w: io.Writer,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -276,9 +275,8 @@ write_mnemonic :: proc(sb: ^strings.Builder, m: Mnemonic, uppercase: bool) {
write_operand :: proc(
sb: ^strings.Builder,
op: ^Operand,
offset_to_label: map[u32]u32,
label_names: ^map[u32]string,
opts: ^Print_Options,
display: ^isa.Label_Display,
opts: ^Print_Options,
) {
switch op.kind {
case .NONE:
@@ -295,8 +293,8 @@ write_operand :: proc(
case .RELATIVE:
target := u32(op.relative)
if id, has := offset_to_label[target]; has {
write_label(sb, id, label_names, opts)
if isa.label_display_at(display, target) {
isa.label_display_write(display, sb, target, opts.label_prefix)
} else {
write_hex_value(sb, u64(target), op.size)
}
@@ -395,23 +393,6 @@ write_hex_value :: proc(sb: ^strings.Builder, v: u64, size: u8) {
}
}
@(private="file")
write_label :: proc(
sb: ^strings.Builder,
label_id: u32,
label_names: ^map[u32]string,
opts: ^Print_Options,
) {
if label_names != nil {
if name, has := label_names^[label_id]; has {
strings.write_string(sb, name)
return
}
}
strings.write_string(sb, opts.label_prefix)
write_decimal_u32(sb, label_id)
}
@(private="file")
write_decimal_u32 :: proc(sb: ^strings.Builder, v: u32) {
if v == 0 { strings.write_byte(sb, '0'); return }

View File

@@ -70,6 +70,8 @@ import "core:rexcode/isa"
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition
Label_Offset :: isa.Label_Offset
Label_Names :: isa.Label_Names
LABEL_UNDEFINED :: isa.LABEL_UNDEFINED
Label_Map :: isa.Label_Map

View File

@@ -57,7 +57,7 @@ sbprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
opts := options
if opts == nil {
@@ -65,13 +65,11 @@ sbprint :: proc(
opts = &defaults
}
offset_to_label: map[u32]u32
defer delete(offset_to_label)
for ld, id in label_defs {
if ld != LABEL_UNDEFINED {
offset_to_label[u32(ld)] = u32(id)
}
}
// Display-side label naming: numbers in ADDRESS order (independent of the internal ids'
// allocation order), caller names keyed by byte offset (isa.Label_Display).
display: isa.Label_Display
isa.label_display_init(&display, label_defs, label_names)
defer isa.label_display_destroy(&display)
for i in 0..<len(instructions) {
inst := &instructions[i]
@@ -80,15 +78,10 @@ sbprint :: proc(
offset = inst_info[i].offset
}
if id, ok := offset_to_label[offset]; ok {
if label_names != nil {
if name, has := label_names[id]; has {
strings.write_string(sb, name)
strings.write_string(sb, ":\n")
}
} else {
fmt.sbprintf(sb, "L%d:\n", id)
}
// A displayable label at this offset — a definition, or a caller-named offset
if isa.label_display_at(&display, offset) {
isa.label_display_write(&display, sb, offset, opts.label_prefix)
strings.write_string(sb, ":\n")
}
if opts.show_offsets {
@@ -107,7 +100,7 @@ sbprint :: proc(
strings.write_string(sb, " ")
for k in 0..<inst.operand_count {
if k > 0 { strings.write_string(sb, ", ") }
write_operand(sb, &inst.ops[k], offset, &offset_to_label, label_names, opts)
write_operand(sb, &inst.ops[k], offset, &display, opts)
}
}
strings.write_string(sb, "\n")
@@ -121,7 +114,7 @@ sbprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sbprint(sb, instructions, inst_info, label_defs, tokens, options, label_names)
strings.write_byte(sb, '\n')
@@ -137,7 +130,7 @@ print :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -150,7 +143,7 @@ println :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -163,7 +156,7 @@ aprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -177,7 +170,7 @@ aprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -191,7 +184,7 @@ tprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -204,7 +197,7 @@ tprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -218,7 +211,7 @@ bprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -232,7 +225,7 @@ bprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -246,7 +239,7 @@ fprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -260,7 +253,7 @@ fprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -274,7 +267,7 @@ wprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -288,7 +281,7 @@ wprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -361,9 +354,8 @@ write_operand :: proc(
sb: ^strings.Builder,
op: ^Operand,
inst_offset: u32,
offset_to_label: ^map[u32]u32,
label_names: ^map[u32]string,
opts: ^Print_Options,
display: ^isa.Label_Display,
opts: ^Print_Options,
) {
#partial switch op.kind {
case .NONE:
@@ -393,14 +385,8 @@ write_operand :: proc(
// (post-decode) or an absolute label target if a label_defs roundtrip
// has resolved it. Prefer label form when we can match the target.
target := u32(i32(inst_offset) + i32(op.relative))
if id, ok := offset_to_label[target]; ok {
if label_names != nil {
if name, has := label_names[id]; has {
strings.write_string(sb, name)
return
}
}
fmt.sbprintf(sb, "L%d", id)
if isa.label_display_at(display, target) {
isa.label_display_write(display, sb, target, opts.label_prefix)
} else {
// Print as PC-relative offset
if op.relative >= 0 {

View File

@@ -20,6 +20,8 @@ import "core:rexcode/isa"
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition
Label_Offset :: isa.Label_Offset
Label_Names :: isa.Label_Names
LABEL_UNDEFINED :: isa.LABEL_UNDEFINED
Label_Map :: isa.Label_Map

View File

@@ -35,7 +35,7 @@ sbprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
opts := options
if opts == nil {
@@ -43,13 +43,11 @@ sbprint :: proc(
opts = &defaults
}
offset_to_label: map[u32]u32
defer delete(offset_to_label)
for ld, id in label_defs {
if ld != LABEL_UNDEFINED {
offset_to_label[u32(ld)] = u32(id)
}
}
// Display-side label naming: numbers in ADDRESS order (independent of the internal ids'
// allocation order), caller names keyed by byte offset (isa.Label_Display).
display: isa.Label_Display
isa.label_display_init(&display, label_defs, label_names)
defer isa.label_display_destroy(&display)
for i in 0..<len(instructions) {
inst := &instructions[i]
@@ -58,15 +56,10 @@ sbprint :: proc(
offset = inst_info[i].offset
}
if id, ok := offset_to_label[offset]; ok {
if label_names != nil {
if name, has := label_names[id]; has {
strings.write_string(sb, name)
strings.write_string(sb, ":\n")
}
} else {
fmt.sbprintf(sb, "L%d:\n", id)
}
// A displayable label at this offset — a definition, or a caller-named offset
if isa.label_display_at(&display, offset) {
isa.label_display_write(&display, sb, offset, opts.label_prefix)
strings.write_string(sb, ":\n")
}
if opts.show_offsets {
@@ -85,7 +78,7 @@ sbprint :: proc(
strings.write_string(sb, " ")
for k in 0..<inst.operand_count {
if k > 0 { strings.write_string(sb, ", ") }
write_operand(sb, &inst.ops[k], offset, &offset_to_label, label_names, opts)
write_operand(sb, &inst.ops[k], offset, &display, opts)
}
}
strings.write_string(sb, "\n")
@@ -99,7 +92,7 @@ sbprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sbprint(sb, instructions, inst_info, label_defs, tokens, options, label_names)
strings.write_byte(sb, '\n')
@@ -115,7 +108,7 @@ print :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -128,7 +121,7 @@ println :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -141,7 +134,7 @@ aprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -155,7 +148,7 @@ aprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -169,7 +162,7 @@ tprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -182,7 +175,7 @@ tprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -196,7 +189,7 @@ bprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -210,7 +203,7 @@ bprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -224,7 +217,7 @@ fprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -238,7 +231,7 @@ fprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -252,7 +245,7 @@ wprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -266,7 +259,7 @@ wprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -369,9 +362,8 @@ write_operand :: proc(
sb: ^strings.Builder,
op: ^Operand,
inst_offset: u32,
offset_to_label: ^map[u32]u32,
label_names: ^map[u32]string,
opts: ^Print_Options,
display: ^isa.Label_Display,
opts: ^Print_Options,
) {
#partial switch op.kind {
case .NONE: // skip
@@ -389,14 +381,8 @@ write_operand :: proc(
}
case .RELATIVE:
target := u32(i32(inst_offset) + i32(op.relative))
if id, ok := offset_to_label[target]; ok {
if label_names != nil {
if name, has := label_names[id]; has {
strings.write_string(sb, name)
return
}
}
fmt.sbprintf(sb, "L%d", id)
if isa.label_display_at(display, target) {
isa.label_display_write(display, sb, target, opts.label_prefix)
} else {
if op.relative >= 0 {
fmt.sbprintf(sb, ".+%d", op.relative)

View File

@@ -137,3 +137,113 @@ print_decimal :: proc(sb: ^strings.Builder, value: u32) {
strings.write_byte(sb, buf[j])
}
}
// -----------------------------------------------------------------------------
// Label display (presentation-side naming)
// -----------------------------------------------------------------------------
//
// Internal label ids are allocation-order handles — the encoder's creation
// order, or the decoder's branch-DISCOVERY order (a loop's latch names the
// header before an earlier forward target). That order is an accident as far
// as a listing is concerned: naming labels by raw id makes the numbers appear
// out of order down the page. Display naming is therefore derived HERE, once
// per print call, independent of the ids:
//
// - every DEFINED label offset gets a display number in ASCENDING ADDRESS
// order, so a listing reads L0, L1, L2 … top to bottom;
// - the caller may name any BYTE OFFSET via `Label_Names`
// (`names[0] = "factorial"` heads the listing with the function name) —
// a named offset is displayable even when no Label_Definition points at
// it, since nothing need branch to a function's entry.
//
// `Label_Offset` is a distinct type so a map keyed by the OLD contract
// (internal label ids) fails to compile instead of silently mis-naming.
Label_Offset :: distinct u32
// Caller-supplied display names, keyed by byte offset into the printed region.
Label_Names :: map[Label_Offset]string
// Per-print-call display state: the sorted set of displayable label offsets
// (display number = index) plus the caller's names.
Label_Display :: struct {
offsets: [dynamic]u32, // ascending; a label's display number is its index here
names: ^Label_Names, // byte-offset-keyed caller names (nil = none)
}
label_display_init :: proc(display: ^Label_Display, label_defs: []Label_Definition, names: ^Label_Names, allocator := context.allocator) {
display.names = names
display.offsets = make([dynamic]u32, 0, len(label_defs), allocator)
insert_sorted :: proc(offsets: ^[dynamic]u32, offset: u32) {
lo, hi := 0, len(offsets)
for lo < hi {
mid := (lo + hi) / 2
if offsets[mid] < offset {
lo = mid + 1
} else {
hi = mid
}
}
if lo < len(offsets) && offsets[lo] == offset {
return // already displayable
}
append(offsets, 0)
copy(offsets[lo + 1:], offsets[lo:])
offsets[lo] = offset
}
for definition in label_defs {
if definition == LABEL_UNDEFINED {
continue
}
insert_sorted(&display.offsets, u32(definition))
}
if names != nil {
for offset in names^ {
insert_sorted(&display.offsets, u32(offset))
}
}
}
label_display_destroy :: proc(display: ^Label_Display) {
delete(display.offsets)
}
// Is there a displayable label at `offset` (a definition, or a caller-named offset)?
label_display_at :: proc(display: ^Label_Display, offset: u32) -> bool {
_, found := label_display_rank(display, offset)
return found
}
// The display number of the label at `offset` (its rank in address order).
label_display_rank :: proc(display: ^Label_Display, offset: u32) -> (rank: int, found: bool) {
lo, hi := 0, len(display.offsets)
for lo < hi {
mid := (lo + hi) / 2
if display.offsets[mid] < offset {
lo = mid + 1
} else {
hi = mid
}
}
if lo < len(display.offsets) && display.offsets[lo] == offset {
return lo, true
}
return 0, false
}
// Write the display name for the label at `offset`: the caller's name for that
// offset if one was supplied, else `<prefix><rank>` with the address-ordered rank.
label_display_write :: proc(display: ^Label_Display, sb: ^strings.Builder, offset: u32, prefix: string) {
if display.names != nil {
if name, has := display.names^[Label_Offset(offset)]; has {
strings.write_string(sb, name)
return
}
}
rank, found := label_display_rank(display, offset)
if !found {
rank = 0 // an undisplayable offset never reaches here from the printers; be lenient
}
strings.write_string(sb, prefix)
print_decimal(sb, u32(rank))
}

View File

@@ -34,6 +34,8 @@ import "core:rexcode/isa"
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition
Label_Offset :: isa.Label_Offset
Label_Names :: isa.Label_Names
LABEL_UNDEFINED :: isa.LABEL_UNDEFINED
Label_Map :: isa.Label_Map

View File

@@ -77,7 +77,7 @@ sbprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
opts := options
if opts == nil {
@@ -85,13 +85,11 @@ sbprint :: proc(
opts = &defaults
}
offset_to_label: map[u32]u32
defer delete(offset_to_label)
for ld, id in label_defs {
if ld != LABEL_UNDEFINED {
offset_to_label[u32(ld)] = u32(id)
}
}
// Display-side label naming: numbers in ADDRESS order (independent of the internal ids'
// allocation order), caller names keyed by byte offset (isa.Label_Display).
display: isa.Label_Display
isa.label_display_init(&display, label_defs, label_names)
defer isa.label_display_destroy(&display)
for i in 0..<len(instructions) {
inst := &instructions[i]
@@ -100,8 +98,9 @@ sbprint :: proc(
offset = inst_info[i].offset
}
if label_id, has := offset_to_label[offset]; has {
write_label(sb, label_id, label_names, opts)
// A displayable label at this offset — a definition, or a caller-named offset?
if isa.label_display_at(&display, offset) {
isa.label_display_write(&display, sb, offset, opts.label_prefix)
strings.write_byte(sb, ':')
strings.write_string(sb, opts.separator)
}
@@ -121,7 +120,7 @@ sbprint :: proc(
strings.write_byte(sb, ',')
if opts.space_after_comma { strings.write_byte(sb, ' ') }
}
write_operand(sb, &inst.ops[slot], inst.mnemonic, offset_to_label, label_names, opts)
write_operand(sb, &inst.ops[slot], inst.mnemonic, &display, opts)
}
}
strings.write_string(sb, opts.separator)
@@ -135,7 +134,7 @@ sbprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sbprint(sb, instructions, inst_info, label_defs, tokens, options, label_names)
strings.write_byte(sb, '\n')
@@ -147,7 +146,7 @@ sbprintln :: proc(
print :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -156,7 +155,7 @@ print :: proc(
println :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -165,7 +164,7 @@ println :: proc(
aprint :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -175,7 +174,7 @@ aprint :: proc(
aprintln :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -185,7 +184,7 @@ aprintln :: proc(
tprint :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -194,7 +193,7 @@ tprint :: proc(
tprintln :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -204,7 +203,7 @@ tprintln :: proc(
bprint :: proc(
buf: []u8,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -214,7 +213,7 @@ bprint :: proc(
bprintln :: proc(
buf: []u8,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -224,7 +223,7 @@ bprintln :: proc(
fprint :: proc(
fd: ^os.File,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -234,7 +233,7 @@ fprint :: proc(
fprintln :: proc(
fd: ^os.File,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -244,7 +243,7 @@ fprintln :: proc(
wprint :: proc(
w: io.Writer,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -254,7 +253,7 @@ wprint :: proc(
wprintln :: proc(
w: io.Writer,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -308,10 +307,9 @@ write_register :: proc(sb: ^strings.Builder, r: Register, uppercase: bool) {
write_operand :: proc(
sb: ^strings.Builder,
op: ^Operand,
mnemonic: Mnemonic,
offset_to_label: map[u32]u32,
label_names: ^map[u32]string,
opts: ^Print_Options,
mnemonic: Mnemonic,
display: ^isa.Label_Display,
opts: ^Print_Options,
) {
switch op.kind {
case .NONE:
@@ -335,31 +333,14 @@ write_operand :: proc(
case .RELATIVE:
target := u32(op.relative)
if id, has := offset_to_label[target]; has {
write_label(sb, id, label_names, opts)
if isa.label_display_at(display, target) {
isa.label_display_write(display, sb, target, opts.label_prefix)
} else {
isa.print_hex(sb, u64(target), opts)
}
}
}
@(private="file")
write_label :: proc(
sb: ^strings.Builder,
label_id: u32,
label_names: ^map[u32]string,
opts: ^Print_Options,
) {
if label_names != nil {
if name, has := label_names^[label_id]; has {
strings.write_string(sb, name)
return
}
}
strings.write_string(sb, opts.label_prefix)
write_decimal_u32(sb, label_id)
}
@(private="file")
write_decimal_u32 :: proc(sb: ^strings.Builder, v: u32) {
if v == 0 { strings.write_byte(sb, '0'); return }

View File

@@ -35,6 +35,8 @@ import "core:rexcode/isa"
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition
Label_Offset :: isa.Label_Offset
Label_Names :: isa.Label_Names
LABEL_UNDEFINED :: isa.LABEL_UNDEFINED
Label_Map :: isa.Label_Map
// Relocation and Relocation_Type live in reloc.odin (per-arch by design).

View File

@@ -72,7 +72,7 @@ sbprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
opts := options
if opts == nil {
@@ -80,13 +80,11 @@ sbprint :: proc(
opts = &defaults
}
offset_to_label: map[u32]u32
defer delete(offset_to_label)
for ld, id in label_defs {
if ld != LABEL_UNDEFINED {
offset_to_label[u32(ld)] = u32(id)
}
}
// Display-side label naming: numbers in ADDRESS order (independent of the internal ids'
// allocation order), caller names keyed by byte offset (isa.Label_Display).
display: isa.Label_Display
isa.label_display_init(&display, label_defs, label_names)
defer isa.label_display_destroy(&display)
for i in 0..<len(instructions) {
inst := &instructions[i]
@@ -95,8 +93,9 @@ sbprint :: proc(
offset = inst_info[i].offset
}
if label_id, has := offset_to_label[offset]; has {
write_label(sb, label_id, label_names, opts)
// A displayable label at this offset — a definition, or a caller-named offset?
if isa.label_display_at(&display, offset) {
isa.label_display_write(&display, sb, offset, opts.label_prefix)
strings.write_byte(sb, ':')
strings.write_string(sb, opts.separator)
}
@@ -118,7 +117,7 @@ sbprint :: proc(
strings.write_byte(sb, ' ')
}
}
write_operand(sb, &inst.ops[slot], offset_to_label, label_names, opts)
write_operand(sb, &inst.ops[slot], &display, opts)
}
}
strings.write_string(sb, opts.separator)
@@ -132,7 +131,7 @@ sbprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^isa.Label_Names = nil,
) {
sbprint(sb, instructions, inst_info, label_defs, tokens, options, label_names)
strings.write_byte(sb, '\n')
@@ -144,7 +143,7 @@ sbprintln :: proc(
print :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -153,7 +152,7 @@ print :: proc(
println :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -162,7 +161,7 @@ println :: proc(
aprint :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -172,7 +171,7 @@ aprint :: proc(
aprintln :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -182,7 +181,7 @@ aprintln :: proc(
tprint :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -191,7 +190,7 @@ tprint :: proc(
tprintln :: proc(
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -201,7 +200,7 @@ tprintln :: proc(
bprint :: proc(
buf: []u8,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -211,7 +210,7 @@ bprint :: proc(
bprintln :: proc(
buf: []u8,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) -> string {
sb := strings.builder_from_bytes(buf)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -221,7 +220,7 @@ bprintln :: proc(
fprint :: proc(
fd: ^os.File,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -231,7 +230,7 @@ fprint :: proc(
fprintln :: proc(
fd: ^os.File,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -241,7 +240,7 @@ fprintln :: proc(
wprint :: proc(
w: io.Writer,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -251,7 +250,7 @@ wprint :: proc(
wprintln :: proc(
w: io.Writer,
instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^isa.Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -344,11 +343,10 @@ write_register :: proc(sb: ^strings.Builder, r: Register, uppercase: bool, eleme
@(private="file")
write_operand :: proc(
sb: ^strings.Builder,
op: ^Operand,
offset_to_label: map[u32]u32,
label_names: ^map[u32]string,
opts: ^Print_Options,
sb: ^strings.Builder,
op: ^Operand,
display: ^isa.Label_Display,
opts: ^Print_Options,
) {
switch op.kind {
case .NONE:
@@ -381,31 +379,14 @@ write_operand :: proc(
case .RELATIVE:
target := u32(op.relative)
if id, has := offset_to_label[target]; has {
write_label(sb, id, label_names, opts)
if isa.label_display_at(display, target) {
isa.label_display_write(display, sb, target, opts.label_prefix)
} else {
isa.print_hex(sb, u64(target), opts)
}
}
}
@(private="file")
write_label :: proc(
sb: ^strings.Builder,
label_id: u32,
label_names: ^map[u32]string,
opts: ^Print_Options,
) {
if label_names != nil {
if name, has := label_names^[label_id]; has {
strings.write_string(sb, name)
return
}
}
strings.write_string(sb, opts.label_prefix)
write_decimal_u32(sb, label_id)
}
@(private="file")
write_decimal_u32 :: proc(sb: ^strings.Builder, v: u32) {
if v == 0 {

View File

@@ -437,49 +437,37 @@ decode_opcode :: proc(state: ^Decoder_State) -> (entry: ^Decode_Entry, vex_entry
idx, mand_66 = resolve_66(DECODE_INDEX_ESC_0F3A, opcode, prefix, state)
}
// If not found, try +r encoding (opcode with register in low 3 bits)
if idx.count == 0 && esc == .NONE {
base_opcode := opcode & 0xF8 // Mask off low 3 bits
idx = didx(DECODE_INDEX_LEGACY, prefix, base_opcode)
/* NOTHING AT THIS OPCODE -- RETRY AT THE +r BASE, where the register rides in the opcode's low
three bits (`50+rd`, `90+rd`, `B8+rd`, `0F C8+rd`, ...). The table stores ONE entry for the
base and the shared +r handler below reads the register out of `opcode & 7`.
// Check if this is actually an Op_R encoding
if idx.count == 0 {
return nil, nil, .INVALID_OPCODE
THIS IS NOT LEGACY-ONLY. Gating the retry on `esc == .NONE` is what made BSWAP undecodable:
`0F C8+rd` is the one +r instruction that lives behind an escape byte, so every `bswap` except
the one whose register happens to be 0 (`bswap rax` = `0F C8`, which lands on the entry
exactly) came back INVALID_OPCODE. Emission was always correct -- only reading it back failed.
The retry deliberately does NOT re-implement the +r decode; it only re-runs the LOOKUP and
falls through to the one handler. There used to be a second copy of that handler here, and it
had drifted from the original in three ways, each its own silent wrong answer: it tested only
the FIRST entry for OP_R (so every `xchg rAX, r` was rejected, 0x90's run having NOP sorted
ahead of XCHG), it passed `prefix` where the legacy row wants 0 (so `66 53`, `push bx`, was
rejected), and it read the operand size off `ops[0]` (which for XCHG is the implicit
accumulator, not the sized operand). All three had already been found and fixed once, in the
handler below -- none of the fixes reached the copy, because nothing made them one thing. */
retried_plus_r := false
if idx.count == 0 {
base_opcode := opcode & 0xF8 // mask off the register bits
switch esc {
case .NONE:
idx = didx(DECODE_INDEX_LEGACY, 0, base_opcode) // prefix 0: see the primary lookup above
case ._0F:
idx, mand_66 = resolve_66(DECODE_INDEX_ESC_0F, base_opcode, prefix, state)
case ._0F38:
idx, mand_66 = resolve_66(DECODE_INDEX_ESC_0F38, base_opcode, prefix, state)
case ._0F3A:
idx, mand_66 = resolve_66(DECODE_INDEX_ESC_0F3A, base_opcode, prefix, state)
}
if first := &LEGACY_DECODE_ENTRIES[idx.start]; first.enc[0] == .OP_R {
// Store the register number for later operand decoding
state.opcode_reg = opcode & 0x07
// For Op_R with multiple entries (e.g., PUSH/POP with R64 and R16),
// select based on prefix_66 and default_64 flag
if idx.count > 1 {
for i in 0..<int(idx.count) {
e := &LEGACY_DECODE_ENTRIES[int(idx.start) + i]
op0 := e.ops[0]
if state.prefix_66 {
if op0 == .R16 {
return e, nil, .NONE
}
} else {
is_64 := state.mode == ._64 && (e.flags.default_64 || (state.rex & 0x08 != 0))
if is_64 && op0 == .R64 {
return e, nil, .NONE
}
if !is_64 && op0 == .R32 {
return e, nil, .NONE
}
// i386: default_64 entries are the "default operand size" form,
// which is 32-bit; bytes encode the same as long-mode R64+default_64.
if state.mode == ._32 && op0 == .R64 && e.flags.default_64 {
return e, nil, .NONE
}
}
}
}
return first, nil, .NONE
}
return nil, nil, .INVALID_OPCODE
retried_plus_r = true
}
if idx.count == 0 {
@@ -497,6 +485,10 @@ decode_opcode :: proc(state: ^Decoder_State) -> (entry: ^Decode_Entry, vex_entry
for i in 0..<int(idx.count) {
if entry_has_opr(&LEGACY_DECODE_ENTRIES[int(idx.start) + i]) {
uses_op_r = true
// The DEFAULT answer for a +r opcode is the first +r entry, not the first entry
// outright: 0x90 sorts NOP ahead of XCHG, so should the size selection below ever
// fall through for an `xchg rAX, r`, `idx.start` would answer NOP.
first_entry = &LEGACY_DECODE_ENTRIES[int(idx.start) + i]
break
}
}
@@ -548,6 +540,14 @@ decode_opcode :: proc(state: ^Decoder_State) -> (entry: ^Decode_Entry, vex_entry
return first_entry, nil, .NONE
}
/* The base-opcode retry above is ONLY meaningful for a +r form, and we now know this is not one.
Falling through with a masked opcode would decode some unrelated neighbour 0x0E would come
back as the 0x08 entry (OR) which is a wrong instruction reported confidently, strictly worse
than saying the byte is not one we know. */
if retried_plus_r {
return nil, nil, .INVALID_OPCODE
}
// Multi-entry opcode: disambiguate by the ModR/M byte (fixed byte / ST(i)
// range / /digit) and operand-size state.
if idx.count > 1 {

View File

@@ -117,19 +117,26 @@ encode :: proc(
#partial switch op.kind {
case .REGISTER:
// R8-R15, XMM8-31, YMM8-31, ZMM8-31 require REX/VEX/EVEX extension.
if reg_needs_rex(op.reg) { invalid = true; break }
if reg_needs_rex(op.reg) {
invalid = true
break
}
// SPL/BPL/SIL/DIL (REG_GPR8 hw 4-7) don't exist in i386;
// those encodings decode as AH/CH/DH/BH there. Users
// wanting high-byte regs should use REG_GPR8H (AH..BH).
if reg_class(op.reg) == REG_GPR8 {
hw := reg_hw(op.reg)
if hw >= 4 && hw <= 7 { invalid = true; break }
if hw >= 4 && hw <= 7 {
invalid = true
break
}
}
case .MEMORY:
m := op.mem
if (mem_has_base(m) && m.base_ext) ||
(mem_has_index(m) && m.index_ext) {
invalid = true; break
invalid = true
break
}
}
}
@@ -222,17 +229,26 @@ encode :: proc(
// only the explicit slots (skipping implicit). For non-implicit forms
// the two are identical (operand_count == total), so nothing changes.
total_form_ops := 0
for op in enc.ops { if op == .NONE { break }; total_form_ops += 1 }
for op in enc.ops {
if op == .NONE {
break
}
total_form_ops += 1
}
fully_explicit := int(inst.operand_count) == total_form_ops
user_idx := 0
for op, i in enc.ops {
if op == .NONE { break }
if op == .NONE {
break
}
uop: ^Operand
if fully_explicit {
uop = &inst.ops[i]
} else if !is_implicit_op_inline(op) {
if user_idx < int(inst.operand_count) { uop = &inst.ops[user_idx] }
if user_idx < int(inst.operand_count) {
uop = &inst.ops[user_idx]
}
user_idx += 1
}
if uop != nil {
@@ -309,8 +325,14 @@ encode :: proc(
#partial switch enc.flags.vex_type{
case .VEX:
// VEX prefix encoding
r: u8 = 1; x: u8 = 1; b: u8 = 1
vvvv: u8 = 0xF; l: u8 = 0; pp: u8 = 0; mmmmm: u8 = 1; w: u8 = 0
r: u8 = 1
x: u8 = 1
b: u8 = 1
vvvv: u8 = 0xF
l: u8 = 0
pp: u8 = 0
mmmmm: u8 = 1
w: u8 = 0
#partial switch enc.flags.esc {
case ._0F: mmmmm = 1
@@ -336,7 +358,9 @@ encode :: proc(
// contributions, gate by kind, clear the inverted bit via AND-mask).
for enc_type, i in enc.enc {
user_op := user_ops[i]
if user_op == nil { continue }
if user_op == nil {
continue
}
is_reg := user_op.kind == .REGISTER
is_mem := user_op.kind == .MEMORY
@@ -371,9 +395,19 @@ encode :: proc(
case .EVEX:
// EVEX prefix encoding (4 bytes)
r: u8 = 1; x: u8 = 1; b: u8 = 1; rr: u8 = 1
mm: u8 = 1; w: u8 = 0; vvvv: u8 = 0xF; pp: u8 = 0
z: u8 = 0; ll: u8 = 0; bb: u8 = 0; vvv: u8 = 1; aaa: u8 = 0
r: u8 = 1
x: u8 = 1
b: u8 = 1
rr: u8 = 1
mm: u8 = 1
w: u8 = 0
vvvv: u8 = 0xF
pp: u8 = 0
z: u8 = 0
ll: u8 = 0
bb: u8 = 0
vvv: u8 = 1
aaa: u8 = 0
#partial switch enc.flags.esc {
case ._0F: mm = 1
@@ -398,7 +432,9 @@ encode :: proc(
for i in 0..<4 {
user_op := user_ops[i]
if user_op == nil { continue }
if user_op == nil {
continue
}
is_reg := user_op.kind == .REGISTER
is_mem := user_op.kind == .MEMORY
@@ -511,10 +547,12 @@ encode :: proc(
out[pos] = 0x0F
pos += 1
case ._0F38:
out[pos] = 0x0F; out[pos+1] = 0x38
out[pos+0] = 0x0F
out[pos+1] = 0x38
pos += 2
case ._0F3A:
out[pos] = 0x0F; out[pos+1] = 0x3A
out[pos+0] = 0x0F
out[pos+1] = 0x3A
pos += 2
}
}
@@ -653,7 +691,10 @@ encode :: proc(
// bytes are written past the real size.
if disp_is_label {
append(&pending_relocations, Relocation{byte_count + pos, disp_label_id, 0, .REL32, 4, u16(instruction_index)})
out[pos] = 0; out[pos+1] = 0; out[pos+2] = 0; out[pos+3] = 0
out[pos+0] = 0
out[pos+1] = 0
out[pos+2] = 0
out[pos+3] = 0
pos += 4
} else {
for _ in 0..<displacement_size {
@@ -679,8 +720,10 @@ encode :: proc(
}
// --- Immediate(s), in operand-slot order (ENTER = C8 has two: IMM16 then IMM8). ---
for slot in 0 ..< 4 {
if user_ops[slot] == nil { continue }
for slot in 0..<4 {
if user_ops[slot] == nil {
continue
}
user_op := user_ops[slot]
#partial switch enc.enc[slot] {
case .IB:
@@ -697,19 +740,26 @@ encode :: proc(
case .IW:
if user_op.kind == .IMMEDIATE {
v := u16(user_op.immediate)
out[pos] = u8(v); out[pos+1] = u8(v >> 8)
out[pos+0] = u8(v)
out[pos+1] = u8(v >> 8)
pos += 2
}
case .ID:
#partial switch user_op.kind {
case .IMMEDIATE:
v := u32(user_op.immediate)
out[pos] = u8(v); out[pos+1] = u8(v >> 8); out[pos+2] = u8(v >> 16); out[pos+3] = u8(v >> 24)
out[pos+0] = u8(v)
out[pos+1] = u8(v >> 8)
out[pos+2] = u8(v >> 16)
out[pos+3] = u8(v >> 24)
pos += 4
case .RELATIVE:
label_id := u32(user_op.relative)
append(&pending_relocations, Relocation{byte_count + pos, label_id, 0, .REL32, 4, u16(instruction_index)})
out[pos] = 0; out[pos+1] = 0; out[pos+2] = 0; out[pos+3] = 0
out[pos+0] = 0
out[pos+1] = 0
out[pos+2] = 0
out[pos+3] = 0
pos += 4
}
case .IQ:
@@ -718,13 +768,25 @@ encode :: proc(
// movabs reg, <label>: placeholder imm64 + ABS64 relocation.
label_id := u32(user_op.immediate)
append(&pending_relocations, Relocation{byte_count + pos, label_id, 0, .ABS64, 8, u16(instruction_index)})
out[pos] = 0; out[pos+1] = 0; out[pos+2] = 0; out[pos+3] = 0
out[pos+4] = 0; out[pos+5] = 0; out[pos+6] = 0; out[pos+7] = 0
out[pos+0] = 0
out[pos+1] = 0
out[pos+2] = 0
out[pos+3] = 0
out[pos+4] = 0
out[pos+5] = 0
out[pos+6] = 0
out[pos+7] = 0
pos += 8
} else {
v := u64(user_op.immediate)
out[pos] = u8(v); out[pos+1] = u8(v >> 8); out[pos+2] = u8(v >> 16); out[pos+3] = u8(v >> 24)
out[pos+4] = u8(v >> 32); out[pos+5] = u8(v >> 40); out[pos+6] = u8(v >> 48); out[pos+7] = u8(v >> 56)
out[pos+0] = u8(v)
out[pos+1] = u8(v >> 8)
out[pos+2] = u8(v >> 16)
out[pos+3] = u8(v >> 24)
out[pos+4] = u8(v >> 32)
out[pos+5] = u8(v >> 40)
out[pos+6] = u8(v >> 48)
out[pos+7] = u8(v >> 56)
pos += 8
}
}
@@ -805,7 +867,9 @@ bmask :: #force_inline proc "contextless" (b: bool) -> u8 {
encoding_matches_inline :: proc "contextless" (inst: ^Instruction, enc: ^Encoding, mode: Mode) -> bool {
// Mode gate: skip i386-only encodings (short-form INC/DEC at 0x40-0x4F)
// when not in Mode._32.
if enc.flags.mode_32_only && mode != ._32 { return false }
if enc.flags.mode_32_only && mode != ._32 {
return false
}
// PUSH/POP FS/GS: the segment operand is fixed by the opcode (0F A0/A1 -> FS,
// 0F A8/A9 -> GS), so a form only matches when the user's segment agrees --
@@ -820,8 +884,10 @@ encoding_matches_inline :: proc "contextless" (inst: ^Instruction, enc: ^Encodin
explicit_count := enc.flags.explicit_count
if !enc.flags.has_implicit {
if inst.operand_count != explicit_count { return false }
for i in 0 ..< explicit_count {
if inst.operand_count != explicit_count {
return false
}
for i in 0..<explicit_count {
eff := mode_rewrite_op_type(enc.ops[i], mode, enc.flags.default_64)
operand_matches_inline(&inst.ops[i], eff) or_return
}
@@ -831,7 +897,9 @@ encoding_matches_inline :: proc "contextless" (inst: ^Instruction, enc: ^Encodin
// Total operand slots (implicit + explicit) in this form.
total_ops: u8 = 0
for op_type in enc.ops {
if op_type == .NONE { break }
if op_type == .NONE {
break
}
total_ops += 1
}
@@ -845,9 +913,13 @@ encoding_matches_inline :: proc "contextless" (inst: ^Instruction, enc: ^Encodin
// path maps slots positionally for this same operand_count == total_ops case.)
if inst.operand_count == total_ops {
for op_type, i in enc.ops {
if op_type == .NONE { break }
if op_type == .NONE {
break
}
if is_implicit_op_inline(op_type) {
if !implicit_operand_matches(&inst.ops[i], op_type) { return false }
if !implicit_operand_matches(&inst.ops[i], op_type) {
return false
}
} else {
eff := mode_rewrite_op_type(op_type, mode, enc.flags.default_64)
operand_matches_inline(&inst.ops[i], eff) or_return
@@ -858,13 +930,21 @@ encoding_matches_inline :: proc "contextless" (inst: ^Instruction, enc: ^Encodin
// Implicit operand(s) omitted by the caller (hand-built `add imm`, `shl rm`):
// operand count must match the explicit-only count; match those in order.
if inst.operand_count != explicit_count { return false }
if inst.operand_count != explicit_count {
return false
}
user_idx: u8 = 0
for op_type in enc.ops {
if op_type == .NONE { break }
if is_implicit_op_inline(op_type) { continue }
if op_type == .NONE {
break
}
if is_implicit_op_inline(op_type) {
continue
}
if user_idx >= inst.operand_count { return false }
if user_idx >= inst.operand_count {
return false
}
effective_op_type := mode_rewrite_op_type(op_type, mode, enc.flags.default_64)
operand_matches_inline(&inst.ops[user_idx], effective_op_type) or_return
user_idx += 1
@@ -918,8 +998,10 @@ operand_matches_inline :: #force_inline proc "contextless" (op: ^Operand, op_typ
case .IMMEDIATE: return imm_matches_inline(op, op_type)
case .RELATIVE:
// Respect user's size preference: size=1 -> REL8, size=4 -> REL32
if op.size == 1 { return op_type == .REL8 }
if op.size == 4 { return op_type == .REL32 }
switch op.size {
case 1: return op_type == .REL8
case 4: return op_type == .REL32
}
// Default: accept either
return op_type == .REL8 || op_type == .REL32
}

View File

@@ -19,3 +19,5 @@ import "core:rexcode/isa"
Label_Definition :: isa.Label_Definition
Label_Map :: isa.Label_Map
LABEL_UNDEFINED :: isa.LABEL_UNDEFINED
Label_Offset :: isa.Label_Offset
Label_Names :: isa.Label_Names

View File

@@ -281,7 +281,7 @@ sbprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil, // Optional: for named label output (id → name)
label_names: ^Label_Names = nil, // Optional: for named label output (id → name)
) {
options := options != nil ? options^ : DEFAULT_PRINT_OPTIONS
@@ -299,30 +299,25 @@ sbprint :: proc(
}
}
// Display-side label naming: numbers in ADDRESS order (independent of the internal ids'
// allocation order), caller names keyed by byte offset (isa.Label_Display).
display: isa.Label_Display
isa.label_display_init(&display, label_defs, label_names)
defer isa.label_display_destroy(&display)
for &inst, instruction_index in instructions {
info := &inst_info[instruction_index]
// Check if there's a label at this offset
for label_def, label_id in label_defs {
if label_def != LABEL_UNDEFINED && u32(label_def) == info.offset {
// Print label definition
start := strings.builder_len(sb^)
name: string; ok: bool
if label_names != nil { name, ok = label_names^[u32(label_id)] }
if ok {
strings.write_string(sb, name)
} else {
strings.write_string(sb, options.label_prefix)
print_decimal(sb, u32(label_id))
}
strings.write_byte(sb, ':')
emit_token(tokens, sb, .LABEL_DEF, 0xFFFF, start)
// A displayable label at this offset — a definition, or a caller-named offset
if isa.label_display_at(&display, info.offset) {
start := strings.builder_len(sb^)
isa.label_display_write(&display, sb, info.offset, options.label_prefix)
strings.write_byte(sb, ':')
emit_token(tokens, sb, .LABEL_DEF, 0xFFFF, start)
start = strings.builder_len(sb^)
strings.write_string(sb, options.separator)
emit_token(tokens, sb, .NEWLINE, 0xFFFF, start)
break
}
start = strings.builder_len(sb^)
strings.write_string(sb, options.separator)
emit_token(tokens, sb, .NEWLINE, 0xFFFF, start)
}
// Print offset if requested, otherwise indent
@@ -383,31 +378,13 @@ sbprint :: proc(
write_memory_with_tokens(sb, op.mem, &options, tokens, u16(instruction_index))
case .RELATIVE:
// Compute absolute target and check if it matches a label
// Compute absolute target and name it if a displayable label sits there
inst_end := i64(info.offset) + i64(inst.length)
target := inst_end + op.relative
// Find label at this target
found_label: u32 = 0
found_label_valid := false
for lbl_id in 0..<u32(len(label_defs)) {
if label_defs[lbl_id] != LABEL_UNDEFINED && i64(label_defs[lbl_id]) == target {
found_label = lbl_id
found_label_valid = true
break
}
}
if found_label_valid {
if target >= 0 && target <= i64(max(u32)) && isa.label_display_at(&display, u32(target)) {
start := strings.builder_len(sb^)
name: string; ok: bool
if label_names != nil { name, ok = label_names^[found_label] }
if ok {
strings.write_string(sb, name)
} else {
strings.write_string(sb, options.label_prefix)
print_decimal(sb, found_label)
}
isa.label_display_write(&display, sb, u32(target), options.label_prefix)
emit_token(tokens, sb, .LABEL_REF, u16(instruction_index), start)
} else {
start := strings.builder_len(sb^)
@@ -557,7 +534,7 @@ sbprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^Label_Names = nil,
) {
#force_inline sbprint(sb, instructions, inst_info, label_defs, tokens, options, label_names)
strings.write_byte(sb, '\n')
@@ -570,7 +547,7 @@ print :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
#force_inline sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -585,7 +562,7 @@ println :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
#force_inline sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -600,7 +577,7 @@ aprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -616,7 +593,7 @@ aprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^Label_Names = nil,
allocator := context.allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -632,7 +609,7 @@ tprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^Label_Names = nil,
allocator := context.temp_allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -648,7 +625,7 @@ tprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^Label_Names = nil,
allocator := context.temp_allocator,
) -> string {
sb := strings.builder_make(allocator)
@@ -665,7 +642,7 @@ bprint :: #force_inline proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^Label_Names = nil,
) -> string {
sb := strings.builder_from_slice(buf)
#force_inline sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -681,7 +658,7 @@ bprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^Label_Names = nil,
) -> string {
sb := strings.builder_from_slice(buf)
#force_inline sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -697,7 +674,7 @@ fprint :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
#force_inline sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -713,7 +690,7 @@ fprintln :: proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
#force_inline sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -729,7 +706,7 @@ wprint :: #force_inline proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
@@ -745,7 +722,7 @@ wprintln :: #force_inline proc(
label_defs: []Label_Definition,
tokens: ^[dynamic]Token = nil,
options: ^Print_Options = nil,
label_names: ^map[u32]string = nil,
label_names: ^Label_Names = nil,
) {
sb := strings.builder_make(context.temp_allocator)
sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names)

View File

@@ -2943,6 +2943,24 @@ run_decode_only_tests :: proc() {
{name = "decode: sse", test_type = .Decode_Only, input_code = {0x0F, 0x57, 0xC0, 0x0F, 0x28, 0xC1, 0x0F, 0x58, 0xC2}},
{name = "decode: vex", test_type = .Decode_Only, input_code = {0xC5, 0xF8, 0x57, 0xC0, 0xC5, 0xF8, 0x28, 0xC1}},
{name = "decode: call/jmp", test_type = .Decode_Only, input_code = {0xE8, 0x00, 0x00, 0x00, 0x00, 0xEB, 0xF9}},
/* +r FORMS, where the register rides in the opcode's low three bits. The decoder finds these
by retrying the lookup at `opcode & 0xF8`, and that retry had three holes, each of which
made a perfectly ordinary instruction undecodable while emission stayed correct.
BSWAP is `0F C8+rd` -- the ONE +r instruction behind an escape byte. The retry used to be
gated on there being no escape byte, so only `bswap eax`/`bswap rax` (register 0, landing on
the table entry exactly) decoded and the other seven registers were INVALID_OPCODE. */
{name = "decode: bswap +r (0F C8+rd)", test_type = .Decode_Only, input_code = {0x0F, 0xC8, 0x0F, 0xC9, 0x0F, 0xCC, 0x0F, 0xCF}},
{name = "decode: bswap +r rex.w", test_type = .Decode_Only, input_code = {0x48, 0x0F, 0xC8, 0x48, 0x0F, 0xCF}},
{name = "decode: bswap +r rex.b", test_type = .Decode_Only, input_code = {0x41, 0x0F, 0xC8, 0x49, 0x0F, 0xCF}},
// XCHG rAX,r is `90+rd`, and 0x90's run has NOP sorted ahead of it -- the retry used to test
// only the FIRST entry for a +r form, so every `xchg rAX, r` was rejected.
{name = "decode: xchg rAX,r (90+rd)", test_type = .Decode_Only, input_code = {0x90, 0x91, 0x97, 0x48, 0x91, 0x66, 0x91}},
// The legacy +r families under an operand-size prefix: the retry passed the 0x66 row, where
// the legacy table wants row 0 (for legacy opcodes 0x66 is operand size, not part of identity).
{name = "decode: +r with 66 prefix", test_type = .Decode_Only, input_code = {0x66, 0x53, 0x66, 0x5B, 0x66, 0xB9, 0x00, 0x00}},
{name = "decode: push/pop/mov +r", test_type = .Decode_Only, input_code = {0x53, 0x5B, 0x41, 0x54, 0x41, 0x5C, 0xB9, 0x00, 0x00, 0x00, 0x00}},
}
for t in tests { run_test(t) }
}
@@ -3010,10 +3028,11 @@ run_label_map_tests :: proc() {
x86.decode(code_buf[:byte_count], nil, &decoded_insts, &decoded_info, &decoded_labels, &decode_errors)
// Print with named labels (printer wants id→name; Label_Map stores name→id).
id_to_name := make(map[u32]string, len(lm.names), context.temp_allocator)
for name, id in lm.names { id_to_name[id] = name }
output := x86.tprint(decoded_insts[:], decoded_info[:], lm.labels[:], label_names=&id_to_name)
// Print with named labels (printer wants BYTE OFFSET → name; Label_Map stores name → id, and
// after encode each id's Label_Definition holds its byte offset).
names := make(x86.Label_Names, len(lm.names), context.temp_allocator)
for name, id in lm.names { names[x86.Label_Offset(u32(lm.labels[id]))] = name }
output := x86.tprint(decoded_insts[:], decoded_info[:], lm.labels[:], label_names=&names)
// Verify output contains named labels
// Note: JNZ and JNE are the same instruction, decoder may output either
@@ -3581,4 +3600,13 @@ main :: proc() {
run_benchmarks()
print_summary()
/* FAIL THE PROCESS WHEN TESTS FAILED. Without this the binary exits 0 no matter what, and an exit
code is the only thing a runner can rely on -- `build.lua` looked for the words "N failed" in the
output instead, which the summary here prints as "N FAILED", so it never matched. Two independent
holes, both open, meant the x86 suite could fail every case it has and the build still reported
PASS. Found when five deliberately-broken decode cases came back green. */
if g_stats.failed > 0 {
os.exit(1)
}
}