mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-20 00:52:33 +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:
@@ -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
|
||||
|
||||
@@ -20,7 +20,7 @@ Instruction_Flags :: bit_field u8 {
|
||||
}
|
||||
|
||||
Instruction :: struct #packed {
|
||||
ops: [4]Operand `fmt:"v,operand_count"`, // 4 * 18 = 68
|
||||
ops: [4]Operand `fmt:"v,operand_count"`, // 4 * 14 = 56
|
||||
mnemonic: Mnemonic, // 2
|
||||
operand_count: u8, // 0..4
|
||||
flags: Instruction_Flags, // 1
|
||||
@@ -28,7 +28,7 @@ Instruction :: struct #packed {
|
||||
length: u8, // 4 or 8 (prefixed)
|
||||
form_id: u16, // 0 = no hint; otherwise 1 + form index
|
||||
}
|
||||
#assert(size_of(Instruction) == 80)
|
||||
#assert(size_of(Instruction) == 64)
|
||||
|
||||
// =============================================================================
|
||||
// Builders
|
||||
|
||||
@@ -46,7 +46,7 @@ mem_x :: #force_inline proc "contextless" (base, index: Register) -> Memory {
|
||||
}
|
||||
|
||||
Operand :: struct #packed {
|
||||
using _: struct #raw_union {
|
||||
using _: struct #raw_union #packed {
|
||||
reg: Register,
|
||||
mem: Memory,
|
||||
immediate: i64,
|
||||
@@ -55,7 +55,7 @@ Operand :: struct #packed {
|
||||
kind: Operand_Kind,
|
||||
size: u8, // operand size in bytes (4 = word, 8 = dword)
|
||||
}
|
||||
#assert(size_of(Operand) == 18)
|
||||
#assert(size_of(Operand) == 14)
|
||||
|
||||
@(require_results)
|
||||
op_reg :: #force_inline proc "contextless" (r: Register) -> Operand {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user