mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-20 09:02:32 +00:00
Merge origin/bill/rexcode: struct repack (#raw_union #packed), wasm arch
Merge gingerBill's latest into bill/rexcode. His changes: minimize the Instruction/Operand structs across ISAs with packed raw-unions (+ the compiler support for #raw_union #packed), the new core:rexcode/wasm arch and wasm/module, encode() now returns (byte_count, ok) instead of a Result struct, decode_one made public, and assorted formatting/inlining. Conflict: arm64/tests/pipeline_smoke.odin CSEL test -- kept the generated 4-arg inst_csel(dst,src,src2,cond) (mnemonic_builders.odin is generated, not from Bill's branch) and adopted Bill's (byte_count, success) encode signature. Required rebuilding ./odin from the merged source for the packed-union syntax. Re-validated after the repack: regenerated all artifacts (idempotent -- no spurious churn), all 10 arches gen/builders/check/test green, and byte-compared the new arm32 BF + mips PS/MMI/DSP/R6 forms to confirm no field truncation. arm64/arm32/mips still 100%.
This commit is contained in:
@@ -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.GPR.ZERO, rv.GPR.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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user