You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Correction (updated): I originally filed this as AArch64-specific. That was wrong. The failure reproduces on x86-64 with the same DOL and the same harness, so this is a Colosseum + LLVM-backend defect, not an ARM one. Details and measurements below.
Building Pokémon Colosseum (GC6E01) with the LLVM backend produces a module that does not get through the boot sequence. The C backend, same DOL, same runtime, same automation harness, boots normally.
Measurements
Cold boot, A-press driver, no savestate, identical harness on each platform:
platform
backend
frames produced
fps
speed
x86-64 Windows
C
4262
44.77
0.749
x86-64 Windows
LLVM
16
0.26
1.00
aarch64 (Pi 4)
C
1417
9.74
0.166
aarch64 (Pi 4)
LLVM
8
5.74 (stale)
0.634
The guest is not deadlocked — it executes (speed is nonzero, the VI keeps ticking), it just never makes forward progress. Note the LLVM arm reports a higherspeed than C on the Pi while producing no frames: it is spinning cheaply instead of doing work.
Luigi's Mansion (GLME01) with the LLVM backend runs fine on the same Pi (769 frames, 6.11 fps), so the AArch64 LLVM backend is not broken in general.
Where it ends up
perf on the wedged aarch64 run puts ~58% of samples in three guest functions, which the DOL disassembly identifies as the thread scheduler:
0x8009BC50 — OSLoadContext, which resumes a thread with rfi
0x800A17E0 — run-queue enqueue / reschedule
None of these appear in the C backend's profile, which is dominated by real game work (func_800FD5E0, paired-single ops, FP helpers).
What is NOT the cause
I instrumented the shared runtime to rule these out rather than guess:
Thread resume is correct.ppc_rfi fires ~150M times in 70s (vs ~150k for C, ~1000x more), and at every sampled resume CPUState is correct: srr0=0x800A183C, r3=0x00000001. 0x800A183C is the instruction after bl OSSaveContext, and r3=1 is exactly the "resumed" return value the scheduler tests. The C backend resumes at the same address with the same value, so this churn is a normal idle loop — the LLVM arm is just doing ~1000x more of it.
Entry at that PC is valid. In the generated IR the address is a real entry case (guest_800A183C_b23), reached from native_entry, and the block loads r3 from CPUState and branches on icmp eq i32 %state2.7, 0 correctly.
Not an unsupported instruction. All 1643 fallbacks in the generated CSV are embedded-data; there are no unmodeled opcodes.
Not exception delivery. Exception counts stayed under threshold in both arms.
I have not isolated the primitive that diverges. The dispatch-address stream diverges from the C backend within the first handful of dispatches at boot, and the LLVM arm reaches far less distinct guest code over a run (909 vs 4000+ distinct addresses in the same window).
Reproduction is easy on x86-64 now, so this no longer needs ARM hardware to chase. Happy to keep digging or hand over what I have.
Correction (updated): I originally filed this as AArch64-specific. That was wrong. The failure reproduces on x86-64 with the same DOL and the same harness, so this is a Colosseum + LLVM-backend defect, not an ARM one. Details and measurements below.
Building Pokémon Colosseum (GC6E01) with the LLVM backend produces a module that does not get through the boot sequence. The C backend, same DOL, same runtime, same automation harness, boots normally.
Measurements
Cold boot, A-press driver, no savestate, identical harness on each platform:
The guest is not deadlocked — it executes (
speedis nonzero, the VI keeps ticking), it just never makes forward progress. Note the LLVM arm reports a higherspeedthan C on the Pi while producing no frames: it is spinning cheaply instead of doing work.Luigi's Mansion (GLME01) with the LLVM backend runs fine on the same Pi (769 frames, 6.11 fps), so the AArch64 LLVM backend is not broken in general.
Where it ends up
perfon the wedged aarch64 run puts ~58% of samples in three guest functions, which the DOL disassembly identifies as the thread scheduler:0x8009BBE0—OSSaveContext(saves GQR1–5 viamfspr 0x393..0x397, CR, LR, MSR, CTR, XER)0x8009BC50—OSLoadContext, which resumes a thread withrfi0x800A17E0— run-queue enqueue / rescheduleNone of these appear in the C backend's profile, which is dominated by real game work (
func_800FD5E0, paired-single ops, FP helpers).What is NOT the cause
I instrumented the shared runtime to rule these out rather than guess:
ppc_rfifires ~150M times in 70s (vs ~150k for C, ~1000x more), and at every sampled resumeCPUStateis correct:srr0=0x800A183C,r3=0x00000001.0x800A183Cis the instruction afterbl OSSaveContext, andr3=1is exactly the "resumed" return value the scheduler tests. The C backend resumes at the same address with the same value, so this churn is a normal idle loop — the LLVM arm is just doing ~1000x more of it.guest_800A183C_b23), reached fromnative_entry, and the block loads r3 fromCPUStateand branches onicmp eq i32 %state2.7, 0correctly.embedded-data; there are no unmodeled opcodes.Still open
I have not isolated the primitive that diverges. The dispatch-address stream diverges from the C backend within the first handful of dispatches at boot, and the LLVM arm reaches far less distinct guest code over a run (909 vs 4000+ distinct addresses in the same window).
Reproduction is easy on x86-64 now, so this no longer needs ARM hardware to chase. Happy to keep digging or hand over what I have.