Skip to content

Repair the faithful-adopt-caner merge: 3 dropped lifter fixes + 6 dangling runtime refs - #90

Open
sp00nznet wants to merge 4 commits into
integrate/faithful-adopt-canerfrom
fix/fold-merge-dropped-fixes
Open

Repair the faithful-adopt-caner merge: 3 dropped lifter fixes + 6 dangling runtime refs#90
sp00nznet wants to merge 4 commits into
integrate/faithful-adopt-canerfrom
fix/fold-merge-dropped-fixes

Conversation

@sp00nznet

Copy link
Copy Markdown
Owner

Fixes for regressions introduced by the faithful-adopt-caner merge itself. Targets #82's branch so that PR isn't rewritten — merge or cherry-pick, whichever suits.

The merge resolution for tools/ppu_lifter.py was not a superset

git diff sagemono/spu/faithful-adopt-caner HEAD -- tools/ppu_lifter.py   # 28 lines

It took sagemono's file wholesale and discarded ~835 lines of the ydkj side. Three of the losses are silent miscompiles:

dropped evidence in a fresh flOw lift
ctx->lr = <ret> on direct bl every lr= field in the runtime logs printed 0x00000000; ctx->lr writes 140k → 29k
TOCFIX ld r2,N(r1) → literal module TOC 14,146 sites reloading r2 from uninitialised stack → garbage TOC → OPD loads read code-as-data
_mem_base() rA=0 D-form literal-zero base loads/stores basing on ctx->gpr[0] (the _xea() X-form twin survived)

After relifting: 14146 TOCFIX / 0 stack-TOC reads / 120101 ctx->lr writes / 0 gpr[0] D-form bases, and the lr= diagnostics match the known-good ps3-draw lift exactly.

