Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ps2xRecomp/include/ps2recomp/code_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ namespace ps2recomp
void setBootstrapInfo(const BootstrapInfo &info);
void setRelocationCallNames(const std::unordered_map<uint32_t, std::string> &callNames);
void setConfiguredJumpTables(const std::vector<JumpTable> &jumpTables);
void setFunctionBounds(const std::vector<Function> &functions);

AnalysisResult collectInternalBranchTargets(const Function &function,
const std::vector<Instruction> &instructions);
Expand All @@ -58,6 +59,8 @@ namespace ps2recomp
std::unordered_map<uint32_t, std::string> m_renamedFunctions;
std::unordered_map<uint32_t, std::string> m_relocationCallNames;
std::unordered_map<uint32_t, std::vector<uint32_t>> m_configJumpTableTargetsByAddress;
std::unordered_map<uint32_t, std::vector<uint32_t>> m_functionResumePoints;
std::unordered_map<uint32_t, uint32_t> m_entryOwnerStarts;
const std::vector<Section>& m_sections;
BootstrapInfo m_bootstrapInfo;

Expand Down
158 changes: 135 additions & 23 deletions ps2xRecomp/src/lib/code_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,63 @@ namespace ps2recomp
return "ps2_" + sanitized;
}

void CodeGenerator::setFunctionBounds(const std::vector<Function> &functions)
{
m_functionResumePoints.clear();
m_entryOwnerStarts.clear();

std::vector<const Function *> owners;
owners.reserve(functions.size());
for (const auto &function : functions)
{
if (!function.isRecompiled || function.isStub || function.isSkipped)
{
continue;
}
if (function.name.rfind("entry_", 0) == 0)
{
continue;
}
owners.push_back(&function);
}

for (const auto &function : functions)
{
if (function.name.rfind("entry_", 0) != 0)
{
continue;
}

const Function *bestOwner = nullptr;
for (const Function *candidate : owners)
{
if (function.start <= candidate->start || function.start >= candidate->end)
{
continue;
}
if (!bestOwner || candidate->start > bestOwner->start)
{
bestOwner = candidate;
}
}

if (!bestOwner)
{
continue;
}

m_entryOwnerStarts[function.start] = bestOwner->start;
m_functionResumePoints[bestOwner->start].push_back(function.start);
}

for (auto &[ownerStart, resumePoints] : m_functionResumePoints)
{
(void)ownerStart;
std::sort(resumePoints.begin(), resumePoints.end());
resumePoints.erase(std::unique(resumePoints.begin(), resumePoints.end()), resumePoints.end());
}
}

