Skip to content
Open
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
60 changes: 45 additions & 15 deletions ps2xRecomp/src/lib/function_emitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,6 @@ namespace ps2recomp
std::stringstream ss;
cg.m_currentFunctionName = function.name;

if (useHeaders)
{
ss << "#include <stdexcept>\n";
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";
ss << "#ifdef PS2_FUNCTION_LOG_TRACKER\n";
ss << "#include \"ps2_log.h\"\n";
ss << "#endif\n\n";
}

CodeGenerator::AnalysisResult analysisResult = cg.collectInternalBranchTargets(function, instructions);
std::vector<uint32_t> resumeTargets(analysisResult.resumeEntryPoints.begin(),
analysisResult.resumeEntryPoints.end());
Expand Down Expand Up @@ -234,6 +220,50 @@ namespace ps2recomp
}

ss << "}\n";
return ss.str();

const std::string body = ss.str();
if (!useHeaders)
{
return body;
}

std::stringstream output;
output << "#include <stdexcept>\n";
output << "#include \"ps2_runtime_macros.h\"\n";
output << "#include \"ps2_runtime.h\"\n";
output << "#include \"ps2_syscalls.h\"\n";
output << "#include \"ps2_stubs.h\"\n\n";
output << "#ifdef PS2_FUNCTION_LOG_TRACKER\n";
output << "#include \"ps2_log.h\"\n";
output << "#endif\n\n";

// Most guest calls are dispatched through PS2Runtime and therefore do
// not need declarations for every generated function. A direct J to
// a recovered function is emitted as a C++ tail call, so declare only
// those targets locally instead of including the global declaration
// header in every generated source file.
constexpr std::string_view callSuffix = "(rdram, ctx, runtime); return;";
std::unordered_set<std::string> directJumpTargets;
size_t searchOffset = 0;
while ((searchOffset = body.find(callSuffix, searchOffset)) != std::string::npos)
{
const size_t lineStart = body.rfind('\n', searchOffset);
const size_t nameStart = body.find_first_not_of(" \t", lineStart == std::string::npos ? 0 : lineStart + 1);
if (nameStart != std::string::npos && nameStart < searchOffset)
{
directJumpTargets.emplace(body.substr(nameStart, searchOffset - nameStart));
}
searchOffset += callSuffix.size();
}
for (const std::string &target : directJumpTargets)
{
output << "void " << target << "(uint8_t*, R5900Context*, PS2Runtime*);\n";
}
if (!directJumpTargets.empty())
{
output << "\n";
}
output << body;
return output.str();
}
}
1 change: 0 additions & 1 deletion ps2xRecomp/src/lib/ps2_recompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ namespace ps2recomp
void writeCombinedOutputPreamble(std::ostream &output)
{
output << "#include <stdexcept>\n";
output << "#include \"ps2_recompiled_functions.h\"\n\n";
output << "#include \"ps2_runtime_macros.h\"\n";
output << "#include \"ps2_runtime.h\"\n";
output << "#include \"ps2_recompiled_stubs.h\"\n";
Expand Down
42 changes: 42 additions & 0 deletions ps2xTest/src/code_generator_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,48 @@ void register_code_generator_tests()
{
MiniTest::Case("CodeGenerator", [](TestCase &tc)
{
tc.Run("function sources avoid the global generated declaration header", [](TestCase &t) {
Function func;
func.name = "isolated_function";
func.start = 0x8000;
func.end = 0x8004;
func.isRecompiled = true;

CodeGenerator gen({}, {});
const std::string generated = gen.generateFunction(func, {makeNop(0x8000)}, true);

t.IsTrue(generated.find("#include \"ps2_runtime.h\"") != std::string::npos,
"generated functions still require the runtime interface");
t.IsTrue(generated.find("#include \"ps2_recompiled_functions.h\"") == std::string::npos,
"only the registration unit should depend on all generated declarations");
t.IsTrue(generated.find("#include \"ps2_recompiled_stubs.h\"") == std::string::npos,
"ordinary generated functions should not depend on every stub declaration");
});

tc.Run("direct generated jumps declare only their target", [](TestCase &t) {
Function func;
func.name = "direct_jump_source";
func.start = 0x8000;
func.end = 0x8008;
func.isRecompiled = true;

Instruction jump{};
jump.address = 0x8000;
jump.opcode = OPCODE_J;
jump.target = (0x9000u >> 2) & 0x3FFFFFFu;
jump.hasDelaySlot = true;
jump.raw = (OPCODE_J << 26) | jump.target;

CodeGenerator gen({}, {});
gen.setRenamedFunctions({{0x9000u, "direct_jump_target"}});
const std::string generated = gen.generateFunction(func, {jump, makeNop(0x8004)}, true);

t.IsTrue(generated.find("void direct_jump_target(uint8_t*, R5900Context*, PS2Runtime*);") != std::string::npos,
"a direct generated jump should receive a local forward declaration");
t.IsTrue(generated.find("#include \"ps2_recompiled_functions.h\"") == std::string::npos,
"a direct generated jump should not restore the global declaration dependency");
});

tc.Run("SYSCALL publishes its continuation before entering the runtime", [](TestCase &t) {
Function func;
func.name = "syscall_resume";
Expand Down