PPU LLVM: relocation support

This commit is contained in:
Nekotekina 2017-07-01 02:08:51 +03:00
parent 9fccb47e7c
commit f0d184f38b
7 changed files with 666 additions and 237 deletions

View file

@ -10,7 +10,6 @@
namespace vm { using namespace ps3; } namespace vm { using namespace ps3; }
const ppu_decoder<ppu_itype> s_ppu_itype; const ppu_decoder<ppu_itype> s_ppu_itype;
const ppu_decoder<ppu_iname> s_ppu_iname;
template<> template<>
void fmt_class_string<ppu_attr>::format(std::string& out, u64 arg) void fmt_class_string<ppu_attr>::format(std::string& out, u64 arg)
@ -36,10 +35,10 @@ void fmt_class_string<bs_t<ppu_attr>>::format(std::string& out, u64 arg)
format_bitset(out, arg, "[", ",", "]", &fmt_class_string<ppu_attr>::format); format_bitset(out, arg, "[", ",", "]", &fmt_class_string<ppu_attr>::format);
} }
void ppu_validate(const std::string& fname, const std::vector<ppu_function>& funcs, u32 reloc) void ppu_module::validate(u32 reloc)
{ {
// Load custom PRX configuration if available // Load custom PRX configuration if available
if (fs::file yml{fname + ".yml"}) if (fs::file yml{path + ".yml"})
{ {
const auto cfg = YAML::Load(yml.to_string()); const auto cfg = YAML::Load(yml.to_string());
@ -57,14 +56,14 @@ void ppu_validate(const std::string& fname, const std::vector<ppu_function>& fun
while (addr > found && index + 1 < funcs.size()) while (addr > found && index + 1 < funcs.size())
{ {
LOG_WARNING(LOADER, "%s.yml : unexpected function at 0x%x (0x%x, 0x%x)", fname, found, addr, size); LOG_WARNING(LOADER, "%s.yml : unexpected function at 0x%x (0x%x, 0x%x)", path, found, addr, size);
index++; index++;
found = funcs[index].addr - reloc; found = funcs[index].addr - reloc;
} }
if (addr < found) if (addr < found)
{ {
LOG_ERROR(LOADER, "%s.yml : function not found (0x%x, 0x%x)", fname, addr, size); LOG_ERROR(LOADER, "%s.yml : function not found (0x%x, 0x%x)", path, addr, size);
continue; continue;
} }
@ -72,7 +71,7 @@ void ppu_validate(const std::string& fname, const std::vector<ppu_function>& fun
{ {
if (size + 4 != funcs[index].size || vm::read32(addr + size) != ppu_instructions::NOP()) if (size + 4 != funcs[index].size || vm::read32(addr + size) != ppu_instructions::NOP())
{ {
LOG_ERROR(LOADER, "%s.yml : function size mismatch at 0x%x(size=0x%x) (0x%x, 0x%x)", fname, found, funcs[index].size, addr, size); LOG_ERROR(LOADER, "%s.yml : function size mismatch at 0x%x(size=0x%x) (0x%x, 0x%x)", path, found, funcs[index].size, addr, size);
} }
} }
@ -80,7 +79,7 @@ void ppu_validate(const std::string& fname, const std::vector<ppu_function>& fun
} }
else else
{ {
LOG_ERROR(LOADER, "%s.yml : function not found at the end (0x%x, 0x%x)", fname, addr, size); LOG_ERROR(LOADER, "%s.yml : function not found at the end (0x%x, 0x%x)", path, addr, size);
break; break;
} }
} }
@ -94,13 +93,13 @@ void ppu_validate(const std::string& fname, const std::vector<ppu_function>& fun
{ {
if (funcs[index].size) if (funcs[index].size)
{ {
LOG_ERROR(LOADER, "%s.yml : function not covered at 0x%x (size=0x%x)", fname, funcs[index].addr, funcs[index].size); LOG_ERROR(LOADER, "%s.yml : function not covered at 0x%x (size=0x%x)", path, funcs[index].addr, funcs[index].size);
} }
index++; index++;
} }
LOG_SUCCESS(LOADER, "%s.yml : validation completed", fname); LOG_SUCCESS(LOADER, "%s.yml : validation completed", path);
} }
} }
@ -522,17 +521,17 @@ namespace ppu_patterns
}; };
} }
std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& segs, const std::vector<std::pair<u32, u32>>& secs, u32 lib_toc, u32 entry) void ppu_module::analyse(u32 lib_toc, u32 entry)
{ {
// Assume first segment is executable // Assume first segment is executable
const u32 start = segs[0].first; const u32 start = segs[0].addr;
const u32 end = segs[0].first + segs[0].second; const u32 end = segs[0].addr + segs[0].size;
// Known TOCs (usually only 1) // Known TOCs (usually only 1)
std::unordered_set<u32> TOCs; std::unordered_set<u32> TOCs;
// Known functions // Known functions
std::map<u32, ppu_function> funcs; std::map<u32, ppu_function> fmap;
// Function analysis workload // Function analysis workload
std::vector<std::reference_wrapper<ppu_function>> func_queue; std::vector<std::reference_wrapper<ppu_function>> func_queue;
@ -543,7 +542,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
// Register new function // Register new function
auto add_func = [&](u32 addr, u32 toc, u32 caller) -> ppu_function& auto add_func = [&](u32 addr, u32 toc, u32 caller) -> ppu_function&
{ {
ppu_function& func = funcs[addr]; ppu_function& func = fmap[addr];
if (caller) if (caller)
{ {
@ -586,7 +585,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
// Grope for OPD section (TODO: optimization, better constraints) // Grope for OPD section (TODO: optimization, better constraints)
for (const auto& seg : segs) for (const auto& seg : segs)
{ {
for (vm::cptr<u32> ptr = vm::cast(seg.first); ptr.addr() < seg.first + seg.second; ptr++) for (vm::cptr<u32> ptr = vm::cast(seg.addr); ptr.addr() < seg.addr + seg.size; ptr++)
{ {
if (ptr[0] >= start && ptr[0] < end && ptr[0] % 4 == 0 && ptr[1] == toc) if (ptr[0] >= start && ptr[0] < end && ptr[0] % 4 == 0 && ptr[1] == toc)
{ {
@ -602,7 +601,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
// Get next reliable function address // Get next reliable function address
auto get_limit = [&](u32 addr) -> u32 auto get_limit = [&](u32 addr) -> u32
{ {
for (auto it = funcs.lower_bound(addr), end = funcs.end(); it != end; it++) for (auto it = fmap.lower_bound(addr), end = fmap.end(); it != end; it++)
{ {
if (test(it->second.attr, ppu_attr::known_addr)) if (test(it->second.attr, ppu_attr::known_addr))
{ {
@ -616,7 +615,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
// Find references indiscriminately // Find references indiscriminately
for (const auto& seg : segs) for (const auto& seg : segs)
{ {
for (vm::cptr<u32> ptr = vm::cast(seg.first); ptr.addr() < seg.first + seg.second; ptr++) for (vm::cptr<u32> ptr = vm::cast(seg.addr); ptr.addr() < seg.addr + seg.size; ptr++)
{ {
const u32 value = *ptr; const u32 value = *ptr;
@ -627,7 +626,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
for (const auto& _seg : segs) for (const auto& _seg : segs)
{ {
if (value >= _seg.first && value < _seg.first + _seg.second) if (value >= _seg.addr && value < _seg.addr + _seg.size)
{ {
addr_heap.emplace(value); addr_heap.emplace(value);
break; break;
@ -639,10 +638,10 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
// Find OPD section // Find OPD section
for (const auto& sec : secs) for (const auto& sec : secs)
{ {
vm::cptr<void> sec_end = vm::cast(sec.first + sec.second); vm::cptr<void> sec_end = vm::cast(sec.addr + sec.size);
// Probe // Probe
for (vm::cptr<u32> ptr = vm::cast(sec.first); ptr < sec_end; ptr += 2) for (vm::cptr<u32> ptr = vm::cast(sec.addr); ptr < sec_end; ptr += 2)
{ {
if (ptr + 6 <= sec_end && !ptr[0] && !ptr[2] && ptr[1] == ptr[4] && ptr[3] == ptr[5]) if (ptr + 6 <= sec_end && !ptr[0] && !ptr[2] && ptr[1] == ptr[4] && ptr[3] == ptr[5])
{ {
@ -677,10 +676,10 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
} }
} }
if (sec_end) LOG_NOTICE(PPU, "Reading OPD section at 0x%x...", sec.first); if (sec_end) LOG_NOTICE(PPU, "Reading OPD section at 0x%x...", sec.addr);
// Mine // Mine
for (vm::cptr<u32> ptr = vm::cast(sec.first); ptr < sec_end; ptr += 2) for (vm::cptr<u32> ptr = vm::cast(sec.addr); ptr < sec_end; ptr += 2)
{ {
// Special case: see "Probe" // Special case: see "Probe"
if (!ptr[0]) ptr += 4; if (!ptr[0]) ptr += 4;
@ -709,7 +708,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
} }
// Clean TOCs // Clean TOCs
for (auto&& pair : funcs) for (auto&& pair : fmap)
{ {
if (pair.second.toc == -1) if (pair.second.toc == -1)
{ {
@ -720,12 +719,12 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
// Find .eh_frame section // Find .eh_frame section
for (const auto& sec : secs) for (const auto& sec : secs)
{ {
vm::cptr<void> sec_end = vm::cast(sec.first + sec.second); vm::cptr<void> sec_end = vm::cast(sec.addr + sec.size);
// Probe // Probe
for (vm::cptr<u32> ptr = vm::cast(sec.first); ptr < sec_end;) for (vm::cptr<u32> ptr = vm::cast(sec.addr); ptr < sec_end;)
{ {
if (ptr % 4 || ptr.addr() < sec.first || ptr >= sec_end) if (ptr % 4 || ptr.addr() < sec.addr || ptr >= sec_end)
{ {
sec_end.set(0); sec_end.set(0);
break; break;
@ -749,7 +748,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
{ {
const u32 cie_off = ptr.addr() - ptr[1] + 4; const u32 cie_off = ptr.addr() - ptr[1] + 4;
if (cie_off % 4 || cie_off < sec.first || cie_off >= sec_end.addr()) if (cie_off % 4 || cie_off < sec.addr || cie_off >= sec_end.addr())
{ {
sec_end.set(0); sec_end.set(0);
break; break;
@ -759,10 +758,10 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
ptr = vm::cast(ptr.addr() + size); ptr = vm::cast(ptr.addr() + size);
} }
if (sec_end && sec.second > 4) LOG_NOTICE(PPU, "Reading .eh_frame section at 0x%x...", sec.first); if (sec_end && sec.size > 4) LOG_NOTICE(PPU, "Reading .eh_frame section at 0x%x...", sec.addr);
// Mine // Mine
for (vm::cptr<u32> ptr = vm::cast(sec.first); ptr < sec_end; ptr = vm::cast(ptr.addr() + ptr[0] + 4)) for (vm::cptr<u32> ptr = vm::cast(sec.addr); ptr < sec_end; ptr = vm::cast(ptr.addr() + ptr[0] + 4))
{ {
if (ptr[0] == 0) if (ptr[0] == 0)
{ {
@ -837,7 +836,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
{ {
for (u32 addr : func.callers) for (u32 addr : func.callers)
{ {
ppu_function& caller = funcs[addr]; ppu_function& caller = fmap[addr];
if (!caller.toc) if (!caller.toc)
{ {
@ -847,7 +846,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
for (u32 addr : func.calls) for (u32 addr : func.calls)
{ {
ppu_function& callee = funcs[addr]; ppu_function& callee = fmap[addr];
if (!callee.toc) if (!callee.toc)
{ {
@ -1077,10 +1076,10 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
if (test(func.attr, ppu_attr::no_size)) if (test(func.attr, ppu_attr::no_size))
{ {
// Get next function // Get next function
const auto _next = funcs.lower_bound(func.blocks.crbegin()->first + 1); const auto _next = fmap.lower_bound(func.blocks.crbegin()->first + 1);
// Get limit // Get limit
const u32 func_end2 = _next == funcs.end() ? func_end : std::min<u32>(_next->first, func_end); const u32 func_end2 = _next == fmap.end() ? func_end : std::min<u32>(_next->first, func_end);
// Set more block entries // Set more block entries
std::for_each(addr_heap.lower_bound(func.addr), addr_heap.lower_bound(func_end2), add_block); std::for_each(addr_heap.lower_bound(func.addr), addr_heap.lower_bound(func_end2), add_block);
@ -1322,14 +1321,14 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
} }
// Function shrinkage, disabled (TODO: it's potentially dangerous but improvable) // Function shrinkage, disabled (TODO: it's potentially dangerous but improvable)
for (auto& _pair : funcs) for (auto& _pair : fmap)
{ {
auto& func = _pair.second; auto& func = _pair.second;
// Get next function addr // Get next function addr
const auto _next = funcs.lower_bound(_pair.first + 1); const auto _next = fmap.lower_bound(_pair.first + 1);
const u32 next = _next == funcs.end() ? end : _next->first; const u32 next = _next == fmap.end() ? end : _next->first;
// Just ensure that functions don't overlap // Just ensure that functions don't overlap
if (func.addr + func.size > next) if (func.addr + func.size > next)
@ -1415,7 +1414,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
{ {
lib_toc = *TOCs.begin(); lib_toc = *TOCs.begin();
for (auto&& pair : funcs) for (auto&& pair : fmap)
{ {
if (pair.second.toc == 0) if (pair.second.toc == 0)
{ {
@ -1425,16 +1424,12 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
} }
// Convert map to vector (destructive) // Convert map to vector (destructive)
std::vector<ppu_function> result; for (auto&& pair : fmap)
for (auto&& pair : funcs)
{ {
auto& func = pair.second; auto& func = pair.second;
LOG_TRACE(PPU, "Function %s (size=0x%x, toc=0x%x, attr %#x)", func.name, func.size, func.toc, func.attr); LOG_TRACE(PPU, "Function %s (size=0x%x, toc=0x%x, attr %#x)", func.name, func.size, func.toc, func.attr);
result.emplace_back(std::move(func)); funcs.emplace_back(std::move(func));
} }
LOG_NOTICE(PPU, "Function analysis: %zu functions (%zu enqueued)", result.size(), func_queue.size()); LOG_NOTICE(PPU, "Function analysis: %zu functions (%zu enqueued)", funcs.size(), func_queue.size());
return result;
} }

View file

@ -38,11 +38,9 @@ struct ppu_function
// PPU Relocation Information // PPU Relocation Information
struct ppu_reloc struct ppu_reloc
{ {
u32 addr;
u32 type; u32 type;
u32 off; u64 data;
u32 ptr;
u8 index_value;
u8 index_addr;
}; };
// PPU Segment Information // PPU Segment Information
@ -58,10 +56,14 @@ struct ppu_segment
struct ppu_module struct ppu_module
{ {
std::string name; std::string name;
std::vector<ppu_reloc> rels; std::string path;
std::vector<ppu_reloc> relocs;
std::vector<ppu_segment> segs; std::vector<ppu_segment> segs;
std::vector<ppu_segment> secs;
std::vector<ppu_function> funcs; std::vector<ppu_function> funcs;
std::vector<ppu_segment> sections;
void analyse(u32 lib_toc, u32 entry);
void validate(u32 reloc);
}; };
// Aux // Aux
@ -131,10 +133,6 @@ struct ppu_pattern_matrix
} }
}; };
extern void ppu_validate(const std::string& fname, const std::vector<ppu_function>& funcs, u32 reloc);
extern std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& segs, const std::vector<std::pair<u32, u32>>& secs, u32 lib_toc, u32 entry);
// PPU Instruction Type // PPU Instruction Type
struct ppu_itype struct ppu_itype
{ {

View file

@ -112,7 +112,7 @@ struct ppu_linkage_info
bool hle = false; bool hle = false;
u32 export_addr = 0; u32 export_addr = 0;
std::set<u32> imports; std::set<u32> imports;
std::set<u32> weak_imports; std::set<u32> frefss;
}; };
// FNID -> (export; [imports...]) // FNID -> (export; [imports...])
@ -342,9 +342,8 @@ static void ppu_initialize_modules(const std::shared_ptr<ppu_linkage_info>& link
} }
} }
// Link variables/weak imports, looks to be literally the same as vrefs // Resolve relocations for variable/function linkage.
// Don't use bool flag in VREFs, originally it didn't have it static void ppu_patch_refs(std::vector<ppu_reloc>* out_relocs, u32 fref, u32 faddr)
static void ppu_patch_refs(u32 fref, u32 faddr)
{ {
struct ref_t struct ref_t
{ {
@ -357,42 +356,52 @@ static void ppu_patch_refs(u32 fref, u32 faddr)
{ {
if (ref->addend) LOG_WARNING(LOADER, "**** REF(%u): Addend value(0x%x, 0x%x)", ref->type, ref->addr, ref->addend); if (ref->addend) LOG_WARNING(LOADER, "**** REF(%u): Addend value(0x%x, 0x%x)", ref->type, ref->addr, ref->addend);
const auto ref_import = vm::ptr<u32>::make(faddr + ref->addend); const u32 raddr = ref->addr;
const u32 rtype = ref->type;
const u32 rdata = faddr + ref->addend;
const auto addr = faddr + ref->addend; if (out_relocs)
{
// Register relocation with unpredictable target (data=0)
ppu_reloc _rel;
_rel.addr = raddr;
_rel.type = rtype;
_rel.data = 0;
out_relocs->emplace_back(_rel);
}
// OPs are probably similar to relocations // OPs must be similar to relocations
switch (u32 type = ref->type) switch (rtype)
{ {
case 0x1: case 1:
{ {
const u32 value = vm::_ref<u32>(ref->addr) = addr; const u32 value = vm::_ref<u32>(ref->addr) = rdata;
LOG_TRACE(LOADER, "**** REF(1): 0x%x <- 0x%x", ref->addr, value); LOG_TRACE(LOADER, "**** REF(1): 0x%x <- 0x%x", ref->addr, value);
break; break;
} }
case 0x4: case 4:
{ {
const u16 value = vm::_ref<u16>(ref->addr) = static_cast<u16>(addr); const u16 value = vm::_ref<u16>(ref->addr) = static_cast<u16>(rdata);
LOG_TRACE(LOADER, "**** REF(4): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr); LOG_TRACE(LOADER, "**** REF(4): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr);
break; break;
} }
case 0x6: case 6:
{ {
const u16 value = vm::_ref<u16>(ref->addr) = static_cast<u16>(addr >> 16) + (addr & 0x8000 ? 1 : 0); const u16 value = vm::_ref<u16>(ref->addr) = static_cast<u16>(rdata >> 16) + (rdata & 0x8000 ? 1 : 0);
LOG_TRACE(LOADER, "**** REF(6): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr); LOG_TRACE(LOADER, "**** REF(6): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr);
break; break;
} }
case 57: case 57:
{ {
const u16 value = vm::_ref<ppu_bf_t<be_t<u16>, 0, 14>>(ref->addr) = static_cast<u16>(addr) >> 2; const u16 value = vm::_ref<ppu_bf_t<be_t<u16>, 0, 14>>(ref->addr) = static_cast<u16>(rdata) >> 2;
LOG_TRACE(LOADER, "**** REF(57): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr); LOG_TRACE(LOADER, "**** REF(57): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr);
break; break;
} }
default: LOG_ERROR(LOADER, "**** REF(%u): Unknown/Illegal type (0x%x, 0x%x)", ref->type, ref->addr, ref->addend); default: LOG_ERROR(LOADER, "**** REF(%u): Unknown/Illegal type (0x%x, 0x%x)", rtype, raddr, ref->addend);
} }
} }
} }
@ -523,9 +532,9 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
//LOG_WARNING(LOADER, "Exported function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name); //LOG_WARNING(LOADER, "Exported function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name);
} }
for (const u32 weak_import_addr : flink.weak_imports) for (const u32 fref : flink.frefss)
{ {
ppu_patch_refs(weak_import_addr, faddr); ppu_patch_refs(nullptr, fref, faddr);
} }
} }
} }
@ -557,7 +566,7 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
// Fix imports // Fix imports
for (const auto vref : vlink.imports) for (const auto vref : vlink.imports)
{ {
ppu_patch_refs(vref, vaddr); ppu_patch_refs(nullptr, vref, vaddr);
//LOG_WARNING(LOADER, "Exported variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name); //LOG_WARNING(LOADER, "Exported variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name);
} }
} }
@ -569,7 +578,7 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
return result; return result;
} }
static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32 imports_start, u32 imports_end) static void ppu_load_imports(std::vector<ppu_reloc>& relocs, const std::shared_ptr<ppu_linkage_info>& link, u32 imports_start, u32 imports_end)
{ {
for (u32 addr = imports_start; addr < imports_end;) for (u32 addr = imports_start; addr < imports_end;)
{ {
@ -577,7 +586,7 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
const std::string module_name(lib.name.get_ptr()); const std::string module_name(lib.name.get_ptr());
LOG_NOTICE(LOADER, "** Imported module '%s' (0x%x, 0x%x)", module_name, lib.unk4, lib.unk5); LOG_NOTICE(LOADER, "** Imported module '%s' (ver=0x%x, attr=0x%x, 0x%x, 0x%x) [0x%x]", module_name, lib.version, lib.attributes, lib.unk4, lib.unk5, addr);
if (lib.num_tlsvar) if (lib.num_tlsvar)
{ {
@ -607,21 +616,17 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
flink.imports.emplace(faddr); flink.imports.emplace(faddr);
mlink.imported = true; mlink.imported = true;
// Link if available // Link address (special HLE function by default)
if (flink.export_addr) const u32 link_addr = flink.export_addr ? flink.export_addr : ppu_function_manager::addr;
{
vm::write32(faddr, flink.export_addr);
}
else
{
vm::write32(faddr, ppu_function_manager::addr);
}
//weak imports, possibly // Write import table
if (((lib.attributes & 0x2000) == 0x2000) && fnids[i + lib.num_func] != 0) //0x2000 seems to be correct flag vm::write32(faddr, link_addr);
// Patch refs if necessary (0x2000 seems to be correct flag indicating the presence of additional info)
if (const u32 frefs = (lib.attributes & 0x2000) ? +fnids[i + lib.num_func] : 0)
{ {
flink.weak_imports.emplace(fnids[i + lib.num_func]); flink.frefss.emplace(frefs);
ppu_patch_refs(fnids[i + lib.num_func], flink.export_addr); ppu_patch_refs(&relocs, frefs, link_addr);
} }
//LOG_WARNING(LOADER, "Imported function '%s' in module '%s' (0x%x)", ppu_get_function_name(module_name, fnid), module_name, faddr); //LOG_WARNING(LOADER, "Imported function '%s' in module '%s' (0x%x)", ppu_get_function_name(module_name, fnid), module_name, faddr);
@ -644,10 +649,7 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
mlink.imported = true; mlink.imported = true;
// Link if available // Link if available
if (vlink.export_addr) ppu_patch_refs(&relocs, vref, vlink.export_addr);
{
ppu_patch_refs(vref, vlink.export_addr);
}
//LOG_WARNING(LOADER, "Imported variable '%s' in module '%s' (0x%x)", ppu_get_variable_name(module_name, vnid), module_name, vlink.first); //LOG_WARNING(LOADER, "Imported variable '%s' in module '%s' (0x%x)", ppu_get_variable_name(module_name, vnid), module_name, vlink.first);
} }
@ -656,10 +658,13 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
} }
} }
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& name) std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& path)
{ {
std::vector<std::pair<u32, u32>> segments; // Create new PRX object
std::vector<std::pair<u32, u32>> sections; const auto prx = idm::make_ptr<lv2_obj, lv2_prx>();
// Access linkage information object
const auto link = fxm::get_always<ppu_linkage_info>();
for (const auto& prog : elf.progs) for (const auto& prog : elf.progs)
{ {
@ -693,7 +698,12 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
ppu_register_range(addr, mem_size); ppu_register_range(addr, mem_size);
} }
segments.emplace_back(std::make_pair(addr, mem_size)); ppu_segment _seg;
_seg.addr = addr;
_seg.size = mem_size;
_seg.type = p_type;
_seg.flags = prog.p_flags;
prx->segs.emplace_back(_seg);
} }
break; break;
@ -714,13 +724,18 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
if (s.sh_type == 1 && addr && size) // TODO: some sections with addr=0 are valid if (s.sh_type == 1 && addr && size) // TODO: some sections with addr=0 are valid
{ {
for (auto i = 0; i < segments.size(); i++) for (auto i = 0; i < prx->segs.size(); i++)
{ {
const u32 saddr = static_cast<u32>(elf.progs[i].p_vaddr); const u32 saddr = static_cast<u32>(elf.progs[i].p_vaddr);
if (addr >= saddr && addr < saddr + elf.progs[i].p_memsz) if (addr >= saddr && addr < saddr + elf.progs[i].p_memsz)
{ {
// "Relocate" section // "Relocate" section
sections.emplace_back(std::make_pair(addr - saddr + segments[i].first, size)); ppu_segment _sec;
_sec.addr = addr - saddr + prx->segs[i].addr;
_sec.size = size;
_sec.type = s.sh_type;
_sec.flags = s.sh_flags & 7;
prx->secs.emplace_back(_sec);
break; break;
} }
} }
@ -749,10 +764,13 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
{ {
const auto& rel = reinterpret_cast<const ppu_prx_relocation_info&>(prog.bin[i]); const auto& rel = reinterpret_cast<const ppu_prx_relocation_info&>(prog.bin[i]);
const u32 raddr = vm::cast(segments.at(rel.index_addr).first + rel.offset, HERE); ppu_reloc _rel;
const u64 rdata = segments.at(rel.index_value).first + rel.ptr.addr(); const u32 raddr = _rel.addr = vm::cast(prx->segs.at(rel.index_addr).addr + rel.offset, HERE);
const u32 rtype = _rel.type = rel.type;
const u64 rdata = _rel.data = prx->segs.at(rel.index_value).addr + rel.ptr.addr();
prx->relocs.emplace_back(_rel);
switch (const u32 type = rel.type) switch (rtype)
{ {
case 1: // R_PPC64_ADDR32 case 1: // R_PPC64_ADDR32
{ {
@ -817,7 +835,12 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
break; break;
} }
default: LOG_ERROR(LOADER, "**** RELOCATION(%u): Illegal/Unknown type! (addr=0x%x; 0x%llx)", type, raddr, rdata); default: LOG_ERROR(LOADER, "**** RELOCATION(%u): Illegal/Unknown type! (addr=0x%x; 0x%llx)", rtype, raddr, rdata);
}
if (rdata == 0)
{
LOG_TODO(LOADER, "**** RELOCATION(%u): 0x%x <- (zero-based value)", rtype, raddr);
} }
} }
@ -826,12 +849,6 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
} }
} }
// Access linkage information object
const auto link = fxm::get_always<ppu_linkage_info>();
// Create new PRX object
auto prx = idm::make_ptr<lv2_obj, lv2_prx>();
if (!elf.progs.empty() && elf.progs[0].p_paddr) if (!elf.progs.empty() && elf.progs[0].p_paddr)
{ {
struct ppu_prx_library_info struct ppu_prx_library_info
@ -847,16 +864,16 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
}; };
// Access library information (TODO) // Access library information (TODO)
const auto& lib_info = vm::cptr<ppu_prx_library_info>(vm::cast(segments[0].first + elf.progs[0].p_paddr - elf.progs[0].p_offset, HERE)); const auto& lib_info = vm::cptr<ppu_prx_library_info>(vm::cast(prx->segs[0].addr + elf.progs[0].p_paddr - elf.progs[0].p_offset, HERE));
const auto& lib_name = std::string(lib_info->name); const auto& lib_name = std::string(lib_info->name);
LOG_WARNING(LOADER, "Library %s (rtoc=0x%x):", lib_name, lib_info->toc); LOG_WARNING(LOADER, "Library %s (rtoc=0x%x):", lib_name, lib_info->toc);
prx->specials = ppu_load_exports(link, lib_info->exports_start, lib_info->exports_end); prx->specials = ppu_load_exports(link, lib_info->exports_start, lib_info->exports_end);
ppu_load_imports(link, lib_info->imports_start, lib_info->imports_end); ppu_load_imports(prx->relocs, link, lib_info->imports_start, lib_info->imports_end);
prx->funcs = ppu_analyse(segments, sections, lib_info->toc, 0); prx->analyse(lib_info->toc, 0);
} }
else else
{ {
@ -868,7 +885,8 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
prx->exit.set(prx->specials[0x3ab9a95e]); prx->exit.set(prx->specials[0x3ab9a95e]);
prx->prologue.set(prx->specials[0x0D10FD3F]); prx->prologue.set(prx->specials[0x0D10FD3F]);
prx->epilogue.set(prx->specials[0x330F7005]); prx->epilogue.set(prx->specials[0x330F7005]);
prx->name = name; prx->name = path.substr(path.find_last_of('/') + 1);
prx->path = path;
return prx; return prx;
} }
@ -879,15 +897,12 @@ void ppu_load_exec(const ppu_exec_object& elf)
LOG_TODO(LOADER, "'Hook static functions' option deactivated"); LOG_TODO(LOADER, "'Hook static functions' option deactivated");
} }
// Set for delayed initialization in ppu_initialize()
const auto _main = fxm::make<ppu_module>();
// Access linkage information object // Access linkage information object
const auto link = fxm::get_always<ppu_linkage_info>(); const auto link = fxm::get_always<ppu_linkage_info>();
// Segment info
std::vector<std::pair<u32, u32>> segments;
// Section info (optional)
std::vector<std::pair<u32, u32>> sections;
// TLS information // TLS information
u32 tls_vaddr = 0; u32 tls_vaddr = 0;
u32 tls_fsize = 0; u32 tls_fsize = 0;
@ -904,10 +919,13 @@ void ppu_load_exec(const ppu_exec_object& elf)
{ {
LOG_NOTICE(LOADER, "** Segment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, flags=0x%x", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_flags); LOG_NOTICE(LOADER, "** Segment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, flags=0x%x", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_flags);
const u32 addr = vm::cast(prog.p_vaddr, HERE); ppu_segment _seg;
const u32 size = ::narrow<u32>(prog.p_memsz, "p_memsz" HERE); const u32 addr = _seg.addr = vm::cast(prog.p_vaddr, HERE);
const u32 size = _seg.size = ::narrow<u32>(prog.p_memsz, "p_memsz" HERE);
const u32 type = _seg.type = prog.p_type;
const u32 flag = _seg.flags = prog.p_flags;
if (prog.p_type == 0x1 /* LOAD */ && prog.p_memsz) if (type == 0x1 /* LOAD */ && prog.p_memsz)
{ {
if (prog.bin.size() > size || prog.bin.size() != prog.p_filesz) if (prog.bin.size() > size || prog.bin.size() != prog.p_filesz)
fmt::throw_exception("Invalid binary size (0x%llx, memsz=0x%x)", prog.bin.size(), size); fmt::throw_exception("Invalid binary size (0x%llx, memsz=0x%x)", prog.bin.size(), size);
@ -924,7 +942,8 @@ void ppu_load_exec(const ppu_exec_object& elf)
ppu_register_range(addr, size); ppu_register_range(addr, size);
} }
segments.emplace_back(std::make_pair(addr, size)); // Store only LOAD segments (TODO)
_main->segs.emplace_back(_seg);
} }
} }
@ -933,12 +952,15 @@ void ppu_load_exec(const ppu_exec_object& elf)
{ {
LOG_NOTICE(LOADER, "** Section: sh_type=0x%x, addr=0x%llx, size=0x%llx, flags=0x%x", s.sh_type, s.sh_addr, s.sh_size, s.sh_flags); LOG_NOTICE(LOADER, "** Section: sh_type=0x%x, addr=0x%llx, size=0x%llx, flags=0x%x", s.sh_type, s.sh_addr, s.sh_size, s.sh_flags);
const u32 addr = vm::cast(s.sh_addr); ppu_segment _sec;
const u32 size = vm::cast(s.sh_size); const u32 addr = _sec.addr = vm::cast(s.sh_addr);
const u32 size = _sec.size = vm::cast(s.sh_size);
const u32 type = _sec.type = s.sh_type;
const u32 flag = _sec.flags = s.sh_flags & 7;
if (s.sh_type == 1 && addr && size) if (s.sh_type == 1 && addr && size)
{ {
sections.emplace_back(std::make_pair(addr, size)); _main->secs.emplace_back(_sec);
} }
} }
@ -1038,7 +1060,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
} }
ppu_load_exports(link, proc_prx_param.libent_start, proc_prx_param.libent_end); ppu_load_exports(link, proc_prx_param.libent_start, proc_prx_param.libent_end);
ppu_load_imports(link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); ppu_load_imports(_main->relocs, link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
} }
break; break;
} }
@ -1209,7 +1231,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
{ {
LOG_WARNING(LOADER, "Loading library: %s", name); LOG_WARNING(LOADER, "Loading library: %s", name);
auto prx = ppu_load_prx(obj, name); auto prx = ppu_load_prx(obj, lle_dir + name);
if (prx->funcs.empty()) if (prx->funcs.empty())
{ {
@ -1218,7 +1240,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
else else
{ {
// TODO: fix arguments // TODO: fix arguments
ppu_validate(lle_dir + name, prx->funcs, prx->funcs[0].addr); prx->validate(prx->funcs[0].addr);
} }
loaded_modules.emplace_back(std::move(prx)); loaded_modules.emplace_back(std::move(prx));
@ -1230,17 +1252,15 @@ void ppu_load_exec(const ppu_exec_object& elf)
} }
} }
{ // Set path (TODO)
_main->name = "";
_main->path = vfs::get(Emu.GetPath());
// Analyse executable (TODO) // Analyse executable (TODO)
ppu_module _main; _main->analyse(0, static_cast<u32>(elf.header.e_entry));
_main.funcs = ppu_analyse(segments, sections, 0, elf.header.e_entry);
// Validate analyser results (not required) // Validate analyser results (not required)
ppu_validate(vfs::get(Emu.GetPath()), _main.funcs, 0); _main->validate(0);
// Set for delayed initialization in ppu_initialize()
fxm::make<ppu_module>(std::move(_main));
}
// Set SDK version // Set SDK version
g_ps3_sdk_version = sdk_version; g_ps3_sdk_version = sdk_version;
@ -1262,7 +1282,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
*argv++ = arg_addr; *argv++ = arg_addr;
} }
argv -= args.size(); argv -= ::size32(args);
// Initialize main thread // Initialize main thread
auto ppu = idm::make_ptr<ppu_thread>("main_thread", primary_prio, primary_stacksize); auto ppu = idm::make_ptr<ppu_thread>("main_thread", primary_prio, primary_stacksize);

View file

@ -982,24 +982,31 @@ extern void ppu_initialize(const ppu_module& info)
// Worker threads // Worker threads
std::vector<std::thread> jthreads; std::vector<std::thread> jthreads;
// Global variables (pointers) to initialize // Global variables to initialize
std::vector<std::pair<std::string, void*>> globals; std::vector<std::pair<std::string, u64>> globals;
// Split module into fragments <= 1 MiB // Split module into fragments <= 1 MiB
std::size_t fpos = 0; std::size_t fpos = 0;
// Copy module information // Difference between function name and current location
ppu_module part = info; const u32 reloc = info.name.empty() ? 0 : info.segs.at(0).addr;
while (fpos < info.funcs.size()) while (fpos < info.funcs.size())
{ {
// First function in current module part
const auto fstart = fpos; const auto fstart = fpos;
std::size_t bsize = 0; // Copy module information (TODO: optimize)
ppu_module part = info;
part.funcs.clear(); part.funcs.clear();
part.funcs.reserve(16000); part.funcs.reserve(16000);
// Unique suffix for each module part
const u32 suffix = info.funcs.at(fstart).addr - reloc;
// Overall block size in bytes
std::size_t bsize = 0;
while (fpos < info.funcs.size()) while (fpos < info.funcs.size())
{ {
auto& func = info.funcs[fpos]; auto& func = info.funcs[fpos];
@ -1018,28 +1025,28 @@ extern void ppu_initialize(const ppu_module& info)
entry.addr = block.first; entry.addr = block.first;
entry.size = block.second; entry.size = block.second;
entry.toc = func.toc; entry.toc = func.toc;
fmt::append(entry.name, "__0x%x", block.first); fmt::append(entry.name, "__0x%x", block.first - reloc);
part.funcs.emplace_back(std::move(entry)); part.funcs.emplace_back(std::move(entry));
} }
fpos++; fpos++;
} }
part.name.clear(); // Version, module name and hash: vX-liblv2.sprx-0123456789ABCDEF.obj
std::string obj_name = "v2";
if (info.name.size()) if (info.name.size())
{ {
part.name += '-'; obj_name += '-';
part.name += info.name; obj_name += info.name;
} }
if (fstart || fpos < info.funcs.size()) if (fstart || fpos < info.funcs.size())
{ {
fmt::append(part.name, "+%06X", info.funcs.at(fstart).addr); fmt::append(obj_name, "+%06X", suffix);
} }
// Compute module hash // Compute module hash
std::string obj_name;
{ {
sha1_context ctx; sha1_context ctx;
u8 output[20]; u8 output[20];
@ -1052,28 +1059,32 @@ extern void ppu_initialize(const ppu_module& info)
continue; continue;
} }
const be_t<u32> addr = func.addr; const be_t<u32> addr = func.addr - reloc;
const be_t<u32> size = func.size; const be_t<u32> size = func.size;
sha1_update(&ctx, reinterpret_cast<const u8*>(&addr), sizeof(addr)); sha1_update(&ctx, reinterpret_cast<const u8*>(&addr), sizeof(addr));
sha1_update(&ctx, reinterpret_cast<const u8*>(&size), sizeof(size)); sha1_update(&ctx, reinterpret_cast<const u8*>(&size), sizeof(size));
for (const auto& block : func.blocks) for (const auto& block : func.blocks)
{ {
if (block.second == 0) if (block.second == 0 || reloc)
{ {
continue; continue;
} }
// TODO: relocations must be taken into account (TODO)
sha1_update(&ctx, vm::ps3::_ptr<const u8>(block.first), block.second); sha1_update(&ctx, vm::ps3::_ptr<const u8>(block.first), block.second);
} }
if (reloc)
{
continue;
}
sha1_update(&ctx, vm::ps3::_ptr<const u8>(func.addr), func.size); sha1_update(&ctx, vm::ps3::_ptr<const u8>(func.addr), func.size);
} }
sha1_finish(&ctx, output); sha1_finish(&ctx, output);
fmt::append(obj_name, "-%016X-%s.obj", reinterpret_cast<be_t<u64>&>(output), jit.cpu());
// Version, module name and hash: vX-liblv2.sprx-0123456789ABCDEF.obj
fmt::append(obj_name, "v2%s-%016X-%s.obj", part.name, reinterpret_cast<be_t<u64>&>(output), jit.cpu());
} }
if (Emu.IsStopped()) if (Emu.IsStopped())
@ -1081,19 +1092,49 @@ extern void ppu_initialize(const ppu_module& info)
break; break;
} }
globals.emplace_back(fmt::format("__mptr%x", part.funcs[0].addr), vm::g_base_addr); globals.emplace_back(fmt::format("__mptr%x", suffix), (u64)vm::g_base_addr);
globals.emplace_back(fmt::format("__cptr%x", part.funcs[0].addr), vm::g_exec_addr); globals.emplace_back(fmt::format("__cptr%x", suffix), (u64)vm::g_exec_addr);
// Initialize segments for relocations
for (u32 i = 0; i < info.segs.size(); i++)
{
globals.emplace_back(fmt::format("__seg%u_%x", i, suffix), info.segs[i].addr);
}
// Get cache path for this executable
std::string cache_path;
if (info.name.empty())
{
cache_path = Emu.GetCachePath();
}
else
{
cache_path = vfs::get("/dev_flash/");
if (info.path.compare(0, cache_path.size(), cache_path) == 0)
{
// Remove prefix for dev_flash files
cache_path.clear();
}
else
{
cache_path = Emu.GetTitleID();
}
cache_path = fs::get_data_dir(cache_path, info.path);
}
// Check object file // Check object file
if (fs::is_file(Emu.GetCachePath() + obj_name)) if (fs::is_file(cache_path + obj_name))
{ {
semaphore_lock lock(jmutex); semaphore_lock lock(jmutex);
ppu_initialize2(jit, part, Emu.GetCachePath(), obj_name); ppu_initialize2(jit, part, cache_path, obj_name);
continue; continue;
} }
// Create worker thread for compilation // Create worker thread for compilation
jthreads.emplace_back([&jit, &jmutex, &jcores, obj_name = obj_name, part = std::move(part)]() jthreads.emplace_back([&jit, &jmutex, &jcores, obj_name = obj_name, part = std::move(part), cache_path = std::move(cache_path)]()
{ {
// Set low priority // Set low priority
thread_ctrl::set_native_priority(-1); thread_ctrl::set_native_priority(-1);
@ -1109,17 +1150,17 @@ extern void ppu_initialize(const ppu_module& info)
// Use another JIT instance // Use another JIT instance
jit_compiler jit2({}, g_cfg.core.llvm_cpu); jit_compiler jit2({}, g_cfg.core.llvm_cpu);
ppu_initialize2(jit2, part, Emu.GetCachePath(), obj_name); ppu_initialize2(jit2, part, cache_path, obj_name);
} }
if (Emu.IsStopped()) if (Emu.IsStopped() || !fs::is_file(cache_path + obj_name))
{ {
return; return;
} }
// Proceed with original JIT instance // Proceed with original JIT instance
semaphore_lock lock(jmutex); semaphore_lock lock(jmutex);
ppu_initialize2(jit, part, Emu.GetCachePath(), obj_name); ppu_initialize2(jit, part, cache_path, obj_name);
}); });
} }
@ -1145,7 +1186,7 @@ extern void ppu_initialize(const ppu_module& info)
{ {
if (block.second) if (block.second)
{ {
ppu_ref(block.first) = ::narrow<u32>(jit.get(fmt::format("__0x%x", block.first))); ppu_ref(block.first) = ::narrow<u32>(jit.get(fmt::format("__0x%x", block.first - reloc)));
} }
} }
} }
@ -1155,7 +1196,7 @@ extern void ppu_initialize(const ppu_module& info)
{ {
if (u64 addr = jit.get(var.first)) if (u64 addr = jit.get(var.first))
{ {
*reinterpret_cast<void**>(addr) = var.second; *reinterpret_cast<u64*>(addr) = var.second;
} }
} }
#endif #endif
@ -1256,11 +1297,17 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co
}); });
// Translate // Translate
const auto func = translator.Translate(module_part.funcs[fi]); if (const auto func = translator.Translate(module_part.funcs[fi]))
{
// Run optimization passes // Run optimization passes
pm.run(*func); pm.run(*func);
} }
else
{
Emu.Pause();
return;
}
}
} }
legacy::PassManager mpm; legacy::PassManager mpm;

View file

@ -1,4 +1,4 @@
#ifdef LLVM_AVAILABLE #ifdef LLVM_AVAILABLE
#include "PPUTranslator.h" #include "PPUTranslator.h"
#include "PPUThread.h" #include "PPUThread.h"
@ -18,8 +18,11 @@ PPUTranslator::PPUTranslator(LLVMContext& context, Module* module, const ppu_mod
, m_info(info) , m_info(info)
, m_pure_attr(AttributeSet::get(m_context, AttributeSet::FunctionIndex, {Attribute::NoUnwind, Attribute::ReadNone})) , m_pure_attr(AttributeSet::get(m_context, AttributeSet::FunctionIndex, {Attribute::NoUnwind, Attribute::ReadNone}))
{ {
// There is no weak linkage on JIT, so let's create variables with different names for each module part
const u32 gsuffix = m_info.name.empty() ? info.funcs[0].addr : info.funcs[0].addr - m_info.segs[0].addr;
// Memory base // Memory base
m_base = new GlobalVariable(*module, ArrayType::get(GetType<char>(), 0x100000000)->getPointerTo(), true, GlobalValue::ExternalLinkage, 0, fmt::format("__mptr%x", info.funcs[0].addr)); m_base = new GlobalVariable(*module, ArrayType::get(GetType<char>(), 0x100000000)->getPointerTo(), true, GlobalValue::ExternalLinkage, 0, fmt::format("__mptr%x", gsuffix));
m_base->setInitializer(ConstantPointerNull::get(cast<PointerType>(m_base->getType()->getPointerElementType()))); m_base->setInitializer(ConstantPointerNull::get(cast<PointerType>(m_base->getType()->getPointerElementType())));
m_base->setExternallyInitialized(true); m_base->setExternallyInitialized(true);
@ -44,7 +47,7 @@ PPUTranslator::PPUTranslator(LLVMContext& context, Module* module, const ppu_mod
m_thread_type = StructType::create(m_context, thread_struct, "context_t"); m_thread_type = StructType::create(m_context, thread_struct, "context_t");
// Callable // Callable
m_call = new GlobalVariable(*module, ArrayType::get(GetType<u32>(), 0x40000000)->getPointerTo(), true, GlobalValue::ExternalLinkage, 0, fmt::format("__cptr%x", info.funcs[0].addr)); m_call = new GlobalVariable(*module, ArrayType::get(GetType<u32>(), 0x40000000)->getPointerTo(), true, GlobalValue::ExternalLinkage, 0, fmt::format("__cptr%x", gsuffix));
m_call->setInitializer(ConstantPointerNull::get(cast<PointerType>(m_call->getType()->getPointerElementType()))); m_call->setInitializer(ConstantPointerNull::get(cast<PointerType>(m_call->getType()->getPointerElementType())));
m_call->setExternallyInitialized(true); m_call->setExternallyInitialized(true);
@ -55,6 +58,56 @@ PPUTranslator::PPUTranslator(LLVMContext& context, Module* module, const ppu_mod
// Metadata for branch weights // Metadata for branch weights
m_md_likely = MDTuple::get(m_context, {md_name, md_high, md_low}); m_md_likely = MDTuple::get(m_context, {md_name, md_high, md_low});
m_md_unlikely = MDTuple::get(m_context, {md_name, md_low, md_high}); m_md_unlikely = MDTuple::get(m_context, {md_name, md_low, md_high});
// Create segment variables
for (const auto& seg : m_info.segs)
{
auto gv = new GlobalVariable(*module, GetType<u64>(), true, GlobalValue::ExternalLinkage, 0, fmt::format("__seg%u_%x", m_segs.size(), gsuffix));
gv->setInitializer(ConstantInt::get(GetType<u64>(), seg.addr));
gv->setExternallyInitialized(true);
m_segs.emplace_back(gv);
}
// Sort relevant relocations (TODO)
const auto caddr = m_info.segs[0].addr;
const auto cend = caddr + m_info.segs[0].size;
for (const auto& rel : m_info.relocs)
{
if (rel.addr >= caddr && rel.addr < cend)
{
// Check relocation type
switch (rel.type)
{
case 20:
case 22:
case 38:
case 43:
case 44:
case 45:
case 46:
case 51:
case 68:
case 73:
case 78:
{
LOG_ERROR(PPU, "64-bit relocation at 0x%x (%u)", rel.addr, rel.type);
continue;
}
}
// Align relocation address (TODO)
if (!m_relocs.emplace(rel.addr & ~3, &rel).second)
{
LOG_ERROR(PPU, "Relocation repeated at 0x%x (%u)", rel.addr, rel.type);
}
}
}
if (!m_info.name.empty())
{
m_reloc = &m_info.segs[0];
}
} }
PPUTranslator::~PPUTranslator() PPUTranslator::~PPUTranslator()
@ -69,8 +122,6 @@ Type* PPUTranslator::GetContextType()
Function* PPUTranslator::Translate(const ppu_function& info) Function* PPUTranslator::Translate(const ppu_function& info)
{ {
m_function = m_module->getFunction(info.name); m_function = m_module->getFunction(info.name);
m_start_addr = info.addr;
m_end_addr = info.addr + info.size;
std::fill(std::begin(m_globals), std::end(m_globals), nullptr); std::fill(std::begin(m_globals), std::end(m_globals), nullptr);
std::fill(std::begin(m_locals), std::end(m_locals), nullptr); std::fill(std::begin(m_locals), std::end(m_locals), nullptr);
@ -78,6 +129,10 @@ Function* PPUTranslator::Translate(const ppu_function& info)
IRBuilder<> irb(m_entry = BasicBlock::Create(m_context, "__entry", m_function)); IRBuilder<> irb(m_entry = BasicBlock::Create(m_context, "__entry", m_function));
m_ir = &irb; m_ir = &irb;
// Instruction address is (m_addr + base)
const u64 base = m_reloc ? m_reloc->addr : 0;
m_addr = info.addr - base;
m_thread = &*m_function->getArgumentList().begin(); m_thread = &*m_function->getArgumentList().begin();
m_base_loaded = m_ir->CreateLoad(m_base); m_base_loaded = m_ir->CreateLoad(m_base);
@ -90,7 +145,7 @@ Function* PPUTranslator::Translate(const ppu_function& info)
// Create tail call to the check function // Create tail call to the check function
m_ir->SetInsertPoint(vcheck); m_ir->SetInsertPoint(vcheck);
Call(GetType<void>(), "__check", m_thread, m_ir->getInt64(m_start_addr))->setTailCallKind(llvm::CallInst::TCK_Tail); Call(GetType<void>(), "__check", m_thread, GetAddr())->setTailCallKind(llvm::CallInst::TCK_Tail);
m_ir->CreateRetVoid(); m_ir->CreateRetVoid();
m_ir->SetInsertPoint(m_body); m_ir->SetInsertPoint(m_body);
@ -104,28 +159,58 @@ Function* PPUTranslator::Translate(const ppu_function& info)
} }
// Process the instructions // Process the instructions
for (m_current_addr = block.first; m_current_addr < block.first + block.second; m_current_addr += 4) for (m_addr = block.first - base; m_addr < block.first + block.second - base; m_addr += 4)
{ {
if (m_body->getTerminator()) if (m_body->getTerminator())
{ {
break; break;
} }
const u32 op = vm::ps3::read32(vm::cast(m_current_addr)); // Find the relocation at current address
const auto rel_found = m_relocs.find(m_addr + base);
if (rel_found != m_relocs.end())
{
m_rel = rel_found->second;
}
else
{
m_rel = nullptr;
}
const u32 op = vm::ps3::read32(vm::cast(m_addr + base));
(this->*(s_ppu_decoder.decode(op)))({op}); (this->*(s_ppu_decoder.decode(op)))({op});
if (m_rel)
{
// This is very bæd
LOG_ERROR(PPU, "LLVM: [0x%x] Unsupported relocation(%u) in '%s'. Please report.", rel_found->first, m_rel->type, m_info.name);
return nullptr;
}
} }
// Finalize current block if necessary (create branch to the next address) // Finalize current block if necessary (create branch to the next address)
if (!m_body->getTerminator()) if (!m_body->getTerminator())
{ {
FlushRegisters(); FlushRegisters();
CallFunction(m_current_addr); CallFunction(m_addr);
} }
} }
return m_function; return m_function;
} }
Value* PPUTranslator::GetAddr(u64 _add)
{
if (m_reloc)
{
// Load segment address from global variable, compute actual instruction address
return m_ir->CreateAdd(m_ir->getInt64(m_addr + _add), m_ir->CreateLoad(m_segs[m_reloc - m_info.segs.data()]));
}
return m_ir->getInt64(m_addr + _add);
}
Type* PPUTranslator::ScaleType(Type* type, s32 pow2) Type* PPUTranslator::ScaleType(Type* type, s32 pow2)
{ {
verify(HERE), (type->getScalarType()->isIntegerTy()); verify(HERE), (type->getScalarType()->isIntegerTy());
@ -159,9 +244,9 @@ void PPUTranslator::CallFunction(u64 target, Value* indirect)
if (!indirect) if (!indirect)
{ {
if (target < 0x10000 || target >= -0x10000) if ((!m_reloc && target < 0x10000) || target >= -0x10000)
{ {
Trap(m_current_addr); Trap();
return; return;
} }
@ -414,11 +499,11 @@ void PPUTranslator::UseCondition(MDNode* hint, Value* cond)
if (cond) if (cond)
{ {
const auto local = BasicBlock::Create(m_context, fmt::format("loc_%llx.cond", m_current_addr), m_function); const auto local = BasicBlock::Create(m_context, "__cond", m_function);
const auto next = BasicBlock::Create(m_context, fmt::format("loc_%llx.next", m_current_addr), m_function); const auto next = BasicBlock::Create(m_context, "__next", m_function);
m_ir->CreateCondBr(cond, local, next, hint); m_ir->CreateCondBr(cond, local, next, hint);
m_ir->SetInsertPoint(next); m_ir->SetInsertPoint(next);
CallFunction(m_current_addr + 4); CallFunction(m_addr + 4);
m_ir->SetInsertPoint(local); m_ir->SetInsertPoint(local);
} }
} }
@ -462,7 +547,7 @@ void PPUTranslator::WriteMemory(Value* addr, Value* value, bool is_be, u32 align
void PPUTranslator::CompilationError(const std::string& error) void PPUTranslator::CompilationError(const std::string& error)
{ {
LOG_ERROR(PPU, "[0x%08llx] 0x%08llx: Error: %s", m_start_addr, m_current_addr, error); LOG_ERROR(PPU, "LLVM: [0x%08x] Error: %s", m_addr + (m_reloc ? m_reloc->addr : 0), error);
} }
@ -1535,13 +1620,13 @@ void PPUTranslator::VXOR(ppu_opcode_t op)
void PPUTranslator::TDI(ppu_opcode_t op) void PPUTranslator::TDI(ppu_opcode_t op)
{ {
UseCondition(m_md_unlikely, CheckTrapCondition(op.bo, GetGpr(op.ra), m_ir->getInt64(op.simm16))); UseCondition(m_md_unlikely, CheckTrapCondition(op.bo, GetGpr(op.ra), m_ir->getInt64(op.simm16)));
Trap(m_current_addr); Trap();
} }
void PPUTranslator::TWI(ppu_opcode_t op) void PPUTranslator::TWI(ppu_opcode_t op)
{ {
UseCondition(m_md_unlikely, CheckTrapCondition(op.bo, GetGpr(op.ra, 32), m_ir->getInt32(op.simm16))); UseCondition(m_md_unlikely, CheckTrapCondition(op.bo, GetGpr(op.ra, 32), m_ir->getInt32(op.simm16)));
Trap(m_current_addr); Trap();
} }
void PPUTranslator::MULLI(ppu_opcode_t op) void PPUTranslator::MULLI(ppu_opcode_t op)
@ -1570,8 +1655,15 @@ void PPUTranslator::CMPI(ppu_opcode_t op)
void PPUTranslator::ADDIC(ppu_opcode_t op) void PPUTranslator::ADDIC(ppu_opcode_t op)
{ {
Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
const auto a = GetGpr(op.ra); const auto a = GetGpr(op.ra);
const auto imm = m_ir->getInt64(op.simm16);
const auto result = m_ir->CreateAdd(a, imm); const auto result = m_ir->CreateAdd(a, imm);
SetGpr(op.rd, result); SetGpr(op.rd, result);
SetCarry(m_ir->CreateICmpULT(result, imm)); SetCarry(m_ir->CreateICmpULT(result, imm));
@ -1580,25 +1672,44 @@ void PPUTranslator::ADDIC(ppu_opcode_t op)
void PPUTranslator::ADDI(ppu_opcode_t op) void PPUTranslator::ADDI(ppu_opcode_t op)
{ {
const auto imm = m_ir->getInt64(op.simm16); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
SetGpr(op.rd, op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm); SetGpr(op.rd, op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm);
} }
void PPUTranslator::ADDIS(ppu_opcode_t op) void PPUTranslator::ADDIS(ppu_opcode_t op)
{ {
const auto imm = m_ir->getInt64(op.simm16 << 16); Value* imm = m_ir->getInt64(op.simm16 << 16);
if (m_rel && m_rel->type == 6)
{
imm = m_ir->CreateShl(SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>()), 16);
m_rel = nullptr;
}
SetGpr(op.rd, op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm); SetGpr(op.rd, op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm);
} }
void PPUTranslator::BC(ppu_opcode_t op) void PPUTranslator::BC(ppu_opcode_t op)
{ {
const u64 target = (op.aa ? 0 : m_current_addr) + op.bt14; const u64 target = (op.aa ? 0 : m_addr) + op.bt14;
if (op.aa && m_reloc)
{
CompilationError("Branch with absolute address");
}
UseCondition(CheckBranchProbability(op.bo), CheckBranchCondition(op.bo, op.bi)); UseCondition(CheckBranchProbability(op.bo), CheckBranchCondition(op.bo, op.bi));
if (op.lk) if (op.lk)
{ {
m_ir->CreateStore(m_ir->getInt64(m_current_addr + 4), m_ir->CreateStructGEP(nullptr, m_thread, &m_lr - m_locals)); m_ir->CreateStore(GetAddr(+4), m_ir->CreateStructGEP(nullptr, m_thread, &m_lr - m_locals));
} }
CallFunction(target); CallFunction(target);
@ -1612,7 +1723,7 @@ void PPUTranslator::SC(ppu_opcode_t op)
} }
const auto num = GetGpr(11); const auto num = GetGpr(11);
RegStore(m_ir->getInt32(m_current_addr), m_cia); RegStore(Trunc(GetAddr()), m_cia);
FlushRegisters(); FlushRegisters();
if (!op.lev && isa<ConstantInt>(num)) if (!op.lev && isa<ConstantInt>(num))
@ -1635,11 +1746,16 @@ void PPUTranslator::SC(ppu_opcode_t op)
void PPUTranslator::B(ppu_opcode_t op) void PPUTranslator::B(ppu_opcode_t op)
{ {
const u64 target = (op.aa ? 0 : m_current_addr) + op.bt24; const u64 target = (op.aa ? 0 : m_addr) + op.bt24;
if (op.aa && m_reloc)
{
CompilationError("Branch with absolute address");
}
if (op.lk) if (op.lk)
{ {
RegStore(m_ir->getInt64(m_current_addr + 4), m_lr); RegStore(GetAddr(+4), m_lr);
} }
FlushRegisters(); FlushRegisters();
@ -1663,7 +1779,7 @@ void PPUTranslator::BCLR(ppu_opcode_t op)
if (op.lk) if (op.lk)
{ {
m_ir->CreateStore(m_ir->getInt64(m_current_addr + 4), m_ir->CreateStructGEP(nullptr, m_thread, &m_lr - m_locals)); m_ir->CreateStore(GetAddr(+4), m_ir->CreateStructGEP(nullptr, m_thread, &m_lr - m_locals));
} }
CallFunction(0, target); CallFunction(0, target);
@ -1726,7 +1842,7 @@ void PPUTranslator::BCCTR(ppu_opcode_t op)
if (op.lk) if (op.lk)
{ {
m_ir->CreateStore(m_ir->getInt64(m_current_addr + 4), m_ir->CreateStructGEP(nullptr, m_thread, &m_lr - m_locals)); m_ir->CreateStore(GetAddr(+4), m_ir->CreateStructGEP(nullptr, m_thread, &m_lr - m_locals));
} }
CallFunction(0, target); CallFunction(0, target);
@ -1870,12 +1986,34 @@ void PPUTranslator::RLWNM(ppu_opcode_t op)
void PPUTranslator::ORI(ppu_opcode_t op) void PPUTranslator::ORI(ppu_opcode_t op)
{ {
SetGpr(op.ra, m_ir->CreateOr(GetGpr(op.rs), op.uimm16)); Value* imm = m_ir->getInt64(op.uimm16);
if (m_rel && m_rel->type == 4)
{
imm = ZExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
SetGpr(op.ra, m_ir->CreateOr(GetGpr(op.rs), imm));
} }
void PPUTranslator::ORIS(ppu_opcode_t op) void PPUTranslator::ORIS(ppu_opcode_t op)
{ {
SetGpr(op.ra, m_ir->CreateOr(GetGpr(op.rs), op.uimm16 << 16)); Value* imm = m_ir->getInt64(op.uimm16 << 16);
if (m_rel && m_rel->type == 5)
{
imm = m_ir->CreateShl(ZExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>()), 16);
m_rel = nullptr;
}
if (m_rel && m_rel->type == 6)
{
imm = m_ir->CreateShl(ZExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>()), 16);
m_rel = nullptr;
}
SetGpr(op.ra, m_ir->CreateOr(GetGpr(op.rs), imm));
} }
void PPUTranslator::XORI(ppu_opcode_t op) void PPUTranslator::XORI(ppu_opcode_t op)
@ -2050,7 +2188,7 @@ void PPUTranslator::TW(ppu_opcode_t op)
FlushRegisters(); FlushRegisters();
} }
Trap(m_current_addr); Trap();
} }
void PPUTranslator::LVSL(ppu_opcode_t op) void PPUTranslator::LVSL(ppu_opcode_t op)
@ -2266,7 +2404,7 @@ void PPUTranslator::ANDC(ppu_opcode_t op)
void PPUTranslator::TD(ppu_opcode_t op) void PPUTranslator::TD(ppu_opcode_t op)
{ {
UseCondition(m_md_unlikely, CheckTrapCondition(op.bo, GetGpr(op.ra), GetGpr(op.rb))); UseCondition(m_md_unlikely, CheckTrapCondition(op.bo, GetGpr(op.ra), GetGpr(op.rb)));
Trap(m_current_addr); Trap();
} }
void PPUTranslator::LVEWX(ppu_opcode_t op) void PPUTranslator::LVEWX(ppu_opcode_t op)
@ -3136,84 +3274,196 @@ void PPUTranslator::DCBZ(ppu_opcode_t op)
void PPUTranslator::LWZ(ppu_opcode_t op) void PPUTranslator::LWZ(ppu_opcode_t op)
{ {
SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetType<u32>())); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType<u32>()));
} }
void PPUTranslator::LWZU(ppu_opcode_t op) void PPUTranslator::LWZU(ppu_opcode_t op)
{ {
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm);
SetGpr(op.rd, ReadMemory(addr, GetType<u32>())); SetGpr(op.rd, ReadMemory(addr, GetType<u32>()));
SetGpr(op.ra, addr); SetGpr(op.ra, addr);
} }
void PPUTranslator::LBZ(ppu_opcode_t op) void PPUTranslator::LBZ(ppu_opcode_t op)
{ {
SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetType<u8>())); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType<u8>()));
} }
void PPUTranslator::LBZU(ppu_opcode_t op) void PPUTranslator::LBZU(ppu_opcode_t op)
{ {
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm);
SetGpr(op.rd, ReadMemory(addr, GetType<u8>())); SetGpr(op.rd, ReadMemory(addr, GetType<u8>()));
SetGpr(op.ra, addr); SetGpr(op.ra, addr);
} }
void PPUTranslator::STW(ppu_opcode_t op) void PPUTranslator::STW(ppu_opcode_t op)
{ {
WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetGpr(op.rs, 32)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetGpr(op.rs, 32));
} }
void PPUTranslator::STWU(ppu_opcode_t op) void PPUTranslator::STWU(ppu_opcode_t op)
{ {
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm);
WriteMemory(addr, GetGpr(op.rs, 32)); WriteMemory(addr, GetGpr(op.rs, 32));
SetGpr(op.ra, addr); SetGpr(op.ra, addr);
} }
void PPUTranslator::STB(ppu_opcode_t op) void PPUTranslator::STB(ppu_opcode_t op)
{ {
WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetGpr(op.rs, 8)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetGpr(op.rs, 8));
} }
void PPUTranslator::STBU(ppu_opcode_t op) void PPUTranslator::STBU(ppu_opcode_t op)
{ {
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm);
WriteMemory(addr, GetGpr(op.rs, 8)); WriteMemory(addr, GetGpr(op.rs, 8));
SetGpr(op.ra, addr); SetGpr(op.ra, addr);
} }
void PPUTranslator::LHZ(ppu_opcode_t op) void PPUTranslator::LHZ(ppu_opcode_t op)
{ {
SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetType<u16>())); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType<u16>()));
} }
void PPUTranslator::LHZU(ppu_opcode_t op) void PPUTranslator::LHZU(ppu_opcode_t op)
{ {
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm);
SetGpr(op.rd, ReadMemory(addr, GetType<u16>())); SetGpr(op.rd, ReadMemory(addr, GetType<u16>()));
SetGpr(op.ra, addr); SetGpr(op.ra, addr);
} }
void PPUTranslator::LHA(ppu_opcode_t op) void PPUTranslator::LHA(ppu_opcode_t op)
{ {
SetGpr(op.rd, SExt(ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetType<s16>()), GetType<s64>())); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
SetGpr(op.rd, SExt(ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType<s16>()), GetType<s64>()));
} }
void PPUTranslator::LHAU(ppu_opcode_t op) void PPUTranslator::LHAU(ppu_opcode_t op)
{ {
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm);
SetGpr(op.rd, SExt(ReadMemory(addr, GetType<s16>()), GetType<s64>())); SetGpr(op.rd, SExt(ReadMemory(addr, GetType<s16>()), GetType<s64>()));
SetGpr(op.ra, addr); SetGpr(op.ra, addr);
} }
void PPUTranslator::STH(ppu_opcode_t op) void PPUTranslator::STH(ppu_opcode_t op)
{ {
WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetGpr(op.rs, 16)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetGpr(op.rs, 16));
} }
void PPUTranslator::STHU(ppu_opcode_t op) void PPUTranslator::STHU(ppu_opcode_t op)
{ {
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm);
WriteMemory(addr, GetGpr(op.rs, 16)); WriteMemory(addr, GetGpr(op.rs, 16));
SetGpr(op.ra, addr); SetGpr(op.ra, addr);
} }
@ -3236,77 +3486,181 @@ void PPUTranslator::STMW(ppu_opcode_t op)
void PPUTranslator::LFS(ppu_opcode_t op) void PPUTranslator::LFS(ppu_opcode_t op)
{ {
SetFpr(op.frd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetType<f32>())); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
SetFpr(op.frd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType<f32>()));
} }
void PPUTranslator::LFSU(ppu_opcode_t op) void PPUTranslator::LFSU(ppu_opcode_t op)
{ {
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm);
SetFpr(op.frd, ReadMemory(addr, GetType<f32>())); SetFpr(op.frd, ReadMemory(addr, GetType<f32>()));
SetGpr(op.ra, addr); SetGpr(op.ra, addr);
} }
void PPUTranslator::LFD(ppu_opcode_t op) void PPUTranslator::LFD(ppu_opcode_t op)
{ {
SetFpr(op.frd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetType<f64>())); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
SetFpr(op.frd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType<f64>()));
} }
void PPUTranslator::LFDU(ppu_opcode_t op) void PPUTranslator::LFDU(ppu_opcode_t op)
{ {
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm);
SetFpr(op.frd, ReadMemory(addr, GetType<f64>())); SetFpr(op.frd, ReadMemory(addr, GetType<f64>()));
SetGpr(op.ra, addr); SetGpr(op.ra, addr);
} }
void PPUTranslator::STFS(ppu_opcode_t op) void PPUTranslator::STFS(ppu_opcode_t op)
{ {
WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetFpr(op.frs, 32)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetFpr(op.frs, 32));
} }
void PPUTranslator::STFSU(ppu_opcode_t op) void PPUTranslator::STFSU(ppu_opcode_t op)
{ {
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm);
WriteMemory(addr, GetFpr(op.frs, 32)); WriteMemory(addr, GetFpr(op.frs, 32));
SetGpr(op.ra, addr); SetGpr(op.ra, addr);
} }
void PPUTranslator::STFD(ppu_opcode_t op) void PPUTranslator::STFD(ppu_opcode_t op)
{ {
WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetFpr(op.frs)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetFpr(op.frs));
} }
void PPUTranslator::STFDU(ppu_opcode_t op) void PPUTranslator::STFDU(ppu_opcode_t op)
{ {
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); Value* imm = m_ir->getInt64(op.simm16);
if (m_rel && m_rel->type == 4)
{
imm = SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>());
m_rel = nullptr;
}
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm);
WriteMemory(addr, GetFpr(op.frs)); WriteMemory(addr, GetFpr(op.frs));
SetGpr(op.ra, addr); SetGpr(op.ra, addr);
} }
void PPUTranslator::LD(ppu_opcode_t op) void PPUTranslator::LD(ppu_opcode_t op)
{ {
SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.ds << 2)) : m_ir->getInt64(op.ds << 2), GetType<u64>())); Value* imm = m_ir->getInt64(op.ds << 2);
if (m_rel && m_rel->type == 57)
{
imm = m_ir->CreateAnd(SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>()), ~3);
m_rel = nullptr;
}
SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType<u64>()));
} }
void PPUTranslator::LDU(ppu_opcode_t op) void PPUTranslator::LDU(ppu_opcode_t op)
{ {
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.ds << 2)); Value* imm = m_ir->getInt64(op.ds << 2);
if (m_rel && m_rel->type == 57)
{
imm = m_ir->CreateAnd(SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>()), ~3);
m_rel = nullptr;
}
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm);
SetGpr(op.rd, ReadMemory(addr, GetType<u64>())); SetGpr(op.rd, ReadMemory(addr, GetType<u64>()));
SetGpr(op.ra, addr); SetGpr(op.ra, addr);
} }
void PPUTranslator::LWA(ppu_opcode_t op) void PPUTranslator::LWA(ppu_opcode_t op)
{ {
SetGpr(op.rd, SExt(ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.ds << 2)) : m_ir->getInt64(op.ds << 2), GetType<s32>()))); Value* imm = m_ir->getInt64(op.ds << 2);
if (m_rel && m_rel->type == 57)
{
imm = m_ir->CreateAnd(SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>()), ~3);
m_rel = nullptr;
}
SetGpr(op.rd, SExt(ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType<s32>())));
} }
void PPUTranslator::STD(ppu_opcode_t op) void PPUTranslator::STD(ppu_opcode_t op)
{ {
WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.ds << 2)) : m_ir->getInt64(op.ds << 2), GetGpr(op.rs)); Value* imm = m_ir->getInt64(op.ds << 2);
if (m_rel && m_rel->type == 57)
{
imm = m_ir->CreateAnd(SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>()), ~3);
m_rel = nullptr;
}
WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetGpr(op.rs));
} }
void PPUTranslator::STDU(ppu_opcode_t op) void PPUTranslator::STDU(ppu_opcode_t op)
{ {
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.ds << 2)); Value* imm = m_ir->getInt64(op.ds << 2);
if (m_rel && m_rel->type == 57)
{
imm = m_ir->CreateAnd(SExt(ReadMemory(GetAddr(+2), GetType<u16>()), GetType<u64>()), ~3);
m_rel = nullptr;
}
const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm);
WriteMemory(addr, GetGpr(op.rs)); WriteMemory(addr, GetGpr(op.rs));
SetGpr(op.ra, addr); SetGpr(op.ra, addr);
} }
@ -3862,7 +4216,7 @@ void PPUTranslator::FCFID(ppu_opcode_t op)
void PPUTranslator::UNK(ppu_opcode_t op) void PPUTranslator::UNK(ppu_opcode_t op)
{ {
FlushRegisters(); FlushRegisters();
Call(GetType<void>(), "__error", m_thread, m_ir->getInt64(m_current_addr), m_ir->getInt32(op.opcode))->setTailCallKind(llvm::CallInst::TCK_Tail); Call(GetType<void>(), "__error", m_thread, GetAddr(), m_ir->getInt32(op.opcode))->setTailCallKind(llvm::CallInst::TCK_Tail);
m_ir->CreateRetVoid(); m_ir->CreateRetVoid();
} }
@ -4123,9 +4477,9 @@ Value* PPUTranslator::CheckTrapCondition(u32 to, Value* left, Value* right)
return trap_condition; return trap_condition;
} }
void PPUTranslator::Trap(u64 addr) void PPUTranslator::Trap()
{ {
Call(GetType<void>(), "__trap", m_thread, m_ir->getInt64(m_current_addr))->setTailCallKind(llvm::CallInst::TCK_Tail); Call(GetType<void>(), "__trap", m_thread, GetAddr())->setTailCallKind(llvm::CallInst::TCK_Tail);
m_ir->CreateRetVoid(); m_ir->CreateRetVoid();
} }

