Replace -> isa.Result with -> (byte_code: u32, ok: bool)

This commit is contained in:
gingerBill
2026-06-15 21:43:58 +01:00
parent 202be0601b
commit c9ce8794c7
53 changed files with 580 additions and 611 deletions

View File

@@ -40,7 +40,7 @@ decode :: proc(
label_defs: ^[dynamic]Label_Definition,
errors: ^[dynamic]Error,
mode: Mode = .A32,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_bytes := u32(len(data))
if mode == .T32 { n_bytes = n_bytes & ~u32(1) }
else { n_bytes = n_bytes & ~u32(3) }
@@ -50,21 +50,20 @@ decode :: proc(
pending_branches: [dynamic]isa.Branch_Target
defer delete(pending_branches)
pc: u32 = 0
for pc < n_bytes {
for byte_count < n_bytes {
word: u32
ilen: u32 = 4
if mode == .A32 {
if pc + 4 > n_bytes { break }
word = read_u32_le(data, pc)
if byte_count + 4 > n_bytes { break }
word = read_u32_le(data, byte_count)
} else {
// T32: 16 or 32 bit
hword_hi := read_u16_le(data, pc)
hword_hi := read_u16_le(data, byte_count)
top5 := (hword_hi >> 11) & 0x1F
if top5 == 0x1D || top5 == 0x1E || top5 == 0x1F {
if pc + 4 > n_bytes { break }
hword_lo := read_u16_le(data, pc + 2)
if byte_count + 4 > n_bytes { break }
hword_lo := read_u16_le(data, byte_count + 2)
// Pack: bits = low_halfword | (high_halfword << 16)
word = u32(hword_lo) | (u32(hword_hi) << 16)
ilen = 4
@@ -76,10 +75,10 @@ decode :: proc(
inst: Instruction
info: Instruction_Info
info.offset = pc
info.offset = byte_count
if !find_and_decode(word, mode, ilen, &inst, &info) {
append(errors, Error{inst_idx = pc, code = .INVALID_OPCODE})
append(errors, Error{inst_idx = byte_count, code = .INVALID_OPCODE})
inst = Instruction{mnemonic = .INVALID, length = u8(ilen), mode = mode}
} else {
inst.length = u8(ilen)
@@ -103,11 +102,12 @@ decode :: proc(
append(instructions, inst)
append(inst_info, info)
pc += ilen
byte_count += ilen
}
isa.infer_labels_from_branches(pending_branches[:], pc, label_defs, relocs)
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
isa.infer_labels_from_branches(pending_branches[:], byte_count, label_defs, relocs)
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -34,38 +34,37 @@ encode :: proc(
errors: ^[dynamic]Error,
resolve: bool = true,
base_address: u64 = 0,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_inst := len(instructions)
if len(code) < n_inst * 4 {
append(errors, Error{inst_idx = 0, code = .BUFFER_OVERFLOW})
return Result{byte_count = 0, success = false}
return
}
errors_start := u32(len(errors))
pending_start := u32(len(relocs))
pc: u32 = 0
inst_pc := make([]u32, n_inst, context.temp_allocator)
// ---- PASS 1 ------------------------------------------------------------
for i in 0..<n_inst {
inst_pc[i] = pc
inst_pc[i] = byte_count
inst := &instructions[i]
word, ilen, ok := encode_one_inline(inst, pc, u16(i), relocs, errors)
if !ok { return Result{byte_count = pc, success = false} }
word, ilen := encode_one_inline(inst, byte_count, u16(i), relocs, errors) or_return
if ilen == 2 {
write_u16_le(code, pc, u16(word))
write_u16_le(code, byte_count, u16(word))
} else {
// T32 32-bit: bits = low_hword | (high_hword << 16); each
// halfword is written little-endian in its own slot.
if inst.mode == .T32 {
write_u16_le(code, pc, u16(word >> 16))
write_u16_le(code, pc + 2, u16(word))
write_u16_le(code, byte_count, u16(word >> 16))
write_u16_le(code, byte_count + 2, u16(word))
} else {
write_u32_le(code, pc, word)
write_u32_le(code, byte_count, word)
}
}
pc += u32(ilen)
byte_count += u32(ilen)
}
// ---- PASS 1.5: label_def instruction-idx -> byte-offset -----------------
@@ -81,7 +80,8 @@ encode :: proc(
}
if !resolve {
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// ---- PASS 2: resolve relocations ----------------------------------------
@@ -97,7 +97,8 @@ encode :: proc(
}
if write_idx != n_relocs { resize(relocs, int(write_idx)) }
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -51,7 +51,6 @@ import "../isa"
// All operand-driven fields live in the zeros of `mask`; the encoder ORs
// them in. The matcher tests `(word & mask) == bits`.
Result :: isa.Result
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition

View File

@@ -21,14 +21,14 @@ check_bytes :: proc(name: string, inst: a.Instruction, want: []u8) {
errors: [dynamic]a.Error
defer { delete(label_defs); delete(code); delete(relocs); delete(errors) }
res := a.encode(insts, label_defs[:], code, &relocs, &errors)
if !res.success {
byte_count, success := a.encode(insts, label_defs[:], code, &relocs, &errors)
if !success {
fmt.printf(" [FAIL] %s: encode failed (errors=%d)\n", name, len(errors))
fail += 1
return
}
if int(res.byte_count) != len(want) {
fmt.printf(" [FAIL] %s: got %d bytes, want %d\n", name, res.byte_count, len(want))
if int(byte_count) != len(want) {
fmt.printf(" [FAIL] %s: got %d bytes, want %d\n", name, byte_count, len(want))
fail += 1
return
}
@@ -55,9 +55,9 @@ check_decode :: proc(name: string, bytes: []u8, want_mn: a.Mnemonic, mode: a.Mod
labels: [dynamic]a.Label_Definition
errors: [dynamic]a.Error
defer { delete(insts); delete(info); delete(labels); delete(errors) }
res := a.decode(bytes, relocs, &insts, &info, &labels, &errors, mode)
if !res.success || len(insts) == 0 {
fmt.printf(" [FAIL] decode %s: success=%v len=%d\n", name, res.success, len(insts))
byte_count, success := a.decode(bytes, relocs, &insts, &info, &labels, &errors, mode)
if !success || len(insts) == 0 {
fmt.printf(" [FAIL] decode %s: success=%v len=%d\n", name, success, len(insts))
fail += 1
return
}
@@ -160,8 +160,8 @@ check_roundtrip :: proc(name: string, inst: a.Instruction) {
errors: [dynamic]a.Error
defer { delete(label_defs); delete(code); delete(relocs); delete(errors) }
res := a.encode(insts, label_defs[:], code, &relocs, &errors)
if !res.success {
byte_count, success := a.encode(insts, label_defs[:], code, &relocs, &errors)
if !success {
fmt.printf(" [FAIL] roundtrip %s: encode failed\n", name)
fail += 1
return
@@ -174,8 +174,8 @@ check_roundtrip :: proc(name: string, inst: a.Instruction) {
dec_err: [dynamic]a.Error
defer { delete(decoded); delete(info); delete(labels); delete(dec_err) }
dec_res := a.decode(code[:res.byte_count], dec_relocs, &decoded, &info, &labels, &dec_err, inst.mode)
if !dec_res.success || len(decoded) == 0 {
dec_byte_count, dec_success := a.decode(code[:byte_count], dec_relocs, &decoded, &info, &labels, &dec_err, inst.mode)
if !dec_success || len(decoded) == 0 {
fmt.printf(" [FAIL] roundtrip %s: decode failed\n", name)
fail += 1
return

View File

@@ -113,8 +113,8 @@ run_sweep_tests :: proc() {
ren_errors: [dynamic]a.Error
out: [4]u8
defer { delete(ren_relocs); delete(ren_errors) }
res := a.encode(insts[:], label_defs[:], out[:], &ren_relocs, &ren_errors, resolve=false)
if !res.success {
byte_count, success := a.encode(insts[:], label_defs[:], out[:], &ren_relocs, &ren_errors, resolve=false)
if !success {
stats.fail_encode += 1
if failed_examples < max_fail_print && (only_print_kind == "" || only_print_kind == "re-enc") {
fmt.printf(" [re-enc ] %v[%d] %08X re-encode failed\n", mn, idx, word)

View File

@@ -36,25 +36,24 @@ decode :: proc(
label_defs: ^[dynamic]Label_Definition,
errors: ^[dynamic]Error,
endianness: Endianness = .LITTLE,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_bytes := u32(len(data)) & ~u32(3)
errors_start := u32(len(errors))
pending_branches: [dynamic]isa.Branch_Target
defer delete(pending_branches)
pc: u32 = 0
for pc < n_bytes {
word := read_u32(data, pc, endianness)
for byte_count < n_bytes {
word := read_u32(data, byte_count, endianness)
inst: Instruction
info: Instruction_Info
entry_idx := decode_one_inline(word, pc, &inst, &info)
entry_idx := decode_one_inline(word, byte_count, &inst, &info)
if entry_idx < 0 {
append(errors, Error{inst_idx = pc, code = .INVALID_OPCODE})
append(errors, Error{inst_idx = byte_count, code = .INVALID_OPCODE})
inst = Instruction{mnemonic = .INVALID, length = 4}
info = Instruction_Info{offset = pc}
info = Instruction_Info{offset = byte_count}
} else {
inst_idx_for_branches := u32(len(instructions))
for slot in 0..<inst.operand_count {
@@ -71,11 +70,12 @@ decode :: proc(
append(instructions, inst)
append(inst_info, info)
pc += 4
byte_count += 4
}
isa.infer_labels_from_branches(pending_branches[:], pc, label_defs, relocs)
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
isa.infer_labels_from_branches(pending_branches[:], byte_count, label_defs, relocs)
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -43,24 +43,22 @@ encode :: proc(
endianness: Endianness = .LITTLE,
resolve: bool = true,
base_address: u64 = 0,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_inst := u32(len(instructions))
if u32(len(code)) < n_inst * 4 {
append(errors, Error{inst_idx = 0, code = .BUFFER_OVERFLOW})
return Result{byte_count = 0, success = false}
return
}
errors_start := u32(len(errors))
pending_start := u32(len(relocs))
pc: u32 = 0
// ---- PASS 1 -----------------------------------------------------------
for i in 0..<n_inst {
inst := &instructions[i]
word, ok := encode_one_inline(inst, pc, u16(i), relocs, errors)
if !ok { return Result{byte_count = pc, success = false} }
write_u32(code, pc, word, endianness)
pc += 4
word := encode_one_inline(inst, byte_count, u16(i), relocs, errors) or_return
write_u32(code, byte_count, word, endianness)
byte_count += 4
}
// ---- PASS 1.5: fixed-width => *4 -------------------------------------
@@ -71,7 +69,8 @@ encode :: proc(
}
if !resolve {
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// ---- PASS 2: resolve relocations -------------------------------------
@@ -87,7 +86,8 @@ encode :: proc(
}
if write_idx != n_relocs { resize(relocs, int(write_idx)) }
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -37,7 +37,6 @@ import "../isa"
// option 13-15 extend type (data-proc extended register, LDR/STR EXT)
// imm3 10-12 extend amount
Result :: isa.Result
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition

View File

@@ -78,8 +78,8 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.inst_r_r_i(.ADD_IMM, a.X0, a.X1, 100),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("ADD_IMM: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("ADD_IMM: encode", success)
eq_word("ADD X0,X1,#100", load_le(code[:], 0), 0x91019020)
}
@@ -92,8 +92,8 @@ run_pipeline_tests :: proc() {
clear(&relocs); clear(&errors)
for i in 0..<len(code) { code[i] = 0 }
insts := []a.Instruction{ a.inst_mov_imm(.MOVZ, a.X0, 0x1234, 1) }
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("MOVZ: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("MOVZ: encode", success)
eq_word("MOVZ X0,#0x1234,LSL#16", load_le(code[:], 0), 0xD2A24680)
}
@@ -111,8 +111,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_reg(a.X0), a.op_reg(a.X1), a.op_shifted(a.X2, .LSL, 3), {}},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("ADD_SR: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("ADD_SR: encode", success)
eq_word("ADD X0,X1,X2,LSL#3", load_le(code[:], 0), 0x8B020C20)
}
@@ -129,8 +129,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_reg(a.X0), a.op_reg(a.SP), a.op_extended(a.W1, .UXTW, 2), {}},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("ADD_ER: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("ADD_ER: encode", success)
eq_word("ADD X0,SP,W1,UXTW#2", load_le(code[:], 0), 0x8B214BE0)
}
@@ -144,8 +144,8 @@ run_pipeline_tests :: proc() {
a.inst_r_r_r (.UDIV, a.X0, a.X1, a.X2),
a.inst_r_r_r_r(.MADD, a.X0, a.X1, a.X2, a.X3),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("UDIV/MADD: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("UDIV/MADD: encode", success)
eq_word("UDIV X0,X1,X2", load_le(code[:], 0), 0x9AC20820)
eq_word("MADD X0,X1,X2,X3", load_le(code[:], 4), 0x9B020C20)
}
@@ -160,8 +160,8 @@ run_pipeline_tests :: proc() {
a.inst_r_r(.CLZ, a.X0, a.X1),
a.inst_r_r(.REV, a.X0, a.X1),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("CLZ/REV: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("CLZ/REV: encode", success)
eq_word("CLZ X0,X1", load_le(code[:], 0), 0xDAC01020)
eq_word("REV X0,X1", load_le(code[:], 4), 0xDAC00C20)
}
@@ -172,8 +172,8 @@ run_pipeline_tests :: proc() {
clear(&relocs); clear(&errors)
for i in 0..<len(code) { code[i] = 0 }
insts := []a.Instruction{ a.inst_csel(.CSEL, a.X0, a.X1, a.X2, .EQ) }
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("CSEL: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("CSEL: encode", success)
eq_word("CSEL X0,X1,X2,EQ", load_le(code[:], 0), 0x9A820020)
}
@@ -189,8 +189,8 @@ run_pipeline_tests :: proc() {
a.inst_ldst(.LDR, a.X0, a.mem_offset(a.X1, 16)),
a.inst_ldst(.STR, a.W0, a.mem_offset(a.SP, 20)),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("LDR/STR: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("LDR/STR: encode", success)
eq_word("LDR X0,[X1,#16]", load_le(code[:], 0), 0xF9400820)
eq_word("STR W0,[SP,#20]", load_le(code[:], 4), 0xB90017E0)
}
@@ -219,8 +219,8 @@ run_pipeline_tests :: proc() {
a.inst_none(.RET),
a.inst_branch(.BL, 0),
}
r := a.encode(insts, ld[:], code[:], &relocs, &errors)
ok("br: encode", r.success)
byte_count, success := a.encode(insts, ld[:], code[:], &relocs, &errors)
ok("br: encode", success)
eq_word("B forward (+3 words)", load_le(code[:], 0), 0x14000003)
eq_word("BL backward (-1 word)", load_le(code[:], 16), 0x97FFFFFF)
}
@@ -249,8 +249,8 @@ run_pipeline_tests :: proc() {
a.inst_tbz(.TBZ, a.X0, 5, 0),
a.inst_none(.NOP), // target
}
r := a.encode(insts, ld[:], code[:], &relocs, &errors)
ok("CBZ/TBZ: encode", r.success)
byte_count, success := a.encode(insts, ld[:], code[:], &relocs, &errors)
ok("CBZ/TBZ: encode", success)
eq_word("CBZ X0,+2 words", load_le(code[:], 0), 0xB4000040)
eq_word("TBZ X0,#5,+1 word", load_le(code[:], 4), 0x36280020)
}
@@ -286,8 +286,8 @@ run_pipeline_tests :: proc() {
a.inst_none(.NOP),
a.inst_none(.NOP),
}
r := a.encode(insts, ld[:], code[:], &relocs, &errors)
ok("ADR: encode", r.success)
byte_count, success := a.encode(insts, ld[:], code[:], &relocs, &errors)
ok("ADR: encode", success)
eq_word("ADR X0,+8", load_le(code[:], 0), 0x10000040)
}
@@ -315,8 +315,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_reg(a.X0), a.op_imm(0xDA10, 2), {}, {}},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("system: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("system: encode", success)
eq_word("NOP", load_le(code[:], 0), 0xD503201F)
eq_word("SVC #1", load_le(code[:], 4), 0xD4000021)
eq_word("MRS X0,NZCV", load_le(code[:], 8), 0xD53B4200)
@@ -330,8 +330,8 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.inst_r_r_r(.FADD, a.d_reg(0), a.d_reg(1), a.d_reg(2)),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("FADD: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("FADD: encode", success)
eq_word("FADD D0,D1,D2", load_le(code[:], 0), 0x1E622820)
}
@@ -349,16 +349,16 @@ run_pipeline_tests :: proc() {
a.inst_cbz(.CBNZ, a.X0, 0),
a.inst_none(.RET),
}
r := a.encode(src, ld[:], code[:], &relocs, &errors)
ok("rt: encode", r.success)
byte_count, success := a.encode(src, ld[:], code[:], &relocs, &errors)
ok("rt: encode", success)
d_insts: [dynamic]a.Instruction
d_info: [dynamic]a.Instruction_Info
d_labels: [dynamic]a.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
d := a.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("rt: decode", d.success)
_, d_success := a.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("rt: decode", d_success)
ok("rt: 3 insts", len(d_insts) == 3)
ok("rt: ADD", d_insts[0].mnemonic == .ADD_IMM)
ok("rt: CBNZ", d_insts[1].mnemonic == .CBNZ)
@@ -385,15 +385,15 @@ run_pipeline_tests :: proc() {
a.inst_b_cond(.EQ, 0),
a.inst_none(.RET),
}
r := a.encode(src, ld[:], code[:], &relocs, &errors)
ok("b.cond: encode", r.success)
byte_count, success := a.encode(src, ld[:], code[:], &relocs, &errors)
ok("b.cond: encode", success)
d_insts: [dynamic]a.Instruction
d_info: [dynamic]a.Instruction_Info
d_labels: [dynamic]a.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
a.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
a.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
text := a.aprint(d_insts[:], d_info[:], d_labels[:],
nil, nil, nil, context.temp_allocator)
@@ -423,8 +423,8 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.inst_r_r_i(.ORR_IMM, a.X0, a.X1, transmute(i64)mask),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("bitmask ORR: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("bitmask ORR: encode", success)
eq_word("ORR X0,X1,#0xFF00.. repeat", load_le(code[:], 0), 0xB2089C20)
// Round-trip: decode and verify operand round-trips back to the raw mask.
@@ -433,7 +433,7 @@ run_pipeline_tests :: proc() {
d_labels: [dynamic]a.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
a.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
a.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("bitmask ORR: decode", len(d_insts) == 1 && d_insts[0].mnemonic == .ORR_IMM)
if len(d_insts) == 1 {
got := u64(d_insts[0].ops[2].immediate)
@@ -457,8 +457,8 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.inst_r_r_i(.AND_IMM, a.W0, a.W1, 0xF0),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("bitmask AND 32: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("bitmask AND 32: encode", success)
eq_word("AND W0,W1,#0xF0", load_le(code[:], 0), 0x12040C20)
// Round-trip.
@@ -467,7 +467,7 @@ run_pipeline_tests :: proc() {
d_labels: [dynamic]a.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
a.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
a.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("bitmask AND 32: decode", len(d_insts) == 1 && d_insts[0].mnemonic == .AND_IMM)
if len(d_insts) == 1 {
got := u64(d_insts[0].ops[2].immediate)
@@ -487,8 +487,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_z_s(0), a.op_z_s(1), a.op_z_s(2), {}},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SVE ADD Z: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SVE ADD Z: encode", success)
eq_word("SVE ADD Z0.S,Z1.S,Z2.S", load_le(code[:], 0), 0x04A20020)
// Round-trip.
@@ -497,7 +497,7 @@ run_pipeline_tests :: proc() {
d_labels: [dynamic]a.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
a.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
a.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("SVE ADD Z: decode", len(d_insts) == 1 && d_insts[0].mnemonic == .SVE_ADD_Z)
}
@@ -514,8 +514,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_z_s(0), a.op_reg(p0), a.op_z_s(0), a.op_z_s(1)},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SVE ADD_PRED: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SVE ADD_PRED: encode", success)
eq_word("SVE ADD Z0.S,P0/M,Z0.S,Z1.S", load_le(code[:], 0), 0x04810000)
}
@@ -532,8 +532,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_reg(p0), a.op_imm(0x1F, 1), {}, {}},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SVE PTRUE: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SVE PTRUE: encode", success)
eq_word("SVE PTRUE P0.B,ALL", load_le(code[:], 0), 0x2518E3E0)
}
@@ -547,8 +547,8 @@ run_pipeline_tests :: proc() {
a.inst_none(.SME_SMSTART),
a.inst_none(.SME_SMSTOP),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SME SMSTART/SMSTOP: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SME SMSTART/SMSTOP: encode", success)
eq_word("SME SMSTART", load_le(code[:], 0), 0xD503477F)
eq_word("SME SMSTOP", load_le(code[:], 4), 0xD503467F)
}
@@ -572,8 +572,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_imm(0, 1), a.op_reg(p0), a.op_reg(p1), a.op_z_s(0)},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SME FMOPA: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SME FMOPA: encode", success)
eq_word("SME FMOPA ZA0.S,P0/M,P1/M,Z0.S,Z0.S", load_le(code[:], 0), 0x80802000)
}
@@ -595,8 +595,8 @@ run_pipeline_tests :: proc() {
a.inst_r(.AMX_STZ, a.X3),
a.inst_none(.AMX_CLR),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("AMX: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("AMX: encode", success)
eq_word("AMX SET", load_le(code[:], 0), 0x00201220)
eq_word("AMX LDX X0", load_le(code[:], 4), 0x00201000)
eq_word("AMX LDY X1", load_le(code[:], 8), 0x00201021)
@@ -610,8 +610,8 @@ run_pipeline_tests :: proc() {
d_labels: [dynamic]a.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
d := a.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("AMX: decode", d.success)
_, d_success := a.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("AMX: decode", d_success)
ok("AMX: 6 insts", len(d_insts) == 6)
ok("AMX: SET", len(d_insts) >= 1 && d_insts[0].mnemonic == .AMX_SET)
ok("AMX: LDX", len(d_insts) >= 2 && d_insts[1].mnemonic == .AMX_LDX)
@@ -651,9 +651,9 @@ run_pipeline_tests :: proc() {
isa.label_set_at(&labels, fwd, &insts) // .L1: at instruction 4
append(&insts, a.inst_none(.RET)) // [4] byte 16: RET
r := a.encode(insts[:], labels[:], code[:], &relocs, &errors)
ok("anon labels: encode", r.success)
ok("anon labels: byte_count = 20", r.byte_count == 20)
byte_count, success := a.encode(insts[:], labels[:], code[:], &relocs, &errors)
ok("anon labels: encode", success)
ok("anon labels: byte_count = 20", byte_count == 20)
// B.LT at byte 8 -> .L1 at byte 16: offset = +8 = +2 words.
// bits = 0x54000000 | (2<<5) | LT(0xB) = 0x5400004B
@@ -670,7 +670,7 @@ run_pipeline_tests :: proc() {
d_labels: [dynamic]a.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
a.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
a.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("anon labels: decode 5 insts", len(d_insts) == 5)
have_back, have_fwd: bool
for ld in d_labels {
@@ -713,8 +713,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_reg(a.X3), a.op_imm(a.DCZID_EL0, 2), {}, {}},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("sysreg: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("sysreg: encode", success)
eq_word("MRS X0,NZCV", load_le(code[:], 0), 0xD53B4200)
eq_word("MRS X1,TPIDR_EL0", load_le(code[:], 4), 0xD53BD041)
eq_word("MRS X2,CNTVCT_EL0", load_le(code[:], 8), 0xD53BE042)
@@ -729,8 +729,8 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.inst_r(.DC_ZVA, a.X0),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("DC ZVA: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("DC ZVA: encode", success)
eq_word("DC ZVA X0", load_le(code[:], 0), 0xD50B7420)
d_insts: [dynamic]a.Instruction
@@ -738,7 +738,7 @@ run_pipeline_tests :: proc() {
d_labels: [dynamic]a.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
a.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
a.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("DC ZVA: decode", len(d_insts) == 1 && d_insts[0].mnemonic == .DC_ZVA)
ok("DC ZVA: Rt = X0", len(d_insts) == 1 && d_insts[0].ops[0].reg == a.X0)
}
@@ -756,8 +756,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_reg(a.X0), a.op_shifted(a.X1, .LSL, 0), {}, {}},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("CMP_SR: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("CMP_SR: encode", success)
eq_word("CMP X0,X1", load_le(code[:], 0), 0xEB01001F)
}
@@ -786,8 +786,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_reg(a.X0), a.op_reg(a.X1), a.op_reg(a.X2), {}},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("MOPS CPY: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("MOPS CPY: encode", success)
eq_word("CPYP X0,X1,X2", load_le(code[:], 0), 0x1D020420)
eq_word("CPYM X0,X1,X2", load_le(code[:], 4), 0x1D420420)
eq_word("CPYE X0,X1,X2", load_le(code[:], 8), 0x1D820420)
@@ -808,8 +808,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_z_s(0), a.op_z_s(1), a.op_z_s(2), a.op_imm(2, 1)},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SVE FMLA indexed: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SVE FMLA indexed: encode", success)
eq_word("SVE FMLA Z0.S, Z1.S, Z2.S[2]", load_le(code[:], 0), 0x64B20020)
}
@@ -835,8 +835,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_z_s(0), a.op_reg(p0), a.op_mem(mem), {}},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SVE LD1W gather: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SVE LD1W gather: encode", success)
eq_word("SVE LD1W Z0.S,P0/Z,[X1,Z2.S,UXTW]", load_le(code[:], 0), 0x85024020)
}
@@ -864,8 +864,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_imm(slice_packed, 2), a.op_reg(p5), a.op_mem(mem), {}},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SME LD1B tile vs LLVM: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("SME LD1B tile vs LLVM: encode", success)
eq_word("SME LD1B ZA0V.B[W14,5],P5/Z,[X10,X21]", load_le(code[:], 0), 0xE015D545)
d_insts: [dynamic]a.Instruction
@@ -873,7 +873,7 @@ run_pipeline_tests :: proc() {
d_labels: [dynamic]a.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
a.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
a.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("SME LD1B tile: decode 1 inst", len(d_insts) == 1)
ok("SME LD1B tile: mnemonic", len(d_insts) == 1 && d_insts[0].mnemonic == .SME_LD1B_TILE)
ok("SME LD1B tile: slice roundtrip",
@@ -891,8 +891,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_v_4s(0), a.op_v_4s(1), a.op_v_4s(2), a.op_imm(0, 1)},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("FCMLA: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("FCMLA: encode", success)
eq_word("FCMLA V0.4S,V1.4S,V2.4S,#0", load_le(code[:], 0), 0x6E82C420)
}
@@ -908,8 +908,8 @@ run_pipeline_tests :: proc() {
a.inst_none(.TCOMMIT),
a.inst_r(.TTEST, a.X1),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("TME: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("TME: encode", success)
eq_word("TSTART X0", load_le(code[:], 0), 0xD5233060)
eq_word("TCOMMIT", load_le(code[:], 4), 0xD503307F)
eq_word("TTEST X1", load_le(code[:], 8), 0xD5233161)
@@ -925,8 +925,8 @@ run_pipeline_tests :: proc() {
a.inst_r_r(.UXTB, a.W0, a.W1),
a.inst_r_r(.SXTW, a.X0, a.W1),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("extend aliases: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("extend aliases: encode", success)
eq_word("UXTB W0,W1", load_le(code[:], 0), 0x53001C20)
eq_word("SXTW X0,W1", load_le(code[:], 4), 0x93407C20)
}
@@ -942,8 +942,8 @@ run_pipeline_tests :: proc() {
a.inst_r_r_r(.ADC, a.X0, a.X1, a.X2),
a.inst_r_r_r(.SBCS, a.X3, a.X4, a.X5),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("ADC/SBCS: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("ADC/SBCS: encode", success)
eq_word("ADC X0,X1,X2", load_le(code[:], 0), 0x9A020020)
eq_word("SBCS X3,X4,X5", load_le(code[:], 4), 0xFA050083)
}
@@ -961,8 +961,8 @@ run_pipeline_tests :: proc() {
ops = {a.op_reg(a.X7), a.op_imm(a.RNDR, 2), {}, {}},
},
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("MRS X7,RNDR: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("MRS X7,RNDR: encode", success)
eq_word("MRS X7,RNDR", load_le(code[:], 0), 0xD53B2407)
}
@@ -979,8 +979,8 @@ run_pipeline_tests :: proc() {
a.inst_ldst(.LDAPUR, a.X0, a.mem_offset(a.X1, 8)),
a.inst_ldst(.STLUR, a.X2, a.mem_offset(a.SP, -8)),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("RCpc unscaled: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("RCpc unscaled: encode", success)
eq_word("LDAPUR X0,[X1,#8]", load_le(code[:], 0), 0xD9408020)
eq_word("STLUR X2,[SP,#-8]", load_le(code[:], 4), 0xD91F83E2)
}
@@ -994,8 +994,8 @@ run_pipeline_tests :: proc() {
a.inst_none(.BTI_J),
a.inst_none(.PSB_CSYNC),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("barriers/BTI: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("barriers/BTI: encode", success)
eq_word("SB", load_le(code[:], 0), 0xD50330FF)
eq_word("BTI j", load_le(code[:], 4), 0xD503245F)
eq_word("PSB CSYNC", load_le(code[:], 8), 0xD503223F)
@@ -1011,8 +1011,8 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.inst_r_r_i(.LSL_IMM, a.W0, a.W1, 4),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("LSL_IMM 32: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("LSL_IMM 32: encode", success)
eq_word("LSL W0,W1,#4", load_le(code[:], 0), 0x531C6C20)
}
@@ -1026,8 +1026,8 @@ run_pipeline_tests :: proc() {
insts := []a.Instruction{
a.inst_r_r_i(.ROR_IMM, a.W0, a.W1, 4),
}
r := a.encode(insts, nil, code[:], &relocs, &errors)
ok("ROR_IMM 32: encode", r.success)
byte_count, success := a.encode(insts, nil, code[:], &relocs, &errors)
ok("ROR_IMM 32: encode", success)
eq_word("ROR W0,W1,#4", load_le(code[:], 0), 0x13811020)
}

View File

@@ -43,7 +43,3 @@ Error :: struct #packed {
}
#assert(size_of(Error) == 8)
Result :: struct {
byte_count: u32, // Bytes written/read
success: bool, // True if no errors
}

View File

@@ -57,7 +57,7 @@ decode :: proc(
label_defs: ^[dynamic]Label_Definition,
errors: ^[dynamic]Error,
endianness: Endianness = .BIG,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_bytes := u32(len(data))
if n_bytes & 3 != 0 {
n_bytes &= ~u32(3) // ignore the dangling tail
@@ -68,18 +68,17 @@ decode :: proc(
defer delete(pending_branches)
// ---- PASS 1 -----------------------------------------------------------
pc: u32 = 0
for pc < n_bytes {
word := read_u32(data, pc, endianness)
for byte_count < n_bytes {
word := read_u32(data, byte_count, endianness)
inst: Instruction
info: Instruction_Info
entry_idx := decode_one_inline(word, pc, &inst, &info)
entry_idx := decode_one_inline(word, byte_count, &inst, &info)
if entry_idx < 0 {
append(errors, Error{inst_idx = pc, code = .INVALID_OPCODE})
append(errors, Error{inst_idx = byte_count, code = .INVALID_OPCODE})
inst = Instruction{mnemonic = .INVALID, length = 4}
info = Instruction_Info{offset = pc}
info = Instruction_Info{offset = byte_count}
} else {
inst_idx_for_branches := u32(len(instructions))
for slot in 0..<inst.operand_count {
@@ -96,13 +95,14 @@ decode :: proc(
append(instructions, inst)
append(inst_info, info)
pc += 4
byte_count += 4
}
// ---- PASS 2: label inference -----------------------------------------
isa.infer_labels_from_branches(pending_branches[:], pc, label_defs, relocs)
isa.infer_labels_from_branches(pending_branches[:], byte_count, label_defs, relocs)
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -71,26 +71,22 @@ encode :: proc(
endianness: Endianness = .BIG,
resolve: bool = true,
base_address: u64 = 0,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_inst := u32(len(instructions))
if u32(len(code)) < n_inst * 4 {
append(errors, Error{inst_idx = 0, code = .BUFFER_OVERFLOW})
return Result{byte_count = 0, success = false}
return
}
errors_start := u32(len(errors))
pending_start := u32(len(relocs))
pc: u32 = 0
// ---- PASS 1 ------------------------------------------------------------
for i in 0..<n_inst {
inst := &instructions[i]
word, ok := encode_one_inline(inst, pc, u16(i), relocs, errors)
if !ok {
return Result{byte_count = pc, success = false}
}
write_u32(code, pc, word, endianness)
pc += 4
word := encode_one_inline(inst, byte_count, u16(i), relocs, errors) or_return
write_u32(code, byte_count, word, endianness)
byte_count += 4
}
// ---- PASS 1.5: rewrite label_defs from inst-idx to byte-offset --------
@@ -102,7 +98,8 @@ encode :: proc(
}
if !resolve {
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// ---- PASS 2: resolve relocations ---------------------------------------
@@ -124,7 +121,8 @@ encode :: proc(
resize(relocs, int(write_idx))
}
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -35,7 +35,6 @@ import "../isa"
// (op=0x18, 0x19, 0x35, 0x37, 0x3F, ...) with their own layouts.
// Re-exports from isa.
Result :: isa.Result
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition

View File

@@ -80,8 +80,8 @@ run_decoder_tests :: proc() {
mips.inst_r_i (.LUI, mips.T0, 0x1234),
mips.inst_shift(.SLL, mips.T0, mips.T1, 5),
}
eres := mips.encode(src, nil, code[:], &relocs, &errors)
dcheck_bool("rt: encode ok", eres.success, true)
ebyte_count, esuccess := mips.encode(src, nil, code[:], &relocs, &errors)
dcheck_bool("rt: encode ok", esuccess, true)
dec_insts: [dynamic]mips.Instruction
dec_info: [dynamic]mips.Instruction_Info
@@ -91,11 +91,11 @@ run_decoder_tests :: proc() {
defer delete(dec_labels)
clear(&errors)
dres := mips.decode(code[:eres.byte_count], nil,
dbyte_count, dsuccess := mips.decode(code[:ebyte_count], nil,
&dec_insts, &dec_info, &dec_labels, &errors)
dcheck_bool("rt: decode ok", dres.success, true)
dcheck_int ("rt: byte_count", int(dres.byte_count), 24)
dcheck_bool("rt: decode ok", dsuccess, true)
dcheck_int ("rt: byte_count", int(dbyte_count), 24)
dcheck_int ("rt: instruction n", len(dec_insts), 6)
dcheck_int ("rt: info n", len(dec_info), 6)
dcheck_int ("rt: errors n", len(errors), 0)
@@ -161,8 +161,8 @@ run_decoder_tests :: proc() {
mips.inst_branch2(.BNE, mips.T0, mips.ZERO, 0),
mips.inst_none(.NOP),
}
eres := mips.encode(src, ld_in[:], code[:], &relocs, &errors)
dcheck_bool("br: encode ok", eres.success, true)
ebyte_count, esuccess := mips.encode(src, ld_in[:], code[:], &relocs, &errors)
dcheck_bool("br: encode ok", esuccess, true)
dec_insts: [dynamic]mips.Instruction
dec_info: [dynamic]mips.Instruction_Info
@@ -172,9 +172,9 @@ run_decoder_tests :: proc() {
defer delete(dec_labels)
clear(&errors)
dres := mips.decode(code[:eres.byte_count], nil,
dbyte_count, dsuccess := mips.decode(code[:ebyte_count], nil,
&dec_insts, &dec_info, &dec_labels, &errors)
dcheck_bool("br: decode ok", dres.success, true)
dcheck_bool("br: decode ok", dsuccess, true)
dcheck_int ("br: insts", len(dec_insts), 4)
dcheck_mnem("br: BNE", dec_insts[2].mnemonic, .BNE)
@@ -206,9 +206,9 @@ run_decoder_tests :: proc() {
mips.inst_none(.NOP),
mips.inst_none(.NOP),
}
eres := mips.encode(src, ld_in[:], code[:], &relocs, &errors,
ebyte_count, esuccess := mips.encode(src, ld_in[:], code[:], &relocs, &errors,
base_address = 0)
dcheck_bool("J: encode ok", eres.success, true)
dcheck_bool("J: encode ok", esuccess, true)
dec_insts: [dynamic]mips.Instruction
dec_info: [dynamic]mips.Instruction_Info
@@ -218,9 +218,9 @@ run_decoder_tests :: proc() {
defer delete(dec_labels)
clear(&errors)
dres := mips.decode(code[:eres.byte_count], nil,
dbyte_count, dsuccess := mips.decode(code[:ebyte_count], nil,
&dec_insts, &dec_info, &dec_labels, &errors)
dcheck_bool("J: decode ok", dres.success, true)
dcheck_bool("J: decode ok", dsuccess, true)
dcheck_mnem("J: mnemonic", dec_insts[0].mnemonic, .J)
dcheck_int ("J: op kind", int(dec_insts[0].ops[0].kind),
int(mips.Operand_Kind.RELATIVE))
@@ -235,8 +235,8 @@ run_decoder_tests :: proc() {
src := []mips.Instruction{
mips.inst_r_r_r(.ADD_S, mips.F4, mips.F5, mips.F6),
}
eres := mips.encode(src, nil, code[:], &relocs, &errors)
dcheck_bool("FPU: encode ok", eres.success, true)
ebyte_count, esuccess := mips.encode(src, nil, code[:], &relocs, &errors)
dcheck_bool("FPU: encode ok", esuccess, true)
dec_insts: [dynamic]mips.Instruction
dec_info: [dynamic]mips.Instruction_Info
@@ -246,9 +246,9 @@ run_decoder_tests :: proc() {
defer delete(dec_labels)
clear(&errors)
dres := mips.decode(code[:eres.byte_count], nil,
dbyte_count, dsuccess := mips.decode(code[:ebyte_count], nil,
&dec_insts, &dec_info, &dec_labels, &errors)
dcheck_bool("FPU: decode ok", dres.success, true)
dcheck_bool("FPU: decode ok", dsuccess, true)
dcheck_mnem("FPU: ADD.S", dec_insts[0].mnemonic, .ADD_S)
i0 := dec_insts[0]
dcheck_reg ("FPU: op0=F4", i0.ops[0].reg, mips.F4)
@@ -262,8 +262,8 @@ run_decoder_tests :: proc() {
for i in 0..<len(code) { code[i] = 0 }
src := []mips.Instruction{mips.inst_none(.RTPS)}
eres := mips.encode(src, nil, code[:], &relocs, &errors)
dcheck_bool("GTE: encode ok", eres.success, true)
ebyte_count, esuccess := mips.encode(src, nil, code[:], &relocs, &errors)
dcheck_bool("GTE: encode ok", esuccess, true)
dec_insts: [dynamic]mips.Instruction
dec_info: [dynamic]mips.Instruction_Info
@@ -273,9 +273,9 @@ run_decoder_tests :: proc() {
defer delete(dec_labels)
clear(&errors)
dres := mips.decode(code[:eres.byte_count], nil,
dbyte_count, dsuccess := mips.decode(code[:ebyte_count], nil,
&dec_insts, &dec_info, &dec_labels, &errors)
dcheck_bool("GTE: decode ok", dres.success, true)
dcheck_bool("GTE: decode ok", dsuccess, true)
dcheck_mnem("GTE: RTPS", dec_insts[0].mnemonic, .RTPS)
dcheck_int ("GTE: opcnt 0", int(dec_insts[0].operand_count), 0)
}
@@ -288,9 +288,9 @@ run_decoder_tests :: proc() {
src := []mips.Instruction{
mips.inst_r_r_r(.ADD, mips.T0, mips.T1, mips.T2),
}
eres := mips.encode(src, nil, code[:], &relocs, &errors,
ebyte_count, esuccess := mips.encode(src, nil, code[:], &relocs, &errors,
endianness = .LITTLE)
dcheck_bool("LE: encode ok", eres.success, true)
dcheck_bool("LE: encode ok", esuccess, true)
dec_insts: [dynamic]mips.Instruction
dec_info: [dynamic]mips.Instruction_Info
@@ -300,10 +300,10 @@ run_decoder_tests :: proc() {
defer delete(dec_labels)
clear(&errors)
dres := mips.decode(code[:eres.byte_count], nil,
dbyte_count, dsuccess := mips.decode(code[:ebyte_count], nil,
&dec_insts, &dec_info, &dec_labels, &errors,
endianness = .LITTLE)
dcheck_bool("LE: decode ok", dres.success, true)
dcheck_bool("LE: decode ok", dsuccess, true)
dcheck_mnem("LE: ADD", dec_insts[0].mnemonic, .ADD)
}
@@ -326,9 +326,9 @@ run_decoder_tests :: proc() {
defer delete(dec_labels)
clear(&errors)
dres := mips.decode(code[:4], nil,
dbyte_count, dsuccess := mips.decode(code[:4], nil,
&dec_insts, &dec_info, &dec_labels, &errors)
dcheck_bool("garbage: success", dres.success, false)
dcheck_bool("garbage: success", dsuccess, false)
dcheck_int ("garbage: insts", len(dec_insts), 1)
dcheck_mnem("garbage: INVALID", dec_insts[0].mnemonic, .INVALID)
dcheck_int ("garbage: errors n", len(errors), 1)

View File

@@ -88,10 +88,10 @@ run_encoder_tests :: proc() {
mips.inst_r_i (.LUI, mips.T0, 0x1234), // 0x3C081234
mips.inst_shift(.SLL, mips.T0, mips.T1, 5), // 0x00094140
}
res := mips.encode(insts, nil, code[:], &relocs, &errors)
byte_count, success := mips.encode(insts, nil, code[:], &relocs, &errors)
check_bool("core: success", res.success, true)
check_int ("core: byte_count", int(res.byte_count), 24)
check_bool("core: success", success, true)
check_int ("core: byte_count", int(byte_count), 24)
check_int ("core: errors len", len(errors), 0)
check_int ("core: relocs len", len(relocs), 0)
check_word("core: ADD t0,t1,t2", load_word_be(code[:], 0), 0x012A4020)
@@ -111,10 +111,10 @@ run_encoder_tests :: proc() {
insts := []mips.Instruction{
mips.inst_r_r_r(.ADD, mips.T0, mips.T1, mips.T2),
}
res := mips.encode(insts, nil, code[:], &relocs, &errors,
byte_count, success := mips.encode(insts, nil, code[:], &relocs, &errors,
endianness = .LITTLE)
check_bool("LE: success", res.success, true)
check_bool("LE: success", success, true)
check_word("LE: ADD (le bytes)",
load_word_le(code[:], 0), // reads as native u32 little-endian
0x012A4020)
@@ -144,12 +144,12 @@ run_encoder_tests :: proc() {
mips.inst_branch2(.BNE, mips.T0, mips.ZERO, 0),
mips.inst_none(.NOP),
}
res := mips.encode(insts, label_defs[:], code[:], &relocs, &errors)
byte_count, success := mips.encode(insts, label_defs[:], code[:], &relocs, &errors)
// BNE t0, zero, -3 = (op=5 << 26) | (rs=8 << 21) | (rt=0 << 16) | 0xFFFD
// = 0x14000000 | 0x01000000 | 0xFFFD
// = 0x1500FFFD
check_bool("brB: success", res.success, true)
check_bool("brB: success", success, true)
check_int ("brB: relocs len",len(relocs), 0) // resolved
check_int ("brB: errors len",len(errors), 0)
check_word("brB: BNE -3w", load_word_be(code[:], 8), 0x1500FFFD)
@@ -177,10 +177,10 @@ run_encoder_tests :: proc() {
mips.inst_none(.NOP),
mips.inst_r_r_r(.ADD, mips.T2, mips.T2, mips.T2),
}
res := mips.encode(insts, label_defs[:], code[:], &relocs, &errors)
byte_count, success := mips.encode(insts, label_defs[:], code[:], &relocs, &errors)
// BEQ t0,t1,+2 = 0x10000000 | (8<<21) | (9<<16) | 0x0002 = 0x11090002
check_bool("brF: success", res.success, true)
check_bool("brF: success", success, true)
check_int ("brF: relocs len",len(relocs), 0)
check_word("brF: BEQ +2w", load_word_be(code[:], 0), 0x11090002)
check_int ("brF: label_def[0]", int(label_defs[0]), 12)
@@ -209,10 +209,10 @@ run_encoder_tests :: proc() {
mips.inst_none(.NOP),
mips.inst_none(.NOP), // target
}
res := mips.encode(insts, label_defs[:], code[:], &relocs, &errors,
byte_count, success := mips.encode(insts, label_defs[:], code[:], &relocs, &errors,
base_address = 0x80000000)
check_bool("J: success", res.success, true)
check_bool("J: success", success, true)
check_int ("J: relocs len", len(relocs), 0)
check_word("J: encoded", load_word_be(code[:], 0), 0x08000004)
}
@@ -230,9 +230,9 @@ run_encoder_tests :: proc() {
insts := []mips.Instruction{
mips.inst_branch2(.BEQ, mips.T0, mips.T1, 0),
}
res := mips.encode(insts, label_defs[:], code[:], &relocs, &errors)
byte_count, success := mips.encode(insts, label_defs[:], code[:], &relocs, &errors)
check_bool("unres: success", res.success, true)
check_bool("unres: success", success, true)
check_int ("unres: relocs left", len(relocs), 1) // kept for linker
check_int ("unres: errors len", len(errors), 0)
}
@@ -256,9 +256,9 @@ run_encoder_tests :: proc() {
insts := []mips.Instruction{
mips.inst_branch2(.BEQ, mips.T0, mips.T1, 0),
}
res := mips.encode(insts, label_defs[:], code[:], &relocs, &errors)
byte_count, success := mips.encode(insts, label_defs[:], code[:], &relocs, &errors)
check_bool("OOR: success", res.success, false) // had errors
check_bool("OOR: success", success, false) // had errors
check_int ("OOR: relocs len", len(relocs), 0) // patched (truncated)
check_int ("OOR: errors len", len(errors), 1)
// Error code should be LABEL_OUT_OF_RANGE.
@@ -277,9 +277,9 @@ run_encoder_tests :: proc() {
mips.inst_none(.NOP),
}
small_code: [4]u8
res := mips.encode(insts, nil, small_code[:], &relocs, &errors)
byte_count, success := mips.encode(insts, nil, small_code[:], &relocs, &errors)
check_bool("OVF: success", res.success, false)
check_bool("OVF: success", success, false)
check_int ("OVF: errors len", len(errors), 1)
check_bool("OVF: error code",
len(errors) > 0 && errors[0].code == .BUFFER_OVERFLOW,
@@ -300,9 +300,9 @@ run_encoder_tests :: proc() {
insts := []mips.Instruction{
mips.inst_r_r_r(.ADD_S, mips.F4, mips.F5, mips.F6),
}
res := mips.encode(insts, nil, code[:], &relocs, &errors)
byte_count, success := mips.encode(insts, nil, code[:], &relocs, &errors)
check_bool("FPU: success", res.success, true)
check_bool("FPU: success", success, true)
check_word("FPU: ADD.S", load_word_be(code[:], 0), 0x46062900)
}
@@ -316,9 +316,9 @@ run_encoder_tests :: proc() {
insts := []mips.Instruction{
mips.inst_none(.RTPS),
}
res := mips.encode(insts, nil, code[:], &relocs, &errors)
byte_count, success := mips.encode(insts, nil, code[:], &relocs, &errors)
check_bool("GTE: success", res.success, true)
check_bool("GTE: success", success, true)
check_word("GTE: RTPS", load_word_be(code[:], 0), 0x4A000001)
}

View File

@@ -38,8 +38,8 @@ encode_and_print :: proc(
defer delete(relocs)
defer delete(errors)
eres := mips.encode(insts, label_defs, code[:], &relocs, &errors)
if !eres.success { return "<encode failed>" }
byte_count, esuccess := mips.encode(insts, label_defs, code[:], &relocs, &errors)
if !esuccess { return "<encode failed>" }
dec_insts: [dynamic]mips.Instruction
dec_info: [dynamic]mips.Instruction_Info
@@ -49,9 +49,9 @@ encode_and_print :: proc(
defer delete(dec_labels)
clear(&errors)
dres := mips.decode(code[:eres.byte_count], nil,
_, dsuccess := mips.decode(code[:byte_count], nil,
&dec_insts, &dec_info, &dec_labels, &errors)
if !dres.success { return "<decode failed>" }
if !dsuccess { return "<decode failed>" }
sb := strings.builder_make(context.temp_allocator)
mips.sbprint(&sb, dec_insts[:], dec_info[:], dec_labels[:])
@@ -162,7 +162,7 @@ run_printer_tests :: proc() {
mips.inst_none(.NOP),
mips.inst_branch2(.BEQ, mips.T0, mips.T1, 0),
}
eres := mips.encode(insts, ld[:], code[:], &relocs, &errors)
byte_count, _ := mips.encode(insts, ld[:], code[:], &relocs, &errors)
dec_insts: [dynamic]mips.Instruction
dec_info: [dynamic]mips.Instruction_Info
@@ -171,7 +171,7 @@ run_printer_tests :: proc() {
defer delete(dec_info)
defer delete(dec_labels)
clear(&errors)
mips.decode(code[:eres.byte_count], nil, &dec_insts, &dec_info, &dec_labels, &errors)
mips.decode(code[:byte_count], nil, &dec_insts, &dec_info, &dec_labels, &errors)
names: map[u32]string
defer delete(names)
@@ -197,7 +197,7 @@ run_printer_tests :: proc() {
errors: [dynamic]mips.Error
defer delete(relocs)
defer delete(errors)
eres := mips.encode(insts, nil, code[:], &relocs, &errors)
byte_count, _ := mips.encode(insts, nil, code[:], &relocs, &errors)
dec_insts: [dynamic]mips.Instruction
dec_info: [dynamic]mips.Instruction_Info
@@ -206,7 +206,7 @@ run_printer_tests :: proc() {
defer delete(dec_info)
defer delete(dec_labels)
clear(&errors)
mips.decode(code[:eres.byte_count], nil, &dec_insts, &dec_info, &dec_labels, &errors)
mips.decode(code[:byte_count], nil, &dec_insts, &dec_info, &dec_labels, &errors)
out := mips.aprint(dec_insts[:], dec_info[:], dec_labels[:],
nil, &opts, nil, context.temp_allocator)

View File

@@ -44,23 +44,22 @@ decode :: proc(
label_defs: ^[dynamic]Label_Definition,
errors: ^[dynamic]Error,
cpu: CPU = .NMOS,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_bytes := u32(len(data))
errors_start := u32(len(errors))
pending_branches: [dynamic]isa.Branch_Target
defer delete(pending_branches)
pc: u32 = 0
for pc < n_bytes {
for byte_count < n_bytes {
inst: Instruction
info: Instruction_Info
entry_idx, consumed := decode_one_inline(data, pc, n_bytes, cpu, &inst, &info)
entry_idx, consumed := decode_one_inline(data, byte_count, n_bytes, cpu, &inst, &info)
if entry_idx < 0 {
append(errors, Error{inst_idx = pc, code = .INVALID_OPCODE})
append(errors, Error{inst_idx = byte_count, code = .INVALID_OPCODE})
inst = Instruction{mnemonic = .INVALID, length = 1}
info = Instruction_Info{offset = pc}
info = Instruction_Info{offset = byte_count}
consumed = 1
} else {
inst_idx_for_branches := u32(len(instructions))
@@ -78,11 +77,12 @@ decode :: proc(
append(instructions, inst)
append(inst_info, info)
pc += consumed
byte_count += consumed
}
isa.infer_labels_from_branches(pending_branches[:], pc, label_defs, relocs)
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
isa.infer_labels_from_branches(pending_branches[:], byte_count, label_defs, relocs)
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -48,47 +48,43 @@ encode :: proc(
errors: ^[dynamic]Error,
resolve: bool = true,
base_address: u64 = 0,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_inst := u32(len(instructions))
errors_start := u32(len(errors))
pending_start := u32(len(relocs))
inst_offsets := make([]u32, n_inst, context.temp_allocator)
pc: u32 = 0
// ---- PASS 1 -----------------------------------------------------------
for i in 0..<n_inst {
inst_offsets[i] = pc
inst_offsets[i] = byte_count
inst := &instructions[i]
form, ok := find_form_inline(inst, u16(i), errors)
if !ok {
return Result{byte_count = pc, success = false}
}
form := find_form_inline(inst, u16(i), errors) or_return
if pc + u32(form.length) > u32(len(code)) {
if byte_count + u32(form.length) > u32(len(code)) {
append(errors, Error{inst_idx = i, code = .BUFFER_OVERFLOW})
return Result{byte_count = pc, success = false}
return
}
// Opcode byte
code[pc] = form.opcode
code[byte_count] = form.opcode
// Operand bytes
if form.enc[0] != .NONE { pack_operand_inline(&inst.ops[0], form.enc[0], pc, u16(i), code, relocs) }
if form.enc[1] != .NONE { pack_operand_inline(&inst.ops[1], form.enc[1], pc, u16(i), code, relocs) }
if form.enc[2] != .NONE { pack_operand_inline(&inst.ops[2], form.enc[2], pc, u16(i), code, relocs) }
if form.enc[0] != .NONE { pack_operand_inline(&inst.ops[0], form.enc[0], byte_count, u16(i), code, relocs) }
if form.enc[1] != .NONE { pack_operand_inline(&inst.ops[1], form.enc[1], byte_count, u16(i), code, relocs) }
if form.enc[2] != .NONE { pack_operand_inline(&inst.ops[2], form.enc[2], byte_count, u16(i), code, relocs) }
inst.length = form.length
pc += u32(form.length)
byte_count += u32(form.length)
}
// ---- PASS 1.5: inst-index -> byte-offset -----------------------------
isa.rewrite_label_defs_to_offsets(label_defs, inst_offsets)
if !resolve {
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// ---- PASS 2: resolve relocations --------------------------------------
@@ -108,7 +104,8 @@ encode :: proc(
resize(relocs, int(write_idx))
}
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -27,7 +27,6 @@ import "../isa"
// 65C816 (SNES, Apple IIgs) is a separate 16/24-bit ISA and lives in a
// sibling subpackage if/when added.
Result :: isa.Result
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition

View File

@@ -69,8 +69,8 @@ encode_one :: proc(insts: []m.Instruction) -> ([]u8, bool) {
@(static) errors: [dynamic]m.Error
clear(&relocs); clear(&errors)
for i in 0..<len(code) { code[i] = 0 }
r := m.encode(insts, nil, code[:], &relocs, &errors)
return code[:r.byte_count], r.success
byte_count, success := m.encode(insts, nil, code[:], &relocs, &errors)
return code[:byte_count], success
}
run_pipeline_tests :: proc() {
@@ -139,10 +139,10 @@ run_pipeline_tests :: proc() {
m.inst_rel(.BNE, 0),
m.inst_none(.RTS),
}
r := m.encode(insts, ld[:], code[:], &relocs, &errors)
ok("br: encode ok", r.success)
byte_count, success := m.encode(insts, ld[:], code[:], &relocs, &errors)
ok("br: encode ok", success)
// BNE rel byte at code[4]; target = 0, next_pc = 5, rel = -5.
eq_bytes("br: bytes", code[:r.byte_count],
eq_bytes("br: bytes", code[:byte_count],
{0xA5, 0x42, 0xCA, 0xD0, 0xFB, 0x60})
// label_defs[0] should be byte offset 0.
ok("br: label_def[0] = 0", int(ld[0]) == 0)
@@ -173,10 +173,10 @@ run_pipeline_tests :: proc() {
mnemonic = .JMP, operand_count = 1, length = 0,
ops = {m.op_label(0, 2), {}, {}},
}
r := m.encode(insts, ld[:], code[:], &relocs, &errors,
byte_count, success := m.encode(insts, ld[:], code[:], &relocs, &errors,
base_address = 0x8000)
ok("jmp lbl: encode ok", r.success)
eq_bytes("jmp lbl: bytes", code[:r.byte_count],
ok("jmp lbl: encode ok", success)
eq_bytes("jmp lbl: bytes", code[:byte_count],
{0xEA, 0x4C, 0x04, 0x80, 0x60})
}
@@ -195,8 +195,8 @@ run_pipeline_tests :: proc() {
m.inst_rel(.BNE, 0),
m.inst_none(.RTS),
}
r := m.encode(src, ld[:], code[:], &relocs, &errors)
ok("rt: encode ok", r.success)
byte_count, success := m.encode(src, ld[:], code[:], &relocs, &errors)
ok("rt: encode ok", success)
d_insts: [dynamic]m.Instruction
d_info: [dynamic]m.Instruction_Info
@@ -205,9 +205,9 @@ run_pipeline_tests :: proc() {
defer delete(d_info)
defer delete(d_labels)
clear(&errors)
d := m.decode(code[:r.byte_count], nil,
_, dsuccess := m.decode(code[:byte_count], nil,
&d_insts, &d_info, &d_labels, &errors)
ok("rt: decode ok", d.success)
ok("rt: decode ok", dsuccess)
ok("rt: 4 insts", len(d_insts) == 4)
ok("rt: LDA", d_insts[0].mnemonic == .LDA)
ok("rt: BNE", d_insts[2].mnemonic == .BNE)
@@ -235,7 +235,7 @@ run_pipeline_tests :: proc() {
m.inst_m(.STZ, m.mem_abs(0x1234)), // 65C02 STZ
m.inst_rel(.BRA, 0), // 65C02 BRA
}
r := m.encode(src, ld[:], code[:], &relocs, &errors)
byte_count, success := m.encode(src, ld[:], code[:], &relocs, &errors)
d_insts: [dynamic]m.Instruction
d_info: [dynamic]m.Instruction_Info
@@ -245,7 +245,7 @@ run_pipeline_tests :: proc() {
defer delete(d_labels)
clear(&errors)
// Decode in 65C02 mode -- $B2 is LDA(zp), $9C is STZ, $80 is BRA.
m.decode(code[:r.byte_count], nil,
m.decode(code[:byte_count], nil,
&d_insts, &d_info, &d_labels, &errors, cpu = .CMOS_65C02)
names: map[u32]string
@@ -291,16 +291,16 @@ run_pipeline_tests :: proc() {
src := []m.Instruction{
m.inst_block(.TII, 0x4000, 0x2000, 0x100),
}
r := m.encode(src, nil, code[:], &relocs, &errors)
ok("huc tii: encode ok", r.success)
ok("huc tii: 7 bytes", int(r.byte_count) == 7)
byte_count, success := m.encode(src, nil, code[:], &relocs, &errors)
ok("huc tii: encode ok", success)
ok("huc tii: 7 bytes", int(byte_count) == 7)
d_insts: [dynamic]m.Instruction
d_info: [dynamic]m.Instruction_Info
d_labels: [dynamic]m.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
m.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors,
m.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors,
cpu = .HUC6280)
ok("huc tii: decode TII", len(d_insts) >= 1 && d_insts[0].mnemonic == .TII)
ok("huc tii: 3 operands", d_insts[0].operand_count == 3)
@@ -321,7 +321,7 @@ encode_or_fail :: proc(insts: []m.Instruction) -> []u8 {
@(static) errors: [dynamic]m.Error
clear(&relocs); clear(&errors)
for i in 0..<len(code) { code[i] = 0 }
r := m.encode(insts, nil, code[:], &relocs, &errors)
if !r.success { return nil }
return code[:r.byte_count]
byte_count, success := m.encode(insts, nil, code[:], &relocs, &errors)
if !success { return nil }
return code[:byte_count]
}

View File

@@ -33,7 +33,7 @@ decode :: proc(
label_defs: ^[dynamic]Label_Definition,
errors: ^[dynamic]Error,
state: Assumed_State = NATIVE_16,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_bytes := u32(len(data))
errors_start := u32(len(errors))
@@ -44,16 +44,15 @@ decode :: proc(
eff := state
if eff.e { eff.m = true; eff.x = true }
pc: u32 = 0
for pc < n_bytes {
for byte_count < n_bytes {
inst: Instruction
info: Instruction_Info
entry_idx, consumed := decode_one_inline(data, pc, n_bytes, eff, &inst, &info)
entry_idx, consumed := decode_one_inline(data, byte_count, n_bytes, eff, &inst, &info)
if entry_idx < 0 {
append(errors, Error{inst_idx = pc, code = .INVALID_OPCODE})
append(errors, Error{inst_idx = byte_count, code = .INVALID_OPCODE})
inst = Instruction{mnemonic = .INVALID, length = 1}
info = Instruction_Info{offset = pc}
info = Instruction_Info{offset = byte_count}
consumed = 1
} else {
inst_idx_for_branches := u32(len(instructions))
@@ -71,11 +70,12 @@ decode :: proc(
append(instructions, inst)
append(inst_info, info)
pc += consumed
byte_count += consumed
}
isa.infer_labels_from_branches(pending_branches[:], pc, label_defs, relocs)
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
isa.infer_labels_from_branches(pending_branches[:], byte_count, label_defs, relocs)
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -40,38 +40,36 @@ encode :: proc(
errors: ^[dynamic]Error,
resolve: bool = true,
base_address: u64 = 0,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_inst := u32(len(instructions))
errors_start := u32(len(errors))
pending_start := u32(len(relocs))
inst_offsets := make([]u32, n_inst, context.temp_allocator)
pc: u32 = 0
for i in 0..<n_inst {
inst_offsets[i] = pc
inst_offsets[i] = byte_count
inst := &instructions[i]
form, ok := find_form_inline(inst, u16(i), errors)
if !ok { return Result{byte_count = pc, success = false} }
form := find_form_inline(inst, u16(i), errors) or_return
if pc + u32(form.length) > u32(len(code)) {
if byte_count + u32(form.length) > u32(len(code)) {
append(errors, Error{inst_idx = i, code = .BUFFER_OVERFLOW})
return Result{byte_count = pc, success = false}
return
}
code[pc] = form.opcode
if form.enc[0] != .NONE { pack_operand_inline(&inst.ops[0], form.enc[0], pc, u16(i), code, relocs) }
if form.enc[1] != .NONE { pack_operand_inline(&inst.ops[1], form.enc[1], pc, u16(i), code, relocs) }
code[byte_count] = form.opcode
if form.enc[0] != .NONE { pack_operand_inline(&inst.ops[0], form.enc[0], byte_count, u16(i), code, relocs) }
if form.enc[1] != .NONE { pack_operand_inline(&inst.ops[1], form.enc[1], byte_count, u16(i), code, relocs) }
inst.length = form.length
pc += u32(form.length)
byte_count += u32(form.length)
}
isa.rewrite_label_defs_to_offsets(label_defs, inst_offsets)
if !resolve {
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
n_relocs := u32(len(relocs))
@@ -90,7 +88,8 @@ encode :: proc(
resize(relocs, int(write_idx))
}
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -30,7 +30,6 @@ import "../isa"
// passes `e=true` in Assumed_State so the decoder picks the 8-bit immediate
// forms.
Result :: isa.Result
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition

View File

@@ -68,9 +68,9 @@ enc :: proc(insts: []m.Instruction) -> []u8 {
@(static) errors: [dynamic]m.Error
clear(&relocs); clear(&errors)
for i in 0..<len(code) { code[i] = 0 }
r := m.encode(insts, nil, code[:], &relocs, &errors)
if !r.success { return nil }
return code[:r.byte_count]
byte_count, success := m.encode(insts, nil, code[:], &relocs, &errors)
if !success { return nil }
return code[:byte_count]
}
main :: proc() {
@@ -140,10 +140,10 @@ main :: proc() {
m.inst_none(.NOP), // 1 byte
m.inst_none(.RTS), // 1 byte (target)
}
r := m.encode(insts, ld[:], code[:], &relocs, &errors)
ok("BRL encode ok", r.success)
byte_count, success := m.encode(insts, ld[:], code[:], &relocs, &errors)
ok("BRL encode ok", success)
// BRL at pc=0, target at byte 4. next_pc = 3. rel = 4-3 = 1.
eq_bytes("BRL forward", code[:r.byte_count], {0x82, 0x01, 0x00, 0xEA, 0x60})
eq_bytes("BRL forward", code[:byte_count], {0x82, 0x01, 0x00, 0xEA, 0x60})
}
// ---- 7. PER (push effective PC-rel, 16-bit signed) -------------------
@@ -165,9 +165,9 @@ main :: proc() {
m.inst_none(.NOP),
m.inst_none(.RTS),
}
r := m.encode(insts, ld[:], code[:], &relocs, &errors)
ok("PER encode ok", r.success)
eq_bytes("PER forward", code[:r.byte_count], {0x62, 0x01, 0x00, 0xEA, 0x60})
byte_count, success := m.encode(insts, ld[:], code[:], &relocs, &errors)
ok("PER encode ok", success)
eq_bytes("PER forward", code[:byte_count], {0x62, 0x01, 0x00, 0xEA, 0x60})
}
// ---- 8. Round-trip: encode -> decode in 16-bit native, print ---------
@@ -186,18 +186,18 @@ main :: proc() {
m.inst_m(.STA, m.mem_long(0x7E1000)), // 4 bytes
m.inst_rel(.BRA, 0), // 2 bytes back to loop
}
r := m.encode(src, ld[:], code[:], &relocs, &errors)
ok("rt: encode ok", r.success)
byte_count, success := m.encode(src, ld[:], code[:], &relocs, &errors)
ok("rt: encode ok", success)
d_insts: [dynamic]m.Instruction
d_info: [dynamic]m.Instruction_Info
d_labels: [dynamic]m.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
d := m.decode(code[:r.byte_count], nil,
_, dsuccess := m.decode(code[:byte_count], nil,
&d_insts, &d_info, &d_labels, &errors,
state = m.NATIVE_16)
ok("rt: decode ok", d.success)
ok("rt: decode ok", dsuccess)
ok("rt: 3 insts", len(d_insts) == 3)
ok("rt: LDA", d_insts[0].mnemonic == .LDA)
ok("rt: STA", d_insts[1].mnemonic == .STA)
@@ -261,14 +261,14 @@ main :: proc() {
m.inst_m(.JML, m.mem_abs_ind_long(0xFFFC)),
m.inst_m(.LDA, m.mem_sr_ind_y(0x10)),
}
r := m.encode(src, nil, code[:], &relocs, &errors)
byte_count, success := m.encode(src, nil, code[:], &relocs, &errors)
d_insts: [dynamic]m.Instruction
d_info: [dynamic]m.Instruction_Info
d_labels: [dynamic]m.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
m.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors,
m.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors,
state = m.NATIVE_16)
text := m.aprint(d_insts[:], d_info[:], d_labels[:],

View File

@@ -34,36 +34,35 @@ decode :: proc(
label_defs: ^[dynamic]Label_Definition,
errors: ^[dynamic]Error,
mode: Mode = .PPC32,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_bytes := u32(len(data)) & ~u32(3)
errors_start := u32(len(errors))
pending_branches: [dynamic]isa.Branch_Target
defer delete(pending_branches)
pc: u32 = 0
for pc < n_bytes {
if pc + 4 > n_bytes { break }
word := read_u32_be(data, pc)
for byte_count < n_bytes {
if byte_count + 4 > n_bytes { break }
word := read_u32_be(data, byte_count)
// Detect prefixed instruction: primary opcode = 1.
is_prefixed := (word >> 26) == 0x01
ilen: u32 = 4
suffix: u32 = 0
if is_prefixed {
if pc + 8 > n_bytes { break }
suffix = read_u32_be(data, pc + 4)
if byte_count + 8 > n_bytes { break }
suffix = read_u32_be(data, byte_count + 4)
ilen = 8
}
inst: Instruction
info: Instruction_Info
info.offset = pc
info.offset = byte_count
match_word := is_prefixed ? suffix : word
prefix_word := is_prefixed ? word : 0
if !find_and_decode(match_word, prefix_word, is_prefixed, mode, &inst, &info) {
append(errors, Error{inst_idx = pc, code = .INVALID_OPCODE})
append(errors, Error{inst_idx = byte_count, code = .INVALID_OPCODE})
inst = Instruction{mnemonic = .INVALID, length = u8(ilen), mode = mode}
} else {
inst.length = u8(ilen)
@@ -74,7 +73,7 @@ decode :: proc(
if op.kind == .RELATIVE && op.relative >= 0 {
// The unpacker stores PC-relative byte offsets; convert
// to absolute target = pc + relative.
target := u32(i32(pc) + i32(op.relative))
target := u32(i32(byte_count) + i32(op.relative))
append(&pending_branches, isa.Branch_Target{
inst_idx = inst_idx,
op_idx = slot,
@@ -86,11 +85,12 @@ decode :: proc(
append(instructions, inst)
append(inst_info, info)
pc += ilen
byte_count += ilen
}
isa.infer_labels_from_branches(pending_branches[:], pc, label_defs, relocs)
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
isa.infer_labels_from_branches(pending_branches[:], byte_count, label_defs, relocs)
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -34,28 +34,24 @@ encode :: proc(
errors: ^[dynamic]Error,
resolve: bool = true,
base_address: u64 = 0,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_inst := u32(len(instructions))
if u32(len(code)) < n_inst * MAX_INST_SIZE {
append(errors, Error{inst_idx = 0, code = .BUFFER_OVERFLOW})
return Result{byte_count = 0, success = false}
return
}
errors_start := u32(len(errors))
pending_start := u32(len(relocs))
pc: u32 = 0
inst_pc := make([]u32, n_inst, context.temp_allocator)
// ---- PASS 1 ------------------------------------------------------------
for i in 0..<n_inst {
inst_pc[i] = pc
inst_pc[i] = byte_count
inst := &instructions[i]
ok := encode_one_inline(inst, pc, code, u16(i), relocs, errors)
if !ok {
return Result{byte_count = pc, success = false}
}
pc += u32(inst.length)
encode_one_inline(inst, byte_count, code, u16(i), relocs, errors) or_return
byte_count += u32(inst.length)
}
// ---- PASS 1.5: label instruction-idx -> byte offset --------------------
@@ -71,7 +67,8 @@ encode :: proc(
}
if !resolve {
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// ---- PASS 2: resolve relocations ---------------------------------------
@@ -87,7 +84,8 @@ encode :: proc(
}
if write_idx != n_relocs { resize(relocs, int(write_idx)) }
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -67,7 +67,6 @@ import "../isa"
// XX4-form vsx 4-op (xxsel) -- + XT + XA + XB + XC + XO + CX + AX + BX + TX
// MLS / MMIRR / 8RR / 8LS prefixed (ISA 3.1) -- 8-byte (4 prefix + 4 suffix)
Result :: isa.Result
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition

View File

@@ -16,15 +16,15 @@ check :: proc(name: string, instructions: []p.Instruction, label_defs: []isa.Lab
errors: [dynamic]p.Error
defer delete(relocs); defer delete(errors)
r := p.encode(instructions, label_defs, code, &relocs, &errors)
if !r.success {
byte_count, success := p.encode(instructions, label_defs, code, &relocs, &errors)
if !success {
fmt.printf(" [FAIL] %s: encode failed, %d errors\n", name, len(errors))
for e in errors { fmt.printf(" code=%v inst_idx=%d\n", e.code, e.inst_idx) }
fail += 1
return
}
if int(r.byte_count) != len(want_bytes) {
fmt.printf(" [FAIL] %s: wrong byte count (got %d, want %d)\n", name, r.byte_count, len(want_bytes))
if int(byte_count) != len(want_bytes) {
fmt.printf(" [FAIL] %s: wrong byte count (got %d, want %d)\n", name, byte_count, len(want_bytes))
fail += 1
return
}

View File

@@ -47,8 +47,8 @@ run_decode_sweep :: proc() {
errors: [dynamic]p.Error
defer delete(decoded); defer delete(info); defer delete(labels); defer delete(errors)
r := p.decode(buf[:ilen], nil, &decoded, &info, &labels, &errors, .PPC64)
if !r.success || len(decoded) == 0 || (len(decoded) > 0 && decoded[0].mnemonic == .INVALID) {
byte_count, success := p.decode(buf[:ilen], nil, &decoded, &info, &labels, &errors, .PPC64)
if !success || len(decoded) == 0 || (len(decoded) > 0 && decoded[0].mnemonic == .INVALID) {
if missing_mn_total < 20 {
fmt.printf(" [UNDECODABLE] %v word=%08x prefixed=%v\n", mn, word, f.flags.prefixed)
}

View File

@@ -182,8 +182,8 @@ test_form :: proc(mn: p.Mnemonic, fi: int, f: ^p.Encoding, fails: ^[dynamic]stri
instructions := []p.Instruction{inst}
label_defs: []isa.Label_Definition
r := p.encode(instructions, label_defs, code, &relocs, &errors)
if !r.success {
byte_count, success := p.encode(instructions, label_defs, code, &relocs, &errors)
if !success {
stats.encode_fail += 1
if len(fails) < 100 { append(fails, fmt.aprintf("ENCODE_FAIL %v[%d] errors=%d", mn, fi, len(errors))) }
return
@@ -196,8 +196,8 @@ test_form :: proc(mn: p.Mnemonic, fi: int, f: ^p.Encoding, fails: ^[dynamic]stri
dec_errors: [dynamic]p.Error
defer delete(decoded); defer delete(dec_info); defer delete(dec_labels); defer delete(dec_errors)
dr := p.decode(code[:r.byte_count], nil, &decoded, &dec_info, &dec_labels, &dec_errors, f.mode)
if !dr.success || len(decoded) == 0 || decoded[0].mnemonic == .INVALID {
dbyte_count, dsuccess := p.decode(code[:byte_count], nil, &decoded, &dec_info, &dec_labels, &dec_errors, f.mode)
if !dsuccess || len(decoded) == 0 || decoded[0].mnemonic == .INVALID {
stats.decode_fail += 1
if len(fails) < 100 {
append(fails, fmt.aprintf("DECODE_FAIL %v[%d] bytes=%02x%02x%02x%02x",
@@ -213,8 +213,8 @@ test_form :: proc(mn: p.Mnemonic, fi: int, f: ^p.Encoding, fails: ^[dynamic]stri
re_errors: [dynamic]p.Error
defer delete(re_relocs); defer delete(re_errors)
rr := p.encode(decoded[:], dec_labels[:], code2, &re_relocs, &re_errors)
if !rr.success {
rrbyte_count, rrsuccess := p.encode(decoded[:], dec_labels[:], code2, &re_relocs, &re_errors)
if !rrsuccess {
stats.reencode_fail += 1
if len(fails) < 100 {
append(fails, fmt.aprintf("REENCODE_FAIL %v[%d] decoded_mn=%v",
@@ -223,14 +223,14 @@ test_form :: proc(mn: p.Mnemonic, fi: int, f: ^p.Encoding, fails: ^[dynamic]stri
return
}
if rr.byte_count != r.byte_count {
if rrbyte_count != byte_count {
stats.byte_mismatch += 1
if len(fails) < 100 {
append(fails, fmt.aprintf("LEN_MISMATCH %v[%d] orig=%d re=%d", mn, fi, r.byte_count, rr.byte_count))
append(fails, fmt.aprintf("LEN_MISMATCH %v[%d] orig=%d re=%d", mn, fi, byte_count, rrbyte_count))
}
return
}
for i in 0..<r.byte_count {
for i in 0..<byte_count {
if code[i] != code2[i] {
stats.byte_mismatch += 1
if len(fails) < 100 {

View File

@@ -18,17 +18,17 @@ check_roundtrip :: proc(name: string, inst: p.Instruction, want_bytes: []u8) {
defer delete(errors)
instructions := []p.Instruction{inst}
r := p.encode(instructions, label_defs, code, &relocs, &errors)
if !r.success {
byte_count, success := p.encode(instructions, label_defs, code, &relocs, &errors)
if !success {
fmt.printf(" [FAIL] %s: encode failed (%d errors)\n", name, len(errors))
for e in errors { fmt.printf(" code=%v inst_idx=%d\n", e.code, e.inst_idx) }
fail_count += 1
return
}
if int(r.byte_count) != len(want_bytes) {
if int(byte_count) != len(want_bytes) {
fmt.printf(" [FAIL] %s: wrong byte count (got %d, want %d)\n",
name, r.byte_count, len(want_bytes))
name, byte_count, len(want_bytes))
fail_count += 1
return
}
@@ -55,8 +55,8 @@ check_roundtrip :: proc(name: string, inst: p.Instruction, want_bytes: []u8) {
defer delete(dec_label_defs)
defer delete(dec_errors)
dr := p.decode(code[:r.byte_count], nil, &decoded, &decoded_info, &dec_label_defs, &dec_errors)
if !dr.success {
dbyte_count, dsuccess := p.decode(code[:byte_count], nil, &decoded, &decoded_info, &dec_label_defs, &dec_errors)
if !dsuccess {
fmt.printf(" [FAIL] %s: decode failed\n", name)
fail_count += 1
return

View File

@@ -25,37 +25,36 @@ decode :: proc(
inst_info: ^[dynamic]Instruction_Info,
label_defs: ^[dynamic]Label_Definition,
errors: ^[dynamic]Error,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_bytes := u32(len(data)) & ~u32(1)
errors_start := u32(len(errors))
pending_branches: [dynamic]isa.Branch_Target
defer delete(pending_branches)
pc: u32 = 0
for pc < n_bytes {
if pc + 2 > n_bytes { break }
hw := u32(read_u16_be(data, pc))
for byte_count < n_bytes {
if byte_count + 2 > n_bytes { break }
hw := u32(read_u16_be(data, byte_count))
inst: Instruction
info: Instruction_Info
info.offset = pc
info.offset = byte_count
matched := try_decode(hw, true, &inst, &info)
ilen: u32 = 2
if !matched {
if pc + 4 > n_bytes {
append(errors, Error{inst_idx = pc, code = .BUFFER_TOO_SHORT})
if byte_count + 4 > n_bytes {
append(errors, Error{inst_idx = byte_count, code = .BUFFER_TOO_SHORT})
break
}
word := (hw << 16) | u32(read_u16_be(data, pc + 2))
word := (hw << 16) | u32(read_u16_be(data, byte_count + 2))
matched = try_decode(word, false, &inst, &info)
ilen = 4
}
if !matched {
append(errors, Error{inst_idx = pc, code = .INVALID_OPCODE})
append(errors, Error{inst_idx = byte_count, code = .INVALID_OPCODE})
inst = Instruction{mnemonic = .INVALID, length = 2, mode = .PPC32_VLE}
ilen = 2
} else {
@@ -70,7 +69,7 @@ decode :: proc(
append(&pending_branches, isa.Branch_Target{
inst_idx = inst_idx,
op_idx = slot,
target = u32(i32(pc) + i32(op.relative)),
target = u32(i32(byte_count) + i32(op.relative)),
})
}
}
@@ -78,11 +77,12 @@ decode :: proc(
append(instructions, inst)
append(inst_info, info)
pc += ilen
byte_count += ilen
}
isa.infer_labels_from_branches(pending_branches[:], pc, label_defs, relocs)
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
isa.infer_labels_from_branches(pending_branches[:], byte_count, label_defs, relocs)
ok = u32(len(errors)) == errors_start
return
}
@(private="file")

View File

@@ -25,24 +25,23 @@ encode :: proc(
errors: ^[dynamic]Error,
resolve: bool = true,
base_address: u64 = 0,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_inst := u32(len(instructions))
if u32(len(code)) < n_inst * MAX_INST_SIZE {
append(errors, Error{inst_idx = 0, code = .BUFFER_OVERFLOW})
return Result{byte_count = 0, success = false}
return
}
errors_start := u32(len(errors))
pending_start := u32(len(relocs))
pc: u32 = 0
inst_pc := make([]u32, n_inst, context.temp_allocator)
for i in 0..<n_inst {
inst_pc[i] = pc
inst_pc[i] = byte_count
inst := &instructions[i]
ok := encode_one_inline(inst, pc, code, u16(i), relocs, errors)
if !ok { return Result{byte_count = pc, success = false} }
pc += u32(inst.length)
encode_one_inline(inst, byte_count, code, u16(i), relocs, errors) or_return
byte_count += u32(inst.length)
}
for &ld in label_defs {
@@ -57,7 +56,8 @@ encode :: proc(
}
if !resolve {
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
n_relocs := u32(len(relocs))
@@ -72,7 +72,8 @@ encode :: proc(
}
if write_idx != n_relocs { resize(relocs, int(write_idx)) }
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
@(private="file")

View File

@@ -17,7 +17,6 @@ import "../isa"
// VLE shares the same GPR/SPR register model as standard PowerPC; types are
// duplicated here to keep ppc_vle as a standalone sibling package.
Result :: isa.Result
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition

View File

@@ -14,15 +14,15 @@ check :: proc(name: string, instructions: []v.Instruction, label_defs: []isa.Lab
errors: [dynamic]v.Error
defer delete(relocs); defer delete(errors)
r := v.encode(instructions, label_defs, code, &relocs, &errors)
if !r.success {
byte_count, success := v.encode(instructions, label_defs, code, &relocs, &errors)
if !success {
fmt.printf(" [FAIL] %s: encode failed\n", name)
for e in errors { fmt.printf(" code=%v inst_idx=%d\n", e.code, e.inst_idx) }
fail_count += 1
return
}
if int(r.byte_count) != len(want) {
fmt.printf(" [FAIL] %s: byte_count %d (want %d)\n", name, r.byte_count, len(want))
if int(byte_count) != len(want) {
fmt.printf(" [FAIL] %s: byte_count %d (want %d)\n", name, byte_count, len(want))
fail_count += 1
return
}
@@ -65,14 +65,14 @@ run_branch_test :: proc() {
relocs: [dynamic]v.Relocation
errors: [dynamic]v.Error
defer delete(relocs); defer delete(errors)
r := v.encode(instructions[:], label_defs[:], code, &relocs, &errors)
if !r.success {
byte_count, success := v.encode(instructions[:], label_defs[:], code, &relocs, &errors)
if !success {
fmt.printf(" [FAIL] se_b+label: encode failed\n")
for e in errors { fmt.printf(" code=%v\n", e.code) }
fail_count += 1
} else {
fmt.printf(" [ok] se_b+label: %d bytes, bytes=", r.byte_count)
for i in 0..<r.byte_count { fmt.printf("%02x", code[i]) }
fmt.printf(" [ok] se_b+label: %d bytes, bytes=", byte_count)
for i in 0..<byte_count { fmt.printf("%02x", code[i]) }
fmt.println()
ok_count += 1
}
@@ -90,13 +90,13 @@ run_branch_test :: proc() {
relocs: [dynamic]v.Relocation
errors: [dynamic]v.Error
defer delete(relocs); defer delete(errors)
r := v.encode(instructions[:], label_defs[:], code, &relocs, &errors)
if !r.success {
byte_count, success := v.encode(instructions[:], label_defs[:], code, &relocs, &errors)
if !success {
fmt.printf(" [FAIL] e_b+label: encode failed\n")
fail_count += 1
} else {
fmt.printf(" [ok] e_b+label: %d bytes, bytes=", r.byte_count)
for i in 0..<r.byte_count { fmt.printf("%02x", code[i]) }
fmt.printf(" [ok] e_b+label: %d bytes, bytes=", byte_count)
for i in 0..<byte_count { fmt.printf("%02x", code[i]) }
fmt.println()
ok_count += 1
}

View File

@@ -14,14 +14,14 @@ check :: proc(name: string, instructions: []v.Instruction, label_defs: []isa.Lab
errors: [dynamic]v.Error
defer delete(relocs); defer delete(errors)
r := v.encode(instructions, label_defs, code, &relocs, &errors)
if !r.success {
byte_count, success := v.encode(instructions, label_defs, code, &relocs, &errors)
if !success {
fmt.printf(" [FAIL] %s: encode failed\n", name)
fail_count += 1
return
}
if int(r.byte_count) != len(want) {
fmt.printf(" [FAIL] %s: bc=%d want=%d\n", name, r.byte_count, len(want))
if int(byte_count) != len(want) {
fmt.printf(" [FAIL] %s: bc=%d want=%d\n", name, byte_count, len(want))
fail_count += 1
return
}
@@ -61,14 +61,14 @@ run_cond_branch :: proc() {
relocs: [dynamic]v.Relocation
errors: [dynamic]v.Error
defer delete(relocs); defer delete(errors)
r := v.encode(instructions[:], label_defs[:], code, &relocs, &errors)
if !r.success {
byte_count, success := v.encode(instructions[:], label_defs[:], code, &relocs, &errors)
if !success {
fmt.printf(" [FAIL] e_bc encode failed (%d errors)\n", len(errors))
for e in errors { fmt.printf(" code=%v\n", e.code) }
fail_count += 1
} else {
fmt.printf(" [ok] e_bc 12, cr0[lt], L ")
for i in 0..<r.byte_count { fmt.printf("%02x", code[i]) }
for i in 0..<byte_count { fmt.printf("%02x", code[i]) }
fmt.println()
ok_count += 1
}

View File

@@ -17,14 +17,14 @@ check :: proc(name: string, instructions: []v.Instruction, label_defs: []isa.Lab
errors: [dynamic]v.Error
defer delete(relocs); defer delete(errors)
r := v.encode(instructions, label_defs, code, &relocs, &errors)
if !r.success {
byte_count, success := v.encode(instructions, label_defs, code, &relocs, &errors)
if !success {
fmt.printf(" [FAIL] %s: encode failed\n", name)
fail_count += 1
return
}
if int(r.byte_count) != len(want_bytes) {
fmt.printf(" [FAIL] %s: byte_count=%d want=%d\n", name, r.byte_count, len(want_bytes))
if int(byte_count) != len(want_bytes) {
fmt.printf(" [FAIL] %s: byte_count=%d want=%d\n", name, byte_count, len(want_bytes))
fail_count += 1
return
}
@@ -45,8 +45,8 @@ check :: proc(name: string, instructions: []v.Instruction, label_defs: []isa.Lab
dec_labs: [dynamic]v.Label_Definition
dec_errs: [dynamic]v.Error
defer delete(decoded); defer delete(info); defer delete(dec_labs); defer delete(dec_errs)
dr := v.decode(code[:r.byte_count], relocs[:], &decoded, &info, &dec_labs, &dec_errs)
if !dr.success {
dbyte_count, dsuccess := v.decode(code[:byte_count], relocs[:], &decoded, &info, &dec_labs, &dec_errs)
if !dsuccess {
fmt.printf(" [FAIL] %s: decode failed\n", name)
fail_count += 1
return
@@ -65,7 +65,7 @@ check :: proc(name: string, instructions: []v.Instruction, label_defs: []isa.Lab
return
}
}
fmt.printf(" [ok] %-35s %d bytes, %d insts\n", name, r.byte_count, len(decoded))
fmt.printf(" [ok] %-35s %d bytes, %d insts\n", name, byte_count, len(decoded))
ok_count += 1
}

View File

@@ -69,15 +69,15 @@ run_extension :: proc() {
relocs: [dynamic]v.Relocation; defer delete(relocs)
errors: [dynamic]v.Error; defer delete(errors)
labels: []isa.Label_Definition
r := v.encode(instructions[:], labels, code, &relocs, &errors)
check("mixed 16/32-bit sequence encodes", r.success)
byte_count, success := v.encode(instructions[:], labels, code, &relocs, &errors)
check("mixed 16/32-bit sequence encodes", success)
decoded: [dynamic]v.Instruction; defer delete(decoded)
info: [dynamic]v.Instruction_Info; defer delete(info)
dlabs: [dynamic]v.Label_Definition; defer delete(dlabs)
derrs: [dynamic]v.Error; defer delete(derrs)
dr := v.decode(code[:r.byte_count], nil, &decoded, &info, &dlabs, &derrs)
check("mixed sequence decodes", dr.success)
dbyte_count, dsuccess := v.decode(code[:byte_count], nil, &decoded, &info, &dlabs, &derrs)
check("mixed sequence decodes", dsuccess)
check("decoded 3 instructions", len(decoded) == 3)
check("first inst is SE_MR (16-bit)", decoded[0].length == 2)
check("second inst is E_ADDI (32-bit)", decoded[1].length == 4)

View File

@@ -43,8 +43,8 @@ test_one :: proc(mn: v.Mnemonic, fi: int, f: ^v.Encoding) {
defer delete(relocs); defer delete(errors)
instructions := []v.Instruction{inst}
r := v.encode(instructions, label_defs, code, &relocs, &errors)
if !r.success {
byte_count, success := v.encode(instructions, label_defs, code, &relocs, &errors)
if !success {
stats.encode_fail += 1
return
}
@@ -55,8 +55,8 @@ test_one :: proc(mn: v.Mnemonic, fi: int, f: ^v.Encoding) {
dec_errors: [dynamic]v.Error
defer delete(decoded); defer delete(info); defer delete(dec_labels); defer delete(dec_errors)
dr := v.decode(code[:r.byte_count], nil, &decoded, &info, &dec_labels, &dec_errors)
if !dr.success || len(decoded) == 0 || decoded[0].mnemonic == .INVALID {
dbyte_count, dsuccess := v.decode(code[:byte_count], nil, &decoded, &info, &dec_labels, &dec_errors)
if !dsuccess || len(decoded) == 0 || decoded[0].mnemonic == .INVALID {
stats.decode_fail += 1
return
}
@@ -67,19 +67,19 @@ test_one :: proc(mn: v.Mnemonic, fi: int, f: ^v.Encoding) {
re_errors: [dynamic]v.Error
defer delete(re_relocs); defer delete(re_errors)
rr := v.encode(decoded[:], dec_labels[:], code2, &re_relocs, &re_errors)
if !rr.success || rr.byte_count != r.byte_count {
rrbyte_count, rrsuccess := v.encode(decoded[:], dec_labels[:], code2, &re_relocs, &re_errors)
if !rrsuccess || rrbyte_count != byte_count {
stats.byte_mismatch += 1
return
}
for i in 0..<r.byte_count {
for i in 0..<byte_count {
if code[i] != code2[i] {
stats.byte_mismatch += 1
if stats.byte_mismatch <= 10 {
fmt.printf(" [BYTE_MISMATCH] %v: orig=", mn)
for j in 0..<r.byte_count { fmt.printf("%02x", code[j]) }
for j in 0..<byte_count { fmt.printf("%02x", code[j]) }
fmt.printf(" re=")
for j in 0..<r.byte_count { fmt.printf("%02x", code2[j]) }
for j in 0..<byte_count { fmt.printf("%02x", code2[j]) }
fmt.printf(" decoded=%v\n", decoded[0].mnemonic)
}
return

View File

@@ -16,14 +16,14 @@ check :: proc(name: string, inst: v.Instruction, want_bytes: []u8) {
defer delete(relocs); defer delete(errors)
instructions := []v.Instruction{inst}
r := v.encode(instructions, label_defs, code, &relocs, &errors)
if !r.success {
byte_count, success := v.encode(instructions, label_defs, code, &relocs, &errors)
if !success {
fmt.printf(" [FAIL] %s: encode failed\n", name)
fail_count += 1
return
}
if int(r.byte_count) != len(want_bytes) {
fmt.printf(" [FAIL] %s: byte_count=%d (want %d)\n", name, r.byte_count, len(want_bytes))
if int(byte_count) != len(want_bytes) {
fmt.printf(" [FAIL] %s: byte_count=%d (want %d)\n", name, byte_count, len(want_bytes))
fail_count += 1
return
}

View File

@@ -15,15 +15,15 @@ check_encode :: proc(name: string, inst: v.Instruction, want_bytes: []u8) {
defer delete(relocs); defer delete(errors)
instructions := []v.Instruction{inst}
r := v.encode(instructions, label_defs, code, &relocs, &errors)
if !r.success {
byte_count, success := v.encode(instructions, label_defs, code, &relocs, &errors)
if !success {
fmt.printf(" [FAIL] %s: encode failed (%d errors)\n", name, len(errors))
for e in errors { fmt.printf(" code=%v\n", e.code) }
fail_count += 1
return
}
if int(r.byte_count) != len(want_bytes) {
fmt.printf(" [FAIL] %s: byte count %d != %d\n", name, r.byte_count, len(want_bytes))
if int(byte_count) != len(want_bytes) {
fmt.printf(" [FAIL] %s: byte count %d != %d\n", name, byte_count, len(want_bytes))
fail_count += 1
return
}
@@ -46,8 +46,8 @@ check_encode :: proc(name: string, inst: v.Instruction, want_bytes: []u8) {
dec_errors: [dynamic]v.Error
defer delete(decoded); defer delete(info); defer delete(dec_labels); defer delete(dec_errors)
dr := v.decode(code[:r.byte_count], nil, &decoded, &info, &dec_labels, &dec_errors)
if !dr.success {
dbyte_count, dsuccess := v.decode(code[:byte_count], nil, &decoded, &info, &dec_labels, &dec_errors)
if !dsuccess {
fmt.printf(" [FAIL] %s: decode failed\n", name)
fail_count += 1
return

View File

@@ -43,35 +43,34 @@ decode :: proc(
label_defs: ^[dynamic]Label_Definition,
errors: ^[dynamic]Error,
xlen: XLEN = .RV64,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_bytes := u32(len(data)) & ~u32(1) // align to halfword (RVC is 2-byte)
errors_start := u32(len(errors))
pending_branches: [dynamic]isa.Branch_Target
defer delete(pending_branches)
pc: u32 = 0
for pc < n_bytes {
for byte_count < n_bytes {
// Read the first halfword; bits[1:0] != 11 means compressed (2 bytes).
hword_lo := read_u16_le(data, pc)
hword_lo := read_u16_le(data, byte_count)
ilen: u32 = 4
word: u32
if (hword_lo & 0x3) != 0x3 {
ilen = 2
word = u32(hword_lo)
} else {
if pc + 4 > n_bytes { break }
word = read_u32_le(data, pc)
if byte_count + 4 > n_bytes { break }
word = read_u32_le(data, byte_count)
}
inst: Instruction
info: Instruction_Info
entry_idx := decode_one_inline(word, pc, xlen, ilen == 2, &inst, &info)
entry_idx := decode_one_inline(word, byte_count, xlen, ilen == 2, &inst, &info)
if entry_idx < 0 {
append(errors, Error{inst_idx = pc, code = .INVALID_OPCODE})
append(errors, Error{inst_idx = byte_count, code = .INVALID_OPCODE})
inst = Instruction{mnemonic = .INVALID, length = u8(ilen)}
info = Instruction_Info{offset = pc}
info = Instruction_Info{offset = byte_count}
} else {
inst.length = u8(ilen)
inst_idx_for_branches := u32(len(instructions))
@@ -89,11 +88,12 @@ decode :: proc(
append(instructions, inst)
append(inst_info, info)
pc += ilen
byte_count += ilen
}
isa.infer_labels_from_branches(pending_branches[:], pc, label_defs, relocs)
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
isa.infer_labels_from_branches(pending_branches[:], byte_count, label_defs, relocs)
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -41,16 +41,15 @@ encode :: proc(
errors: ^[dynamic]Error,
resolve: bool = true,
base_address: u64 = 0,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_inst := u32(len(instructions))
if u32(len(code)) < n_inst * 4 {
append(errors, Error{inst_idx = 0, code = .BUFFER_OVERFLOW})
return Result{byte_count = 0, success = false}
return
}
errors_start := u32(len(errors))
pending_start := u32(len(relocs))
pc: u32 = 0
// Per-instruction byte offsets so label_defs (instruction-indexed)
// can be rewritten to byte-offset after pass 1 in the presence of
@@ -59,16 +58,15 @@ encode :: proc(
// ---- PASS 1 -----------------------------------------------------------
for i in 0..<n_inst {
inst_pc[i] = pc
inst_pc[i] = byte_count
inst := &instructions[i]
word, ilen, ok := encode_one_inline(inst, pc, u16(i), relocs, errors)
if !ok { return Result{byte_count = pc, success = false} }
word, ilen := encode_one_inline(inst, byte_count, u16(i), relocs, errors) or_return
if ilen == 2 {
write_u16_le(code, pc, u16(word))
write_u16_le(code, byte_count, u16(word))
} else {
write_u32_le(code, pc, word)
write_u32_le(code, byte_count, word)
}
pc += u32(ilen)
byte_count += u32(ilen)
}
// ---- PASS 1.5: rewrite label_defs (instruction-index -> byte-offset) --
@@ -84,7 +82,8 @@ encode :: proc(
}
if !resolve {
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// ---- PASS 2: resolve relocations --------------------------------------
@@ -100,7 +99,8 @@ encode :: proc(
}
if write_idx != n_relocs { resize(relocs, int(write_idx)) }
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -31,7 +31,6 @@ import "../isa"
// pattern, `mask` flags which positions are static. Operand-driven bits
// land in the zero positions of `bits`.
Result :: isa.Result
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition

View File

@@ -91,8 +91,8 @@ run_pipeline_tests :: proc() {
rv.inst_u (.LUI, rv.T0, 0x12345),
rv.inst_u (.AUIPC,rv.RA, 0x10),
}
r := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("R/I/U: encode ok", r.success)
byte_count, success := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("R/I/U: encode ok", success)
eq_word("R: ADD t0,a0,a1", load_le(code[:], 0), 0x00B502B3)
eq_word("I: ADDI sp,sp,-16", load_le(code[:], 4), 0xFF010113)
eq_word("U: LUI t0,0x12345", load_le(code[:], 8), 0x123452B7)
@@ -114,8 +114,8 @@ run_pipeline_tests :: proc() {
rv.inst_load (.LW, rv.T0, rv.mem(rv.SP, 100)),
rv.inst_store(.SW, rv.A0, rv.mem(rv.SP, -8)),
}
r := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("LW/SW: encode ok", r.success)
byte_count, success := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("LW/SW: encode ok", success)
eq_word("LW t0,100(sp)", load_le(code[:], 0), 0x06412283)
eq_word("SW a0,-8(sp)", load_le(code[:], 4), 0xFEA12C23)
}
@@ -144,8 +144,8 @@ run_pipeline_tests :: proc() {
rv.inst_branch(.BNE, rv.T0, rv.ZERO, 0),
rv.inst_r_r_i(.ADDI, rv.ZERO, rv.ZERO, 0),
}
r := rv.encode(insts, ld[:], code[:], &relocs, &errors)
ok("br: encode ok", r.success)
byte_count, success := rv.encode(insts, ld[:], code[:], &relocs, &errors)
ok("br: encode ok", success)
ok("br: no leftover relocs", len(relocs) == 0)
eq_word("BNE rel=-8", load_le(code[:], 8), 0xFE029CE3)
}
@@ -170,9 +170,9 @@ run_pipeline_tests :: proc() {
rv.inst_r_r_i(.ADDI, rv.SP, rv.SP, 0),
rv.inst_jalr(rv.ZERO, rv.RA, 0),
}
r := rv.encode(insts, ld[:], code[:], &relocs, &errors)
ok("JAL: encode ok", r.success)
eq_word("JAL ra,+8", load_le(code[:], 0), 0x008000EF)
byte_count, success := rv.encode(insts, ld[:], code[:], &relocs, &errors)
ok("JAL: encode ok", success)
eq_word("JAL ra,+8", load_le(code[:], 0), 0x008000EF)
}
// ---- 5. Round-trip: encode -> decode -> print -----------------------
@@ -189,16 +189,16 @@ run_pipeline_tests :: proc() {
rv.inst_load (.LW, rv.A0, rv.mem(rv.SP, 0)),
rv.inst_branch(.BNE, rv.T0, rv.ZERO, 0),
}
r := rv.encode(src, ld[:], code[:], &relocs, &errors)
ok("rt: encode ok", r.success)
byte_count, success := rv.encode(src, ld[:], code[:], &relocs, &errors)
ok("rt: encode ok", success)
d_insts: [dynamic]rv.Instruction
d_info: [dynamic]rv.Instruction_Info
d_labels: [dynamic]rv.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
d := rv.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("rt: decode ok", d.success)
dbyte_count, dsuccess := rv.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("rt: decode ok", dsuccess)
ok("rt: 3 insts", len(d_insts) == 3)
ok("rt: ADDI", d_insts[0].mnemonic == .ADDI)
ok("rt: LW", d_insts[1].mnemonic == .LW)
@@ -223,15 +223,15 @@ run_pipeline_tests :: proc() {
rv.inst_r_r_r(.DIV, rv.T1, rv.A0, rv.A1),
rv.inst_r_r_r(.REMU, rv.T2, rv.A0, rv.A1),
}
r := rv.encode(src, nil, code[:], &relocs, &errors)
ok("M: encode ok", r.success)
byte_count, success := rv.encode(src, nil, code[:], &relocs, &errors)
ok("M: encode ok", success)
d_insts: [dynamic]rv.Instruction
d_info: [dynamic]rv.Instruction_Info
d_labels: [dynamic]rv.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
rv.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
rv.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("M: MUL", d_insts[0].mnemonic == .MUL)
ok("M: DIV", d_insts[1].mnemonic == .DIV)
ok("M: REMU", d_insts[2].mnemonic == .REMU)
@@ -258,8 +258,8 @@ run_pipeline_tests :: proc() {
},
},
}
r := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("A: encode ok", r.success)
byte_count, success := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("A: encode ok", success)
eq_word("A: AMOADD.W", load_le(code[:], 0), 0x00B522AF)
}
@@ -283,8 +283,8 @@ run_pipeline_tests :: proc() {
},
},
}
r := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("F: encode ok", r.success)
byte_count, success := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("F: encode ok", success)
eq_word("F: FADD.S", load_le(code[:], 0), 0x00C58553)
d_insts: [dynamic]rv.Instruction
@@ -292,7 +292,7 @@ run_pipeline_tests :: proc() {
d_labels: [dynamic]rv.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
rv.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
rv.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
text := rv.aprint(d_insts[:], d_info[:], d_labels[:],
nil, nil, nil, context.temp_allocator)
@@ -317,8 +317,8 @@ run_pipeline_tests :: proc() {
rv.Register(rv.REG_FPR | 12), // fa2
rv.Register(rv.REG_FPR | 13)), // fa3
}
r := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("D: encode ok", r.success)
byte_count, success := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("D: encode ok", success)
eq_word("D: FMADD.D", load_le(code[:], 0), 0x6AC58543)
}
@@ -333,8 +333,8 @@ run_pipeline_tests :: proc() {
insts := []rv.Instruction{
rv.inst_csr(.CSRRW, rv.A0, 0xF14, rv.ZERO),
}
r := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("CSR: encode ok", r.success)
byte_count, success := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("CSR: encode ok", success)
eq_word("CSR: csrrw", load_le(code[:], 0), 0xF1401573)
}
@@ -382,9 +382,9 @@ run_pipeline_tests :: proc() {
ops = {rv.op_reg(rv.A2), rv.op_reg(rv.A3), {}, {}},
},
}
r := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("C: encode ok", r.success)
ok("C: byte count", r.byte_count == 8)
byte_count, success := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("C: encode ok", success)
ok("C: byte count", byte_count == 8)
get_hw := proc(buf: []u8, off: u32) -> u16 {
return u16(buf[off]) | (u16(buf[off+1]) << 8)
}
@@ -402,7 +402,7 @@ run_pipeline_tests :: proc() {
d_labels: [dynamic]rv.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
rv.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
rv.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("C: decode 4 insts", len(d_insts) == 4)
ok("C: NOP", len(d_insts) >= 1 && d_insts[0].mnemonic == .C_NOP)
ok("C: LI", len(d_insts) >= 2 && d_insts[1].mnemonic == .C_LI)
@@ -429,16 +429,16 @@ run_pipeline_tests :: proc() {
ops = {rv.op_reg(rv.A2), rv.op_reg(rv.A0), {}, {}},
},
}
r := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("C: mixed encode", r.success)
ok("C: mixed bytes = 8", r.byte_count == 8)
byte_count, success := rv.encode(insts, nil, code[:], &relocs, &errors)
ok("C: mixed encode", success)
ok("C: mixed bytes = 8", byte_count == 8)
d_insts: [dynamic]rv.Instruction
d_info: [dynamic]rv.Instruction_Info
d_labels: [dynamic]rv.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
rv.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
rv.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("C: mixed decode 3", len(d_insts) == 3)
ok("C: [0]=C.LI len=2", len(d_insts) >= 1 && d_insts[0].mnemonic == .C_LI && d_insts[0].length == 2)
ok("C: [1]=ADDI len=4", len(d_insts) >= 2 && d_insts[1].mnemonic == .ADDI && d_insts[1].length == 4)
@@ -483,9 +483,9 @@ run_pipeline_tests :: proc() {
},
rv.inst_none(.C_NOP),
}
r := rv.encode(insts, ld[:], code[:], &relocs, &errors)
ok("C.BEQZ: encode", r.success)
ok("C.BEQZ: byte_count=8", r.byte_count == 8)
byte_count, success := rv.encode(insts, ld[:], code[:], &relocs, &errors)
ok("C.BEQZ: encode", success)
ok("C.BEQZ: byte_count=8", byte_count == 8)
get_hw := proc(buf: []u8, off: u32) -> u16 {
return u16(buf[off]) | (u16(buf[off+1]) << 8)
@@ -502,7 +502,7 @@ run_pipeline_tests :: proc() {
d_labels: [dynamic]rv.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
rv.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
rv.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("C.BEQZ: decode count", len(d_insts) == 4)
ok("C.BEQZ: [0] mnemonic", len(d_insts) >= 1 && d_insts[0].mnemonic == .C_BEQZ)
ok("C.BEQZ: target = 6", len(d_insts) >= 1 && d_insts[0].ops[1].kind == .RELATIVE && u32(d_insts[0].ops[0].relative + d_insts[0].ops[1].relative)*0+ u32(d_insts[0].ops[1].relative) == 6)
@@ -552,9 +552,9 @@ run_pipeline_tests :: proc() {
ops = {rv.op_label(0), {}, {}, {}},
},
}
r := rv.encode(insts, ld[:], code[:], &relocs, &errors)
ok("C.J: encode", r.success)
ok("C.J: byte_count=10", r.byte_count == 10)
byte_count, success := rv.encode(insts, ld[:], code[:], &relocs, &errors)
ok("C.J: encode", success)
ok("C.J: byte_count=10", byte_count == 10)
get_hw := proc(buf: []u8, off: u32) -> u16 {
return u16(buf[off]) | (u16(buf[off+1]) << 8)
@@ -571,7 +571,7 @@ run_pipeline_tests :: proc() {
d_labels: [dynamic]rv.Label_Definition
defer delete(d_insts); defer delete(d_info); defer delete(d_labels)
clear(&errors)
rv.decode(code[:r.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
rv.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("C.J: decode count", len(d_insts) == 4)
ok("C.J: [3] mnemonic", len(d_insts) >= 4 && d_insts[3].mnemonic == .C_J)
ok("C.J: target = 0", len(d_insts) >= 4 && d_insts[3].ops[0].kind == .RELATIVE && u32(d_insts[3].ops[0].relative) == 0)
@@ -601,8 +601,8 @@ run_pipeline_tests :: proc() {
// Target at byte 2 + 64*4 = 258 -- out of range for 9-bit signed (max 254)
append(&long_insts, rv.inst_none(.C_NOP))
r := rv.encode(long_insts[:], ld[:], big_code[:], &relocs, &errors)
ok("C.BEQZ out-of-range: error", !r.success && len(errors) > 0)
byte_count, success := rv.encode(long_insts[:], ld[:], big_code[:], &relocs, &errors)
ok("C.BEQZ out-of-range: error", !success && len(errors) > 0)
if len(errors) > 0 {
ok("C.BEQZ out-of-range: code", errors[0].code == .LABEL_OUT_OF_RANGE)
}

View File

@@ -29,25 +29,24 @@ decode :: proc(
label_defs: ^[dynamic]Label_Definition,
errors: ^[dynamic]Error,
endianness: Endianness = .BIG,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_bytes := u32(len(data)) & ~u32(3) // drop dangling tail
errors_start := u32(len(errors))
pending_branches: [dynamic]isa.Branch_Target
defer delete(pending_branches)
pc: u32 = 0
for pc < n_bytes {
word := read_u32(data, pc, endianness)
for byte_count < n_bytes {
word := read_u32(data, byte_count, endianness)
inst: Instruction
info: Instruction_Info
entry_idx := decode_one_inline(word, pc, &inst, &info)
entry_idx := decode_one_inline(word, byte_count, &inst, &info)
if entry_idx < 0 {
append(errors, Error{inst_idx = pc, code = .INVALID_OPCODE})
append(errors, Error{inst_idx = byte_count, code = .INVALID_OPCODE})
inst = Instruction{mnemonic = .INVALID, length = 4}
info = Instruction_Info{offset = pc}
info = Instruction_Info{offset = byte_count}
} else {
inst_idx_for_branches := u32(len(instructions))
for slot in 0..<inst.operand_count {
@@ -64,12 +63,13 @@ decode :: proc(
append(instructions, inst)
append(inst_info, info)
pc += 4
byte_count += 4
}
isa.infer_labels_from_branches(pending_branches[:], pc, label_defs, relocs)
isa.infer_labels_from_branches(pending_branches[:], byte_count, label_defs, relocs)
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -38,25 +38,21 @@ encode :: proc(
endianness: Endianness = .BIG,
resolve: bool = true,
base_address: u64 = 0,
) -> Result {
) -> (byte_count: u32, ok: bool) {
n_inst := u32(len(instructions))
if u32(len(code)) < n_inst * 4 {
append(errors, Error{inst_idx = 0, code = .BUFFER_OVERFLOW})
return Result{byte_count = 0, success = false}
return
}
errors_start := u32(len(errors))
pending_start := u32(len(relocs))
pc: u32 = 0
for i in 0..<n_inst {
inst := &instructions[i]
word, ok := encode_one_inline(inst, pc, u16(i), relocs, errors)
if !ok {
return Result{byte_count = pc, success = false}
}
write_u32(code, pc, word, endianness)
pc += 4
word := encode_one_inline(inst, byte_count, u16(i), relocs, errors) or_return
write_u32(code, byte_count, word, endianness)
byte_count += 4
}
// PASS 1.5
@@ -67,7 +63,8 @@ encode :: proc(
}
if !resolve {
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// PASS 2
@@ -87,7 +84,8 @@ encode :: proc(
resize(relocs, int(write_idx))
}
return Result{byte_count = pc, success = u32(len(errors)) == errors_start}
ok = u32(len(errors)) == errors_start
return
}
// =============================================================================

View File

@@ -32,7 +32,6 @@ import "../isa"
// is sign-extended and pre-scaled by element size, so the effective
// range is `±64 × element_size` bytes.
Result :: isa.Result
Error :: isa.Error
Error_Code :: isa.Error_Code
Label_Definition :: isa.Label_Definition

View File

@@ -76,8 +76,8 @@ run_rsp_pipeline_tests :: proc() {
rsp.inst_r_m (.LW, rsp.T0, rsp.mem(rsp.SP, 16)),
rsp.inst_none (.NOP),
}
e := rsp.encode(insts, nil, code[:], &relocs, &errors)
ok ("scalar: encode success", e.success)
byte_count, success := rsp.encode(insts, nil, code[:], &relocs, &errors)
ok ("scalar: encode success", success)
eq_word ("scalar: ADD word", load_be(code[:], 0), 0x012A4020)
eq_word ("scalar: ADDIU word", load_be(code[:], 4), 0x25280064)
eq_word ("scalar: LW word", load_be(code[:], 8), 0x8FA80010)
@@ -90,8 +90,8 @@ run_rsp_pipeline_tests :: proc() {
defer delete(d_info)
defer delete(d_labels)
clear(&errors)
d := rsp.decode(code[:e.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("scalar: decode success", d.success)
dbyte_count, dsuccess := rsp.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("scalar: decode success", dsuccess)
ok("scalar: 4 insts", len(d_insts) == 4)
ok("scalar[0] ADD", d_insts[0].mnemonic == .ADD)
ok("scalar[3] NOP", d_insts[3].mnemonic == .NOP)
@@ -115,8 +115,8 @@ run_rsp_pipeline_tests :: proc() {
insts := []rsp.Instruction{
rsp.inst_v_v_v(.VMULF, rsp.VR0, rsp.VR1, rsp.VR2, 3),
}
e := rsp.encode(insts, nil, code[:], &relocs, &errors)
ok ("vu: encode", e.success)
byte_count, success := rsp.encode(insts, nil, code[:], &relocs, &errors)
ok ("vu: encode", success)
eq_word("vu: VMULF", load_be(code[:], 0), 0x4A620800)
d_insts: [dynamic]rsp.Instruction
@@ -126,8 +126,8 @@ run_rsp_pipeline_tests :: proc() {
defer delete(d_info)
defer delete(d_labels)
clear(&errors)
d := rsp.decode(code[:e.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("vu: decode", d.success)
dbyte_count, dsuccess := rsp.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("vu: decode", dsuccess)
ok("vu: VMULF mnem", d_insts[0].mnemonic == .VMULF)
i0 := d_insts[0]
ok("vu: vd=$v0", i0.ops[0].reg == rsp.VR0)
@@ -153,8 +153,8 @@ run_rsp_pipeline_tests :: proc() {
insts := []rsp.Instruction{
rsp.inst_v_vmem(.LQV, rsp.VR4, rsp.vmem(rsp.T0, 0, 16)),
}
e := rsp.encode(insts, nil, code[:], &relocs, &errors)
ok ("vls: encode", e.success)
byte_count, success := rsp.encode(insts, nil, code[:], &relocs, &errors)
ok ("vls: encode", success)
eq_word("vls: LQV", load_be(code[:], 0), 0xC9042010)
d_insts: [dynamic]rsp.Instruction
@@ -164,8 +164,8 @@ run_rsp_pipeline_tests :: proc() {
defer delete(d_info)
defer delete(d_labels)
clear(&errors)
d := rsp.decode(code[:e.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("vls: decode", d.success)
dbyte_count, dsuccess := rsp.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("vls: decode", dsuccess)
ok("vls: LQV mnem", d_insts[0].mnemonic == .LQV)
i0 := d_insts[0]
ok("vls: vt=$v4", i0.ops[0].reg == rsp.VR4)
@@ -195,8 +195,8 @@ run_rsp_pipeline_tests :: proc() {
rsp.inst_branch2(.BNE, rsp.T0, rsp.ZERO, 0),
rsp.inst_none(.NOP),
}
e := rsp.encode(insts, ld_in[:], code[:], &relocs, &errors)
ok("br: encode", e.success)
byte_count, success := rsp.encode(insts, ld_in[:], code[:], &relocs, &errors)
ok("br: encode", success)
eq_word("br: BNE word", load_be(code[:], 8), 0x1500FFFD)
d_insts: [dynamic]rsp.Instruction
@@ -206,8 +206,8 @@ run_rsp_pipeline_tests :: proc() {
defer delete(d_info)
defer delete(d_labels)
clear(&errors)
d := rsp.decode(code[:e.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("br: decode", d.success)
dbyte_count, dsuccess := rsp.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("br: decode", dsuccess)
ok("br: 1 label inferred", len(d_labels) == 1)
ok("br: label at byte 0", int(d_labels[0]) == 0)
@@ -233,8 +233,8 @@ run_rsp_pipeline_tests :: proc() {
ops = {rsp.op_reg(rsp.T0), rsp.op_reg(rsp.VCO), {}, {}},
},
}
e := rsp.encode(insts, nil, code[:], &relocs, &errors)
ok("cop2c: encode", e.success)
byte_count, success := rsp.encode(insts, nil, code[:], &relocs, &errors)
ok("cop2c: encode", success)
eq_word("cop2c: CFC2",load_be(code[:], 0), 0x48480000)
d_insts: [dynamic]rsp.Instruction
@@ -244,8 +244,8 @@ run_rsp_pipeline_tests :: proc() {
defer delete(d_info)
defer delete(d_labels)
clear(&errors)
d := rsp.decode(code[:e.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("cop2c: decode", d.success)
dbyte_count, dsuccess := rsp.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
ok("cop2c: decode", dsuccess)
ok("cop2c: CFC2 mnem",d_insts[0].mnemonic == .CFC2)
text := rsp.aprint(d_insts[:], d_info[:], d_labels[:],
@@ -268,8 +268,8 @@ run_rsp_pipeline_tests :: proc() {
ops = {rsp.op_reg(rsp.T0), rsp.op_reg(rsp.Register(rsp.REG_CP0 | 4)), {}, {}},
},
}
e := rsp.encode(insts, nil, code[:], &relocs, &errors)
ok("cp0: encode", e.success)
byte_count, success := rsp.encode(insts, nil, code[:], &relocs, &errors)
ok("cp0: encode", success)
eq_word("cp0: MTC0", load_be(code[:], 0), 0x40882000)
d_insts: [dynamic]rsp.Instruction
@@ -279,7 +279,7 @@ run_rsp_pipeline_tests :: proc() {
defer delete(d_info)
defer delete(d_labels)
clear(&errors)
rsp.decode(code[:e.byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
rsp.decode(code[:byte_count], nil, &d_insts, &d_info, &d_labels, &errors)
text := rsp.aprint(d_insts[:], d_info[:], d_labels[:],
nil, nil, nil, context.temp_allocator)

View File

@@ -1016,21 +1016,20 @@ decode :: proc(
label_defs: ^[dynamic]Label_Definition,
errors: ^[dynamic]Error,
mode: Mode = ._64,
) -> Result {
) -> (byte_count: u32, ok: bool) {
if mode == ._16 {
// Real-mode decoding is not implemented; the ModRM addressing
// model differs from protected/long mode and needs a separate
// decode path. See Mode enum comment in encoding_types.odin.
fmt.panicf("x64.decode: Mode._16 (real mode) is not yet supported")
}
ok = true
if len(data) == 0 {
return Result{success = true}
return
}
data_length := len(data)
pos := 0
has_errors := false
data_length := u32(len(data))
// Track branch targets for label inference (resolved in pass 2 by isa).
pending_branches: [dynamic]isa.Branch_Target
@@ -1040,26 +1039,26 @@ decode :: proc(
// PASS 1: Decode all instructions, collect branch targets
// =========================================================================
for pos < data_length {
for byte_count < data_length {
inst: Instruction
info: Instruction_Info
// Record offset
info.offset = u32(pos)
info.offset = byte_count
// Initialize decoder state
state := Decoder_State{
data = data[pos:],
data = data[byte_count:],
position = 0,
mode = mode,
segment = NONE,
mode = mode,
segment = NONE,
}
// Phase 1: Parse prefixes
err := decode_prefixes(&state)
if err != nil {
append(errors, Error{inst_idx = u32(len(instructions)), code = err})
has_errors = true
ok = false
break
}
@@ -1092,7 +1091,7 @@ decode :: proc(
append(instructions, inst)
append(inst_info, info)
pos += state.position
byte_count += u32(state.position)
continue
}
}
@@ -1103,7 +1102,7 @@ decode :: proc(
entry, vex_entry, err = decode_opcode(&state)
if err != nil {
append(errors, Error{inst_idx = u32(len(instructions)), code = err})
has_errors = true
ok = false
break
}
@@ -1114,12 +1113,12 @@ decode :: proc(
inst, err = decode_operands(&state, entry)
} else {
append(errors, Error{inst_idx = u32(len(instructions)), code = .INVALID_OPCODE})
has_errors = true
ok = false
break
}
if err != nil {
append(errors, Error{inst_idx = u32(len(instructions)), code = err})
has_errors = true
ok = false
break
}
@@ -1164,7 +1163,7 @@ decode :: proc(
}
// Check for relative operands and record pending branch targets
inst_end := pos + state.position
inst_end := byte_count + u32(state.position)
for op_idx in 0..<inst.operand_count {
op := &inst.ops[op_idx]
if op.kind == .RELATIVE {
@@ -1183,17 +1182,14 @@ decode :: proc(
append(instructions, inst)
append(inst_info, info)
pos += state.position
byte_count += u32(state.position)
}
// =========================================================================
// PASS 2: Infer labels from branch targets within the decoded region
// =========================================================================
isa.infer_labels_from_branches(pending_branches[:], u32(pos), label_defs, relocs)
isa.infer_labels_from_branches(pending_branches[:], byte_count, label_defs, relocs)
return Result{
byte_count = u32(pos),
success = !has_errors,
}
return
}

View File

@@ -65,7 +65,7 @@ encode :: proc(
resolve: bool = true,
base_address: u64 = 0,
mode: Mode = ._64, // i386 vs x86-64 mode
) -> Result {
) -> (byte_count: u32, ok: bool) {
if mode == ._16 {
// Real-mode encoding is not implemented; the ModRM addressing
// model differs from protected/long mode and needs a separate
@@ -73,8 +73,7 @@ encode :: proc(
fmt.panicf("x64.encode: Mode._16 (real mode) is not yet supported")
}
code_pos: u32 = 0
has_errors := false
ok = true
// Temp storage for pending relocations (before resolution)
pending_relocations: [dynamic]Relocation
@@ -91,19 +90,19 @@ encode :: proc(
for &inst, instruction_index in instructions {
// Record this instruction's byte offset
inst_offsets[instruction_index] = code_pos
inst_offsets[instruction_index] = byte_count
// Validate operand_count bounds
if inst.operand_count > 4 {
append(errors, Error{u32(instruction_index), .INVALID_OPERAND_COUNT, {}})
has_errors = true
ok = false
continue
}
// Check buffer space
if code_pos + MAX_INST_SIZE > u32(len(code)) {
if byte_count + MAX_INST_SIZE > u32(len(code)) {
append(errors, Error{u32(instruction_index), .BUFFER_OVERFLOW, {}})
has_errors = true
ok = false
continue
}
@@ -136,7 +135,7 @@ encode :: proc(
}
if invalid {
append(errors, Error{u32(instruction_index), .OPERAND_MISMATCH, {}})
has_errors = true
ok = false
continue
}
}
@@ -145,7 +144,7 @@ encode :: proc(
encodings := encoding_forms(inst.mnemonic)
if len(encodings) == 0 {
append(errors, Error{u32(instruction_index), .INVALID_MNEMONIC, {}})
has_errors = true
ok = false
continue
}
@@ -160,7 +159,7 @@ encode :: proc(
if matched_enc == nil {
append(errors, Error{u32(instruction_index), .NO_MATCHING_ENCODING, {}})
has_errors = true
ok = false
continue
}
@@ -169,7 +168,7 @@ encode :: proc(
// =====================================================================
enc := matched_enc
out := code[code_pos:]
out := code[byte_count:]
pos: u32 = 0
// --- Legacy Prefixes ---
@@ -398,7 +397,7 @@ encode :: proc(
// the instruction is not legal i386.
if mode == ._32 && rex != 0 {
append(errors, Error{u32(instruction_index), .OPERAND_MISMATCH, {}})
has_errors = true
ok = false
continue
}
@@ -598,7 +597,7 @@ encode :: proc(
case .RELATIVE:
// Relative reference - record relocation
label_id := u32(user_op.relative)
append(&pending_relocations, Relocation{code_pos + pos, label_id, 0, .REL8, 1, u16(instruction_index)})
append(&pending_relocations, Relocation{byte_count + pos, label_id, 0, .REL8, 1, u16(instruction_index)})
out[pos] = 0
pos += 1
}
@@ -623,7 +622,7 @@ encode :: proc(
pos += 4
case .RELATIVE:
label_id := u32(user_op.relative)
append(&pending_relocations, Relocation{code_pos + pos, label_id, 0, .REL32, 4, u16(instruction_index)})
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
pos += 4
}
@@ -639,7 +638,7 @@ encode :: proc(
}
}
code_pos += pos
byte_count += pos
}
// =========================================================================
@@ -677,7 +676,7 @@ encode :: proc(
next_pc := patch_offset + 1
if !patch_pcrel_i8(code, patch_offset, target_offset, next_pc, relocation.addend) {
append(errors, Error{u32(relocation.inst_idx), .LABEL_OUT_OF_RANGE, {}})
has_errors = true
ok = false
}
case .REL32:
@@ -692,10 +691,7 @@ encode :: proc(
}
}
return Result{
byte_count = code_pos,
success = !has_errors,
}
return
}
// -----------------------------------------------------------------------------

View File

@@ -12,7 +12,6 @@ import "../isa"
// SECTION: 6.0 Re-exports from isa (status, relocation)
// -----------------------------------------------------------------------------
Result :: isa.Result
Error :: isa.Error
Error_Code :: isa.Error_Code
// Relocation and Relocation_Type live in reloc.odin (per-arch by design).

View File

@@ -322,7 +322,7 @@ run_test :: proc(t: Test) -> bool {
relocs: [dynamic]x86.Relocation
defer delete(relocs)
encode_result := x86.encode(
byte_count, ok := x86.encode(
t.instructions,
labels_copy[:len(t.labels)],
code_buf[:],
@@ -333,11 +333,11 @@ run_test :: proc(t: Test) -> bool {
)
// Copy encoded bytes
for i in 0..<encode_result.byte_count {
for i in 0..<byte_count {
append(&encoded_code, code_buf[i])
}
if !encode_result.success {
if !ok {
fmt.printf("%s[FAIL]%s %s - encoding failed\n", RED, RESET, t.name)
for err in encode_errors {
fmt.printf(" Error at inst %d: %v\n", err.inst_idx, err.code)
@@ -348,13 +348,13 @@ run_test :: proc(t: Test) -> bool {
// Verify expected bytes if provided
if len(t.expected_code) > 0 {
if int(encode_result.byte_count) != len(t.expected_code) {
if int(byte_count) != len(t.expected_code) {
fmt.printf("%s[FAIL]%s %s - code size %d != expected %d\n",
RED, RESET, t.name, encode_result.byte_count, len(t.expected_code))
RED, RESET, t.name, byte_count, len(t.expected_code))
g_stats.failed += 1
return false
}
for i in 0..<encode_result.byte_count {
for i in 0..<byte_count {
if encoded_code[i] != t.expected_code[i] {
fmt.printf("%s[FAIL]%s %s - byte %d: 0x%02X != expected 0x%02X\n",
RED, RESET, t.name, i, encoded_code[i], t.expected_code[i])
@@ -529,7 +529,7 @@ run_test :: proc(t: Test) -> bool {
// =========================================================================
if len(code_to_decode) > 0 {
decode_result := x86.decode(
byte_count, ok := x86.decode(
code_to_decode,
nil,
&decoded_insts,
@@ -538,7 +538,7 @@ run_test :: proc(t: Test) -> bool {
&decode_errors,
)
if !decode_result.success {
if !ok {
fmt.printf("%s[FAIL]%s %s - decoding failed\n", RED, RESET, t.name)
if len(decode_errors) > 0 {
for err in decode_errors {
@@ -2991,9 +2991,8 @@ run_label_map_tests :: proc() {
defer delete(relocs)
defer delete(errs)
result := x86.encode(instructions[:], lm.labels[:], code_buf[:], &relocs, &errs, true, 0)
if !result.success {
byte_count, ok := x86.encode(instructions[:], lm.labels[:], code_buf[:], &relocs, &errs, true, 0)
if !ok {
fmt.printf("%s[FAIL]%s Label_Map test - encoding failed\n", RED, RESET)
g_stats.failed += 1
return
@@ -3009,7 +3008,7 @@ run_label_map_tests :: proc() {
defer delete(decoded_labels)
defer delete(decode_errors)
x86.decode(code_buf[:result.byte_count], nil, &decoded_insts, &decoded_info, &decoded_labels, &decode_errors)
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)
@@ -3088,8 +3087,8 @@ run_benchmarks :: proc() {
for _ in 0..<ITERATIONS {
clear(&relocs)
clear(&errs)
result := x86.encode(bench_insts[:], labels[:], code_buf[:], &relocs, &errs, true, 0)
enc_bytes += int(result.byte_count)
byte_count, _ := x86.encode(bench_insts[:], labels[:], code_buf[:], &relocs, &errs, true, 0)
enc_bytes += int(byte_count)
}
enc_dur := time.duration_seconds(time.since(enc_start))
@@ -3098,8 +3097,8 @@ run_benchmarks :: proc() {
{
clear(&relocs)
clear(&errs)
result := x86.encode(bench_insts[:], labels[:], code_buf[:], &relocs, &errs, true, 0)
encoded_len = result.byte_count
byte_count, _ := x86.encode(bench_insts[:], labels[:], code_buf[:], &relocs, &errs, true, 0)
encoded_len = byte_count
}
// Decode