diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index dbd1a4d0fd69..1b1a4372388d 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -2096,6 +2096,16 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b if (itype != ppu_itype::BC && itype != ppu_itype::B) { + if (i == fend - 4) + { + if (!(itype & ppu_itype::branch) && itype != ppu_itype::SC) + { + // Inserts a branch to following code + fail = true; + break; + } + } + continue; } @@ -2106,6 +2116,16 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b fail = true; break; } + + if (itype == ppu_itype::BC && (op.bo & 0x14) != 0x14) + { + if (i == fend - 4) + { + // Can branch to next + fail = true; + break; + } + } } if (fail) @@ -2141,11 +2161,16 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b if (data.first > 1) { - dups_count++; + duplicate_map[func.addr] = data.second; + + for (const auto& [addr, size] : func.blocks) + { + duplicate_map[addr] = data.second + (addr - func.addr); + } if (func.addr != data.second) { - duplicate_map[func.addr] = data.second; + dups_count++; } ppu_log.trace("Found PPU function duplicate: func 0x%x (%d times)", data.second, data.first); diff --git a/rpcs3/Emu/Cell/PPUAnalyser.h b/rpcs3/Emu/Cell/PPUAnalyser.h index e765c7310722..114a7b00c23e 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.h +++ b/rpcs3/Emu/Cell/PPUAnalyser.h @@ -108,6 +108,7 @@ struct ppu_module secs = info.secs; allocations = info.allocations; addr_to_seg_index = info.addr_to_seg_index; + duplicate_map = info.duplicate_map; } bool analyse(u32 lib_toc, u32 entry, u32 end, const std::basic_string& applied, std::function check_aborted = {}); diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index a934b31a633d..8c0b56baa1b7 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -4643,8 +4643,15 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size) // Copy block or function entry ppu_function& entry = part.funcs.emplace_back(func); + u32 og_func = entry.addr; + + if (auto it = info.duplicate_map.find(entry.addr); it != info.duplicate_map.end()) + { + og_func = it->second; + } + // Fixup some information - entry.name = fmt::format("__0x%x", entry.addr - reloc); + entry.name = fmt::format("__0x%x", og_func - reloc); if (has_mfvscr && g_cfg.core.ppu_set_sat_bit) { @@ -5037,6 +5044,8 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size) bool early_exit = false; + std::map func_ptr_map; + // Get and install function addresses for (const auto& func : info.funcs) { @@ -5063,10 +5072,14 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size) const auto name = fmt::format("__0x%x", og_func - reloc); + ppu_intrp_func_t dummy{}; + ppu_intrp_func_t& func_ptr = is_first ? func_ptr_map[name] : dummy; + // Try to locate existing function if it is not the first time - const auto addr = is_first ? ensure(reinterpret_cast(jit->get(name))) + const auto addr = is_first ? (func_ptr ? func_ptr : ensure(reinterpret_cast(jit->get(name)))) : reinterpret_cast(ensure(jit_mod.funcs[index])); + func_ptr = addr; jit_mod.funcs.emplace_back(addr); if (func.size == 4 && !BLR_func && *info.get_ptr(func.addr) == ppu_instructions::BLR()) @@ -5155,6 +5168,11 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co { if (func.size) { + if (auto it = module_part.duplicate_map.find(func.addr); it != module_part.duplicate_map.end() && it->second != it->first) + { + continue; + } + const auto f = cast(_module->getOrInsertFunction(func.name, _func).getCallee()); f->setCallingConv(CallingConv::GHC); f->addParamAttr(1, llvm::Attribute::NoAlias); diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index 8f53e645727e..b277cff27bc9 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -148,6 +148,7 @@ Function* PPUTranslator::Translate(const ppu_function& info) const u64 base = m_reloc ? m_reloc->addr : 0; m_addr = info.addr - base; m_attr = info.attr; + m_func_base = m_addr; // Don't emit check in small blocks without terminator bool need_check = info.size >= 16; @@ -304,13 +305,30 @@ Value* PPUTranslator::VecHandleResult(Value* val) Value* PPUTranslator::GetAddr(u64 _add) { + const bool is_duplicate = m_info.duplicate_map.contains(m_func_base); + const auto old_cia = std::exchange(m_cia, nullptr); + const auto cia_reg = is_duplicate ? ZExt(RegLoad(m_cia)) : nullptr; + const u32 inst_diff = is_duplicate ? m_addr - m_func_base : m_addr; + + m_cia = old_cia; + Value* cia = nullptr; + if (m_reloc) { // Load segment address from global variable, compute actual instruction address - return m_ir->CreateAdd(m_ir->getInt64(m_addr + _add), m_seg0); + cia = m_ir->CreateAdd(m_ir->getInt64(inst_diff + _add), m_seg0); + } + else + { + cia = m_ir->getInt64(inst_diff + _add); + } + + if (cia_reg) + { + cia = m_ir->CreateAdd(cia_reg, cia); } - return m_ir->getInt64(m_addr + _add); + return cia; } Type* PPUTranslator::ScaleType(Type* type, s32 pow2) diff --git a/rpcs3/Emu/Cell/PPUTranslator.h b/rpcs3/Emu/Cell/PPUTranslator.h index d72dc1de20c7..805aaa1df229 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.h +++ b/rpcs3/Emu/Cell/PPUTranslator.h @@ -28,6 +28,9 @@ class PPUTranslator final : public cpu_translator // Current position-independent address u64 m_addr = 0; + // Function start + u64 m_func_base = 0; + // Function attributes bs_t m_attr{};