Still dropped, not restored here (flOw's EBOOT doesn't use them, so I can't verify them): VRSAVE mfspr/mtspr, stvlx/stvrx(l), vrfin/vrfiz/vrfip/vrfim, vexptefp, vlogefp. Worth a follow-up.

The runtime didn't link for any title

Undefined symbols in a .lib are only diagnosed when a title links it, so the archive built clean and this went unnoticed.

The SPU interpreter was not "consolidated into spu_channels.c" as 6dd8524 says — it was deleted, while both call sites in lv2_register.c stayed. Restored, along with spu_run_interp_job.

The function registry genuinely was consolidated, so restoring spu_fn_registry.c wholesale duplicates spu_register_function/spu_lookup/spu_begin_image; only spu_lifted_lookup was missing and it's re-added as the 3-line wrapper it always was.

lbp_hle_complete_pending, g_taskset_policy_bytes/size and g_vm_page_bitmap are title symbols referenced from shared runtime code. Each gets a default definition in its own one-symbol TU, per the existing spu_tsp_weak.c pattern — a port supplying the real one never pulls the archive member. They must stay separate files; a port can supply one without the others. g_vm_page_bitmap storage moves out of lbp/main.cpp, which is precisely why every non-LBP port failed.

Gotcha: the runtime CMakeLists globs runtime/*.c without CONFIGURE_DEPENDS — a new TU needs cmake -S . -B build re-run or it's silently never compiled.

Two more dropped/missing behaviours

  • cellSaveData: ps3-draw returned CELL_OK on CBRESULT_ERR_NODATA with the comment "an ERROR return leaves the title parked in MODE_AUTO_LOAD". The merge restored the spec-correct CELL_SAVEDATA_ERROR_NODATA and flOw parked exactly as predicted — 0 flips, never reaching m_InitEntityHierarchy. Applied to both entry points: flOw calls the old non-_2 cellSaveDataAutoLoad, which 9db188d added as a separate function, so patching only AutoLoad2 does nothing. Per the SDK the error return is correct; that flOw can't handle it is an unexplained bug in its own state machine, and the code now says so.
  • sys_ppu_thread: guest threads never got SetThreadStackGuarantee, so on a stack overflow the handler faults while reporting and the process dies with a bare AV inside the handler, losing the backtrace. main() already did this for the main thread.

Status

flOw builds and boots substantially further against this branch (209 → 306 log lines: savedata clears, thr_auto_load() completes), then hits a stack overflow before the app loop. FLOW_NO_SPU_WL=1 reproduces it, so it's the PPU lift. Lead for the follow-up: this lifter emits 7,915 g_trampoline_fn sites vs ps3-draw's 19,171, and 16 mid-function tail-entry wrappers vs 1,141, at near-identical function counts (38,287 vs 38,421) — so it's the branch routing, not the function list. The trampoline exists specifically to stop backward branches re-entering a prologue and growing the stack.

🤖 Generated with Claude Code

sp00nznet and others added 4 commits August 9, 2026 21:17
The faithful-adopt-caner merge took sagemono's ppu_lifter.py wholesale
(`git diff sagemono/spu/faithful-adopt-caner HEAD -- tools/ppu_lifter.py`
is 28 lines), discarding ~835 lines of the ydkj side. Three of the losses
are silent miscompiles, all caught relifting flOw:

- Direct `bl` no longer set ctx->lr. PPC `bl` writes LR = next insn; without
  it every mflr reads stale state and every stack-saved LR is garbage. In a
  run log EVERY lr= field printed 0x00000000. ctx->lr writes: 140k -> 29k.

- The ELFv1 `ld r2,N(r1)` TOC-restore lowering was gone. The recomp has no
  glink stub writing that save slot, so the reload pulls uninitialised stack
  into r2 -> garbage TOC -> OPD/table loads read code-as-data. flOw's lift
  had 14146 such sites. Guarded on a single TOC candidate, as before;
  multi-TOC titles keep the stack read.

- D-form loads/stores lost the rA=0-means-literal-0 rule (PowerISA V2.03
  3.3.2/3.3.3). _mem_base() restored alongside the existing _xea() that
  already implements the X-form twin.

Verified on flOw: 14146 TOCFIX / 0 stack-TOC reads / 120101 ctx->lr writes /
0 gpr[0] D-form bases, and the runtime's lr= diagnostics now match the
known-good ps3-draw lift exactly.

Still dropped by the same merge, NOT restored here (flOw's EBOOT does not
use them, so they are unverified): VRSAVE mfspr/mtspr, stvlx/stvrx(l),
vrfin/vrfiz/vrfip/vrfim, vexptefp, vlogefp.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…efs)

The static lib archives fine -- undefined symbols in a .lib are only
diagnosed when a title links it -- so these went unnoticed. flOw could not
link at all against this branch.

The SPU interpreter was not "consolidated into spu_channels.c" as 6dd8524
claims; it was deleted, while both of its call sites in lv2_register.c
stayed. Restored spu_interp.{c,h} + spu_interp_tables.inc from 6dd8524^ and
the spu_run_interp_job static-inline into spu_lifted_job.h.

The FUNCTION REGISTRY genuinely was consolidated (spu_register_function /
spu_lookup / spu_begin_image now live in spu_channels.c), so restoring
spu_fn_registry.c wholesale duplicates them -- only its spu_lifted_lookup
was missing, added here as the 3-line wrapper over spu_lookup it always was.
spu_spurs_taskset_syscall goes back to non-static: the pure interpreter
calls it too.

lbp_hle_complete_pending, g_taskset_policy_bytes/size and g_vm_page_bitmap
are title-provided symbols referenced from shared runtime code. Each gets a
default definition in its own one-symbol TU, following the existing
spu_tsp_weak.c pattern: a port that supplies the real one resolves the
reference from its own object and never pulls the archive member, so no
duplicate symbol. They must stay SEPARATE files -- a port can supply one
without the others. g_vm_page_bitmap storage moves out of lbp/main.cpp
(which is why every non-LBP port failed) into runtime/spu/spu_vm_pagemap.c;
LBP's fault handler keeps seeding it through the extern.

spurs_policy.c bails out of the taskset-policy path on a zero-size blob
rather than executing an empty local store.

NB: the runtime CMakeLists globs runtime/*.c without CONFIGURE_DEPENDS, so
a new TU needs `cmake -S . -B build` re-run or it is silently not compiled.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ps3-draw returned CELL_OK when funcStat sets CELL_SAVEDATA_CBRESULT_ERR_NODATA
(-4), with the comment "an ERROR return leaves the title parked in
MODE_AUTO_LOAD". The merge took the spec-correct CELL_SAVEDATA_ERROR_NODATA
(0x8002B40B) instead and flOw parked exactly as that comment predicted: no
app loop, 0 flips, never reaching m_InitEntityHierarchy.

Applied to BOTH entry points. flOw calls the old non-_2 cellSaveDataAutoLoad,
which 9db188d added as a separate function -- patching only AutoLoad2 (as I
first did) changes nothing.

Per the SDK the error return is correct and a real title handles it; that
flOw does not is an unexplained bug in its own MODE_AUTO_LOAD state machine.
The comment says so, so the next person does not "fix" this back and lose
another boot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
main() calls SetThreadStackGuarantee(256KB) so the STACK_OVERFLOW handler has
room to report; guest threads got nothing. Deep recompiled call chains do
overflow even a 256 MB stack (a lifter bug that turns a tail call into
recursion is unbounded), and without the guarantee the handler itself faults
while reporting -- the process dies with a bare access violation INSIDE the
handler and the backtrace naming the recursing function is lost.

Found while chasing what looked like a plain segfault in flOw: the Windows
Application event log gave fault offset 0x2839, which llvm-symbolizer
resolved to boot_main.cpp:488 -- the stack-overflow VEH itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
sp00nznet added a commit that referenced this pull request Aug 15, 2026
Folds every foldable open PR onto master: the #90/#82/#81/#79/#78 integration chain (fast-forward) plus canersaka's six fix PRs (#83-#88) cherry-picked with authorship preserved. Build clean, ctest 1/1.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant