Found a third emit-non-compilable-code path during Pokemon Stadium
runner build: print_branch lambda (line 371) checks for tail-call
candidates when the branch target is outside the function, prints
a warning if not found — then falls through to emit
`goto L_<branch_target>` to a label that doesn't exist in the
generated function. Linker compiles fail with "use of undeclared
label L_XXXXXXXX".
Fixed by emitting the same recomp_unhandled_branch runtime call as
the other tolerant-emit paths, plus an early return so the fall-
through to emit_goto doesn't run.
Also exposes recomp_unhandled_* declarations in include/recomp.h
(after the existing recomp_context definition + recomp_syscall_handler
decl) so generated C compiles cleanly without consumer-side header
patches.
171 → ~600+ tolerant-emit warnings on Pokemon Stadium pass; full
build pipeline now produces a linked PokemonStadiumRecomp.exe
that loads librecomp/ultramodern and runs cleanly to a "link-only
entry" message. Real boot still needs runtime wiring (renderer,
scheduler, init).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert five fatal "engine cannot translate" code paths from
process_instruction-returns-false (which kills the whole recompile)
into per-call runtime-abort emits that compile cleanly:
1. Unhandled branch (target not in functions_by_vram, not a label)
→ recomp_unhandled_branch(rdram, ctx, instr_vram, target)
+ emit_return so subsequent code is unreachable
2. JAL to unresolved target (no symbol in lookup table)
→ recomp_unhandled_call(rdram, ctx, instr_vram, target)
3. JALR with non-RA link register
→ recomp_unhandled_jalr(rdram, ctx, instr_vram, target_value, rd)
4. Unhandled MFC0 / MTC0 register
→ ctx->rN = recomp_unhandled_cop0_read(rdram, ctx, instr_vram, reg)
→ recomp_unhandled_cop0_write(rdram, ctx, instr_vram, reg, value)
5. Unhandled instruction opcode (no decoder match)
→ recomp_unhandled_instruction(rdram, ctx, instr_vram, opcode_name)
The function still compiles. Other instructions still execute
normally. Only when the unsupported instruction is reached at
runtime does the abort fire — with full diagnostic context
(function, vram, opcode/target/register). Consumers implement the
runtime entry points and decide policy (abort, log+continue,
escalate to host emulator, etc.).
Per project principles
(F:\Projects\recomp-template\NES\PRINCIPLES.md #12): NOT a stub.
No behavior is simulated. The unimplementable path is left as a
loud, contextful runtime hole — the inverse of silent failure.
Driven by the Pokemon Stadium port: 171 instances of these errors
cluster in IPL3/boot/cache-mgmt/asm helpers. Pre-fix: 1 fatal
error per run, ~hundreds of by-hand toml entries needed.
Post-fix: clean exit=0, 14747 functions processed, full overlay
table emitted.
Lambda capture fix: print_func_call_by_address now captures
&output_file and instr_vram so the JAL emit path can use them.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace output_file.clear() (which only resets stream flags, not
buffered text) with a proper ostringstream buffer in
N64Recomp::recompile_function. On failure, the buffer is discarded
and never reaches the real output_file — preventing partial
function output (signature + open braces, no body) from corrupting
shared multi-function output files.
Caller behavior is unchanged: recompile_function still returns
false on failure, main.cpp still calls exit(EXIT_FAILURE). No
stubs are generated, no failures are masked. This is a strict fix
of the misleading "clearing output file" code path.
Also improves the error message to include the failing
instruction index for easier triage.
Discovered while running the engine against MM US v1.1 ROM, where
ActorShadow_DrawSquare hits an INVALID instruction at
0x800B4028 (opcode 0x4B9B08C8) — the partial output was breaking
the file's syntax even though main.cpp was about to exit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add function hooks to mod symbol format
* Add function sizes to section function tables
* Add support for function hooks in live generator
* Add an option to the context to force function lookup for all non-relocated function calls
* Include relocs in overlay data
* Include R_MIPS_26 relocs in symbol file dumping/parsing
* Add manual patch symbols (syms.ld) to the output overlay file and relocs
* Fix which relocs were being emitted for patch sections
* Fix sign extension issue with mfc1, add TODO for banker's rounding
This commit implements the "live recompiler", which is another backend for the recompiler that generates platform-specific assembly at runtime. This is still static recompilation as opposed to dynamic recompilation, as it still requires information about the binary to recompile and leverages the same static analysis that the C recompiler uses. However, similarly to dynamic recompilation it's aimed at recompiling binaries at runtime, mainly for modding purposes.
The live recompiler leverages a library called sljit to generate platform-specific code. This library provides an API that's implemented on several platforms, including the main targets of this component: x86_64 and ARM64.
Performance is expected to be slower than the C recompiler, but should still be plenty fast enough for running large amounts of recompiled code without an issue. Considering these ROMs can often be run through an interpreter and still hit their full speed, performance should not be a concern for running native code even if it's less optimal than the C recompiler's codegen.
As mentioned earlier, the main use of the live recompiler will be for loading mods in the N64Recomp runtime. This makes it so that modders don't need to ship platform-specific binaries for their mods, and allows fixing bugs with recompilation down the line without requiring modders to update their binaries.
This PR also includes a utility for testing the live recompiler. It accepts binaries in a custom format which contain the instructions, input data, and target data. Documentation for the test format as well as most of the tests that were used to validate the live recompiler can be found here. The few remaining tests were hacked together binaries that I put together very hastily, so they need to be cleaned up and will probably be uploaded at a later date. The only test in that suite that doesn't currently succeed is the div test, due to unknown behavior when the two operands aren't properly sign extended to 64 bits. This has no bearing on practical usage, since the inputs will always be sign extended as expected.
* implement nrm filename toml input
* change name of mod toml setting to 'mod_filename'
* add renaming and re mode
* fix --dump-context arg, fix entrypoint detection
* refactor re_mode to function_trace_mode
* adjust trace mode to use a general TRACE_ENTRY() macro
* fix some renaming and trace mode comments, revert no toml entrypoint code, add TODO to broken block
* fix arg2 check and usage string
* Terminate offline mod recompilation if any functions fail to recompile
* Fixed edge case with switch case jump table detection when lo16 immediate is exactly 0
* Prevent emitting duplicate reference symbol defines in offline mod recompilation
* Fix function calls and add missing runtime function pointers in offline mod recompiler
* Remove reference context from parse_mod_symbols argument
* Add support for special dependency names (self and base recomp), fix non-compliant offline mod recompiler output
* Fix export names not being set on functions when parsing mod syms, add missing returns to mod parsing
* Switch offline mod recompilation to use a base global event index instead of per-event global indices
* Add support for creating events in normal recompilation
* Output recomp API version in offline mod recompiler
* Removed dependency version from mod symbols (moved to manifest)
* Added mod manifest generation to mod tool
* Implement mod file creation in Windows
* Fixed some error prints not using stderr
* Implement mod file creation on posix systems
* De-hardcode symbol file path for offline mod recompiler
* Fix duplicate import symbols issue and prevent emitting unused imports
* Initial implementation of binary operation table
* Initial implementation of unary operation table
* More binary op types, moved binary expression string generation into separate function
* Added and implemented conditional branch instruction table
* Fixed likely swap on bgezal, fixed extra indent branch close and missing
indent on branch statement
* Add operands for other uses of float registers
* Added CHECK_FR generation to binary operation processing, moved float comparison instructions to binary op table
* Finished moving float arithmetic instructions to operation tables
* Added store instruction operation table
* Created Generator interface, separated operation types and tables and C generation code into new files
* Fix mov.d using the wrong input operand
* Move recompiler core logic into a core library and make the existing CLI consume the core library
* Removed unnecessary config input to recompilation functions
* Moved parts of recomp_port.h into new internal headers in src folder
* Changed recomp port naming to N64Recomp
* Remove some unused code and document which Context fields are actually required for recompilation
* Implement mod symbol parsing
* Restructure mod symbols to make replacements global instead of per-section
* Refactor elf parsing into static Context method for reusability
* Move elf parsing into a separate library
* WIP elf to mod tool, currently working without relocations or API exports/imports
* Make mod tool emit relocs and patch binary for non-relocatable symbol references as needed
* Implemented writing import and exports in the mod tool
* Add dependencies to the mod symbol format, finish exporting and importing of mod symbols
* Add first pass offline mod recompiler (generates C from mods that can be compiled and linked into a dynamic library)
* Add strict mode and ability to generate exports for normal recompilation (for patches)
* Move mod context fields into base context, move import symbols into separate vector, misc cleanup
* Some cleanup by making some Context members private
* Add events (from dependencies and exported) and callbacks to the mod symbol format and add support to them in elf parsing
* Add runtime-driven fields to offline mod recompiler, fix event symbol relocs using the wrong section in the mod tool
* Move file header writing outside of function recompilation
* Allow cross-section relocations, encode exact target section in mod relocations, add way to tag reference symbol relocations
* Add local symbol addresses array to offline mod recompiler output and rename original one to reference section addresses
* Add more comments to the offline mod recompiler's output
* Fix handling of section load addresses to match objcopy behavior, added event parsing to dependency tomls, minor cleanup
* Fixed incorrect size used for finding section segments
* Add missing includes for libstdc++
* Rework callbacks and imports to use the section name for identifying the dependency instead of relying on per-dependency tomls