Skip to content

Commit 1e0b510

Browse files
committed
PPU LLVM: Recycle identical functions
1 parent d66ce56 commit 1e0b510

File tree

4 files changed

+135
-3
lines changed

4 files changed

+135
-3
lines changed

rpcs3/Emu/Cell/PPUAnalyser.cpp

+107
Original file line numberDiff line numberDiff line change
@@ -2046,6 +2046,113 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
20462046
}
20472047

20482048
ppu_log.notice("Block analysis: %zu blocks (%zu enqueued)", funcs.size(), block_queue.size());
2049+
2050+
std::unordered_map<std::string_view, std::pair<u32, u32>> duplicate_data_map;
2051+
duplicate_map.clear();
2052+
2053+
for (auto& func : funcs)
2054+
{
2055+
if (func.size == 0 || func.size > 10000u)
2056+
{
2057+
continue;
2058+
}
2059+
2060+
auto& data = duplicate_data_map[std::string_view{get_ptr<char>(func.addr), func.size}];
2061+
2062+
const usz count = data.first;
2063+
2064+
if (!count)
2065+
{
2066+
data.first++;
2067+
data.second = func.addr;
2068+
continue;
2069+
}
2070+
2071+
if (!data.second)
2072+
{
2073+
continue;
2074+
}
2075+
2076+
if (count == 1)
2077+
{
2078+
const u32 faddr = func.addr;
2079+
const u32 fend = func.addr + func.size;
2080+
2081+
bool fail = false;
2082+
2083+
for (const auto& [addr, size] : func.blocks)
2084+
{
2085+
if (size == 0)
2086+
{
2087+
continue;
2088+
}
2089+
2090+
auto i_ptr = ensure(get_ptr<u32>(addr));
2091+
2092+
for (u32 i = addr; i < addr + size; i += 4, i_ptr++)
2093+
{
2094+
const ppu_opcode_t op{*i_ptr};
2095+
const auto itype = s_ppu_itype.decode(op.opcode);
2096+
2097+
if (itype != ppu_itype::BC && itype != ppu_itype::B)
2098+
{
2099+
continue;
2100+
}
2101+
2102+
const u32 target = (op.aa ? 0 : i) + (itype == ppu_itype::B ? +op.bt24 : +op.bt14);
2103+
2104+
if (target >= fend || target < faddr)
2105+
{
2106+
fail = true;
2107+
break;
2108+
}
2109+
}
2110+
2111+
if (fail)
2112+
{
2113+
break;
2114+
}
2115+
}
2116+
2117+
if (fail)
2118+
{
2119+
data.first = 1;
2120+
data.second = 0;
2121+
continue;
2122+
}
2123+
}
2124+
2125+
data.first++;
2126+
2127+
// Choose the lowest function as the source
2128+
data.second = std::min<u32>(data.second, func.addr);
2129+
}
2130+
2131+
usz dups_count = 0;
2132+
2133+
for (auto& func : funcs)
2134+
{
2135+
if (func.size == 0 || func.size > 10000u)
2136+
{
2137+
continue;
2138+
}
2139+
2140+
const auto data = ::at32(duplicate_data_map, std::string_view{get_ptr<char>(func.addr), func.size});
2141+
2142+
if (data.first > 1)
2143+
{
2144+
dups_count++;
2145+
2146+
if (func.addr != data.second)
2147+
{
2148+
duplicate_map[func.addr] = data.second;
2149+
}
2150+
2151+
ppu_log.trace("Found PPU function duplicate: func 0x%x (%d times)", data.second, data.first);
2152+
}
2153+
}
2154+
2155+
ppu_log.success("Function duplication count: %d/%d (%g%)", dups_count, duplicate_data_map.size(), dups_count * 100.0 / duplicate_data_map.size());
20492156
return true;
20502157
}
20512158

rpcs3/Emu/Cell/PPUAnalyser.h

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ struct ppu_module
9595
std::vector<ppu_function> funcs{};
9696
std::deque<std::shared_ptr<void>> allocations;
9797
std::map<u32, u32> addr_to_seg_index;
98+
std::unordered_map<u32, u32> duplicate_map;
9899

99100
// Copy info without functions
100101
void copy_part(const ppu_module& info)

rpcs3/Emu/Cell/PPUThread.cpp

+18-2
Original file line numberDiff line numberDiff line change
@@ -4146,7 +4146,7 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
41464146
}
41474147

41484148
ppu_log.notice("Failed to precompile '%s' (prx: %s, ovl: %s): Attempting tratment as executable file", path, prx_err, ovl_err);
4149-
possible_exec_file_paths.push(path, offset, file_size);
4149+
possible_exec_file_paths.push(file_queue[func_i]);
41504150
inc_fdone = 0;
41514151
}
41524152
});
@@ -5054,7 +5054,14 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size)
50545054
break;
50555055
}
50565056

5057-
const auto name = fmt::format("__0x%x", func.addr - reloc);
5057+
u32 og_func = func.addr;
5058+
5059+
if (auto it = info.duplicate_map.find(func.addr); it != info.duplicate_map.end())
5060+
{
5061+
og_func = it->second;
5062+
}
5063+
5064+
const auto name = fmt::format("__0x%x", og_func - reloc);
50585065

50595066
// Try to locate existing function if it is not the first time
50605067
const auto addr = is_first ? ensure(reinterpret_cast<ppu_intrp_func_t>(jit->get(name)))
@@ -5194,6 +5201,15 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co
51945201

51955202
if (module_part.funcs[fi].size)
51965203
{
5204+
const u32 faddr = module_part.funcs[fi].addr;
5205+
auto it = module_part.duplicate_map.find(faddr);
5206+
5207+
if (it != module_part.duplicate_map.end() && it->second != faddr)
5208+
{
5209+
ppu_log.trace("LLVM: Function 0x%x was skipped (duplicate)", faddr);
5210+
continue;
5211+
}
5212+
51975213
// Translate
51985214
if (const auto func = translator.Translate(module_part.funcs[fi]))
51995215
{

rpcs3/Emu/Cell/PPUTranslator.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,15 @@ void PPUTranslator::CallFunction(u64 target, Value* indirect)
419419

420420
if (!indirect)
421421
{
422-
callee = m_module->getOrInsertFunction(fmt::format("__0x%x", target_last - base), type);
422+
const auto it = m_info.duplicate_map.find(target_last);
423+
const u32 first_func = it == m_info.duplicate_map.end() ? target_last : it->second;
424+
425+
if (base)
426+
{
427+
ensure(first_func >= base && target_last >= base);
428+
}
429+
430+
callee = m_module->getOrInsertFunction(fmt::format("__0x%x", first_func - base), type);
423431
cast<Function>(callee.getCallee())->setCallingConv(CallingConv::GHC);
424432
}
425433
}

0 commit comments

Comments
 (0)