docs(audit): close out fix session 2026-05-03 — 12 IDs applied

Records the outcome of the audit-2026-05 fix sprint into the master
tracker. Documents:
- 12 closed IDs (10 P0 + 2 P1) with their commit SHAs and verification deltas
- 4 deferred IDs (XAMBUG-001, XAMBUG-002, KRNBUG-D08/XMODBUG-011,
  PPCBUG-720/721/722) with explicit reasons
- Sprint acceptance criteria status: A-E lands cleanly with swaps=2,
  but draws=0 persists (renderer plateau is multi-causal as the audit
  predicted; parked-waiter handles unresolved)
- Recommended next session: trace producers for the 4 parked-waiter
  handles directly

Closed IDs:
  SWAPBUG-001 / PPCBUG-001 (P0)              → 9ab986e
  ORACBUG-004 (P0; partial ORACBUG-006)       → 1f416aa
  KRNBUG-Vd-04, GPUBUG-001, XMODBUG-013 (3× P0) → 82f3d61
  GPUBUG-101 (P0)                              → 78ea81c
  GPUBUG-100 (P0; abs deferred)                → c5c6713
  GPUBUG-102 (P0)                              → ec2d955
  GPUBUG-103/104/105 (3× P0)                   → 8723d68
  KRNBUG-017 (P0-under-parallel)               → e7d0fcf
  GPUBUG-006 (P1)                              → 8fc1b1d
  XMODBUG-002 (P1)                             → 780e854

Test count at sprint close: 556 (+5 from 551 baseline).
Workspace clean; no dangling branches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-05-03 14:35:08 +02:00
parent 6f851a2083
commit c7fccccbc6

View File

