Feature/runtime ecosystem refactor (#107)

* feat: remove memory and pad from stub section

* feat: add support for resume entry targets in CodeGenerator (this allow jumps in address outside function)
feat: refactor entry point discovery one more try to reduce big generated file

* feat: optmizations for release build

* feat: remove unused  file

* feat: added some test cases for code gen

* feat: added log macro and remove win specific code

* feat: refactor runtime folder structure
feat: added reset sound driver RPC state and compatibility layout
feat: rename and added new test
feat: update RPC calls to use defined constants
feat: added more PSMC(16, 32)
feat: change cd read to try find the asset ignoring case sensitive
fix: fix some render problems
feat: add logs on pad
feat: added more RPC handles

* feat: added game override for code veronica

* feat: apply vita patch

* feat: flags to disable build

* feat: fix merges
feat: break a lot of tests

* feat: better throw error on empty cd path
feat: remove recompiler unusde function
feat: apply missing patch

* feat: gamedp is now part of lib
feat: missing file

* feat: small cleanup

* feat: missing vita changes

* feat: fix more merge

* feat: fix tests

* feat: last missing feature

* feat: added missing import

* feat: rename test local functions

* feat: init syscall on ps2 list

* feat: added DMA helpers

* feat: faster builds
feat: more implement for darkcloud

* feat: back missing file

* feat: added missing includes

* feat: remove test

* feat: missing include

* feat: read register funtion

* feat: build  fix

* feat: force  exit on detach thread
This commit is contained in:
Ranieri
2026-04-04 23:09:56 -03:00
committed by GitHub
parent 553a9027d8
commit 93e221feaa
137 changed files with 26594 additions and 15314 deletions
+178 -9
View File
@@ -153,6 +153,17 @@ namespace ps2recomp
}
}
void CodeGenerator::setResumeEntryTargets(const std::unordered_map<uint32_t, std::vector<uint32_t>> &resumeTargetsByOwner)
{
m_resumeEntryTargetsByOwner = resumeTargetsByOwner;
for (auto &[owner, targets] : m_resumeEntryTargetsByOwner)
{
(void)owner;
std::sort(targets.begin(), targets.end());
targets.erase(std::unique(targets.begin(), targets.end()), targets.end());
}
}
std::string CodeGenerator::getFunctionName(uint32_t address) const
{
auto it = m_renamedFunctions.find(address);
@@ -230,9 +241,14 @@ namespace ps2recomp
auto emitInternalTarget = [&](uint32_t target, uint32_t sourcePc, std::string_view indent)
{
ss << fmt::format("{}ctx->pc = 0x{:X}u;\n", indent, target);
if (target <= sourcePc)
const bool isCallLikeEdge =
(branchInst.opcode == OPCODE_JAL) ||
(branchInst.opcode == OPCODE_SPECIAL && branchInst.function == SPECIAL_JALR);
if (target <= sourcePc && !isCallLikeEdge)
{
ss << fmt::format("{}runtime->cooperativeGuestYield();\n", indent);
ss << fmt::format("{}if (runtime->shouldPreemptGuestExecution()) {{\n", indent);
ss << fmt::format("{} return;\n", indent);
ss << fmt::format("{}}}\n", indent);
ss << fmt::format("{}goto label_{:x};\n", indent, target);
}
else
@@ -652,7 +668,7 @@ namespace ps2recomp
CodeGenerator::~CodeGenerator() = default;
CodeGenerator::AnalysisResult CodeGenerator::collectInternalBranchTargets(
const Function &function, const std::vector<Instruction> &instructions)
const Function &function, const std::vector<Instruction> &instructions, const std::vector<Function> *allFunctions)
{
AnalysisResult result;
std::unordered_set<uint32_t> instructionAddresses;
@@ -660,6 +676,97 @@ namespace ps2recomp
bool hasIndirectRegisterJump = false;
std::vector<const Instruction*> indirectJumps;
auto isExecutableAddress = [&](uint32_t address) -> bool
{
for (const auto &section : m_sections)
{
if (!section.isCode)
{
continue;
}
if (address >= section.address && address < (section.address + section.size))
{
return true;
}
}
return false;
};
auto findContainingExternalFunction = [&](uint32_t address) -> const Function *
{
if (!allFunctions || !isExecutableAddress(address))
{
return nullptr;
}
const Function *best = nullptr;
for (const auto &candidateFn : *allFunctions)
{
if (!candidateFn.isRecompiled || candidateFn.isStub || candidateFn.isSkipped)
{
continue;
}
if (candidateFn.name.rfind("entry_", 0) == 0)
{
continue;
}
if (address < candidateFn.start || address >= candidateFn.end)
{
continue;
}
if (!best || candidateFn.start > best->start)
{
best = &candidateFn;
}
}
return best;
};
auto queueExternalEntryTarget = [&](uint32_t target)
{
const Function *containingFn = findContainingExternalFunction(target);
if (!containingFn)
{
return;
}
if (containingFn->start == function.start)
{
return;
}
if (target == containingFn->start)
{
return;
}
result.externalEntryPoints.insert(target);
};
auto queueResumeEntryTarget = [&](uint32_t resumeAddr)
{
if (resumeAddr >= function.start && resumeAddr < function.end &&
instructionAddresses.contains(resumeAddr))
{
result.entryPoints.insert(resumeAddr);
result.resumeEntryPoints.insert(resumeAddr);
}
};
auto queueLoopResumeEntryTarget = [&](uint32_t target, uint32_t sourcePc)
{
if (target > sourcePc || target == function.start)
{
return;
}
queueResumeEntryTarget(target);
};
for (const auto &inst : instructions)
{
instructionAddresses.insert(inst.address);
@@ -685,6 +792,11 @@ namespace ps2recomp
instructionAddresses.contains(target))
{
result.entryPoints.insert(target);
queueLoopResumeEntryTarget(target, inst.address);
}
else
{
queueExternalEntryTarget(target);
}
}
else if (isStaticJump)
@@ -694,15 +806,20 @@ namespace ps2recomp
instructionAddresses.contains(target))
{
result.entryPoints.insert(target);
queueLoopResumeEntryTarget(target, inst.address);
if (inst.opcode == OPCODE_JAL)
{
uint32_t returnAddr = inst.address + 8;
if (returnAddr >= function.start && returnAddr < function.end &&
instructionAddresses.contains(returnAddr))
{
result.entryPoints.insert(returnAddr);
}
queueResumeEntryTarget(inst.address + 8u);
}
}
else
{
queueExternalEntryTarget(target);
if (inst.opcode == OPCODE_JAL)
{
queueResumeEntryTarget(inst.address + 8u);
}
}
}
@@ -712,6 +829,11 @@ namespace ps2recomp
{
bool needsJrFallback = false;
for (const Instruction* jrInst : indirectJumps) {
if (jrInst->function == SPECIAL_JALR)
{
queueResumeEntryTarget(jrInst->address + 8u);
}
bool foundTable = false;
uint32_t jrReg = jrInst->rs;
@@ -790,6 +912,10 @@ namespace ps2recomp
{
jrTargets.push_back(target);
}
else
{
queueExternalEntryTarget(target);
}
}
if (!jrTargets.empty())
@@ -849,6 +975,10 @@ namespace ps2recomp
uniqueTargets.insert(target);
}
}
else
{
queueExternalEntryTarget(target);
}
} else {
validJumpTable = false;
break;
@@ -909,6 +1039,20 @@ namespace ps2recomp
}
AnalysisResult analysisResult = collectInternalBranchTargets(function, instructions);
std::vector<uint32_t> resumeTargets(analysisResult.resumeEntryPoints.begin(),
analysisResult.resumeEntryPoints.end());
auto resumeIt = m_resumeEntryTargetsByOwner.find(function.start);
if (resumeIt != m_resumeEntryTargetsByOwner.end())
{
resumeTargets.insert(resumeTargets.end(), resumeIt->second.begin(), resumeIt->second.end());
}
std::sort(resumeTargets.begin(), resumeTargets.end());
resumeTargets.erase(std::unique(resumeTargets.begin(), resumeTargets.end()), resumeTargets.end());
for (uint32_t target : resumeTargets)
{
analysisResult.entryPoints.insert(target);
}
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";
@@ -926,6 +1070,16 @@ namespace ps2recomp
ss << " PS_LOG_ENTRY(\"" << sanitizedName << "\");\n";
ss << "#endif\n";
ss << "\n";
if (!resumeTargets.empty())
{
ss << " switch (ctx->pc) {\n";
for (uint32_t target : resumeTargets)
{
ss << " case 0x" << std::hex << target << "u: goto label_" << target << ";\n" << std::dec;
}
ss << " default: break;\n";
ss << " }\n\n";
}
ss << " ctx->pc = 0x" << std::hex << function.start << "u;\n"
<< std::dec;
ss << "\n";
@@ -3782,6 +3936,21 @@ namespace ps2recomp
emitRegistration(first, second);
}
ss << "\n // Register resumable entry points\n";
for (const auto &[ownerStart, targets] : m_resumeEntryTargetsByOwner)
{
const std::string ownerName = getFunctionName(ownerStart);
if (ownerName.empty())
{
continue;
}
for (uint32_t target : targets)
{
emitRegistration(target, ownerName);
}
}
ss << "\n // Register stub functions\n";
for (const auto &[first, second] : stubFunctions)
{