std::string CodeGenerator::handleBranchDelaySlots(
const Instruction &branchInst,
const Instruction &delaySlot,
Expand Down Expand Up @@ -315,7 +372,12 @@ namespace ps2recomp

if (!funcName.empty())
{
ss << fmt::format(" if (runtime->hasFunction(0x{:X}u)) {{\n", target);
// Always use runtime dispatch (lookupFunction). The else branch
// that direct-called by symbol is dead code: all functions are
// registered before any execute. Removing it eliminates the
// #include "ps2_recompiled_functions.h" dependency from each
// .cpp file, dramatically improving ccache hit rates.
ss << " {\n";
ss << fmt::format(" auto targetFn = runtime->lookupFunction(0x{:X}u);\n", target);
if (branchInst.opcode == OPCODE_J)
{
Expand All @@ -325,21 +387,17 @@ namespace ps2recomp
{
ss << " const uint32_t __entryPc = ctx->pc;\n";
ss << " targetFn(rdram, ctx, runtime);\n";
ss << fmt::format(" while (runtime->hasFunction(ctx->pc) && runtime->lookupFunction(ctx->pc) == targetFn) {{\n");
ss << " const uint32_t __resumePc = ctx->pc;\n";
ss << fmt::format(" if (__resumePc == 0x{:X}u) {{ break; }}\n", fallthroughPc);
ss << " targetFn(rdram, ctx, runtime);\n";
ss << fmt::format(" if (ctx->pc == __resumePc) {{ ctx->pc = 0x{:X}u; break; }}\n", fallthroughPc);
ss << " }\n";
ss << fmt::format(" if (ctx->pc == __entryPc) {{ ctx->pc = 0x{:X}u; }}\n", fallthroughPc);
ss << fmt::format(" if (ctx->pc != 0x{:X}u) {{ return; }}\n", fallthroughPc);
}
ss << " } else {\n";

if (branchInst.opcode == OPCODE_J)
{
ss << " " << funcName << "(rdram, ctx, runtime); return;\n";
}
else
{
ss << " const uint32_t __entryPc = ctx->pc;\n";
ss << " " << funcName << "(rdram, ctx, runtime);\n";
ss << fmt::format(" if (ctx->pc == __entryPc) {{ ctx->pc = 0x{:X}u; }}\n", fallthroughPc);
ss << fmt::format(" if (ctx->pc != 0x{:X}u) {{ return; }}\n", fallthroughPc);
ss << fmt::format(" if (ctx->pc != 0x{:X}u) {{\n", fallthroughPc);
ss << fmt::format(" fprintf(stderr, \"[PC_MISMATCH] at 0x{:X}: called 0x%x, expected ret 0x{:X}, got 0x%x\\n\", __entryPc, ctx->pc);\n", branchInst.address, fallthroughPc);
ss << " return;\n";
ss << " }\n";
}
ss << " }\n";
}
Expand Down Expand Up @@ -372,7 +430,7 @@ namespace ps2recomp
}
else
{
ss << fmt::format(" if (ctx->pc != 0x{:X}u) {{ return; }}\n", fallthroughPc);
ss << fmt::format(" if (ctx->pc != 0x{:X}u) {{ fprintf(stderr, \"[PC_MISMATCH] at 0x{:X}: called reloc, expected ret 0x{:X}, got 0x%x\\n\", ctx->pc); return; }}\n", fallthroughPc, branchInst.address, fallthroughPc);
}
emittedRelocCall = true;
}
Expand All @@ -390,8 +448,17 @@ namespace ps2recomp
}
else
{
ss << fmt::format(" while (runtime->hasFunction(ctx->pc) && runtime->lookupFunction(ctx->pc) == targetFn) {{\n");
ss << " const uint32_t __resumePc = ctx->pc;\n";
ss << fmt::format(" if (__resumePc == 0x{:X}u) {{ break; }}\n", fallthroughPc);
ss << " targetFn(rdram, ctx, runtime);\n";
ss << fmt::format(" if (ctx->pc == __resumePc) {{ ctx->pc = 0x{:X}u; break; }}\n", fallthroughPc);
ss << " }\n";
ss << fmt::format(" if (ctx->pc == __entryPc) {{ ctx->pc = 0x{:X}u; }}\n", fallthroughPc);
ss << fmt::format(" if (ctx->pc != 0x{:X}u) {{ return; }}\n", fallthroughPc);
ss << fmt::format(" if (ctx->pc != 0x{:X}u) {{\n", fallthroughPc);
ss << fmt::format(" fprintf(stderr, \"[PC_MISMATCH] at 0x{:X}: called 0x{:X}, expected ret 0x{:X}, got 0x%x\\n\", ctx->pc);\n", branchInst.address, target, fallthroughPc);
ss << " return;\n";
ss << " }\n";
}
ss << " }\n";
}
Expand Down Expand Up @@ -448,8 +515,17 @@ namespace ps2recomp
ss << " auto targetFn = runtime->lookupFunction(jumpTarget);\n";
ss << " const uint32_t __entryPc = ctx->pc;\n";
ss << " targetFn(rdram, ctx, runtime);\n";
ss << fmt::format(" while (runtime->hasFunction(ctx->pc) && runtime->lookupFunction(ctx->pc) == targetFn) {{\n");
ss << " const uint32_t __resumePc = ctx->pc;\n";
ss << fmt::format(" if (__resumePc == 0x{:X}u) {{ break; }}\n", fallthroughPc);
ss << " targetFn(rdram, ctx, runtime);\n";
ss << fmt::format(" if (ctx->pc == __resumePc) {{ ctx->pc = 0x{:X}u; break; }}\n", fallthroughPc);
ss << " }\n";
ss << fmt::format(" if (ctx->pc == __entryPc) {{ ctx->pc = 0x{:X}u; }}\n", fallthroughPc);
ss << fmt::format(" if (ctx->pc != 0x{:X}u) {{ return; }}\n", fallthroughPc);
ss << fmt::format(" if (ctx->pc != 0x{:X}u) {{\n", fallthroughPc);
ss << fmt::format(" fprintf(stderr, \"[PC_MISMATCH] at 0x{:X}: jalr 0x%x, expected ret 0x{:X}, got 0x%x\\n\", __entryPc, ctx->pc);\n", branchInst.address, fallthroughPc);
ss << " return;\n";
ss << " }\n";
ss << " }\n";
}

