mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-19 16:42:33 +00:00
Replace -> isa.Result with -> (byte_code: u32, ok: bool)
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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[:],
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user