mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-29 00:11:34 +00:00
Merge pull request #7362 from odin-lang/bill/rexcode
`core:rexcode` improvements
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -142,9 +142,9 @@ x86.label_set(&lm, "done", &instructions)
|
||||
result := x86.encode(instructions[:], lm.labels[:], code[:], &relocs, &errors)
|
||||
|
||||
// Printer wants id→name; Label_Map stores name→id, 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`)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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}} },
|
||||
|
||||
@@ -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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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) }
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user