Expand Down Expand Up @@ -899,7 +975,6 @@ namespace ps2recomp
{
ss << "#include \"ps2_runtime_macros.h\"\n";
ss << "#include \"ps2_runtime.h\"\n";
ss << "#include \"ps2_recompiled_functions.h\"\n";
ss << "#include \"ps2_recompiled_stubs.h\"\n\n";
ss << "#include \"ps2_syscalls.h\"\n";
ss << "#include \"ps2_stubs.h\"\n\n";
Expand All @@ -909,6 +984,12 @@ namespace ps2recomp
}

AnalysisResult analysisResult = collectInternalBranchTargets(function, instructions);
std::unordered_set<uint32_t> resumeTargets = analysisResult.entryPoints;
auto resumeIt = m_functionResumePoints.find(function.start);
if (resumeIt != m_functionResumePoints.end())
{
resumeTargets.insert(resumeIt->second.begin(), resumeIt->second.end());
}
const std::unordered_set<uint32_t>& internalTargets = analysisResult.entryPoints;
ss << "// Function: " << function.name << "\n";
ss << "// Address: 0x" << std::hex << function.start << " - 0x" << function.end << std::dec << "\n";
Expand All @@ -926,6 +1007,23 @@ namespace ps2recomp
ss << " PS_LOG_ENTRY(\"" << sanitizedName << "\");\n";
ss << "#endif\n";
ss << "\n";
if (!resumeTargets.empty())
{
ss << " const uint32_t __resumePc = ctx->pc;\n";
ss << fmt::format(" if (__resumePc != 0x{:X}u) {{\n", function.start);
ss << " switch (__resumePc) {\n";
std::vector<uint32_t> sortedResumeTargets(resumeTargets.begin(), resumeTargets.end());
std::sort(sortedResumeTargets.begin(), sortedResumeTargets.end());
for (uint32_t target : sortedResumeTargets)
{
ss << fmt::format(" case 0x{:X}u: ctx->pc = __resumePc; goto label_{:x};\n",
target, target);
}
ss << " default: break;\n";
ss << " }\n";
ss << " }\n";
ss << "\n";
}
ss << " ctx->pc = 0x" << std::hex << function.start << "u;\n"
<< std::dec;
ss << "\n";
Expand All @@ -934,7 +1032,7 @@ namespace ps2recomp
{
const Instruction &inst = instructions[i];

if (internalTargets.contains(inst.address))
if (resumeTargets.contains(inst.address))
{
ss << "label_" << std::hex << inst.address << std::dec << ":\n";
}
Expand Down Expand Up @@ -965,7 +1063,7 @@ namespace ps2recomp
delaySlot = &syntheticDelaySlot;
}

if (hasDecodedDelaySlot && internalTargets.contains(delaySlot->address))
if (hasDecodedDelaySlot && resumeTargets.contains(delaySlot->address))
{
ss << "label_" << std::hex << delaySlot->address << std::dec << ":\n";
}
Expand Down Expand Up @@ -3717,10 +3815,11 @@ namespace ps2recomp
ss << "#include \"ps2_runtime.h\"\n";
ss << "#include \"ps2_recompiled_functions.h\"\n";
ss << "#include \"ps2_stubs.h\"\n";
ss << "#include \"ps2_recompiled_stubs.h\"//this will give duplicated erros because runtime maybe has it define already, just delete the TODOS ones\n";
ss << "#include \"ps2_recompiled_stubs.h\"\n";
ss << "#include \"ps2_syscalls.h\"\n\n";

