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:
Brendan Punsky
2026-06-18 05:44:48 -04:00
committed by Flāvius
92 changed files with 9914 additions and 5421 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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