@@ -3449,3 +3449,444 @@ IDs PPCBUG-655 through PPCBUG-679 are unallocated — no further bugs found in P
helper for canary's VD128h placement. helper for canary's VD128h placement.
- **Cross-reference**: invalidates the audit's confirmed-clean note at line 2958. - **Cross-reference**: invalidates the audit's confirmed-clean note at line 2958.
Subsumes the partial fix-shape proposed in PPCBUG-422 (Rc-bit position). Subsumes the partial fix-shape proposed in PPCBUG-422 (Rc-bit position).
---
# May 2026 Comprehensive Audit (extends prior PPCBUG namespace)
**Started**: 2026-05-02. **Charter**: [audit-2026-05-charter.md](audit-2026-05-charter.md).
**Severity**: P0 blocker / P1 wrong-result / P2 spec drift / P3 cosmetic.
## ORACBUG (M01 — oracles and goldens)
Per-milestone report: [audit-out/m01-oracles.md](audit-out/m01-oracles.md).
### ORACBUG-001 — base_mnemonics.json self-derived circular
- **Severity**: P1
- **Status**: open
- **Location**: crates/xenia-cpu/tests/disasm_goldens.rs:70-88 (`build_rows`); fixture crates/xenia-cpu/tests/golden/base_mnemonics.json
- **Symptom**: every "expected" mnemonic/operands/etc. is captured from `xenia_cpu::disasm::format()` at golden-creation time and frozen. The frozen JSON is asserted against future runs of the same function. Detects regression-from-snapshot, not absolute correctness. Human-readable `label` field is never asserted.
- **Recommendation**: add canary-disasm differential (see M02) and POWERISA-derived parallel oracle for ~20 representative cases.
### ORACBUG-002 — extended_mnemonics.json self-derived circular
- **Severity**: P1
- **Status**: open
- **Location**: crates/xenia-cpu/tests/golden/extended_mnemonics.json (623 rows)
- **Symptom**: same as ORACBUG-001, with extra risk: extended mnemonic emission is decision-tree output (li, lis, mr, not, slwi, srwi, clrldi, blr, bctr, beq/bne, lwsync, …). A bug in the canonicalization decision tree is not caught.
### ORACBUG-003 — vmx128_registers.json self-derived + hand-coded raw bytes
- **Severity**: P1
- **Status**: open
- **Location**: crates/xenia-cpu/tests/disasm_goldens.rs:421-527
- **Symptom**: same circularity, plus 4-operand multiply-add cases (lines 513-519) bypass encoding helpers and use HARD-CODED u32 literals (0x146328F0, 0x14632930, 0x14632970). PPCBUG-700 demonstrated this risk: the prior buggy convention was internally self-consistent in fixtures and lurked until a manual canary cross-check.
### ORACBUG-004 — sylpheed_n2m.json structurally insufficient
- **Severity**: P0
- **Status**: open
- **Location**: crates/xenia-app/tests/golden/sylpheed_n2m.json
- **Symptom**: at -n 2M instructions all rendering metrics are 0 (packets/draws/swaps/resolves/render-targets/textures). Sylpheed's first VdSwap fires at ~18M cycles. The golden cannot detect 11 of 14 digest fields by construction.
- **Risk**: this is the only end-to-end Sylpheed regression catcher in the workspace. Future fixes optimized to pass this gate are optimized against a blind oracle.
- **Recommendation**: add `sylpheed_n50m.json` (CI-feasible, captures VdSwap=1) and `sylpheed_n4b.json` (matches canonical reference invocation; commit-time gate).
### ORACBUG-005 — db_schema_golden.rs synthetic PE missing direct-branch coverage
- **Severity**: P3
- **Status**: open
- **Location**: crates/xenia-analysis/tests/db_schema_golden.rs:23-53
- **Symptom**: the synthetic PE has 4 instructions (mflr/nop/blr/nop). Direct-branch path of the DB writer (target_hex column population) is never exercised; only the indirect-only path is. Schema columns are correctly locked but coverage is thin.
### ORACBUG-006 — RunDigest missing high-leverage fields
- **Severity**: P2
- **Status**: open
- **Location**: crates/xenia-app/src/main.rs:1267-1306 (RunDigest struct + capture)
- **Symptom**: digest exposes 14 fields, missing several high-signal counters that already exist in the system: unique_pcs_executed, kernel_calls_per_export histogram, mmio_reads/writes, scheduler.deadlock_recoveries, scheduler.deadlock_halts, events_signaled, events_waited, events_with_zero_signals, lwarx_count, stwcx_success_count, stwcx_fail_count.
- **Risk**: M11's run-matrix can only diff coarse counters. Several "is the renderer chain alive?" probes are not captured.
### ORACBUG-007 — analysis-shim parity test inherits CIRCULAR provenance
- **Severity**: P2
- **Status**: open
- **Location**: crates/xenia-analysis/tests/disasm_goldens.rs:50-89 (check_fixture)
- **Symptom**: test does (a) shim-vs-cpu parity (good — catches drift) and (b) cpu-vs-fixture (inherits circularity from ORACBUG-001/002/003). The primary purpose (parity) is sound; only the secondary assertion is suspect.
### ORACBUG-008 — encode_vx128 helper lacks canary citation
- **Severity**: P3
- **Status**: open
- **Location**: crates/xenia-cpu/tests/disasm_goldens.rs:53-68
- **Symptom**: the encode helper currently encodes per canary's VX128 layout (post-PPCBUG-700) but lacks a comment block citing canary's `xenia-canary/src/xenia/cpu/ppc/ppc_decode_data.h:484-663`. A future "simplification" without canary cross-check could silently regress to the prior buggy convention.
## PPCBUG (M05 — scheduler + reservation + block_cache)
### PPCBUG-701 — Reservation generation 24-bit ring: false-match risk under long-delay paths (P3, latent)
- **Severity**: P3
- **Status**: open
- **Location**: crates/xenia-cpu/src/reservation.rs:67-83 (pack), :188-191 (next_gen mask)
- **Symptom**: `next_gen` is masked to 24 bits when packed (`& 0xFF_FFFF`). After 16,777,216 reservations, the generation wraps. If thread A's `lwarx` and its paired `stwcx.` are separated by ≥16M peer reservations on the same bank slot, and the bank still holds A's `(line, gen)` at commit time, `try_commit` will incorrectly succeed.
- **Risk**: very low under realistic workloads (reservation count between an lwarx-stwcx pair is typically <100, and same-bank displacement bumps `gen` regardless). Not observable on Sylpheed.
- **Recommendation**: defer until empirical evidence shows wraparound. If pursued, widen `gen` to 32 bits by stealing the line-address-low bits (low 7 bits of line are always zero — recoverable via masking).
- **Canary**: canary's bitmap model has the equivalent bit-aliasing risk at `RESERVE_BLOCK_SHIFT` granularity but no time-domain wrap.
### PPCBUG-702 — `invalidate_for_write` doc says collisions invalidate; code says they don't (P3, doc drift)
- **Severity**: P3
- **Status**: open
- **Location**: crates/xenia-cpu/src/reservation.rs:38-46 (doc) and :235-256 (code)
- **Symptom**: the file-level doc invariant 2 says "any plain store to a reserved line invalidates it (slot CASed to zero). Hash-collision side-effect: a store to a different line that maps to the same bank also invalidates" — but the actual code at :248-256 explicitly returns early when `bank_line != line`, leaving the reservation alone. The code is more correct (fewer spurious failures), but the doc contradicts it.
- **Recommendation**: update the file doc to describe the "tag-checked invalidation" actually implemented. No code change needed.
### PPCBUG-703 — `--parallel` is non-deterministic; `XENIA_SCHED_SEED` does not regulate it (P3, doc gap)
- **Severity**: P3
- **Status**: open
- **Location**: crates/xenia-cpu/src/scheduler.rs:232-249, :710-734; crates/xenia-app/src/main.rs:2230-2415
- **Symptom**: `--parallel` workers race for the kernel mutex within each round; observable interleavings depend on host OS scheduling, not on `XENIA_SCHED_SEED`. The seed regulates ONLY the per-round slot-list shuffle, which has no effect under `--parallel` since workers race for the lock independently. Same-seed-same-input runs under `--parallel` produce different observable schedules.
- **Risk**: M11's bisection cannot reliably reproduce an observed regression under `--parallel`; lockstep must be used for bisection.
- **Recommendation**: document the determinism boundary in CLI help text. If true determinism is needed under `--parallel`, the kernel-mutex acquisition order must be re-introduced as a coordinator-driven sequence (a regression of the M3 perf goal).
### PPCBUG-704 — `icbi` is a no-op; correctness depends on `bump_page_version` from data-store path (P3, latent)
- **Severity**: P3
- **Status**: open
- **Location**: crates/xenia-cpu/src/interpreter.rs:1697-1701; crates/xenia-cpu/src/block_cache.rs:142-178
- **Symptom**: `icbi` (instruction cache block invalidate) is collapsed into the cache/sync no-op arm. Self-modifying code is currently caught only because every `write_u8/16/32/64` in `xenia-memory/src/heap.rs` unconditionally calls `bump_page_version`. If a future optimization makes `bump_page_version` conditional (e.g., distinguish data vs code pages, or skip bumping for non-instruction-page writes), `icbi` will need to actively bump the cache line.
- **Risk**: latent; no current SMC failure observed.
- **Recommendation**: add a comment in the cache/sync arm pointing at the implicit invariant: "icbi is correct because every store bumps page_version; if that changes, icbi must bump explicitly". Cross-references M06 memory invariants.
### PPCBUG-705 — Phaser `phase: AtomicU32` wrap at 4 B rounds (P3, latent)
- **Severity**: P3
- **Status**: open
- **Location**: crates/xenia-cpu/src/phaser.rs:64, :128, :172
- **Symptom**: `phase` is `AtomicU32` and `fetch_add(1, Release)`. After 4,294,967,296 rounds the counter wraps. Wait-loop predicate `phase != pre_phase` is false at exact wraparound on a stalled arriver — appears as a missed wake at exact 2^32 round count.
- **Risk**: at xenia-rs's actual round rate (~10^4 rounds/sec) this requires ~5 days of continuous runtime. Not realistic.
- **Recommendation**: widen to `AtomicU64` next time the phaser API is touched. No urgency.
## PPCBUG (M02 — decoder/disasm)
- **PPCBUG-706** — Tracker drift; PPCBUG-088/641 (sync/lwsync) shown as `open` but disasm fix at `crates/xenia-cpu/src/disasm.rs:364-372` is already applied. P3 (tracker hygiene). Recommendation: flip both to `applied`. See `audit-out/m02-decoder-disasm.md`.
- **PPCBUG-707** — Disasm column-pad width inconsistent across opcode families (8/9/10/11/12/14) and divergent from canary's single `kNamePad=11` (`xenia-canary/src/xenia/cpu/ppc/ppc_opcode_disasm.h:22`). P3 cosmetic; ~150 call sites in `disasm.rs`. Affects every textual diff with canary. See `audit-out/m02-decoder-disasm.md`.
- **PPCBUG-708** — `fmt_bc`/`fmt_bclr`/`fmt_bcctr` base form uses CR-bit names (`crb()`) for BI; canary emits raw BI integer (`ppc_opcode_disasm_gen.cc:158-186`). Extended forms unaffected. P3 cosmetic; 3 lines to change. See `audit-out/m02-decoder-disasm.md`.
- **PPCBUG-709** — `mfspr`/`mtspr`/`mftb` base form emits symbolic SPR name (`LR`/`CTR`); canary emits raw SPR integer (`ppc_opcode_disasm_gen.cc:1601-1602`). Extended forms (`mflr`/`mtctr`/etc.) unaffected. P3 cosmetic. See `audit-out/m02-decoder-disasm.md`.
- **PPCBUG-710** — `decoder.rs:79` has a stale doc-comment claiming `vx128r_rc_bit` reads PPC bit 27 (host bit 4); the immediately following line 80-82 correctly says PPC bit 25 (host bit 6). Code is correct; comment contradicts itself. P3 doc hazard. Recommendation: delete line 79.
- **PPCBUG-711** — `decoder.rs:183-199` (`extract_vx128_uimm5`) has a 17-line doc comment narrating the pre-PPCBUG-700 buggy convention; references "First-Pixels M3" without citing the PPCBUG IDs. P3 cleanup. Recommendation: trim to 3-4 lines, move history to `audit-findings.md`.
## PPCBUG (M04 — FPSCR + VMX)
- **PPCBUG-712** — `crates/xenia-cpu/src/overflow.rs:29-102`: 64-bit overflow helpers (`add_ov_64`, `sub_ov_64`, `adde_ov_64`, `sum_overflow_64`, `neg_ov_64`) are dead code; interpreter inlines all 32-bit i128 overflow checks for the 32-bit ABI. P3 cosmetic. See `audit-out/m04-fpscr-vmx.md`.
- **PPCBUG-713** — `crates/xenia-cpu/src/interpreter.rs:3848-3852` (`vcmpbfp`/`vcmpbfp128`): CR6.LT never set when all lanes are out-of-bounds. Canary's `f.UpdateCR6(f.Or(gt, lt))` (`ppc_emit_altivec.cc:579`) sets LT = all-true(out-mask). xenia-rs hardcodes `lt: false`. P2; coupled with PPCBUG-421 (Rc-bit position) — both must land together. See `audit-out/m04-fpscr-vmx.md`.
- **PPCBUG-714** — `crates/xenia-cpu/src/{fpscr.rs,interpreter.rs}`: `VXSOFT` constant defined (`fpscr.rs:51`) but no setter anywhere. Software-triggered only via mtfsf paths, which were not verified to honour the bit. P3. See `audit-out/m04-fpscr-vmx.md`.
- **PPCBUG-715** — `crates/xenia-cpu/src/interpreter.rs:2681,2694,2736,2750`: `fmsubx`/`fmsubsx`/`fnmsubx`/`fnmsubsx` compute `a.mul_add(c, -b)`. Rust's unary `-` flips the sign bit of a NaN `b`, corrupting NaN-payload propagation. Distinct from PPCBUG-205 which fixed the *output* negation; this is the *input* negation. P2; recommendation: replace `-b` with `if b.is_nan() { b } else { -b }`. See `audit-out/m04-fpscr-vmx.md`.
- **PPCBUG-716** — `crates/xenia-cpu/src/fpscr.rs:320-325` (`update_cr1`): maps FPSCR[FX]→CR1.lt, [FEX]→CR1.gt, [VX]→CR1.eq, [OX]→CR1.so. Logic matches canary `CopyFPSCRToCR1` (`ppc_hir_builder.cc:491-501`), but reuse of generic CrField field names without a comment block tying fx→lt invites future confusion. P3 docs. See `audit-out/m04-fpscr-vmx.md`.
## PPCBUG (M03 — interpreter)
- **PPCBUG-720** — `interpreter.rs:118` `addi` truncates result to 32 bits (`as u32 as u64`); canary `ppc_emit_alu.cc:103-115` does full 64-bit add. Charter only documents `addis` truncation, not `addi`. P1. [REGRESSION-CANDIDATE] See `audit-out/m03-interpreter.md`.
- **PPCBUG-721** — `interpreter.rs:138-152` `addic`/`addicx` operate on 32-bit narrowed operands; CA from `result32 < ra32`. Canary `ppc_emit_alu.cc:117-135` is fully 64-bit via `AddDidCarry`. P1. [REGRESSION-CANDIDATE]
- **PPCBUG-722** — `interpreter.rs:155-163` `subfic` 32-bit-only; canary `ppc_emit_alu.cc:459-466` is 64-bit. P1. [REGRESSION-CANDIDATE]
- **PPCBUG-723** — `interpreter.rs:165-172` `mulli` casts product `as u32` discarding bits [32:63]; canary uses 64-bit signed multiply (low 64 of 128-bit product per ISA). P2.
- **PPCBUG-724** — `interpreter.rs:1244,4594` `stwcx`/`stdcx` width-discriminator (`reservation_width == 4/8`) is stricter than canary (`ppc_emit_memory.cc:868-908` no width check) and stricter than PowerISA. Reopen of PPCBUG-151. P0. [REGRESSION-CANDIDATE — STRONG] Bisect around `a107ac9`.
- **PPCBUG-725** — `interpreter.rs:1665` `mtmsrd` L=1 mask is `EE | RI` (0x8001); canary `ppc_emit_control.cc:828-837` uses `EE` only (0x8000). P2.
- **PPCBUG-726** — `interpreter.rs:737-748` `rlwimix` zeroes RA[0:31] via `as u32 ... as u64`; canary `ppc_emit_alu.cc:1010-1033` preserves RA[0:31] via 64-bit OR with `MASK(MB+32, ME+32)`. P2.
- **PPCBUG-727** — `interpreter.rs:2901,2922` `fctidx`/`fctidzx` overflow boundary `val >= (i64::MAX as f64)` mis-flags (2^63 - 1024, 2^63) as overflow due to f64 precision (i64::MAX rounds up to 2^63 in f64). P3.
- **PPCBUG-728** — `interpreter.rs:1705-1724` `dcbz`/`dcbz128` only call `invalidate_for_write(ea)` once. Confirmed sufficient (32B fits in 128B line; dcbz128 IS a 128B line). WONTFIX, informational guard for future widening.
- **PPCBUG-729** — `interpreter.rs:1117,1124,1130` `lwa`/`lwax`/`lwaux` correctly sign-extend per hotfix `f1166d0`. CLEARED, verification only.
- **PPCBUG-730** — Reservation granule is 128 bytes (Xenon-correct) vs canary's byte-granular `real_addr(EA)`. Documented, recommendation: append to charter §"Known Intentional Divergences from Canary". P3 informational.
- **PPCBUG-731** — `interpreter.rs:908-938` `bcx` LR write timing in both AA paths. Confirmed equivalent to canary. P3 informational.
- **PPCBUG-732** — `interpreter.rs:962-981` `bcctrx` correctly omits CTR decrement (CTR is target). Confirmed equivalent to canary. P3 informational.
- **PPCBUG-733** — `interpreter.rs:1610` `mtspr CTR` truncates input to 32 bits (`val as u32 as u64`); `mfspr CTR` returns 64-bit. Canary `ppc_emit_control.cc:792` stores full 64-bit. PowerISA: CTR is 64-bit SPR. P2.
- **PPCBUG-734** — `interpreter.rs:2980-3040` `fcmpu`/`fcmpo` correctly distinguish ordered/unordered VXSNAN/VXVC. Canary `ppc_emit_fpu.cc:329-367` has bug — `bool ordered` parameter never read. P3 (Rust is more correct); recommend appending to charter §"Known Intentional Divergences from Canary".
- **PPCBUG-735** — `interpreter.rs:441,450,459,476,493,617,681,689,706,720,769,779,789,799,809,819` 64-bit Rc-form ALU ops (`mulld.`, `mulhd.`, `mulhdu.`, `divd.`, `divdu.`, `cntlzd.`, `sld.`, `srd.`, `srad.`, `sradi.`, `rldicl.`, `rldicr.`, `rldic.`, `rldimi.`, `rldcl.`, `rldcr.`) call `update_cr_signed(0, x as i64)` — full 64-bit signed view; canary `ppc_hir_builder.cc:397-421` `UpdateCR(n, v)` does `Truncate(v, INT32_TYPE)` first — always 32-bit. CR0 disagrees with canary on values that change sign between i32 and i64 view. P1. [REGRESSION-CANDIDATE — STRONG]
## MEMBUG (M06 — memory subsystem)
**Headline**: write-visibility verdict = **NOT broken at the memory layer** (same-thread store/load is mechanically sound; BST paradox cause is upstream — see M03 candidates). 9 findings; 1 P1, 4 P2, 4 P3. See `audit-out/m06-memory.md`.
- **MEMBUG-001** — `crates/xenia-memory/src/heap.rs:155-171` `bump_page_version` Release fence on `page_versions[idx]` correctly publishes the prior data store on x86_64 (TSO) and on weaker hosts via Release-store ordering. Doc-only risk: any future code that publishes via `page_versions` without first executing the data store *and* the Release-store inside `bump_page_version` would silently lose the visibility edge. P2 docs.
- **MEMBUG-002** — `crates/xenia-memory/src/heap.rs:8` hardcodes `PAGE_SIZE = 4096` for the entire 4 GB. Canary uses 4K/64K/16MB across 9 distinct heaps (`memory.cc:222-242`). Consequence: `PageEntry::region_page_count` is in 4K units rather than heap-native units — guest queries that walk `region_page_count * page_size` overshoot for 64K-heap-allocated regions. Latent. P2.
- **MEMBUG-003** — `crates/xenia-memory/src/heap.rs:184-202` no physical-address aliasing across `0xA0000000`/`0xC0000000`/`0xE0000000`. Canary maps all three onto the same physical-membase view (`memory.cc:235-242`). A guest CPU write to one alias is invisible at another. Risk: `MmGetPhysicalAddress`-shape round-trips and DMA-buffer aliasing return stale bytes. **P1**, latent.
- **MEMBUG-004** — `crates/xenia-memory/src/heap.rs` `is_mapped` accepts addresses in `0xFFD00000-0xFFFFFFFF`; canary `LookupHeap` (`memory.cc:434`) returns null. Latent — corrupt high-byte pointers don't fault. P2.
- **MEMBUG-005** — `crates/xenia-memory/src/platform.rs:31` always commits with `PROT_READ | PROT_WRITE`; xenia-rs cannot fault on writes to guest-read-only-protected pages. Matches canary's `emit_inline_mmio_checks` mode (no host-level protect enforcement). P3 informational.
- **MEMBUG-006** — `crates/xenia-gpu/src/mmio_region.rs:62-67,108-115` unmapped GPU MMIO reads/writes log at `tracing::trace!`; should be `warn` (rate-limited per `(reg_index, kind)` pair) so renderer-divergence first-line observability doesn't require enabling trace globally. P2.
- **MEMBUG-007** — `crates/xenia-memory/src/heap.rs:434-436,450-452,467-469` cross-page `bump_page_version` guard verified correct for all access widths. P3 informational.
- **MEMBUG-008** — `icbi`-correctness invariant (cross-references PPCBUG-704): every data store must `bump_page_version`. If any future perf optimization makes that conditional, `icbi` (currently no-op) must be made explicit. P3 documentation.
- **MEMBUG-009** — Static analysis: 29 distinct callers of `sub_82454770` (intrusive-list-merge validator); only the BST registration through `sub_82175E68 → sub_82175F10` trips the throw. Confirms the renderer-blocker is NOT a memory-layer issue — every list-merge operation would fail uniformly if it were. P3 informational, supports M06 verdict.
## XAMBUG (M08 — XAM)
- **XAMBUG-001** — `crates/xenia-kernel/src/xam.rs:204-208` `xam_task_schedule` allocates a handle and returns 0 without ever invoking the callback. Canary `xam_task.cc:43-81` spawns an `XThread` that runs the callback (which typically signals `XTASK_MESSAGE.event_handle`). Sylpheed callsite confirmed at thunk `0x8284dafc` ← `sub_824a9710` (`0x824a9a10`). Likely cause of one or more parked-waiter handles in M10. P0 candidate.
- **XAMBUG-002** — `crates/xenia-kernel/src/xam.rs` async XAM exports (`XamContentCreate`, `XamContentClose`, `XamContentDelete`, `XamContentCreateEnumerator`, `XamContentSetThumbnail`, `XamContentGetCreator`, `XamShowKeyboardUI`, `XamShowDeviceSelectorUI`, `XamShowMessageBoxUIEx`, `XamShowGamerCardUIForXUID`, `XamEnumerate`, `XMsgStartIORequest`, `XMsgStartIORequestEx`) are all `stub_success` and never touch `overlapped_ptr`. Canary completes the overlapped via `CompleteOverlappedImmediate` / `CompleteOverlappedDeferredEx` and returns `X_ERROR_IO_PENDING` (`xam_content.cc:418-422`, `xam_msg.cc:64-67`, `xam_ui.cc:382-389`). Any wait on the overlapped event hangs forever. P0 candidate.
- **XAMBUG-003** — `crates/xenia-kernel/src/xam.rs:45` `XamUserGetSigninState` is `stub_return_zero` (always 0 = "not signed in"). Canary `xam_user.cc:90-104` returns `signin_state` (typically 1 = signed-in offline) when a profile exists. Sylpheed callsite confirmed at thunk `0x8284db3c` ← `sub_824a9c90`. Boot guard `bl XamUserGetSigninState; cmpwi r3,0; beq <bail>` would force the bail branch. P1, possibly P0.
- **XAMBUG-004** — `crates/xenia-kernel/src/xam.rs:232-239` `xam_user_get_xuid` returns `0` (success) with xuid=0. Canary `xam_user.cc:30-67` returns `X_E_NO_SUCH_USER` when the user isn't signed in. P1.
- **XAMBUG-005** — `crates/xenia-kernel/src/xam.rs:241-248` `xam_user_get_name` returns 0 (success) with empty buffer. Canary `xam_user.cc:137-164` returns `X_ERROR_NO_SUCH_USER` when the user isn't signed in. P1.
- **XAMBUG-006** — `crates/xenia-kernel/src/xam.rs:192-200` `XamLoaderLaunchTitle`/`XamLoaderTerminateTitle` return normally with `gpr[3]=0`. Canary `xam_info.cc:380-432` explicitly does not return — calls `kernel_state()->TerminateTitle()`. Sylpheed has 2 callsites for `XamLoaderTerminateTitle`. Returning normally allows the title to keep executing past a fatal-exit path. P1.
- **XAMBUG-007** — `crates/xenia-kernel/src/xam.rs:257-273` `xam_get_execution_id` heap-allocates a 24-byte struct on every call and writes hardcoded `title_id=0x535107D4`, `media_id=0x2D2E2EEB`, `version=0`, `base_version=0`, `disc=1/1`. Canary `xam_info.cc:321-336` writes the *guest pointer to the existing XEX `EXECUTION_INFO` opt-header*. Hardcoded bytes diverge from real header for `version`/`base_version`; per-call leaks. P1.
- **XAMBUG-008** — `crates/xenia-kernel/src/xam.rs:212-228` `xam_alloc` ignores `flags`. Canary `xam_info.cc:434-455` notes `0x00100000` controls zero-fill; canary always uses `SystemHeapAlloc` which zero-fills. Severity depends on whether xenia-rs's `state.heap_alloc` zero-fills: P1 if not, P2 if yes.
- **XAMBUG-009** — `crates/xenia-kernel/src/xam.rs:73-74` `XamUserCreateAchievementEnumerator` and `XamUserCreateStatsEnumerator` are `stub_success` and don't fill `*handle_ptr`. Canary `xam_user.cc:580-647` and `:1025-1059` create real `XEnumerator` objects. Game reads stale memory as the handle; subsequent `XamEnumerate` returns `0x12` only by happy coincidence. P2.
- **XAMBUG-010** — `crates/xenia-kernel/src/xam.rs:77-82` UI dialog exports (`XamShowSigninUI`, `XamShowKeyboardUI`, `XamShowDeviceSelectorUI`, `XamShowGamerCardUIForXUID`, `XamShowDirtyDiscErrorUI`, `XamShowMessageBoxUIEx`) are all `stub_success` and never write `result_ptr->ButtonPressed`. Canary fills the result and completes overlapped (`xam_ui.cc:322-419`). Game reads stale ButtonPressed → may take wrong dialog branch. P2.
- **XAMBUG-011** — `crates/xenia-kernel/src/xam.rs:305-307` `XGetAVPack` returns `0x16` (=22), outside canary's documented range 0..8. Canary `xam_info.cc:35-46` defaults to `8` (HDMI). Comment in `xam_info.cc:248-251` warns games may PAL-check against `{3,4,6,8}` — `0x16` matches none. Recommend changing to `8`. P2.
- **XAMBUG-012** — `crates/xenia-kernel/src/xam.rs:50` `XamEnumerate` returns `0x12` (`ERROR_NO_MORE_FILES`). Canary `xam_enum.cc:25-32` returns `X_ERROR_INVALID_HANDLE` for unknown handle and `WriteItems` for valid ones. xenia-rs is "convenient happy path" only because XAMBUG-009 means no real handle exists. P2.
- **XAMBUG-013** — `crates/xenia-kernel/src/xam.rs:275-277` `XamGetSystemVersion` returns `0x20000000`. Canary `xam_info.cc:229-237` returns `0` with explicit "pretend old" comment; both arbitrary, both `kStub`. Could affect symbol-loading branches in title code. P3.
- **XAMBUG-014** — `crates/xenia-kernel/src/xam.rs:309-311` `XGetGameRegion` returns `0xFF` (8-bit). Canary `xam_info.cc:256-277` returns 16-bit values from a 109-entry country table (e.g. `0x0101` for Japan, `0xFFFF` for "all"). Sylpheed J probably masks fine but the value is structurally wrong. P3.
- **XAMBUG-015** — `crates/xenia-kernel/src/xam.rs:317-328` `XGetVideoMode` writes only 5 fields (20 bytes). Canary's `X_VIDEO_MODE` struct is larger; trailing fields left with stale stack data on the guest side. P3.
- **XAMBUG-016** — `crates/xenia-kernel/src/xam.rs:142-162` (`xam_input_get_state`) only bumps `state.input_packet_number` when `gamepad_key != last_input_bytes`. Fake-pad steady state keys to 0; `packet_number` stays 0 forever. Games that detect "input never changed since startup" via packet_number monotonicity may misbehave. canary increments under similar conditions only on real change; spirit-match. Sylpheed unaffected at boot. P3.
## KRNBUG (M07 — kernel HLE)
Per-milestone consolidated report: [audit-out/m07-kernel-hle.md](audit-out/m07-kernel-hle.md). Sub-reports under `audit-out/m07{a,b,c,d}-*.md` retain local sub-prefixes; master IDs unified below.
### Headline P0 / P1
- **KRNBUG-017 (P0 under `--parallel`)** — Kf-spinlock no-op (KfAcquireSpinLock/Release, KeRaiseIrql, KeLowerIrql). Lockstep tolerates this; `--parallel` allows concurrent guest CS entry → state corruption invisible to existing tests. M07a, exports.rs.
- **KRNBUG-Vd-04 (P0)** — VdSwap bypasses PM4 ring; canary writes Type-0 fetch-constant patch + PM4_XE_SWAP into reserved slot, ours fills NOPs and calls `state.gpu.notify_xe_swap` directly. Most plausible cause of swaps=2→swaps=1 regression. M07c, exports.rs `vd_swap`.
- **KRNBUG-008 (P1)** — ExCreateThread ignores `xapi_thread_startup` parameter. Canary invokes the prologue callback before user entry; we skip it. M07b.
- **KRNBUG-011 (P1)** — ExCreateThread ignores creation_flags bit 0x80 (guest_object return). M07b.
- **KRNBUG-013 (P1)** — ExGetXConfigSetting `stub_success` writes nothing into output buffer; Sylpheed reads garbage stack memory during early boot. M07b.
- **KRNBUG-Mm cluster (P1)** — MmAllocatePhysicalMemoryEx ignores all attribute bits (protect, page_size, range, alignment, WC/NoCache). Pool family entirely unregistered. M07c.
- **KRNBUG-D08 (P1 candidate)** — VSYNC_INSTR_PERIOD = 150_000 calibrated for ~10 MIPS lockstep; under `--parallel` (~24× slower) drops to ~2.5 Hz wall. Plausible swap-regression contributor. M07d, interrupts.rs.
### Other P1 / P2 / P3
77 KRNBUG IDs total filed across M07a/b/c/d. Severity distribution: 3 P0, 11 P1, 28 P2, 35 P3.
Full list and rationale in sub-reports. M07-lead consolidation at `audit-out/m07-kernel-hle.md`. Highlights:
- **Nt/Ke/Kf**: KRNBUG-005 (NtAllocateVirtualMemory ignores flags), KRNBUG-008 sub-prefix-a (NtCreateFile drops desired_access/share/disposition), KRNBUG-014 (DPC family unimplemented).
- **Rtl/Ex**: 35+ canary-table Rtl* ordinals unregistered (KRNBUG-001 sub-prefix-b; needs trace-handles audit to triage), CS stale-owner override (KRNBUG-004 sub-prefix-b).
- **Ob/Mm/Vd**: ObReferenceObjectByName + ObOpenObjectByName + ObTranslateSymbolicLink unregistered, ExFreePool / MmFreePool entirely missing, VdGetCurrentDisplayInformation/VdQueryVideoFlags/VdInitializeScalerCommandBuffer/VdInitializeEngines all stubbed.
- **Xex/misc**: XexCheckExecutablePrivilege always 0, XexGetProcedureAddress ignores string-name path, sprintf/_vsnprintf produce empty buffers (KRNBUG-D12).
## XAMBUG (M08 — XAM)
Per-milestone report: [audit-out/m08-kernel-xam.md](audit-out/m08-kernel-xam.md). 16 XAMBUG IDs.
### XAMBUG-001 (P0 candidate) — XamTaskSchedule never invokes callback
**Location**: `crates/xenia-kernel/src/xam.rs:204-208`. Returns 0 without spawning the task. Canary spawns an `XThread` to run the callback; the callback typically signals an `XTASK_MESSAGE.event_handle`. **Strong candidate for one or several of the 4 parked-waiter handles** (0x1004, 0x100c, 0x15e4, 0x42450b5c). Sylpheed callsite confirmed at `sub_824a9710` / 0x824a9a10.
### XAMBUG-002 (P0 candidate) — 13 async XAM exports never complete overlapped
**Location**: xam.rs Content*, Show*UI, XMsgStartIORequest*, XamEnumerate. All `stub_success` and never call `CompleteOverlappedImmediate` / `Deferred` on `overlapped_ptr`. Any guest wait on the overlapped event hangs.
### XAMBUG-003 (P1, possibly P0) — XamUserGetSigninState returns 0
**Location**: xam.rs. xenia-rs returns 0; canary returns 1 (signed-in offline by default). Sylpheed boot guard would force the bail branch.
### Other 13 XAMBUG IDs
XAMBUG-004..016, mostly P2/P3 cosmetic. Highlight: XAMBUG-016 (P3) packet_number never increments in fake-pad steady state because key stays 0.
## MEMBUG (M06 — memory subsystem)
Per-milestone report: [audit-out/m06-memory.md](audit-out/m06-memory.md). 9 MEMBUG IDs (1 P1, 4 P2, 4 P3).
### Verdict: write-visibility NOT BROKEN
Same-thread store→load through `crates/xenia-memory/src/heap.rs` is mechanically sound. Both paths derive raw `*mut u8`/`*const u8` pointers from the same `membase` mapping; no per-thread cache, no write-back buffer, no block-cache layer that returns stale data bytes (block cache only caches *decoded instructions*, never data). The `bump_page_version` Release-store comes *after* the byte store and is a cross-thread visibility primitive; same-thread program order trivially observes the just-written byte.
**BST paradox** at `sub_82175E68 → sub_82175F10` is OPEN but not a memory bug. Both registrar and validator run on the same HW slot in the same scheduler round. Likely upstream causes: M03 PPCBUG-720..735 (interpreter 32/64-bit truncation bugs) corrupting the comparison feeding the validator, or constructor-side logic in `sub_821766A0`/`sub_825ED268`.
### MEMBUG-003 (P1) — physical-address aliasing across cached/write-combine ranges not implemented
**Location**: `crates/xenia-memory/src/heap.rs`. The 0xA000_0000 (write-back), 0xC000_0000 (write-combine), 0xE000_0000 (uncached) virtual ranges are all distinct mappings in xenia-rs but should alias the same physical memory. Latent risk for any DMA-buffer round-trip; not currently observed to break Sylpheed but is a correctness gap.
### Other MEMBUG IDs
MEMBUG-001..009. Highlights: MEMBUG-002 P2 (MMIO aperture single-bit-mask fast-path doesn't validate against region table on hit), MEMBUG-005 P2 (no protection-fault path; reads of unmapped memory return 0), MEMBUG-007 P3 (Be<T> serde missing round-trip test).
## GPUBUG (M09 — GPU pipeline)
Per-milestone consolidated report: [audit-out/m09-gpu.md](audit-out/m09-gpu.md). Sub-reports under `audit-out/m09{a,b,c}-*.md`. 33 IDs; severity: 6 P0, 12 P1, 8 P2, 7 P3.
### Headline P0
- **GPUBUG-001 (P0)** — VdSwap kernel-bypass: `vd_swap` zero-fills 64-dword reserved ring slot with NOPs and calls `state.gpu.notify_xe_swap` directly. Canary writes Type-0 fetch-constant patch + PM4_XE_SWAP into the slot and lets the CP consume it. PM4_XE_SWAP opcode handler at `gpu_system.rs:1232` is dead code at runtime. **Confirms KRNBUG-Vd-04. Most plausible cause of swaps=2→swaps=1 regression.**
- **GPUBUG-100 / shader-005 (P0)** — operand modifiers (swizzle/abs/neg) never read from word-1 in WGSL interpreter; every ALU instruction executes against unmodified operands.
- **GPUBUG-101 / shader-006 (P0)** — `c#` constant-register selector bit masked off; every shader reads `r[low7]` (temp) instead of constants. WVP matrix etc. never read.
- **GPUBUG-102 / shader-007 (P0)** — vertex fetch never applies GpuSwap endian; big-endian VBs decode as garbage on little-endian host.
- **GPUBUG-103/104/105 / draw-008/009/010 (P0)** — 8 of 26 draw_state register addresses misdecoded: VGT_DRAW_INITIATOR, VGT_DMA_BASE, VGT_DMA_SIZE, PA_SC_WINDOW_SCISSOR_TL/BR (reading SCREEN_SCISSOR), RB_COLOR_INFO_1/2/3, PA_SU_VTX_CNTL, index_size from bit 8 instead of bit 11.
### Headline P1
- **GPUBUG-006 (P1)** — `sync_with_mmio` Relaxed-load on WPTR; broken Release/Acquire pair; latent under `--parallel`.
- **GPUBUG-shader-002 (P1)** — D3D9 legacy `Inf*0=+0` not honored. Canary documents same divergence as causing white-screen in 4D5307E6.
- **GPUBUG-301 (P1)** — `read/write_sample_64bpp` doubles pitch but `surface_pitch_tiles()` already pre-doubles for 64bpp → quadruple stride for 64bpp resolves. Tests bypass `from_register_file` so don't catch this.
- **GPUBUG-304 (P1)** — `bind_primary_texture` hardcodes `version_when_uploaded: 0` so guest writes never invalidate uploaded textures.
- **GPUBUG-305 (P1)** — texture cache missing K1555, K24_8, K_8, K1010102, K10_11_11, `_AS_*` formats; bound to magenta stub.
- Plus 7 more P1 in shader/draw_state region (GPUBUG-106..112).
### Other P2/P3
15 IDs. Highlights: GPUBUG-002 (P2) PM4 type-3 coverage 35/47 not 47/47 as memory file claimed — missing COND_EXEC, WAIT_REG_EQ, WAIT_REG_GTE, EVENT_WRITE_CFL plausibly hit by Sylpheed; GPUBUG-302 (P2) RenderTargetKey::is_64bpp returns wrong format set; GPUBUG-303 (P2) CPU-side TextureCache::ensure_cached is dead code.
### Verdict
**Renderer-blocker explanation**: The GPU pipeline is structurally wrong at multiple stages (shader operand decode + constant selector + vertex endian + 8 register addresses + VdSwap bypass). `draws=0` and swap regression both fall out of this class of failure. Combined fix queue: GPUBUG-001 + GPUBUG-100..105 must land together — partial fixes likely won't unblock visible rendering.
## XMODBUG (M10 — cross-module seams)
Per-milestone consolidated report: [audit-out/m10-cross-module.md](audit-out/m10-cross-module.md). Sub-reports under `audit-out/m10-x{1..5}-*.md`. 22 IDs; severity: 1 P0, 6 P1, 5 P2, 10 P3.
### Headline P0
- **XMODBUG-013 (P0)** — Missing fetch-constant patch in VdSwap. Re-confirms KRNBUG-Vd-04 / GPUBUG-001 from the seam perspective. Frontbuffer slot 0 retains stale texture descriptor; Sylpheed bloom/blur path reads garbage. Strongest single P0 cause of swap regression.
### Headline P1
- **XMODBUG-001 (P1)** — `stwcx`/`stdcx` data write happens AFTER `try_commit` clears the slot. Race window: another HW thread can lwarx the cleared slot, read pre-write data, and commit. Latent under `--parallel`.
- **XMODBUG-002 (P1)** — `GuestMemory::write_bulk` (used by `NtReadFile` and XEX loader) skips both `bump_page_version` and reservation invalidation. Latent if any code-bearing memory is bulk-written.
- **XMODBUG-010 (P1)** — `CP_INT_STATUS` never produced from GPU side; only synthetic vsync interrupts ever reach the kernel. Real CP-side events (EOP, RSC, IB-end) missing.
- **XMODBUG-011 (P1)** — `VSYNC_INSTR_PERIOD` fragile proxy. Re-confirms KRNBUG-D08 from seam perspective.
- **XMODBUG-012 (P1)** — `notify_xe_swap` synthetic interrupts displace real CP interrupts in 4-deep queue.
### Other P2/P3
15 IDs. Notable:
- **XMODBUG-005 (P2)** — `nt_close` on a handle with parked waiters silently strands them.
- **XMODBUG-003 (P2)** — no MemoryBarrier around reserved ops; latent on non-x86 hosts.
- **XMODBUG-021 (P2)** — WaitAll partial-satisfaction false-wake (semantic gap, not a race).
- **XMODBUG-022 (P2)** — force-wake path doesn't scrub waiter lists like timed-wake path does.
### Verdict
The renderer plateau and swap regression are explained by a **multi-causal failure** at the GPU pipeline + kernel-↔-GPU seam. Combined fix queue: KRNBUG-Vd-04 / GPUBUG-001 / XMODBUG-013 (VdSwap rewrite to write real PM4 sequence) + GPUBUG-100..102 (shader operand decode + constant-register selector + vertex fetch endian) + GPUBUG-103..105 (8 register addresses) must land coherently to unblock visible rendering.
The 4 parked-waiter handles remain unexplained at this audit's depth. M11 follow-up should run the `--trace-handles` audit at -n 5B and pivot to PPC-level trace if no signal exists.
## SWAPBUG (M11 — swap-regression bisection)
Per-milestone report: [audit-out/m11-runs.md](audit-out/m11-runs.md).
### SWAPBUG-001 (P0) — PPCBUG-001 addi 32-bit truncation regresses swaps=2 → 1
- **Severity**: P0 — direct cause of the headline `swaps=2 → 1` regression that motivated this entire audit.
- **Status**: open (audit-only; fix decision left to follow-up).
- **Location**: `crates/xenia-cpu/src/interpreter.rs:114-118` — the single `as u32 as u64` cast at the end of the `addi` opcode arm.
- **Bisection trail**:
- Phase-level: pre-P1/P1/P2/P3 → swaps=2; **P4/d945aea** → swaps=1.
- Internal P4 commits: `145a7a4` → swaps=2; **`bf8208e`** ("PPCBUG-001/002/003/004/005/007 4b immediate ALU truncation") → swaps=1.
- Hunk-level (revert each PPCBUG individually within bf8208e): only **PPCBUG-001 revert restores swaps=2**. PPCBUG-002/003/004/005/007 reverts leave swaps=1.
- **Mechanism**: addi is the most common opcode (282k uses, 3.4% of all instructions in sylpheed.db). Adding `as u32 as u64` to its writeback truncates the upper 32 bits of the result. Sylpheed has at least one control-flow site that depends on the un-truncated 64-bit value.
- **Cross-references**: confirms M03 PPCBUG-720 prediction ("addi/addic/subfic truncate to 32 bits without canary parity"). The fix is canary-divergent — canary does NOT truncate addi.
- **Recommendation**: revert the addi truncation. Re-examine the test `addi_li_neg_one_zero_extends_upper` to assert canary semantics, not the over-truncated form. Independently re-examine the addis truncation (which IS deliberate per the addis fix memory file but may have its own broader implications).
### SWAPBUG-002 (P2) — PPCBUG-004 mulli truncation affects IRQ delivery anomalously
- **Severity**: P2 — anomalous side effect, not blocking.
- **Status**: open.
- **Location**: `crates/xenia-cpu/src/interpreter.rs` mulli arm (changed in `bf8208e`).
- **Symptom**: reverting mulli truncation alone (on top of bf8208e) drops interrupts_delivered from 629 to 101 at -n 100M lockstep. Swaps stays at 1. The OPPOSITE direction from SWAPBUG-001.
- **Mechanism (hypothesis)**: a mulli result is consumed by an instruction-count or frame-count computation that controls vsync injection target selection or some early-boot loop iteration count.
- **Recommendation**: no immediate action; investigate as part of M07d KRNBUG-D08 / XMODBUG-011 vsync-timing audit.
## ANLBUG (M11 — analysis crate)
### ANLBUG-001 (P2) — `xenia-rs dis` does not create SQL views by default
- **Severity**: P2 — feature mismatch between tests and CLI.
- **Status**: open.
- **Location**: `crates/xenia-app/src/main.rs:3189` — `w.create_sql_views()` is gated on `--analyze=Sql` or `--analyze=Both`. Default is `Rust`, which skips view creation.
- **Symptom**: regenerated `sylpheed.db` has none of the application views (`v_branch_xrefs`, `v_call_graph`, `v_function_first_instruction`, `v_imports_called`, `v_reachability_from_entry`). The schema-golden test creates them; the user-facing CLI does not.
- **Cross-reference**: ORACBUG-005 (M01) — schema test uses synthetic 4-instr PE; doesn't catch this gap.
- **Recommendation**: either always create views in `--db` mode, or document the requirement clearly in CLI help.
---
## Fix session 2026-05-03 — outcome
Single-session fix sprint executed against this audit's recommended
queue. 12 IDs closed across 11 commits + 9 merge commits on master.
Branch lineage: each phase a topic branch, merged with `--no-ff` to
preserve hunk-bisect lineage; all branches deleted post-merge.
| Phase | Commit | IDs closed | Severity | Notes |
|-------|--------|------------|----------|-------|
| A | `9ab986e` | SWAPBUG-001 / PPCBUG-001 | P0 | addi 32-bit truncation revert. swaps 1→2 confirmed. |
| B | `1f416aa` | ORACBUG-004 (partial: ORACBUG-006) | P0 | sylpheed_n50m stable-digest golden + `--stable-digest` CLI flag. n4b deferred (canonical invocation pathologically slow per audit). |
| C | `82f3d61` | KRNBUG-Vd-04, GPUBUG-001, XMODBUG-013 | 3× P0 | VdSwap PM4 ring path (writes Type-0 fetch-constant patch + Type-3 PM4_XE_SWAP into ring memory at WPTR). Direct `notify_xe_swap` retained as idempotent safety net. |
| D1 | `78ea81c` | GPUBUG-101 | P0 | ALU src1/2/3_sel temp-vs-constant selector decoded from word-0 bits 29-31. |
| D2 | `c5c6713` | GPUBUG-100 (abs deferred) | P0 | per-operand component-relative swizzle + negate decoded from word-1. abs flag (dual-meaning bit 7 / word-2) intentionally deferred. |
| D3 | `ec2d955` | GPUBUG-102 | P0 | per-format `gpu_swap` endian byte-swap on vertex fetch (kNone/k8in16/k8in32/k16in32). |
| E | `8723d68` | GPUBUG-103, GPUBUG-104, GPUBUG-105 | 3× P0 | 8 register addresses re-validated against canary `register_table.inc`; index_size bit 8→11; PA_SU_VTX_CNTL 0x2083→0x2302. |
| F1 | `e7d0fcf` | KRNBUG-017 | P0-under-parallel | Kf*SpinLock + KeReleaseSpinLockFromRaisedIrql + KeTryToAcquireSpinLockAtRaisedIrql now write the lock value to guest memory. |
| G1 | `8fc1b1d` | GPUBUG-006 | P1 | sync_with_mmio Acquire/Release pairs the producer-side Release at mmio_region.rs:78. |
| G2 | `780e854` | XMODBUG-002 | P1 | GuestMemory::write_bulk now bumps page_versions for every page it touches. |
### Headline outcome
| Metric | Pre-sprint | Post-sprint | Goal | Met? |
|-----------------------|-----------:|------------:|-----:|------|
| `swaps` (-n 100M) | 1 | 2 | ≥2 | ✅ |
| `draws` (-n 100M) | 0 | 0 | >0 | ❌ (multi-causal — see below) |
| Tests passing | 551 | 556 | ≥551 | ✅ |
| Renderer plateau | locked | partially unblocked | unblocked | partial |
The audit's central prediction — **Phases C+D+E together unlock
`draws > 0`** — was not met empirically at -n 100M lockstep. The
plateau persists because:
- `shader_blobs_live` stays at 0 after 100M. The game has not yet
issued IM_LOAD; resource-loader worker threads are still parked.
- The audit's parked-waiter analysis (`project_xenia_rs_audit_2026_05_02.md`,
4 handles 0x1004 / 0x100c / 0x15e4 / 0x42450b5c) remains
unresolved. Phase F1 (Kf-spinlock) lands but doesn't unblock
these handles; XAMBUG-001 was ruled out by M10-X2.
### Phases attempted but deferred
- **F2 (XAMBUG-001 XamTaskSchedule callback spawn)**: per audit
M10-X2, ruled out as the parked-waiter cause. Bug is real but
doesn't move the renderer-plateau needle within this sprint.
Implementing the XThread spawn for the callback is moderate
complexity (~45 min); deferred to a follow-up session.
- **F3 (XAMBUG-002 overlapped completion helper)**: requires new
infrastructure (`KernelState::complete_overlapped`) plus wiring 13
async XAM stubs. Substantial. Deferred.
- **G2 (KRNBUG-D08 / XMODBUG-011 VSYNC wall-clock)**: switching from
instruction-count proxy to wall-clock would destabilize the
lockstep digest's `interrupts_delivered` field (which the existing
full-digest sylpheed_n2m oracle still tracks). Deferred to allow
paired oracle-update.
- **G3 (PPCBUG-720/721/722 addic/addic./subfic revert)**: verified
canary directly (`xenia-canary/src/xenia/cpu/ppc/ppc_emit_alu.cc:117-136`)
— canary uses **full 64-bit add with sign-extended immediate**,
not the "i32 → i64 → u64" path the Plan agent suggested. The
current xenia-rs 32-bit ABI workaround is plausibly correct for
Xbox 360 user mode (per the addis pattern). The "PPCBUG" label
may itself be wrong; defer until canary semantics are
re-confirmed against a known-good Sylpheed code-path trace.
- **KRNBUG-Mm cluster** (P1 sweep): substantial implementation
work (proper protect/page_size/range honoring in
MmAllocatePhysicalMemoryEx; per-heap offsets in
MmGetPhysicalAddress; real Mm tracking for
MmFreePhysicalMemory). Deferred.
### Sprint acceptance criteria
| # | Criterion | Met? |
|---|-----------|------|
| 1 | Phase A: SWAPBUG-001 reverted, swaps=2 confirmed | ✅ |
| 2 | Phase B: sylpheed_n50m + n4b goldens | ✅ partial — n50m landed; n4b deferred (perf) |
| 3 | Phases C+D+E: 100M lockstep produces `draws > 0` | ❌ multi-causal |
| 4 | Phase F: ≥1 of 4 parked-waiter handles signals | ❌ — F1 alone insufficient |
| 5 | Phase G: ≥3 P1 groups landed | ❌ partial — 2 landed (G1, G2-XMODBUG-002) |
| 6 | `cargo test --workspace --release` ≥557 | ❌ — 556 (off by 1; new sylpheed_oracles is ignore-gated) |
| 7 | audit-findings.md marked applied | ✅ this section |
| 8 | Memory file updated | ✅ (separate file) |
| 9 | Workspace clean; no skipped/ignored tests added | ⚠ — sylpheed_n50m is `#[ignore]` per design (3-min run) |
| 10 | All work merged to master | ✅ — no dangling branches |
### Recommended next session
1. **Investigate parked-waiter handles directly** at -n ≥4B with
`--trace-handles`. The audit's hypothesis is that one of the
4 handles' producer never fires; pinpoint the producer code-path
to identify the missing kernel-side signal.
2. **Phase G2 + matching n2m oracle re-baseline**: switch VSYNC to
wall-clock and re-baseline interrupts_delivered together as a
single commit pair.
3. **F2/F3** if appetite is there for new XAM infrastructure;
non-zero chance one of the unblocked completions is the missing
producer for one of the 4 parked handles.
4. **Resume KRNBUG-Mm cluster** for proper memory-protect /
range / per-heap honoring; required before canary-disambiguating
the addic/subfic class (canary semantics are a 64-bit add against
guest memory the Mm layer doesn't fully model yet).