asmkit-rs 0.6.2

Portable assembler toolkit for encoding x86/x64, AArch64, and RISC-V
Documentation
# Backend structure

Each architecture under `src/{x86,aarch64,riscv}/` follows the same shape:

- `instdb.rs`: generated, dense `InstId` enum + static tables (encodings, operand
  signatures, RW effects, features)
- `operands.rs`: architecture-specific operand types (registers, memory, immediates)
- `assembler.rs`: hand-written, the `Assembler`, with `emit_n(InstId, &[&Operand])` as the
  single emit entry point
- `emitter.rs`: generated, per-mnemonic typed traits (`asm.mov(RAX, 42)`) forwarding to
  `emit_n`
- `instapi.rs`: hand-written, implements `query_rw_info` and friends over `src/core/inst.rs`'s
  generic `Inst`, using the tables in `instdb.rs`
- `arch_traits.rs`: glue implementing the shared `src/core` traits for this architecture

x86 additionally has:

- `emit.rs`: validates operand signatures before encoding
- `encoder.rs` / `encoder_tables.rs`: does the actual bit packing
- `opcode.rs`: opcode/prefix constants

AArch64 additionally has `rwflags.rs` (per-instruction PSTATE read/write, asmkit-only,
generated). RISC-V additionally has `opcodes.rs` (opcode constants, partly hand-maintained
above a generation marker, see `meta/riscv.py`).

`src/core/` holds everything shared across backends: the generic `Inst`/`Operand` model
(`inst.rs`, `operand.rs`), `CodeBuffer`/`Section`/`Linker` for buffer management and
multi-module linking, `Builder` for recording/replaying instructions, `patch.rs` for
JSC-style patching, and `jit_allocator.rs` for executable memory.

# Code generation (`meta/`)

Everything above marked "generated" is produced from external instruction databases
(AsmJit for x86/AArch64, riscv-opcodes/riscv-unified-db for RISC-V) by Python scripts in
`meta/`, driven by `meta/regen.sh`. See [meta/README.md](meta/README.md) for the pinned
inputs, the generator pipeline, and licensing obligations for the generated files.

# Adding a new backend

This walks through wiring up a new architecture (`newarch` below) end to end. RISC-V is
the best reference for a backend that isn't driven by AsmJit's database: its `instdb.rs`
and `opcodes.rs` are generated from a different external source, and its `emitter.rs`/
`assembler.rs` are structured the same as x86/AArch64.

## 1. Feature flag and module wiring

- Add a feature to `Cargo.toml`: `newarch = []`, and add it to `default` if the backend
  should ship by default.
- Add `pub mod newarch;` (feature-gated) to `src/lib.rs`, alongside the existing
  `x86`/`aarch64`/`riscv` modules.
- Create `src/newarch/mod.rs` re-exporting `assembler`, `emitter`, `instapi::query_rw_info`,
  `instdb`'s CPU feature types, `operands`, and any opcode/regs modules; mirror
  `src/riscv/mod.rs`.

## 2. `Arch` enum and `arch_traits.rs`

- Add a variant to `Arch` in `src/core/arch_traits.rs` (pick an unused discriminant; the
  even/odd-width convention documented next to the enum is cosmetic, not load-bearing,
  keep it if convenient).
- Add `NEWARCH_ARCH_TRAITS: ArchTraits` (feature-gated, with a `NO_ARCH_TRAITS` fallback
  when the feature is off, copy the `X86`/`RISCV` `#[cfg]` pairs) and wire it into
  whatever dispatches on `Arch` (`ArchTraits::for_arch` or equivalent).
- Implement `src/newarch/arch_traits.rs`: the `ArchTraits` table (stack/frame/link/IP
  register ids, stack alignment, and the `RegType`/`TypeId`/signature tables for every
  register class the architecture has).

## 3. Operands

Write `src/newarch/operands.rs`: register types and constants (implementing
`src/core/operand.rs`'s `RegTraits`/`OperandCast`), memory operand types, and any
architecture-specific immediate wrappers. This is hand-written; it's the public API
surface users interact with directly (`asm.mov(RAX, 42)`).

## 4. Instruction database (`instdb.rs`)

This is normally generated, not hand-written, because instruction sets are large and
error-prone to transcribe by hand. Decide where the source data comes from, for example
follow the RISC-V pattern (`meta/riscv.py`): pull from whatever authoritative
machine-readable source exists for the ISA (riscv-opcodes/riscv-unified-db in that case),
and generate the same shape of tables: an `InstId` enum, per-instruction operand
signatures, and encoding data, so the rest of `src/core` (which is architecture-agnostic
over these tables) doesn't need to special-case the backend.

Whichever source you use, the generator should also be able to emit `emitter.rs` (the typed
per-mnemonic traits) from the same signature data, and, if the architecture has flag/CPU-state
read/write effects worth modeling, an `RwInfo`-shaped table for `instapi.rs` to consume.

Add the new generator invocation and its output files to `GENERATED_FILES` and the step list
in `meta/regen.sh`, and document any new pinned external input (repo, commit, license) in
`meta/README.md`'s input table.

## 5. `assembler.rs` and encoding

Write the actual bit-packing/encoding logic and the `Assembler` type implementing
`src/core/builder.rs`'s `InstSink` trait (`arch()`, `emit_inst()`, `bind_label()`) plus
whatever direct `emit_n(InstId, &[&Operand])` entry point the generated `emitter.rs` calls
into. For a fixed-width instruction set this can be a single file; x86's variable-length
encoding needed the extra `emit.rs` (operand-signature validation) / `encoder.rs` /
`encoder_tables.rs` split, don't replicate that split unless the architecture actually
needs it.

## 6. `instapi.rs`

Hand-written code implementing `query_rw_info` (and any other per-instruction query
functions) over the generic `Inst` type from `src/core/inst.rs`, reading whatever RW/flags
tables `instdb.rs` (or a sibling generated file, like AArch64's `rwflags.rs`) provides.