A high-performance x86-64 instruction encoder/decoder written in Odin.
- Encoder: Assembles instructions to machine code with label resolution and relocation support
- Decoder: Disassembles machine code back to structured instructions
- Printer: Generates assembly text output with optional syntax highlighting tokens
- Table-driven: O(1) mnemonic lookup using pre-generated encoding tables
- Comprehensive ISA coverage: GPR, SSE, AVX, AVX-512, BMI, FMA, AES-NI, and more
import "x64"
code: [4096]u8
labels: []x64.Label_Def
relocs: [dynamic]x64.Reloc
errors: [dynamic]x64.Encode_Error
// Build instructions
instructions := []x64.Instruction{
x64.inst_r_r(.MOV, x64.RAX, x64.RDI),
x64.inst_r_r(.ADD, x64.RAX, x64.RSI),
x64.inst_none(.RET),
}
// Encode to machine code
result := x64.encode(instructions[:], labels, code[:], &relocs, &errors)
// result.code_size contains the number of bytes written
// Decode back to instructions
decoded_insts: [dynamic]x64.Instruction
decoded_info: [dynamic]x64.Instruction_Info
decoded_labels: [dynamic]x64.Label_Def
decode_errors: [dynamic]x64.Error
x64.decode(code[:result.code_size], nil, &decoded_insts, &decoded_info, &decoded_labels, &decode_errors)
// Print disassembly
disasm := x64.print(decoded_insts[:], decoded_info[:], decoded_labels[:], nil, nil)Helper procedures for constructing instructions:
x64.inst_none(.RET) // No operands
x64.inst_r(.PUSH, x64.RAX) // Single register
x64.inst_r_r(.MOV, x64.RAX, x64.RBX) // Register to register
x64.inst_r_i(.MOV, x64.EAX, 42, 4) // Immediate (with size)
x64.inst_r_r_r(.VADDPS, x64.XMM0, x64.XMM1, x64.XMM2) // 3-operand VEX
x64.inst_m_r(.MOV, x64.mem_base(x64.RSP), 8, x64.RAX) // Memory destination
x64.inst_r_m(.MOV, x64.RAX, x64.mem_base_disp(x64.RBP, -8), 8) // Memory source
x64.inst_rel(.JMP, label_id, 1) // Relative branch with labelx64.mem_base(x64.RAX) // [RAX]
x64.mem_base_disp(x64.RBP, -16) // [RBP - 16]
x64.mem_base_index(x64.RAX, x64.RCX, 4) // [RAX + RCX*4]
x64.mem_base_index_disp(x64.RAX, x64.RCX, 8, 32) // [RAX + RCX*8 + 32]
x64.mem_rip_rel(0) // [RIP + disp]Labels enable forward/backward references in branches. Use Label_Map for named labels:
lm: x64.Label_Map
x64.label_map_init(&lm)
defer x64.label_map_destroy(&lm)
instructions: [dynamic]x64.Instruction
// Reserve forward reference
done := x64.label_reserve(&lm, "done")
// Define label at current position
loop := x64.label_named(&lm, "loop", &instructions)
x64.emit_r(&instructions, .DEC, x64.RDI)
x64.emit_rel(&instructions, .JNZ, loop)
x64.emit_rel(&instructions, .JMP, done)
// Define reserved label
x64.label_set(&lm, "done", &instructions)
x64.emit_ri(&instructions, .MOV, x64.EAX, 42, 4)
x64.emit_none(&instructions, .RET)
// Encode
result := x64.encode(instructions[:], lm.labels[:], code[:], &relocs, &errors)
// Print with named labels
output := x64.print(decoded_insts[:], decoded_info[:], lm.labels[:], label_names = &lm.names)
// Output:
// loop:
// dec rdi
// jne loop
// jmp done
// done:
// mov eax, 0x2a
// retFor simple cases without names, use dynamic arrays directly:
labels: [dynamic]x64.Label_Def
instructions: [dynamic]x64.Instruction
loop := x64.label(&labels, &instructions) // Define at current position
x64.emit_r(&instructions, .DEC, x64.RDI)
x64.emit_rel(&instructions, .JNZ, loop)
done := x64.label_forward(&labels) // Reserve for forward ref
x64.emit_rel(&instructions, .JMP, done)
labels[done] = x64.Label_Def(len(instructions)) // Define positioncd tests
odin run .The test suite validates encoding, decoding, and execution of 2000+ test cases across integer, SSE, and AVX instructions.
x64/
x64.odin # Main encoder/decoder implementation
enc_table.odin # Encoding table
decode_tables.odin # Generated decoding table
tests/
test.odin # Test suite
tools/ # Table generation utilities