mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-19 16:42: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:
@@ -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,7 +21,7 @@ Instruction_Flags :: bit_field u8 {
|
||||
}
|
||||
|
||||
Instruction :: struct #packed {
|
||||
ops: [4]Operand `fmt:"v,operand_count"`, // 4 * 22 = 88
|
||||
ops: [4]Operand `fmt:"v,operand_count"`, // 4 * 18 = 68
|
||||
mnemonic: Mnemonic, // 2
|
||||
cond: u8, // 0..15 (AL=14)
|
||||
operand_count: u8, // 0..4
|
||||
@@ -35,9 +35,9 @@ Instruction :: struct #packed {
|
||||
// bits). User-constructed instructions leave it at 0; the encoder then
|
||||
// falls back to first-shape-match. Stored as u16 over the two padding bytes.
|
||||
form_id: u16,
|
||||
_: [7]u8,
|
||||
}
|
||||
#assert(size_of(Instruction) == 97)
|
||||
// 88 + 9 = 97 bytes (packed)
|
||||
#assert(size_of(Instruction) == 88)
|
||||
|
||||
// =============================================================================
|
||||
// Builders
|
||||
|
||||
@@ -94,7 +94,7 @@ mem_reg_shift :: #force_inline proc "contextless" (
|
||||
// ---- Operand structure -----------------------------------------------------
|
||||
|
||||
Operand :: struct #packed {
|
||||
using _: struct #raw_union {
|
||||
using _: struct #raw_union #packed {
|
||||
reg: Register,
|
||||
mem: Memory,
|
||||
immediate: i64,
|
||||
@@ -107,9 +107,7 @@ Operand :: struct #packed {
|
||||
lane: u8, // SIMD lane index for DPR_ELEM / QPR_ELEM
|
||||
cond: u8, // condition code 0..15 (default = AL = 14)
|
||||
}
|
||||
#assert(size_of(Operand) == 22)
|
||||
// 16-byte raw_union (Memory is largest) + 6 bytes of trailing fields = 22 bytes
|
||||
// (packed; no alignment padding).
|
||||
#assert(size_of(Operand) == 18)
|
||||
|
||||
// ---- Operand builders ------------------------------------------------------
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user