View file

@ -116,6 +116,9 @@ class PPUTranslator final //: public CPUTranslator
// PPU Module // PPU Module
const ppu_module& m_info; const ppu_module& m_info;
// Relevant relocations
std::map<u64, const ppu_reloc*> m_relocs;
// Attributes for function calls which are "pure" and may be optimized away if their results are unused // Attributes for function calls which are "pure" and may be optimized away if their results are unused
const llvm::AttributeSet m_pure_attr; const llvm::AttributeSet m_pure_attr;
@ -125,14 +128,23 @@ class PPUTranslator final //: public CPUTranslator
// LLVM function // LLVM function
llvm::Function* m_function; llvm::Function* m_function;
// Function range
u64 m_start_addr, m_end_addr, m_current_addr;
llvm::MDNode* m_md_unlikely; llvm::MDNode* m_md_unlikely;
llvm::MDNode* m_md_likely; llvm::MDNode* m_md_likely;
// Current position-independent address
u64 m_addr = 0;
// Relocation info
const ppu_segment* m_reloc = nullptr;
// Set by instruction code after processing the relocation
const ppu_reloc* m_rel = nullptr;
/* Variables */ /* Variables */
// Segments
std::vector<llvm::GlobalVariable*> m_segs;
// Memory base // Memory base
llvm::GlobalVariable* m_base; llvm::GlobalVariable* m_base;
llvm::Value* m_base_loaded; llvm::Value* m_base_loaded;
@ -179,6 +191,9 @@ class PPUTranslator final //: public CPUTranslator
#undef DEF_VALUE #undef DEF_VALUE
public: public:
// Get current instruction address
llvm::Value* GetAddr(u64 _add = 0);
// Change integer size for integer or integer vector type (by 2^degree) // Change integer size for integer or integer vector type (by 2^degree)
llvm::Type* ScaleType(llvm::Type*, s32 pow2 = 0); llvm::Type* ScaleType(llvm::Type*, s32 pow2 = 0);
@ -346,8 +361,8 @@ public:
// Check condition for trap instructions // Check condition for trap instructions
llvm::Value* CheckTrapCondition(u32 to, llvm::Value* left, llvm::Value* right); llvm::Value* CheckTrapCondition(u32 to, llvm::Value* left, llvm::Value* right);
// Emit trap // Emit trap for current address
void Trap(u64 addr); void Trap();
// Get condition for branch instructions // Get condition for branch instructions
llvm::Value* CheckBranchCondition(u32 bo, u32 bi); llvm::Value* CheckBranchCondition(u32 bo, u32 bi);

View file

@ -109,7 +109,7 @@ error_code prx_load_module(std::string path, u64 flags, vm::ptr<sys_prx_load_mod
return CELL_PRX_ERROR_ILLEGAL_LIBRARY; return CELL_PRX_ERROR_ILLEGAL_LIBRARY;
} }
const auto prx = ppu_load_prx(obj, path.substr(path.find_last_of('/') + 1)); const auto prx = ppu_load_prx(obj, vfs::get(path));
if (!prx) if (!prx)
{ {