Repository navigation
Recompilation status
Last updated: 2026-10-20 (R5 N5: the frame drawn through Metal, experimental, in bloodborne-runtime). What the recompilation has done so far, and what is next.
| Goal | Status | |
|---|---|---|
| R0 | Groundwork: function table, library labels, CPU profile, record and replay, native functions in the game | ✅ Done |
| R1 | Recompiler prototype: generated C for the recorded functions, checked by replay | ✅ Done |
| R2 | Coverage: every function generated; the game runs recompiled, the translator as fallback | ✅ Done |
| R3 | Speed: integer, flags and vector instructions in C; memory ordering relaxed where safe | 🟡 In progress |
| R4 | Annotations: names, signatures and structs make the generated C readable | ⬜ |
| R5 | The engine's graphics on each platform's own GPU API: native renderer (bloodborne-runtime) | 🟡 N1 closed; N5 (Metal backend): the frame draws through Metal behind an experimental renderer option |
2026-10-24. The game's executable has 241,821 functions, about 11.3 million instructions.
| Progress | ||
|---|---|---|
| Functions recompiled, running in the game |
████████████████████ 99.6% |
240,973 of 241,821 |
| Instructions recompiled |
███████████████████░ 95.3% |
10,743,599 of 11,268,132 |
| Instructions in plain C (of those recompiled) |
████████████████████ 99.1% |
10,641,693 of 10,743,599 |
| CPU time recompiled, against the translator |
████████████████████ 98% |
420% of one core against 428% |
| Functions checked call by call (recordings) |
░░░░░░░░░░░░░░░░░░░░ 0.4% |
933 of 241,821 |
| Functions labelled by library (2026-10-24) |
█████████████░░░░░░░ 62.7% |
151,562 of 241,821 |
Every function is generated; the ones left to the translator are the 660 import stubs, the 130
functions that call setjmp, and 58 that the game's patches (56) or the runtime's live switches
(2) change. Instructions recompiled count what control flow reaches; the rest is padding and code
nothing reaches. Every recompiled function can run in the game; the call-by-call checks cover the
busiest, and the fuzzing covers every instruction form the generator handles.
- Function table: 241,821 functions: 162,959 from the unwind tables, the rest found through calls, jumps and code pointers (many small leaf functions have no unwind entry). Every piece of code the game ran on a test route lies inside a known function.
- Imports named: the game's 660 calls into the PS4 system libraries, by name.
- Library labels: 62.7% of the functions, 83.8% of the code (below).
- Profiles: CPU time per function from the running game.
- Record and replay: calls of up to 16 functions recorded at once, with no visible slowdown; the originals replay exactly.
- Native functions in the game: a library of native functions is loaded at start and called straight from translated code, at no measurable cost; the recompiler's output uses this path.
- Generated C for every function: 241,031 functions, 10.74 million instructions, 99.1% of them in plain C. The whole game builds in 11.5 minutes into a 404 MB library; a change rebuilds only the files it touches.
- In the game: 240,973 functions run recompiled; standing in the Hunter's Dream the game plays at 57–59 FPS (59.1 with the translator alone; the frame rate is bound by rendering).
- CPU time: the library takes the same CPU time as the translator, at the same frame rate (420% of one core against 428% in the Hunter's Dream, 2026-10-24). It took 28% more before the lookup of recompiled functions became one flat table, and 14% more before the instructions the generated C left to the interpreter (11.8 million a second there) got C of their own.
- Imports: calls of the PS4 libraries the runtime provides go from recompiled code straight to them, 2.4 million a second; 1.45 million a second still enter the translator (the PS4 libc's own code, and functions that hand their rest back).
- Correct: recordings of 40 calls each for the 933 busiest functions: 911 match every call; the other 22 also fail when the original runs (they read the time stamp counter, or memory other threads change).
- Fuzzed: every distinct instruction form in the game the generator handles (4,892 of them, at most 30 per mnemonic) runs from 200 random states through the interpreter and through its generated C, with no differences.
- Safe with patches: a function whose code a game patch changed stays with the translator.
-
A lock bug in the interpreter: after a failed
lock cmpxchgit reported a separately loaded value, so a failed exchange could look successful and two threads took the same lock. It was the occasional heap panic during save load; fixed for everyone (main). -
A gap in the recorder:
vmaskmovdquwrites to an address it doesn't name (rdi). - Speed: recompiled code first ran at 51 FPS. A shared counter every thread added to, calls between recompiled functions going through the runtime, and instructions left to the interpreter cost the difference.
-
setjmp and longjmp: recompiled import stubs left the runtime's own return address where
setjmpsaves it, and a laterlongjmp(the game's PNG reader) came back into a frame that was gone. Import stubs and the 40 functions that callsetjmpnow stay with the translator, and each call the runtime makes into the translator has its own return address.
| Library | Functions | Code |
|---|---|---|
Game (SPRJ) |
~37,100 | 7.8 MiB |
| Dantelion2 (FromSoftware core library) | ~35,800 | 7.4 MiB |
| Havok 2014.1 | ~28,500 | 9.7 MiB |
| Scaleform GFx (menus, HUD) | ~20,700 | 4.2 MiB |
| FD4 (FromSoftware engine) | ~16,400 | 4.1 MiB |
| FMOD Ex 4.44.50 | ~7,200 | 1.4 MiB |
| Lua, YEBIS, zlib, other source paths | ~5,900 | 1.9 MiB |
| Not labelled yet | ~90,300 | 7.1 MiB |
| Total | 241,821 | ~44 MiB |
The labels (2026-10-24) start from source paths and messages and spread to the functions between
two of one library, then through vtables, callers and callees. Hiding a tenth of them and spreading
again gives back 96.4% the same (tools/recomp/label.py --check). The 90,259 left are small
(median 18 bytes).
In 20 s of play in the Hunter's Dream, 933 functions ran on the main thread and the busiest 145 took half its time: no small set of functions dominates, so every function gets recompiled.
- Speed (R3), cheaper than the translator: the runtime between recompiled functions (
rc_call, the registers copied around every call), the wide-character libc calls in native code, jump tables as Cswitches (fewer hand-backs to the translator), hot functions together in the library; then measure CPU time, not only frames. - Longer and more varied routes (other areas, bosses, travel, cutscenes) to run more of the recompiled code in the game.
- Known library code (zlib, Lua) matched against its sources: real names (R4).
- Recompile at install time from the player's own copy, in the launcher.
Update the date, the milestone table, the progress bars (20 blocks, one per 5%) and the numbers: checks come from tools/recomp/replay.sh,
calls a second from the game's log.
Documentation
Repositories