diff --git a/ps2xRecomp/include/ps2recomp/ps2_recompiler.h b/ps2xRecomp/include/ps2recomp/ps2_recompiler.h index fc2aa3c..b327dda 100644 --- a/ps2xRecomp/include/ps2recomp/ps2_recompiler.h +++ b/ps2xRecomp/include/ps2recomp/ps2_recompiler.h @@ -36,6 +36,9 @@ namespace ps2recomp std::vector &functions, std::unordered_map> &decodedFunctions, const std::vector
§ions); + static size_t ResliceEntryFunctions( + std::vector &functions, + std::unordered_map> &decodedFunctions); private: ConfigManager m_configManager; diff --git a/ps2xRecomp/src/lib/ps2_recompiler.cpp b/ps2xRecomp/src/lib/ps2_recompiler.cpp index 81523fe..f22da25 100644 --- a/ps2xRecomp/src/lib/ps2_recompiler.cpp +++ b/ps2xRecomp/src/lib/ps2_recompiler.cpp @@ -511,6 +511,171 @@ namespace ps2recomp return stats; } + + bool isEntryFunctionName(const std::string &name) + { + return name.rfind("entry_", 0) == 0; + } + + size_t resliceEntryFunctionsImpl( + std::vector &functions, + std::unordered_map> &decodedFunctions) + { + std::vector boundaryStarts; + boundaryStarts.reserve(functions.size()); + for (const auto &function : functions) + { + if (!function.isRecompiled || function.isStub || function.isSkipped) + { + continue; + } + boundaryStarts.push_back(function.start); + } + + std::sort(boundaryStarts.begin(), boundaryStarts.end()); + boundaryStarts.erase(std::unique(boundaryStarts.begin(), boundaryStarts.end()), boundaryStarts.end()); + + auto findContainingFunction = [&](uint32_t address) -> const Function * + { + const Function *best = nullptr; + for (const auto &function : functions) + { + if (!function.isRecompiled || function.isStub || function.isSkipped) + { + continue; + } + + if (isEntryFunctionName(function.name)) + { + continue; + } + + if (address < function.start || address >= function.end) + { + continue; + } + + auto decodedIt = decodedFunctions.find(function.start); + if (decodedIt == decodedFunctions.end()) + { + continue; + } + + const auto &decoded = decodedIt->second; + const bool hasAddress = std::any_of(decoded.begin(), decoded.end(), + [&](const Instruction &candidate) + { return candidate.address == address; }); + if (!hasAddress) + { + continue; + } + + if (!best || function.start > best->start) + { + best = &function; + } + } + return best; + }; + + size_t reslicedCount = 0; + + for (auto &function : functions) + { + if (!function.isRecompiled || function.isStub || function.isSkipped) + { + continue; + } + + if (!isEntryFunctionName(function.name)) + { + continue; + } + + const Function *containingFunction = findContainingFunction(function.start); + uint32_t sliceEndAddress = containingFunction ? containingFunction->end : function.end; + auto nextIt = std::upper_bound(boundaryStarts.begin(), boundaryStarts.end(), function.start); + if (nextIt != boundaryStarts.end() && *nextIt < sliceEndAddress) + { + sliceEndAddress = *nextIt; + } + + if (sliceEndAddress <= function.start) + { + continue; + } + + const std::vector *sourceInstructions = nullptr; + if (containingFunction) + { + auto containingDecodedIt = decodedFunctions.find(containingFunction->start); + if (containingDecodedIt == decodedFunctions.end()) + { + continue; + } + sourceInstructions = &containingDecodedIt->second; + } + else + { + auto entryDecodedIt = decodedFunctions.find(function.start); + if (entryDecodedIt == decodedFunctions.end()) + { + continue; + } + sourceInstructions = &entryDecodedIt->second; + } + + auto sliceBeginIt = std::find_if(sourceInstructions->begin(), sourceInstructions->end(), + [&](const Instruction &candidate) + { return candidate.address == function.start; }); + if (sliceBeginIt == sourceInstructions->end()) + { + continue; + } + + auto sliceEndIt = std::find_if(sliceBeginIt, sourceInstructions->end(), + [&](const Instruction &candidate) + { return candidate.address >= sliceEndAddress; }); + if (sliceEndIt == sliceBeginIt) + { + continue; + } + + std::vector slicedInstructions(sliceBeginIt, sliceEndIt); + bool changed = (function.end != sliceEndAddress); + auto existingIt = decodedFunctions.find(function.start); + if (existingIt == decodedFunctions.end()) + { + changed = true; + } + else if (!changed) + { + const auto &existing = existingIt->second; + if (existing.size() != slicedInstructions.size()) + { + changed = true; + } + else if (!existing.empty()) + { + if (existing.front().address != slicedInstructions.front().address || + existing.back().address != slicedInstructions.back().address) + { + changed = true; + } + } + } + + function.end = sliceEndAddress; + decodedFunctions[function.start] = std::move(slicedInstructions); + + if (changed) + { + ++reslicedCount; + } + } + + return reslicedCount; + } } PS2Recompiler::PS2Recompiler(const std::string &configPath) @@ -1088,6 +1253,13 @@ namespace ps2recomp << " additional entry point(s) inside existing functions across " << stats.passCount << " pass(es)." << std::endl; } + + const size_t reslicedCount = resliceEntryFunctionsImpl(m_functions, m_decodedFunctions); + if (reslicedCount > 0) + { + std::cout << "Resliced " << reslicedCount + << " entry function(s) after discovery." << std::endl; + } } bool PS2Recompiler::decodeFunction(Function &function) @@ -1291,6 +1463,13 @@ namespace ps2recomp return stats.discoveredCount; } + size_t PS2Recompiler::ResliceEntryFunctions( + std::vector &functions, + std::unordered_map> &decodedFunctions) + { + return resliceEntryFunctionsImpl(functions, decodedFunctions); + } + StubTarget PS2Recompiler::resolveStubTarget(const std::string &name) { if (!ps2_runtime_calls::resolveSyscallName(name).empty()) diff --git a/ps2xTest/src/ps2_recompiler_tests.cpp b/ps2xTest/src/ps2_recompiler_tests.cpp index dd8149b..29f2849 100644 --- a/ps2xTest/src/ps2_recompiler_tests.cpp +++ b/ps2xTest/src/ps2_recompiler_tests.cpp @@ -120,6 +120,148 @@ void register_ps2_recompiler_tests() } }); + tc.Run("entry reslice trims earlier entries after late discovery", [](TestCase &t) { + std::vector functions = { + makeFunction("container", 0x1000u, 0x1018u), + makeFunction("entry_1008", 0x1008u, 0x1018u), + makeFunction("entry_100c", 0x100Cu, 0x1018u) + }; + + std::unordered_map> decodedFunctions; + decodedFunctions[0x1000u] = { + makeNopLike(0x1000u), + makeNopLike(0x1004u), + makeNopLike(0x1008u), + makeNopLike(0x100Cu), + makeNopLike(0x1010u), + makeNopLike(0x1014u) + }; + decodedFunctions[0x1008u] = { + makeNopLike(0x1008u), + makeNopLike(0x100Cu), + makeNopLike(0x1010u), + makeNopLike(0x1014u) + }; + decodedFunctions[0x100Cu] = { + makeNopLike(0x100Cu), + makeNopLike(0x1010u), + makeNopLike(0x1014u) + }; + + size_t resliced = PS2Recompiler::ResliceEntryFunctions(functions, decodedFunctions); + t.Equals(resliced, static_cast(1), + "expected only the earlier entry to be resliced"); + + auto findByStart = [&](uint32_t start) -> const Function* { + auto it = std::find_if(functions.begin(), functions.end(), + [&](const Function &fn) { return fn.start == start; }); + if (it == functions.end()) + { + return nullptr; + } + return &(*it); + }; + + const Function *entry1008 = findByStart(0x1008u); + const Function *entry100C = findByStart(0x100Cu); + t.IsNotNull(entry1008, "entry at 0x1008 should exist"); + t.IsNotNull(entry100C, "entry at 0x100C should exist"); + if (entry1008) + { + t.Equals(entry1008->end, 0x100Cu, + "entry 0x1008 should be trimmed to next entry start"); + } + if (entry100C) + { + t.Equals(entry100C->end, 0x1018u, + "entry 0x100C should still end at containing end"); + } + + auto decoded1008It = decodedFunctions.find(0x1008u); + auto decoded100CIt = decodedFunctions.find(0x100Cu); + t.IsTrue(decoded1008It != decodedFunctions.end(), "decoded slice for 0x1008 should exist"); + t.IsTrue(decoded100CIt != decodedFunctions.end(), "decoded slice for 0x100C should exist"); + if (decoded1008It != decodedFunctions.end()) + { + t.Equals(decoded1008It->second.size(), static_cast(1), + "entry 0x1008 slice should stop before 0x100C"); + if (!decoded1008It->second.empty()) + { + t.Equals(decoded1008It->second.front().address, 0x1008u, + "entry 0x1008 slice should begin at 0x1008"); + } + } + if (decoded100CIt != decodedFunctions.end()) + { + t.Equals(decoded100CIt->second.size(), static_cast(3), + "entry 0x100C slice should keep remaining instructions"); + } + }); + + tc.Run("entry reslice handles entries without containing function", [](TestCase &t) { + std::vector functions = { + makeFunction("entry_1008", 0x1008u, 0x1018u), + makeFunction("entry_100c", 0x100Cu, 0x1018u) + }; + + std::unordered_map> decodedFunctions; + decodedFunctions[0x1008u] = { + makeNopLike(0x1008u), + makeNopLike(0x100Cu), + makeNopLike(0x1010u), + makeNopLike(0x1014u) + }; + decodedFunctions[0x100Cu] = { + makeNopLike(0x100Cu), + makeNopLike(0x1010u), + makeNopLike(0x1014u) + }; + + size_t resliced = PS2Recompiler::ResliceEntryFunctions(functions, decodedFunctions); + t.Equals(resliced, static_cast(1), + "expected only the earlier entry to be resliced"); + + auto findByStart = [&](uint32_t start) -> const Function* { + auto it = std::find_if(functions.begin(), functions.end(), + [&](const Function &fn) { return fn.start == start; }); + if (it == functions.end()) + { + return nullptr; + } + return &(*it); + }; + + const Function *entry1008 = findByStart(0x1008u); + const Function *entry100C = findByStart(0x100Cu); + t.IsNotNull(entry1008, "entry at 0x1008 should exist"); + t.IsNotNull(entry100C, "entry at 0x100C should exist"); + if (entry1008) + { + t.Equals(entry1008->end, 0x100Cu, + "entry 0x1008 should be trimmed to next entry start"); + } + if (entry100C) + { + t.Equals(entry100C->end, 0x1018u, + "entry 0x100C should keep original end"); + } + + auto decoded1008It = decodedFunctions.find(0x1008u); + auto decoded100CIt = decodedFunctions.find(0x100Cu); + t.IsTrue(decoded1008It != decodedFunctions.end(), "decoded slice for 0x1008 should exist"); + t.IsTrue(decoded100CIt != decodedFunctions.end(), "decoded slice for 0x100C should exist"); + if (decoded1008It != decodedFunctions.end()) + { + t.Equals(decoded1008It->second.size(), static_cast(1), + "entry 0x1008 slice should stop before 0x100C"); + } + if (decoded100CIt != decodedFunctions.end()) + { + t.Equals(decoded100CIt->second.size(), static_cast(3), + "entry 0x100C slice should keep remaining instructions"); + } + }); + tc.Run("non-executable section targets are ignored", [](TestCase &t) { std::vector
sections = { {".text", 0x1000u, 0x2000u, 0u, true, false, false, true, nullptr},