mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2026-06-20 14:53:02 +00:00
Adds a public N64Recomp::discover_function_bounds() in src/analysis.h
that performs a BFS-based control-flow walk of a function's body,
following:
- Conditional branches (target + fall-through)
- Unconditional j/jal targets when intra-body
- jr $ra returns (block ends after delay slot)
- jr-via-jump-table dispatches: the existing register-state
simulator from analyze_function detects the lui+addiu+addu+lw+jr
pattern and records the jtbl base; we then read entries out of
the body bytes and feed targets back into the BFS until
convergence.
Returns the function's byte size (max-reachable + 4 to cover the
delay slot of the last instruction). On failure, populates a specific
error message with the offending offset and reason — caller treats
this as a build error, NOT a graceful skip (per the project's
no-stubs principle).
Wires into decompressed.cpp's pattern path, replacing the prior
inline BFS that had a TODO for jump-table handling. The pattern
caller now propagates failures via `synthesize_decompressed_patterns`
returning false, which surfaces in main.cpp's exit_failure path.
Concrete behavior change: activating a pattern that includes a
fragment with computed jumps now produces a build error pointing at
the specific section name + offset + the analyzer's failure reason,
instead of silently producing a partial binary. Tested on Stadium's
0x8FF00000 slot — first failing wrapper is at ROM 0x8CC400 with an
indirect jr at offset 0x827C the simulator doesn't pattern-match.
The static [[input.decompressed_section]] path for fragment78 is
unaffected (still recompiles cleanly, no regression on boot logo +
PIKA jingle).
Future work surfaced by this change: the simulator's lui+addiu
+addu+lw+jr pattern doesn't cover every jump-table shape Stadium
uses. Each gap surfaces as a specific build-error offset; resolution
is to extend analyze_instruction to recognize the additional pattern
(or, when it's a true tail-call rather than a jtbl, distinguish
those at the jr site).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
51 lines
No EOL
1.9 KiB
C++
51 lines
No EOL
1.9 KiB
C++
#ifndef __RECOMP_ANALYSIS_H__
|
|
#define __RECOMP_ANALYSIS_H__
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "recompiler/context.h"
|
|
|
|
namespace N64Recomp {
|
|
struct AbsoluteJump {
|
|
uint32_t jump_target;
|
|
uint32_t instruction_vram;
|
|
|
|
AbsoluteJump(uint32_t jump_target, uint32_t instruction_vram) : jump_target(jump_target), instruction_vram(instruction_vram) {}
|
|
};
|
|
|
|
struct FunctionStats {
|
|
std::vector<JumpTable> jump_tables;
|
|
};
|
|
|
|
bool analyze_function(const Context& context, const Function& function, const std::vector<rabbitizer::InstructionCpu>& instructions, FunctionStats& stats);
|
|
|
|
// Discover the byte-size of a function whose entry sits at
|
|
// `entry_offset` within `body`. Performs a BFS-based control-flow
|
|
// walk that follows conditional branches (target + fall-through),
|
|
// unconditional j/jal targets when intra-body, jr $ra returns,
|
|
// and jr-via-jump-table dispatches (resolved by the existing
|
|
// register-state simulator from analyze_function).
|
|
//
|
|
// `body` is the raw decompressed bytes of the section's body in
|
|
// big-endian instruction layout (same shape as Function::words but
|
|
// as a byte buffer; bytes_size is the upper bound).
|
|
//
|
|
// `vram_base` is the link-time vram of body[0] — used to translate
|
|
// branch/jal targets back to body offsets.
|
|
//
|
|
// On success, sets `size_out` to the function's byte size (always
|
|
// a multiple of 4) and returns true.
|
|
//
|
|
// On failure, populates `error_out` with a specific message
|
|
// identifying the offending instruction or jump-table issue, and
|
|
// returns false. Per the project's no-stubs principle, callers
|
|
// should treat false as a build-time error, NOT a graceful skip.
|
|
bool discover_function_bounds(
|
|
const uint8_t* body, size_t bytes_size,
|
|
uint32_t vram_base, uint32_t entry_offset,
|
|
size_t& size_out, std::string& error_out);
|
|
}
|
|
|
|
#endif |