mirror of
https://github.com/odin-lang/Odin.git
synced 2026-09-02 02:03:35 +00:00
Use INSTRUCTION_TABLE now
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -32,10 +32,11 @@ Clobber :: lib.Clobber
|
||||
|
||||
Blob :: struct { global, file, typ: string }
|
||||
BLOBS := [?]Blob{
|
||||
{"ENCODE_FORMS", "arm64.encode_forms.bin", "Encoding"},
|
||||
{"ENCODE_RUNS", "arm64.encode_runs.bin", "Encode_Run"},
|
||||
{"DECODE_ENTRIES", "arm64.entries.bin", "Decode_Entry"},
|
||||
{"DECODE_INDEX_OP0", "arm64.idx_op0.bin", "Decode_Index"},
|
||||
{"ENCODE_FORMS", "arm64.encode_forms.bin", "Encoding"},
|
||||
{"CLOBBER_FORMS", "arm64.clobber_forms.bin", "Clobber"},
|
||||
{"ENCODE_RUNS", "arm64.encode_runs.bin", "Encode_Run"},
|
||||
{"DECODE_ENTRIES", "arm64.entries.bin", "Decode_Entry"},
|
||||
{"DECODE_INDEX_OP0", "arm64.idx_op0.bin", "Decode_Index"},
|
||||
}
|
||||
|
||||
DIR_GEN :: #directory + "/generated/"
|
||||
@@ -73,16 +74,29 @@ emit_encode_tables :: proc() -> (total: int) {
|
||||
strings.write_string(&sb, "// Flattened encode forms + per-mnemonic run index (source: ENCODING_TABLE).\n\n")
|
||||
strings.write_string(&sb, "import lib \"../..\"\n\n")
|
||||
|
||||
for m in Mnemonic { total += len(ENCODING_TABLE[m]) }
|
||||
for m in Mnemonic { total += len(INSTRUCTION_TABLE[m]) }
|
||||
|
||||
strings.write_string(&sb, "@(rodata)\n")
|
||||
fmt.sbprintfln(&sb, "ENCODE_FORMS := [%d]lib.Encoding{{", total)
|
||||
for m in Mnemonic {
|
||||
forms := ENCODING_TABLE[m]
|
||||
forms := INSTRUCTION_TABLE[m]
|
||||
if len(forms) == 0 { continue }
|
||||
fmt.sbprintfln(&sb, "\t// .%v", m)
|
||||
for f in forms {
|
||||
write_row(&sb, f.mnemonic, f.ops, f.enc, f.bits, f.mask, f.feature, f.flags)
|
||||
e := f.encoding
|
||||
write_row(&sb, e.mnemonic, e.ops, e.enc, e.bits, e.mask, e.feature, e.flags)
|
||||
}
|
||||
}
|
||||
strings.write_string(&sb, "}\n\n")
|
||||
|
||||
strings.write_string(&sb, "@(rodata)\n")
|
||||
fmt.sbprintfln(&sb, "CLOBBER_FORMS := [%d]lib.Clobber{{", total)
|
||||
for m in Mnemonic {
|
||||
forms := INSTRUCTION_TABLE[m]
|
||||
if len(forms) == 0 { continue }
|
||||
fmt.sbprintfln(&sb, "\t// .%v", m)
|
||||
for f in forms {
|
||||
write_clobber(&sb, f.clobber)
|
||||
}
|
||||
}
|
||||
strings.write_string(&sb, "}\n\n")
|
||||
@@ -93,7 +107,7 @@ emit_encode_tables :: proc() -> (total: int) {
|
||||
strings.write_string(&sb, "ENCODE_RUNS := [lib.Mnemonic]lib.Encode_Run{\n")
|
||||
start := 0
|
||||
for m in Mnemonic {
|
||||
c := len(ENCODING_TABLE[m])
|
||||
c := len(INSTRUCTION_TABLE[m])
|
||||
name := reflect.enum_string(m)
|
||||
fmt.sbprintf(&sb, "\t.%s", name)
|
||||
for _ in 0..<run_w-len(name) { strings.write_byte(&sb, ' ') }
|
||||
@@ -105,6 +119,72 @@ emit_encode_tables :: proc() -> (total: int) {
|
||||
return
|
||||
}
|
||||
|
||||
write_clobber :: proc(sb: ^strings.Builder, clobber: lib.Clobber) {
|
||||
strings.write_string(sb, "\t{")
|
||||
defer strings.write_string(sb, "},\n")
|
||||
|
||||
n := 0
|
||||
for field in reflect.struct_fields_zipped(lib.Clobber) {
|
||||
value := reflect.struct_field_value(clobber, field)
|
||||
if reflect.is_bit_set(field.type) {
|
||||
if !reflect.is_nil(value) {
|
||||
if n > 0 { strings.write_string(sb, ", ") }
|
||||
defer n += 1
|
||||
|
||||
value_int := value
|
||||
switch type_info_of(value.id).size {
|
||||
case 1: value_int.id = u8
|
||||
case 2: value_int.id = u16
|
||||
case 4: value_int.id = u32
|
||||
case 8: value_int.id = u64
|
||||
}
|
||||
bits := reflect.as_u64(value_int) or_else panic("cannot get bits")
|
||||
|
||||
bs := reflect.type_info_base(field.type).variant.(reflect.Type_Info_Bit_Set)
|
||||
et := reflect.type_info_base(bs.elem)
|
||||
lower := bs.lower
|
||||
upper := bs.upper
|
||||
|
||||
strings.write_string(sb, field.name)
|
||||
strings.write_string(sb, "={")
|
||||
defer strings.write_byte(sb, '}')
|
||||
|
||||
e, is_enum := et.variant.(reflect.Type_Info_Enum)
|
||||
|
||||
commas := 0
|
||||
loop: for i in transmute(bit_set[0..<64])bits {
|
||||
elem := i64(i) + lower
|
||||
if commas > 0 { strings.write_string(sb, ", ") }
|
||||
|
||||
if is_enum {
|
||||
for ev, evi in e.values {
|
||||
v := u64(ev)
|
||||
if v == u64(elem) {
|
||||
strings.write_string(sb, ".")
|
||||
strings.write_string(sb, e.names[evi])
|
||||
commas += 1
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.sbprintf(sb, "%d", elem)
|
||||
commas += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if reflect.is_boolean(field.type) {
|
||||
if reflect.as_bool(value) or_else false {
|
||||
if n > 0 { strings.write_string(sb, ", ") }
|
||||
defer n += 1
|
||||
strings.write_string(sb, field.name)
|
||||
strings.write_string(sb, "=true")
|
||||
}
|
||||
} else {
|
||||
panic("unhandled type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Decode side
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -119,13 +199,14 @@ emit_decode_tables :: proc() -> (total: int) {
|
||||
all: [dynamic]Entry
|
||||
defer delete(all)
|
||||
for mn in Mnemonic {
|
||||
for f in ENCODING_TABLE[mn] {
|
||||
op0_static := u8((f.bits >> 25) & 0xF)
|
||||
op0_mask := u8((f.mask >> 25) & 0xF)
|
||||
for form in INSTRUCTION_TABLE[mn] {
|
||||
e := form.encoding
|
||||
op0_static := u8((e.bits >> 25) & 0xF)
|
||||
op0_mask := u8((e.mask >> 25) & 0xF)
|
||||
// Enumerate every 4-bit bucket B such that (B & op0_mask) == op0_static.
|
||||
for b: u8 = 0; b < 16; b += 1 {
|
||||
if (b & op0_mask) != op0_static { continue }
|
||||
append(&all, Entry{f.mnemonic, f.ops, f.enc, f.bits, f.mask, f.feature, f.flags, b})
|
||||
append(&all, Entry{e.mnemonic, e.ops, e.enc, e.bits, e.mask, e.feature, e.flags, b})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,7 @@ w :: proc(file: string, data: []u8) {
|
||||
|
||||
main :: proc() {
|
||||
w(TABLES + "arm64.encode_forms.bin", raw(&ENCODE_FORMS, size_of(ENCODE_FORMS)))
|
||||
w(TABLES + "arm64.clobber_forms.bin", raw(&CLOBBER_FORMS, size_of(CLOBBER_FORMS)))
|
||||
w(TABLES + "arm64.encode_runs.bin", raw(&ENCODE_RUNS, size_of(ENCODE_RUNS)))
|
||||
w(TABLES + "arm64.entries.bin", raw(&DECODE_ENTRIES, size_of(DECODE_ENTRIES)))
|
||||
w(TABLES + "arm64.idx_op0.bin", raw(&DECODE_INDEX_OP0, size_of(DECODE_INDEX_OP0)))
|
||||
|
||||
@@ -1,5 +1,91 @@
|
||||
package rexcode_arm64_tablegen
|
||||
|
||||
// =============================================================================
|
||||
// rexcode · AArch64 INSTRUCTION_TABLE (v1: base integer + FP scalar)
|
||||
// =============================================================================
|
||||
//
|
||||
// One []Form per Mnemonic. A Form fuses an instruction form's Encoding with its
|
||||
// Clobber, so INSTRUCTION_TABLE[m][i] carries both the bit pattern and the
|
||||
// read/write + side-effect summary for form i of mnemonic m. This replaces the
|
||||
// old pair of index-aligned tables (ENCODING_TABLE[m][i] / CLOBBER_TABLE[m][i]);
|
||||
// co-locating the two in one struct removes the alignment invariant, so a form's
|
||||
// encoding and clobber can no longer drift out of sync.
|
||||
//
|
||||
//
|
||||
// --- Encoding: (bits, mask) model --------------------------------------------
|
||||
//
|
||||
// Same shape as MIPS/RISC-V. `bits` carries the static opcode pattern; `mask`
|
||||
// covers exactly the bits that are fixed by the form. Operand-driven fields
|
||||
// (Rd/Rn/Rm/imm*/sh/cond/...) land in zero positions of `bits` and are ORed in
|
||||
// by the encoder.
|
||||
//
|
||||
// Sections (each follows the ARM ARM "C4.1.x Data-processing /" division):
|
||||
// §1 Data-processing -- immediate (Add/Sub imm, Mov-wide, PC-rel)
|
||||
// §2 Data-processing -- shifted reg (Add/Sub/AND/ORR/EOR/BIC/ORN/EON)
|
||||
// §3 Data-processing -- extended reg (Add/Sub extended)
|
||||
// §4 Data-processing -- 2-source (LSLV/LSRV/ASRV/RORV, UDIV/SDIV)
|
||||
// §5 Data-processing -- 3-source (MADD/MSUB/SMADDL/.../UMULH)
|
||||
// §6 Data-processing -- 1-source (CLZ/CLS/RBIT/REV/REV16/REV32)
|
||||
// §7 Conditional (CSEL/CSINC/CSINV/CSNEG)
|
||||
// §8 Branches (B/BL/BR/BLR/RET, B.cond, CBZ, TBZ)
|
||||
// §9 Loads / stores (LDR/STR families, LDP/STP, LDUR)
|
||||
// §10 System (NOP/HINT/ISB/MSR/MRS/SVC/...)
|
||||
// §11 FP scalar (FMOV/FADD/FCVT/FCMP/FCSEL/...)
|
||||
//
|
||||
// Logical-immediate forms (AND/ORR/EOR/ANDS imm) use the bitmask-immediate
|
||||
// encoding (N:imms:immr); they're deferred to a follow-up turn that adds the
|
||||
// bitmask encoder helper.
|
||||
//
|
||||
//
|
||||
// --- Clobber: read/write + side-effect summary -------------------------------
|
||||
//
|
||||
// One Clobber per Form -- a read/write + side-effect summary for every form of
|
||||
// every mnemonic. Derivation (from each form's operand TYPES, operand ROLES,
|
||||
// and Encoding_Flags):
|
||||
//
|
||||
// written / read -- operand slots (0..3) whose register / memory base is a
|
||||
// def / use. A slot counts only if its TYPE is register-
|
||||
// or memory-like; immediate slots never appear. Direction
|
||||
// comes from the ROLE, refined by instruction class:
|
||||
// * RD/VD/PD/RT/RT2/ZA_TILE* are destinations, EXCEPT
|
||||
// the data operand of a store (RT/VD on ST*), which is
|
||||
// a source; and the status result of STXR/STXP (RD),
|
||||
// which stays a def while RT/RT2 become sources.
|
||||
// * A pre/post-index base (OFFSET_BASE_PRE/POST) is both
|
||||
// read and written (address write-back).
|
||||
// * A destination register re-listed as a later source
|
||||
// slot (SVE destructive Zdn, accumulate) is read.
|
||||
// * Branches define no GPR operand; targets/modifiers
|
||||
// are reads.
|
||||
// implicit_wr/rd -- LR on BL/BLR/RET, LR+SP on PAC*SP / RETAA, X16/X17 on
|
||||
// PAC*1716, etc.
|
||||
// nzcv_wr -- set for sets_flags forms (ADDS/SUBS/ANDS/CMP/CCMP/FCMP,
|
||||
// SVE compares, WHILE*, PTRUES, ...).
|
||||
// nzcv_rd -- any COND operand (CSEL/FCSEL/B.cond/CCMP), plus the
|
||||
// carry readers ADC/ADCS/SBC/SBCS/NGC/NGCS; CCMP/CCMN both.
|
||||
// fpsr_wr/reads_fpcr -- per FP-op family (see notes); saturating integer ops
|
||||
// (SQ*/UQ*/SUQ*/USQ*/SQRDML*) set QC.
|
||||
// reads_mem/writes_mem -- loads / stores / RMW atomics / MOPS.
|
||||
// side_effects -- control flow, barriers, atomics, cache/TLB, PAC, BTI,
|
||||
// wait, privileged system access, FFR, etc.
|
||||
//
|
||||
// Heuristic / best-effort fields (structurally faithful, semantically approximate):
|
||||
// * FPSR exception-bit sets and reads_fpcr are assigned per mnemonic family,
|
||||
// not per-corner-case; treat them as "flags this class may raise".
|
||||
// * Accumulate detection (destination is read-modified) is name-driven for
|
||||
// the families that fold the accumulator into the destination (MLA/MLS/DOT/
|
||||
// FMLA/FCMLA/SQRDML*/MOPA, shift-accumulate, bit-insert, AES/SHA/SM3/SM4,
|
||||
// PAC in-place). Multiply-adds with a separate Ra/Va operand (MADD/FMADD/...)
|
||||
// are correctly NOT dest-reads.
|
||||
// * LD1-4/ST1-4 register lists occupy a single operand slot, so max operand
|
||||
// count stays at 4 (matches Operand_Set / the encoder's model).
|
||||
// * Acquire/release accesses carry the FENCE side effect to model their
|
||||
// one-way ordering (distinct from the register/memory read/write sets).
|
||||
// * Requires the companion clobber_types.odin (adds X16/X17 to Clobber_Reg
|
||||
// and FFR to Side_Effect).
|
||||
// =============================================================================
|
||||
|
||||
|
||||
Form :: struct {
|
||||
using encoding: Encoding,
|
||||
clobber: Clobber,
|
||||
|
||||
@@ -44,10 +44,11 @@ Decode_Index :: struct #packed {
|
||||
// Loaded tables (rodata, embedded from tables/*.bin at compile time)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@(rodata) ENCODE_FORMS := #load("tables/arm64.encode_forms.bin", []Encoding)
|
||||
@(rodata) ENCODE_RUNS := #load("tables/arm64.encode_runs.bin", []Encode_Run)
|
||||
@(rodata) DECODE_ENTRIES := #load("tables/arm64.entries.bin", []Decode_Entry)
|
||||
@(rodata) DECODE_INDEX_OP0 := #load("tables/arm64.idx_op0.bin", []Decode_Index)
|
||||
@(rodata) ENCODE_FORMS := #load("tables/arm64.encode_forms.bin", []Encoding)
|
||||
@(rodata) CLOBBER_FORMS := #load("tables/arm64.clobber_forms.bin", []Clobber)
|
||||
@(rodata) ENCODE_RUNS := #load("tables/arm64.encode_runs.bin", []Encode_Run)
|
||||
@(rodata) DECODE_ENTRIES := #load("tables/arm64.entries.bin", []Decode_Entry)
|
||||
@(rodata) DECODE_INDEX_OP0 := #load("tables/arm64.idx_op0.bin", []Decode_Index)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Accessors
|
||||
|
||||
BIN
core/rexcode/isa/arm64/tables/arm64.clobber_forms.bin
Normal file
BIN
core/rexcode/isa/arm64/tables/arm64.clobber_forms.bin
Normal file
Binary file not shown.
Reference in New Issue
Block a user