[iterate-4A] jit: opcode-frequency histogram (XENIA_JIT_HIST) for coverage targeting
Gated 1-in-16 sampled opcode histogram in the recompiler (tallied in run_block, printed sorted with cumulative % at clean exit). Drives which opcodes the JIT lowers first. Boot+movie result: workload is FP-heavy — addi 15%, lwz 12%, rlwinm 10%, lfs 9%, stfs 7.5%, lfsx 5%, fmaddsx 4.6%, bc 3.7%, fmulsx 3.6%, stw 3.4% ... top-20 = 90%, ~33% floating-point. This is why we go straight to a Cranelift machine-code JIT (which attacks the FP op bodies) rather than a closure-threaded stage (which only removes dispatch). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4396,6 +4396,8 @@ fn dump_thread_diagnostic(
|
||||
}
|
||||
// JIT differential harness: checked/skipped/mismatch tally at clean exit.
|
||||
xenia_cpu::recompiler::report_diff_summary();
|
||||
// JIT opcode-frequency histogram (XENIA_JIT_HIST) — drives M1 coverage.
|
||||
xenia_cpu::recompiler::report_histogram();
|
||||
|
||||
// STEP-10 diagnostic (observe-only, env-gated `XENIA_DUMP_SLOTS=1`).
|
||||
// Prints each scheduler slot's full runqueue with the fields needed to
|
||||
|
||||
@@ -43,6 +43,7 @@ use std::sync::atomic::{AtomicU64, AtomicU8, Ordering};
|
||||
use crate::block_cache::DecodedBlock;
|
||||
use crate::context::PpcContext;
|
||||
use crate::interpreter::{execute, step_block, StepResult};
|
||||
use crate::opcode::PpcOpcode;
|
||||
use xenia_memory::MemoryAccess;
|
||||
|
||||
/// Cached env gate. 0 = uninitialised, 1 = on, 2 = off.
|
||||
@@ -74,6 +75,58 @@ pub fn diff_enabled() -> bool {
|
||||
cached_flag(&F, "XENIA_JIT_DIFF")
|
||||
}
|
||||
|
||||
// ---- opcode histogram (XENIA_JIT_HIST) — data-drives M1 coverage ---------
|
||||
|
||||
#[inline]
|
||||
fn hist_enabled() -> bool {
|
||||
static F: AtomicU8 = AtomicU8::new(0);
|
||||
cached_flag(&F, "XENIA_JIT_HIST")
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static HIST: RefCell<HashMap<PpcOpcode, u64>> = RefCell::new(HashMap::new());
|
||||
static HIST_TICK: Cell<u32> = const { Cell::new(0) };
|
||||
}
|
||||
|
||||
/// Sample 1-in-16 instructions — relative opcode frequencies are unbiased at
|
||||
/// this rate and the HashMap cost stops dominating the run.
|
||||
#[inline]
|
||||
fn hist_tally(op: PpcOpcode) {
|
||||
let t = HIST_TICK.with(|c| {
|
||||
let n = c.get().wrapping_add(1);
|
||||
c.set(n);
|
||||
n
|
||||
});
|
||||
if t & 0xF == 0 {
|
||||
HIST.with(|h| *h.borrow_mut().entry(op).or_insert(0) += 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Print the opcode-frequency histogram (top 40 + cumulative %), so M1 coverage
|
||||
/// targets the opcodes that actually dominate boot+movie execution.
|
||||
pub fn report_histogram() {
|
||||
if !hist_enabled() {
|
||||
return;
|
||||
}
|
||||
let mut v: Vec<(PpcOpcode, u64)> =
|
||||
HIST.with(|h| h.borrow().iter().map(|(&k, &c)| (k, c)).collect());
|
||||
v.sort_by(|a, b| b.1.cmp(&a.1));
|
||||
let total: u64 = v.iter().map(|(_, c)| c).sum();
|
||||
eprintln!("=== JIT opcode histogram (total={total}, {} distinct) ===", v.len());
|
||||
let mut cum = 0u64;
|
||||
for (i, (op, c)) in v.iter().take(40).enumerate() {
|
||||
cum += c;
|
||||
eprintln!(
|
||||
" {:>2}. {:<14?} {:>12} {:>5.1}% cum {:>5.1}%",
|
||||
i + 1,
|
||||
op,
|
||||
c,
|
||||
100.0 * *c as f64 / total as f64,
|
||||
100.0 * cum as f64 / total as f64
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute one decoded block. **M0: interpreter fallback for every opcode.**
|
||||
///
|
||||
/// Byte-for-byte the same loop as [`crate::interpreter::step_block`]: bump
|
||||
@@ -84,9 +137,13 @@ pub fn run_block(
|
||||
mem: &dyn MemoryAccess,
|
||||
block: &DecodedBlock,
|
||||
) -> StepResult {
|
||||
let hist = hist_enabled();
|
||||
let mut result = StepResult::Continue;
|
||||
for instr in &block.instrs {
|
||||
let expected_next = instr.addr.wrapping_add(4);
|
||||
if hist {
|
||||
hist_tally(instr.opcode);
|
||||
}
|
||||
// M0 fallback: identical to the interpreter. Future stages dispatch
|
||||
// lowered ops here and only fall back for uncompiled opcodes.
|
||||
result = execute(ctx, mem, instr);
|
||||
|
||||
Reference in New Issue
Block a user