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

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

View File

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

View File

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

View File

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