From 6560a37b8b438631f3632989faeb4480a154734f Mon Sep 17 00:00:00 2001 From: Ran-j Date: Wed, 2 Sep 2026 17:40:43 -0300 Subject: [PATCH] feat: update memory hint handling and enhance entry point discovery logic --- ps2xRecomp/src/lib/instruction_translator.cpp | 16 +-- ps2xRecomp/src/lib/ps2_recompiler.cpp | 114 +++++++++++++++--- 2 files changed, 99 insertions(+), 31 deletions(-) diff --git a/ps2xRecomp/src/lib/instruction_translator.cpp b/ps2xRecomp/src/lib/instruction_translator.cpp index ae38ffa..7684097 100644 --- a/ps2xRecomp/src/lib/instruction_translator.cpp +++ b/ps2xRecomp/src/lib/instruction_translator.cpp @@ -69,14 +69,8 @@ namespace ps2recomp MemoryAccessHint InstructionTranslator::effectiveMemoryHintFor(const Instruction &inst, const MemoryAccessHint &memoryHint) const { - MemoryAccessHint effectiveMemoryHint = memoryHint; - if (inst.isMmio) - { - effectiveMemoryHint.hasAddress = true; - effectiveMemoryHint.address = inst.mmioAddress; - } - - return effectiveMemoryHint; + // TODO disable for now since it causing issues with some games. + return memoryHint; } std::string InstructionTranslator::translateMemoryRead(const Instruction &inst, @@ -190,11 +184,11 @@ namespace ps2recomp case OPCODE_LW: return fmt::format("SET_GPR_S32(ctx, {}, (int32_t){});", inst.rt, genRead(32, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate))); case OPCODE_LBU: - return fmt::format("SET_GPR_U32(ctx, {}, (uint8_t){});", inst.rt, genRead(8, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate))); + return fmt::format("SET_GPR_ZE32(ctx, {}, (uint8_t){});", inst.rt, genRead(8, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate))); case OPCODE_LHU: - return fmt::format("SET_GPR_U32(ctx, {}, (uint16_t){});", inst.rt, genRead(16, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate))); + return fmt::format("SET_GPR_ZE32(ctx, {}, (uint16_t){});", inst.rt, genRead(16, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate))); case OPCODE_LWU: - return fmt::format("SET_GPR_U32(ctx, {}, {});", inst.rt, genRead(32, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate))); + return fmt::format("SET_GPR_ZE32(ctx, {}, {});", inst.rt, genRead(32, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate))); case OPCODE_SB: return genWrite(8, fmt::format("ADD32(GPR_U32(ctx, {}), {})", inst.rs, inst.simmediate), fmt::format("(uint8_t)GPR_U32(ctx, {})", inst.rt)) + ";"; case OPCODE_SH: diff --git a/ps2xRecomp/src/lib/ps2_recompiler.cpp b/ps2xRecomp/src/lib/ps2_recompiler.cpp index 0547eef..bb35a7a 100644 --- a/ps2xRecomp/src/lib/ps2_recompiler.cpp +++ b/ps2xRecomp/src/lib/ps2_recompiler.cpp @@ -288,7 +288,8 @@ namespace ps2recomp std::unordered_map> &decodedFunctions, const std::vector
§ions, CodeGenerator *codeGenerator, - const std::function &decodeExternalFunction) + const std::function &decodeExternalFunction, + const std::unordered_set &seedEntryAddresses = {}) { std::unordered_set existingStarts; for (const auto &function : functions) @@ -312,6 +313,19 @@ namespace ps2recomp return false; }; + auto executableSectionEnd = [&](uint32_t address) -> std::optional + { + for (const auto §ion : sections) + { + if (!section.isCode || address < section.address || address >= section.address + section.size) + { + continue; + } + return section.address + section.size; + } + return std::nullopt; + }; + auto isSimpleReturnThunkStart = [](const Instruction &inst) -> bool { return inst.opcode == OPCODE_SPECIAL && @@ -397,6 +411,14 @@ namespace ps2recomp pendingStarts.insert(target); }; + if (stats.passCount == 1u) + { + for (uint32_t target : seedEntryAddresses) + { + queuePendingEntry(target); + } + } + for (const auto &function : functions) { if (!function.isRecompiled || function.isStub || function.isSkipped) @@ -534,13 +556,24 @@ namespace ps2recomp } else { - auto nextStartOpt = findNextBoundaryStart(target); - if (!nextStartOpt.has_value() || nextStartOpt.value() <= target) + const auto sectionEndOpt = executableSectionEnd(target); + if (!sectionEndOpt.has_value()) { continue; } - entryFunction.end = nextStartOpt.value(); + uint32_t entryEnd = sectionEndOpt.value(); + auto nextStartOpt = findNextBoundaryStart(target); + if (nextStartOpt.has_value() && nextStartOpt.value() < entryEnd) + { + entryEnd = nextStartOpt.value(); + } + if (entryEnd <= target) + { + continue; + } + + entryFunction.end = entryEnd; if (!decodeExternalFunction(entryFunction)) { continue; @@ -1869,6 +1902,63 @@ namespace ps2recomp return; } + std::unordered_set guestFallbackEntryAddresses = m_entryPointHintStarts; + for (uint32_t address : m_stubFunctionStarts) + { + const auto bindingIt = m_stubHandlerBindingsByStart.find(address); + if (bindingIt == m_stubHandlerBindingsByStart.end() || + resolveStubTarget(bindingIt->second) == StubTarget::Unknown) + { + guestFallbackEntryAddresses.insert(address); + } + } + + // Prefer the existing wrapper when a configured entry lies inside a + // decoded function. If Ghidra/analyzer omitted the whole routine, + // synthesize a standalone guest function bounded by the next known + // function instead of leaving a valid executable target unregistered. + collectInternalEntryTargetsImpl(m_functions, m_decodedFunctions, guestFallbackEntryAddresses, m_resumeEntryTargetsByOwner); + + std::unordered_set coveredEntryAddresses; + coveredEntryAddresses.reserve(m_functions.size() + guestFallbackEntryAddresses.size()); + for (const auto &function : m_functions) + { + coveredEntryAddresses.insert(function.start); + } + for (const auto &[owner, targets] : m_resumeEntryTargetsByOwner) + { + coveredEntryAddresses.insert(targets.begin(), targets.end()); + } + + std::unordered_set standaloneEntryAddresses; + for (uint32_t address : guestFallbackEntryAddresses) + { + if (!coveredEntryAddresses.contains(address)) + { + standaloneEntryAddresses.insert(address); + } + } + + if (!standaloneEntryAddresses.empty()) + { + const EntryDiscoveryStats configuredStats = discoverAdditionalEntryPointsImpl( + m_functions, + m_decodedFunctions, + m_sections, + nullptr, + [this](Function &function) + { return decodeFunction(function); }, + standaloneEntryAddresses); + if (configuredStats.discoveredCount > 0u) + { + m_reporter.recordAdditionalEntryPoints(configuredStats.discoveredCount); + std::ostringstream msg; + msg << "synthesized " << configuredStats.discoveredCount + << " standalone configured guest entry point(s)"; + m_reporter.progress(msg.str()); + } + } + auto findContainingFunction = [&](uint32_t address) -> const Function * { const Function *best = nullptr; @@ -1959,22 +2049,6 @@ namespace ps2recomp targets.push_back(target); } } - - std::unordered_set guestFallbackEntryAddresses = m_entryPointHintStarts; - for (uint32_t address : m_stubFunctionStarts) - { - const auto bindingIt = m_stubHandlerBindingsByStart.find(address); - if (bindingIt == m_stubHandlerBindingsByStart.end() || - resolveStubTarget(bindingIt->second) == StubTarget::Unknown) - { - guestFallbackEntryAddresses.insert(address); - } - } - collectInternalEntryTargetsImpl( - m_functions, - m_decodedFunctions, - guestFallbackEntryAddresses, - m_resumeEntryTargetsByOwner); size_t totalTargets = 0u; for (auto it = m_resumeEntryTargetsByOwner.begin(); it != m_resumeEntryTargetsByOwner.end();)