// Registration function
// Registration function — this file needs ps2_recompiled_functions.h
// for forward declarations of all function symbols
ss << "void registerAllFunctions(PS2Runtime& runtime) {\n";

std::vector<std::pair<uint32_t, std::string>> normalFunctions;
Expand All @@ -3737,6 +3836,19 @@ namespace ps2recomp
continue;

std::string generatedName = getFunctionName(function.start);
if (function.name.rfind("entry_", 0) == 0)
{
auto ownerIt = m_entryOwnerStarts.find(function.start);
if (ownerIt != m_entryOwnerStarts.end())
{
generatedName = getFunctionName(ownerIt->second);
}
}

if (generatedName.empty())
{
continue;
}

if (function.isSkipped)
{
Expand Down
58 changes: 40 additions & 18 deletions ps2xRecomp/src/lib/ps2_recompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ namespace ps2recomp

bool shouldGenerateCodeForFunction(const Function &function)
{
if (function.isRecompiled && function.name.rfind("entry_", 0) == 0)
{
return false;
}
return function.isRecompiled || function.isStub || function.isSkipped;
}

Expand Down Expand Up @@ -370,36 +374,22 @@ namespace ps2recomp

const auto &instructions = decodedIt->second;

for (const auto &inst : instructions)
auto queuePendingEntry = [&](uint32_t target)
{
auto targetOpt = getStaticEntryTarget(inst);
if (!targetOpt.has_value())
{
continue;
}

const StaticEntryTarget staticTarget = targetOpt.value();
const uint32_t target = staticTarget.target;

if ((target & 0x3) != 0 || !isExecutableAddress(target))
{
continue;
return;
}

if (existingStarts.contains(target) || pendingStarts.contains(target))
{
continue;
return;
}

const bool targetInCurrentFunction = std::any_of(
instructions.begin(), instructions.end(),
[&](const Instruction &candidate)
{ return candidate.address == target; });
if (targetInCurrentFunction && !staticTarget.isCall)
{
// jumps with the current decoded function remain labels/gotos.
continue;
}

const Function *containingFunction = findContainingFunction(target);
if (targetInCurrentFunction)
Expand All @@ -410,7 +400,7 @@ namespace ps2recomp
pending.containingEnd = function.end;
pendingEntries.push_back(pending);
pendingStarts.insert(target);
continue;
return;
}

PendingEntry pending{};
Expand All @@ -423,6 +413,37 @@ namespace ps2recomp

pendingEntries.push_back(pending);
pendingStarts.insert(target);
};

for (const auto &inst : instructions)
{
// Calls can yield and bubble their return PC up to the top-level
// dispatcher, so the post-call fallthrough must be resumable.
if (inst.isCall && inst.address <= (std::numeric_limits<uint32_t>::max() - 8u))
{
queuePendingEntry(inst.address + 8u);
}

auto targetOpt = getStaticEntryTarget(inst);
if (!targetOpt.has_value())
{
continue;
}

const StaticEntryTarget staticTarget = targetOpt.value();
const uint32_t target = staticTarget.target;

const bool targetInCurrentFunction = std::any_of(
instructions.begin(), instructions.end(),
[&](const Instruction &candidate)
{ return candidate.address == target; });
if (targetInCurrentFunction && !staticTarget.isCall)
{
// jumps with the current decoded function remain labels/gotos.
continue;
}

queuePendingEntry(target);
}
}

Expand Down Expand Up @@ -996,6 +1017,7 @@ namespace ps2recomp
if (m_codeGenerator)
{
m_codeGenerator->setRenamedFunctions(m_functionRenames);
m_codeGenerator->setFunctionBounds(m_functions);
}

if (m_bootstrapInfo.valid && m_codeGenerator)
Expand Down