[iterate-4A] jit: Cranelift block-JIT scaffolding + addi/addis PoC (pipeline proven)

Stage-2 foundation. Adds cranelift-jit/frontend/module/codegen 0.128.4 (the
1.90-compatible line; 0.133 needs Rust 1.94) and a new crates/xenia-cpu/src/jit.rs.

Jit owns a JITModule (and thus all compiled code memory). compile(&DecodedBlock)
lowers a block to native code ONLY if every opcode is covered() — otherwise
returns None and the caller interprets the whole block (coverage grows
opcode-by-opcode). ABI: extern "C" fn(*mut PpcContext, *const MemEnv) -> u32
(StepResult discriminant); guest registers are loaded/stored directly from the
#[repr(C)] PpcContext at offset_of! offsets. A covered block is straight-line and
always runs to completion, so pc advances to end_pc and cycle_count/timebase bump
by the instruction count once — matching the interpreter's per-instruction bump.

Covered set so far: addi, addis. Unit test jit_matches_interpreter_addi_block
compiles a 32x addi block and asserts the JIT's r3/pc/cycle_count/timebase match
the interpreter exactly — proves module setup, offset_of register access, IR
emission, the extern "C" calling convention, and cycle/pc accounting end-to-end.

Not yet wired into run_superblock (needs a compiled-block cache + routing); every
future opcode will be validated against the interpreter via the M0 XENIA_JIT_DIFF
harness before it counts as covered.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-06 20:45:31 +02:00
parent 07cd272412
commit cc7ff58cb0
4 changed files with 501 additions and 0 deletions

View File

@@ -10,6 +10,10 @@ xenia-memory = { workspace = true }
tracing = { workspace = true }
bitflags = { workspace = true }
thiserror = { workspace = true }
cranelift-jit = "0.128.4"
cranelift-frontend = "0.128.4"
cranelift-module = "0.128.4"
cranelift-codegen = "0.128.4"
[dev-dependencies]
serde = { workspace = true }