Feature/stub by address (#61)

* feat: split runtime code in small files to be easy to develop

* feat: split stubs in inl files

* feat: function auto link function treat functions with underscore as same as without underscore

* feat: remove underscore prefix from stubs

* feat: thread and flags refactor

* feat: propagate request stop
feat: some elf validation on runtime

* feat: remove Z7 compiler options
feat: added a literal float case error on vu

* fix: fix critical recompiler error on marking pc calls

* feat: better system interrupt
refactor: small refactor on thread system

* feat: stubs implements for resident evil code veronica

* feat: merge fileio

* feat: stub by address

* feat: codegen now auto-routes J/JAL callssites with known relocation symbols to syscalls/stubs
This commit is contained in:
Ranieri
2026-02-18 01:35:20 -03:00
committed by GitHub
parent 4a538d3589
commit 043bf3bf8d
8 changed files with 158 additions and 17 deletions
+54 -10
View File
@@ -2,6 +2,7 @@
#include "ps2recomp/instructions.h"
#include "ps2recomp/ps2_recompiler.h"
#include "ps2recomp/types.h"
#include "ps2_runtime_calls.h"
#include <fmt/format.h>
#include <sstream>
#include <algorithm>
@@ -115,6 +116,11 @@ namespace ps2recomp
m_bootstrapInfo = info;
}
void CodeGenerator::setRelocationCallNames(const std::unordered_map<uint32_t, std::string> &callNames)
{
m_relocationCallNames = callNames;
}
std::string CodeGenerator::getFunctionName(uint32_t address) const
{
auto it = m_renamedFunctions.find(address);
@@ -260,18 +266,54 @@ namespace ps2recomp
}
else
{
ss << " {\n";
ss << fmt::format(" auto targetFn = runtime->lookupFunction(0x{:X}u);\n", target);
ss << " targetFn(rdram, ctx, runtime);\n";
if (branchInst.opcode == OPCODE_J)
bool emittedRelocCall = false;
const auto relocIt = m_relocationCallNames.find(branchInst.address);
if (relocIt != m_relocationCallNames.end() && !relocIt->second.empty())
{
ss << " return;\n";
const std::string_view resolvedSyscallName =
ps2_runtime_calls::resolveSyscallName(relocIt->second);
const std::string_view resolvedStubName =
ps2_runtime_calls::resolveStubName(relocIt->second);
if (!resolvedSyscallName.empty() || !resolvedStubName.empty())
{
const bool isSyscall = !resolvedSyscallName.empty();
const std::string_view handlerName = isSyscall ? resolvedSyscallName : resolvedStubName;
ss << " {\n";
ss << " const uint32_t __entryPc = ctx->pc;\n";
ss << " "
<< (isSyscall ? "ps2_syscalls::" : "ps2_stubs::")
<< handlerName << "(rdram, ctx, runtime);\n";
ss << " if (ctx->pc == __entryPc) { ctx->pc = getRegU32(ctx, 31); }\n";
ss << " }\n";
if (branchInst.opcode == OPCODE_J)
{
ss << " return;\n";
}
else
{
ss << fmt::format(" if (ctx->pc != 0x{:X}u) {{ return; }}\n", fallthroughPc);
}
emittedRelocCall = true;
}
}
else
if (!emittedRelocCall)
{
ss << fmt::format(" if (ctx->pc != 0x{:X}u) {{ return; }}\n", fallthroughPc);
ss << " {\n";
ss << fmt::format(" auto targetFn = runtime->lookupFunction(0x{:X}u);\n", target);
ss << " targetFn(rdram, ctx, runtime);\n";
if (branchInst.opcode == OPCODE_J)
{
ss << " return;\n";
}
else
{
ss << fmt::format(" if (ctx->pc != 0x{:X}u) {{ return; }}\n", fallthroughPc);
}
ss << " }\n";
}
ss << " }\n";
}
}
}
@@ -574,6 +616,8 @@ namespace ps2recomp
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";
}
std::unordered_set<uint32_t> internalTargets = collectInternalBranchTargets(function, instructions);
@@ -2104,7 +2148,7 @@ namespace ps2recomp
default:
return fmt::format("// Unhandled VU0 Special1 function: 0x{:X}", special1_func);
}
}
}
default:
return fmt::format("// Unhandled COP2 format: 0x{:X}", format);
}
@@ -2449,7 +2493,7 @@ namespace ps2recomp
// VCALLMS calls a VU0 microprogram at the specified immediate address.
// VU0 micro memory is 4KB = 512 instructions (8 bytes each). Index is 0-511.
uint16_t instr_index = static_cast<uint16_t>((inst.raw >> 6) & 0x1FF); // imm15[8:0]
uint32_t target_byte_addr = static_cast<uint32_t>(instr_index) << 3; // Convert instruction index to byte address
uint32_t target_byte_addr = static_cast<uint32_t>(instr_index) << 3; // Convert instruction index to byte address
return fmt::format(
"{{ "
+17
View File
@@ -1090,9 +1090,26 @@ namespace ps2recomp
reloc.offset = static_cast<uint32_t>(offset);
reloc.info = (symbol << 8) | (type & 0xFF);
reloc.symbol = symbol;
reloc.symbolName.clear();
reloc.type = type;
reloc.addend = static_cast<int32_t>(addend);
if (symbol < symbols.get_symbols_num())
{
std::string symName;
ELFIO::Elf64_Addr symValue = 0;
ELFIO::Elf_Xword symSize = 0;
unsigned char symBind = 0;
unsigned char symType = 0;
ELFIO::Elf_Half symSectionIndex = 0;
unsigned char symOther = 0;
if (symbols.get_symbol(symbol, symName, symValue, symSize,
symBind, symType, symSectionIndex, symOther))
{
reloc.symbolName = symName;
}
}
m_relocations.push_back(reloc);
}
}
+52 -3
View File
@@ -247,6 +247,11 @@ namespace ps2recomp
try
{
m_config = m_configManager.loadConfig();
m_skipFunctions.clear();
m_skipFunctionStarts.clear();
m_stubFunctions.clear();
m_stubFunctionStarts.clear();
m_stubHandlerBindingsByStart.clear();
for (const auto &name : m_config.skipFunctions)
{
@@ -270,6 +275,19 @@ namespace ps2recomp
if (selector.start.has_value())
{
m_stubFunctionStarts.insert(*selector.start);
if (!selector.name.empty())
{
auto bindingIt = m_stubHandlerBindingsByStart.find(*selector.start);
if (bindingIt != m_stubHandlerBindingsByStart.end() &&
bindingIt->second != selector.name)
{
std::cerr << "Warning: Multiple stub handler bindings for 0x"
<< std::hex << *selector.start << std::dec
<< " (keeping latest '" << selector.name
<< "', previous '" << bindingIt->second << "')" << std::endl;
}
m_stubHandlerBindingsByStart[*selector.start] = selector.name;
}
}
}
@@ -356,6 +374,25 @@ namespace ps2recomp
m_decoder = std::make_unique<R5900Decoder>();
m_codeGenerator = std::make_unique<CodeGenerator>(m_symbols);
std::unordered_map<uint32_t, std::string> relocationCallNames;
relocationCallNames.reserve(m_relocations.size());
for (const auto &reloc : m_relocations)
{
if (reloc.symbolName.empty())
{
continue;
}
auto inserted = relocationCallNames.emplace(reloc.offset, reloc.symbolName);
if (!inserted.second && inserted.first->second != reloc.symbolName)
{
std::cerr << "Warning: multiple relocation symbols at 0x"
<< std::hex << reloc.offset << std::dec
<< " (keeping '" << inserted.first->second
<< "', ignoring '" << reloc.symbolName << "')" << std::endl;
}
}
m_codeGenerator->setRelocationCallNames(relocationCallNames);
m_codeGenerator->setBootstrapInfo(m_bootstrapInfo);
fs::create_directories(m_config.outputPath);
@@ -501,8 +538,16 @@ namespace ps2recomp
}
else
{
const std::string_view resolvedSyscallName = ps2_runtime_calls::resolveSyscallName(function.name);
const std::string_view resolvedStubName = ps2_runtime_calls::resolveStubName(function.name);
std::string dispatchName = function.name;
const auto bindingIt = m_stubHandlerBindingsByStart.find(function.start);
if (bindingIt != m_stubHandlerBindingsByStart.end() &&
!bindingIt->second.empty())
{
dispatchName = bindingIt->second;
}
const std::string_view resolvedSyscallName = ps2_runtime_calls::resolveSyscallName(dispatchName);
const std::string_view resolvedStubName = ps2_runtime_calls::resolveStubName(dispatchName);
if (!resolvedSyscallName.empty())
{
stub << "ps2_syscalls::" << resolvedSyscallName << "(rdram, ctx, runtime); ";
@@ -513,7 +558,11 @@ namespace ps2recomp
}
else
{
stub << "ps2_stubs::TODO_NAMED(\"" << escapeCStringLiteral(function.name) << "\", rdram, ctx, runtime); ";
if (dispatchName.empty())
{
dispatchName = function.name;
}
stub << "ps2_stubs::TODO_NAMED(\"" << escapeCStringLiteral(dispatchName) << "\", rdram, ctx, runtime); ";
}
}