mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-28 02:56:36 -04:00
recompiler: split SCUS_*_full.c into parallel-compilable shards
Emit a shared <stem>_decls.h (runtime extern prologue, static-inline unaligned helpers, all func + alias-body forward decls) and distribute function bodies across N ~40k-line <stem>_full_NN.c shards instead of one monolithic <stem>_full.c. Overlapping-alias host bodies become externally linked so shard bucketing has no atomicity constraint. runtime.cmake's GAME_GENERATED_FULL_C is now multi-value (single path still works, so other games are unaffected until they regen + glob). Tomba: SCUS_942.36_full.c 41MB single TU -> 36 shards; compile (-O3) 4m35s -> 51s (-j16, ~5.4x). Generated function bytes verified byte-identical (6245/6245 functions). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2456,8 +2456,9 @@ std::vector<GeneratedFunction> CodeGenerator::generate_alias_group(
|
||||
std::stringstream body;
|
||||
body << fmt::format("/* Overlapping-alias body for host func_{:08X}: {} entries */\n",
|
||||
host, aliases.size());
|
||||
body << fmt::format("static void psx_alias_body_{:08X}(CPUState* cpu, uint32_t entry)\n{{\n",
|
||||
body << fmt::format("void psx_alias_body_{:08X}(CPUState* cpu, uint32_t entry)\n{{\n",
|
||||
host);
|
||||
alias_body_decls_.push_back(fmt::format("void psx_alias_body_{:08X}(CPUState* cpu, uint32_t entry)", host));
|
||||
// CPS (§25): a dispatched continuation (callee published cpu->pc = $ra) is
|
||||
// routed into its block here, BEFORE the entry switch — cpu->pc overrides
|
||||
// the `entry` arg. The dispatch routes a continuation by calling the first
|
||||
@@ -2538,6 +2539,7 @@ std::vector<GeneratedFunction> CodeGenerator::generate_all_functions(
|
||||
// game dispatch table (psx_dispatch_game_compiled) reflects exactly the
|
||||
// functions emitted here (the final generate_file pass is authoritative).
|
||||
cps_continuation_owner_.clear();
|
||||
alias_body_decls_.clear();
|
||||
|
||||
fmt::print("\n=== C Code Generation ===\n\n");
|
||||
fmt::print("Generating C code for {} functions...\n", functions.size());
|
||||
@@ -2658,18 +2660,7 @@ std::vector<GeneratedFunction> CodeGenerator::generate_all_functions(
|
||||
return results;
|
||||
}
|
||||
|
||||
std::string CodeGenerator::generate_file(
|
||||
const std::vector<Function>& functions,
|
||||
const std::map<uint32_t, ControlFlowGraph>& cfgs) {
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
// File header
|
||||
ss << "/* Generated by PSXRecomp - PlayStation 1 Static Recompiler */\n\n";
|
||||
|
||||
// Include the generic PSX runtime header.
|
||||
// This provides CPUState, GTE/trap declarations, and call_by_address().
|
||||
ss << "#include \"psx_runtime.h\"\n\n";
|
||||
void CodeGenerator::emit_runtime_externs(std::ostream& ss) const {
|
||||
ss << "extern void debug_server_log_call_entry(uint32_t func_addr);\n";
|
||||
ss << "#ifndef PSX_NO_DEBUG_TOOLS\n";
|
||||
ss << "extern void debug_server_cyc_observe(uint32_t block_leader_phys);\n";
|
||||
@@ -2701,6 +2692,10 @@ std::string CodeGenerator::generate_file(
|
||||
ss << "extern uint32_t psx_ws_backdrop_value(uint32_t orig, int is_end, int window_cols); /* ws backdrop preload (gpu.c) */\n";
|
||||
ss << "extern void gte_ws_set_suppress(int on); /* widescreen far-backdrop un-squash (gte.cpp) */\n";
|
||||
ss << "extern uint32_t g_debug_last_store_pc; /* exact PC of the executing SW/SH/SB — wtrace/readtrace producer attribution (debug_server.c) */\n\n";
|
||||
}
|
||||
|
||||
void CodeGenerator::emit_unaligned_helpers(std::ostream& ss, bool as_inline) const {
|
||||
const char* kw = as_inline ? "static inline " : "static ";
|
||||
|
||||
// Emit reference implementations for unaligned memory helpers.
|
||||
// These implement the MIPS lwl/lwr/swl/swr semantics.
|
||||
@@ -2717,7 +2712,7 @@ std::string CodeGenerator::generate_file(
|
||||
ss << " * addr is the effective (possibly unaligned) address.\n";
|
||||
ss << " * rt_value is the current value of the destination register.\n";
|
||||
ss << " */\n";
|
||||
ss << "static uint32_t psx_lwl(CPUState* cpu, uint32_t addr, uint32_t rt_value, uint32_t rt, uint32_t reg_mask) {\n";
|
||||
ss << kw << "uint32_t psx_lwl(CPUState* cpu, uint32_t addr, uint32_t rt_value, uint32_t rt, uint32_t reg_mask) {\n";
|
||||
ss << " uint32_t word = psx_cyc_load_word(cpu, addr & ~3u, rt, reg_mask); /* full load interlock */\n";
|
||||
ss << " switch (addr & 3u) {\n";
|
||||
ss << " case 0: return (rt_value & 0x00FFFFFFu) | (word << 24);\n";
|
||||
@@ -2731,7 +2726,7 @@ std::string CodeGenerator::generate_file(
|
||||
ss << " * addr is the effective (possibly unaligned) address.\n";
|
||||
ss << " * rt_value is the current value of the destination register.\n";
|
||||
ss << " */\n";
|
||||
ss << "static uint32_t psx_lwr(CPUState* cpu, uint32_t addr, uint32_t rt_value, uint32_t rt, uint32_t reg_mask) {\n";
|
||||
ss << kw << "uint32_t psx_lwr(CPUState* cpu, uint32_t addr, uint32_t rt_value, uint32_t rt, uint32_t reg_mask) {\n";
|
||||
ss << " uint32_t word = psx_cyc_load_word(cpu, addr & ~3u, rt, reg_mask); /* full load interlock */\n";
|
||||
ss << " switch (addr & 3u) {\n";
|
||||
ss << " case 0: return word;\n";
|
||||
@@ -2745,7 +2740,7 @@ std::string CodeGenerator::generate_file(
|
||||
ss << " * addr is the effective (possibly unaligned) address.\n";
|
||||
ss << " * rt_value is the value of the source register.\n";
|
||||
ss << " */\n";
|
||||
ss << "static void psx_swl(CPUState* cpu, uint32_t addr, uint32_t rt_value) {\n";
|
||||
ss << kw << "void psx_swl(CPUState* cpu, uint32_t addr, uint32_t rt_value) {\n";
|
||||
ss << " uint32_t aligned_addr = addr & ~3u;\n";
|
||||
ss << " uint32_t word = cpu->read_word(aligned_addr);\n";
|
||||
ss << " switch (addr & 3u) {\n";
|
||||
@@ -2761,7 +2756,7 @@ std::string CodeGenerator::generate_file(
|
||||
ss << " * addr is the effective (possibly unaligned) address.\n";
|
||||
ss << " * rt_value is the value of the source register.\n";
|
||||
ss << " */\n";
|
||||
ss << "static void psx_swr(CPUState* cpu, uint32_t addr, uint32_t rt_value) {\n";
|
||||
ss << kw << "void psx_swr(CPUState* cpu, uint32_t addr, uint32_t rt_value) {\n";
|
||||
ss << " uint32_t aligned_addr = addr & ~3u;\n";
|
||||
ss << " uint32_t word = cpu->read_word(aligned_addr);\n";
|
||||
ss << " switch (addr & 3u) {\n";
|
||||
@@ -2773,6 +2768,38 @@ std::string CodeGenerator::generate_file(
|
||||
ss << " cpu->write_word(aligned_addr, word);\n";
|
||||
ss << "}\n";
|
||||
ss << "\n";
|
||||
}
|
||||
|
||||
std::string CodeGenerator::build_shared_decls_header(const std::vector<GeneratedFunction>& gen_funcs) const {
|
||||
std::stringstream h;
|
||||
h << "/* Generated by PSXRecomp - shared declarations for split full.c shards. DO NOT EDIT. */\n";
|
||||
h << "#pragma once\n";
|
||||
h << "#include \"psx_runtime.h\"\n\n";
|
||||
emit_runtime_externs(h);
|
||||
emit_unaligned_helpers(h, /*as_inline=*/true);
|
||||
if (!gen_funcs.empty()) {
|
||||
h << "/* Forward declarations for all recompiled functions */\n";
|
||||
for (const auto& gf : gen_funcs) h << gf.signature << ";\n";
|
||||
for (const auto& decl : alias_body_decls_) h << decl << ";\n";
|
||||
h << "\n";
|
||||
}
|
||||
return h.str();
|
||||
}
|
||||
|
||||
std::string CodeGenerator::generate_file(
|
||||
const std::vector<Function>& functions,
|
||||
const std::map<uint32_t, ControlFlowGraph>& cfgs) {
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
// File header
|
||||
ss << "/* Generated by PSXRecomp - PlayStation 1 Static Recompiler */\n\n";
|
||||
|
||||
// Include the generic PSX runtime header.
|
||||
// This provides CPUState, GTE/trap declarations, and call_by_address().
|
||||
ss << "#include \"psx_runtime.h\"\n\n";
|
||||
emit_runtime_externs(ss);
|
||||
emit_unaligned_helpers(ss, /*as_inline=*/false);
|
||||
|
||||
// Make mutable copies for potential function splitting
|
||||
std::vector<Function> functions_mut(functions);
|
||||
@@ -2981,6 +3008,7 @@ std::string CodeGenerator::generate_file(
|
||||
for (const auto& gen_func : gen_funcs) {
|
||||
ss << gen_func.signature << ";\n";
|
||||
}
|
||||
for (const auto& decl : alias_body_decls_) ss << decl << ";\n";
|
||||
ss << "\n";
|
||||
}
|
||||
|
||||
@@ -2989,6 +3017,10 @@ std::string CodeGenerator::generate_file(
|
||||
ss << gen_func.full_code << "\n";
|
||||
}
|
||||
|
||||
// Stash for the split-shard writer (main_psx); copy is intentional so the
|
||||
// caller can consume it after generate_file() returns the monolith text.
|
||||
last_gen_funcs_ = gen_funcs;
|
||||
|
||||
// Preserve the exact post-split CFG inventory used by the emitted C. The
|
||||
// dispatch generator consumes this so synthesized fallthrough functions
|
||||
// receive the same byte-precise native-validity ranges as ordinary entries.
|
||||
|
||||
Reference in New Issue
Block a user