mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-21 18:22:33 +01:00
Syscall analysis implemented
This commit is contained in:
parent
63e690ca11
commit
9db7de29fb
@ -299,8 +299,9 @@ void decode_x64_reg_op(const u8* code, x64_op_t& out_op, x64_reg_t& out_reg, siz
|
||||
switch (op2)
|
||||
{
|
||||
case 0x11:
|
||||
case 0x29:
|
||||
{
|
||||
if (!repe && !repne && !oso) // MOVUPS xmm/m, xmm
|
||||
if (!repe && !repne) // MOVUPS/MOVAPS/MOVUPD/MOVAPD xmm/m, xmm
|
||||
{
|
||||
out_op = X64OP_STORE;
|
||||
out_reg = get_modRM_reg_xmm(code, rex);
|
||||
|
@ -42,7 +42,7 @@ namespace utils
|
||||
void *dynamic_library::get_impl(const std::string &name) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return GetProcAddress((HMODULE)m_handle, name.c_str());
|
||||
return (void*)GetProcAddress((HMODULE)m_handle, name.c_str());
|
||||
#else
|
||||
return dlsym(m_handle, (char *)name.c_str());
|
||||
#endif
|
||||
|
@ -2363,7 +2363,7 @@ s32 ppu_error_code::report(s32 error, const char* text)
|
||||
{
|
||||
if (auto func = static_cast<PPUThread*>(thread)->last_function)
|
||||
{
|
||||
LOG_ERROR(PPU, "Function '%s' failed with 0x%08x : %s", func, error, text);
|
||||
LOG_ERROR(PPU, "'%s' failed with 0x%08x : %s", func, error, text);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4,7 +4,12 @@
|
||||
|
||||
using ppu_function_t = void(*)(PPUThread&);
|
||||
|
||||
#define BIND_FUNC(func) [](PPUThread& ppu){ ppu.last_function = #func; ppu_func_detail::do_call(ppu, func); }
|
||||
#define BIND_FUNC(func) static_cast<ppu_function_t>([](PPUThread& ppu){\
|
||||
const auto old_f = ppu.last_function;\
|
||||
ppu.last_function = #func;\
|
||||
ppu_func_detail::do_call(ppu, func);\
|
||||
ppu.last_function = old_f;\
|
||||
})
|
||||
|
||||
struct ppu_va_args_t
|
||||
{
|
||||
|
@ -125,9 +125,22 @@ extern void ppu_initialize(const std::string& name, const std::vector<std::pair<
|
||||
// Function lookup table. Not supposed to grow after emulation start.
|
||||
std::vector<ppu_function_t> g_ppu_function_cache;
|
||||
|
||||
// Function name cache in format %s.%s (module name, function name)
|
||||
std::vector<std::string> g_ppu_function_names;
|
||||
|
||||
// Function NID cache for autopause. Autopause tool should probably be rewritten.
|
||||
std::vector<u32> g_ppu_fnid_cache;
|
||||
|
||||
extern std::string ppu_get_module_function_name(u32 index)
|
||||
{
|
||||
if (index < g_ppu_function_names.size())
|
||||
{
|
||||
return g_ppu_function_names[index];
|
||||
}
|
||||
|
||||
return fmt::format(".%u", index);
|
||||
}
|
||||
|
||||
extern void ppu_execute_function(PPUThread& ppu, u32 index)
|
||||
{
|
||||
if (index < g_ppu_function_cache.size())
|
||||
@ -137,21 +150,8 @@ extern void ppu_execute_function(PPUThread& ppu, u32 index)
|
||||
|
||||
if (const auto func = g_ppu_function_cache[index])
|
||||
{
|
||||
const auto previous_function = ppu.last_function; // TODO: use gsl::finally or something, but only if it's equally fast
|
||||
|
||||
try
|
||||
{
|
||||
func(ppu);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
logs::HLE.format(Emu.IsStopped() ? logs::level::warning : logs::level::error, "Function '%s' aborted", ppu.last_function);
|
||||
ppu.last_function = previous_function;
|
||||
throw;
|
||||
}
|
||||
|
||||
LOG_TRACE(HLE, "Function '%s' finished, r3=0x%llx", ppu.last_function, ppu.GPR[3]);
|
||||
ppu.last_function = previous_function;
|
||||
func(ppu);
|
||||
LOG_TRACE(HLE, "'%s' finished, r3=0x%llx", ppu_get_module_function_name(index), ppu.GPR[3]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -159,6 +159,16 @@ extern void ppu_execute_function(PPUThread& ppu, u32 index)
|
||||
throw fmt::exception("Function not registered (index %u)" HERE, index);
|
||||
}
|
||||
|
||||
extern ppu_function_t ppu_get_function(u32 index)
|
||||
{
|
||||
if (index < g_ppu_function_cache.size())
|
||||
{
|
||||
return g_ppu_function_cache[index];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extern u32 ppu_generate_id(const char* name)
|
||||
{
|
||||
// Symbol name suffix
|
||||
@ -312,7 +322,10 @@ static void ppu_initialize_modules()
|
||||
|
||||
// Reinitialize function cache
|
||||
g_ppu_function_cache = ppu_function_manager::get();
|
||||
g_ppu_fnid_cache = std::vector<u32>(g_ppu_function_cache.size());
|
||||
g_ppu_function_names.clear();
|
||||
g_ppu_function_names.resize(g_ppu_function_cache.size());
|
||||
g_ppu_fnid_cache.clear();
|
||||
g_ppu_fnid_cache.resize(g_ppu_function_cache.size());
|
||||
|
||||
// "Use" all the modules for correct linkage
|
||||
for (auto& module : registered)
|
||||
@ -322,6 +335,7 @@ static void ppu_initialize_modules()
|
||||
for (auto& function : module->functions)
|
||||
{
|
||||
LOG_TRACE(LOADER, "** 0x%08X: %s", function.first, function.second.name);
|
||||
g_ppu_function_names.at(function.second.index) = fmt::format("%s.%s", module->name, function.second.name);
|
||||
g_ppu_fnid_cache.at(function.second.index) = function.first;
|
||||
}
|
||||
|
||||
@ -1531,10 +1545,12 @@ void ppu_exec_loader::load() const
|
||||
{
|
||||
// TODO
|
||||
const u32 index = ::size32(g_ppu_function_cache);
|
||||
const std::string& fname = ppu_get_function_name(module.first, fnid);
|
||||
g_ppu_function_cache.emplace_back();
|
||||
g_ppu_function_names.emplace_back(fmt::format("%s.%s", module.first, fname));
|
||||
g_ppu_fnid_cache.emplace_back(fnid);
|
||||
|
||||
LOG_ERROR(LOADER, "Unknown function '%s' in module '%s' (index %u)", ppu_get_function_name(module.first, fnid), module.first, index);
|
||||
LOG_ERROR(LOADER, "Unknown function '%s' in module '%s' (index %u)", fname, module.first, index);
|
||||
|
||||
for (auto& import : entry.second.second)
|
||||
{
|
||||
@ -1544,11 +1560,11 @@ void ppu_exec_loader::load() const
|
||||
|
||||
if (!ppu_patch_import_stub(stub, index))
|
||||
{
|
||||
LOG_ERROR(LOADER, "Failed to inject code for function '%s' in module '%s' (0x%x)", ppu_get_function_name(module.first, fnid), module.first, stub);
|
||||
LOG_ERROR(LOADER, "Failed to inject code for function '%s' in module '%s' (0x%x)", fname, module.first, stub);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_NOTICE(LOADER, "Injected hack for function '%s' in module '%s' (*0x%x)", ppu_get_function_name(module.first, fnid), module.first, stub);
|
||||
LOG_NOTICE(LOADER, "Injected hack for function '%s' in module '%s' (*0x%x)", fname, module.first, stub);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,20 +200,7 @@ public:
|
||||
template<typename T, T Func, typename... Args, typename RT = std::result_of_t<T(Args...)>>
|
||||
inline RT ppu_execute_function_or_callback(const char* name, PPUThread& ppu, Args&&... args)
|
||||
{
|
||||
const auto previous_function = ppu.last_function; // TODO
|
||||
|
||||
try
|
||||
{
|
||||
return Func(std::forward<Args>(args)...);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_ERROR(PPU, "Function call '%s' aborted", ppu.last_function);
|
||||
ppu.last_function = previous_function;
|
||||
throw;
|
||||
}
|
||||
|
||||
ppu.last_function = previous_function;
|
||||
return Func(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
#define CALL_FUNC(ppu, func, ...) ppu_execute_function_or_callback<decltype(&func), &func>(#func, ppu, __VA_ARGS__)
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
//#include "llvm/IR/Dominators.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
//#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
//#include "llvm/IR/Module.h"
|
||||
//#include "llvm/IR/Function.h"
|
||||
@ -27,6 +27,7 @@
|
||||
//#include "llvm/Analysis/LoopInfo.h"
|
||||
//#include "llvm/Analysis/ScalarEvolution.h"
|
||||
#include "llvm/Analysis/Lint.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
#include "llvm/Transforms/Scalar.h"
|
||||
#include "llvm/Transforms/IPO.h"
|
||||
#include "llvm/Transforms/Vectorize.h"
|
||||
@ -63,7 +64,7 @@ struct ppu_addr_hash
|
||||
}
|
||||
};
|
||||
|
||||
static std::unordered_map<u32, void(*)(PPUThread&), ppu_addr_hash> s_ppu_compiled;
|
||||
static std::unordered_map<u32, ppu_function_t, ppu_addr_hash> s_ppu_compiled; // TODO
|
||||
|
||||
|
||||
|
||||
@ -126,13 +127,11 @@ void PPUThread::cpu_task()
|
||||
{
|
||||
//SetHostRoundingMode(FPSCR_RN_NEAR);
|
||||
|
||||
if (custom_task)
|
||||
{
|
||||
if (check_status()) return;
|
||||
|
||||
return custom_task(*this);
|
||||
}
|
||||
return custom_task ? custom_task(*this) : fast_call(pc, static_cast<u32>(GPR[2]));
|
||||
}
|
||||
|
||||
void PPUThread::cpu_task_main()
|
||||
{
|
||||
if (g_cfg_ppu_decoder.get() == ppu_decoder_type::llvm)
|
||||
{
|
||||
const auto found = s_ppu_compiled.find(pc);
|
||||
@ -311,39 +310,54 @@ be_t<u64>* PPUThread::get_stack_arg(s32 i, u64 align)
|
||||
|
||||
void PPUThread::fast_call(u32 addr, u32 rtoc)
|
||||
{
|
||||
auto old_PC = pc;
|
||||
auto old_stack = GPR[1];
|
||||
auto old_rtoc = GPR[2];
|
||||
auto old_LR = LR;
|
||||
auto old_task = std::move(custom_task);
|
||||
const auto old_PC = pc;
|
||||
const auto old_stack = GPR[1];
|
||||
const auto old_rtoc = GPR[2];
|
||||
const auto old_LR = LR;
|
||||
const auto old_task = std::move(custom_task);
|
||||
const auto old_func = last_function;
|
||||
|
||||
pc = addr;
|
||||
GPR[2] = rtoc;
|
||||
LR = Emu.GetCPUThreadStop();
|
||||
custom_task = nullptr;
|
||||
last_function = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
cpu_task();
|
||||
cpu_task_main();
|
||||
|
||||
if (GPR[1] != old_stack && !state.test(cpu_state::ret) && !state.test(cpu_state::exit)) // GPR[1] shouldn't change
|
||||
{
|
||||
throw fmt::exception("Stack inconsistency (addr=0x%x, rtoc=0x%x, SP=0x%llx, old=0x%llx)", addr, rtoc, GPR[1], old_stack);
|
||||
}
|
||||
}
|
||||
catch (cpu_state _s)
|
||||
{
|
||||
state += _s;
|
||||
if (_s != cpu_state::ret) throw;
|
||||
}
|
||||
catch (EmulationStopped)
|
||||
{
|
||||
if (last_function) LOG_WARNING(PPU, "'%s' aborted", last_function);
|
||||
last_function = old_func;
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (last_function) LOG_ERROR(PPU, "'%s' aborted", last_function);
|
||||
last_function = old_func;
|
||||
throw;
|
||||
}
|
||||
|
||||
state -= cpu_state::ret;
|
||||
|
||||
pc = old_PC;
|
||||
|
||||
if (GPR[1] != old_stack) // GPR[1] shouldn't change
|
||||
{
|
||||
throw EXCEPTION("Stack inconsistency (addr=0x%x, rtoc=0x%x, SP=0x%llx, old=0x%llx)", addr, rtoc, GPR[1], old_stack);
|
||||
}
|
||||
|
||||
GPR[1] = old_stack;
|
||||
GPR[2] = old_rtoc;
|
||||
LR = old_LR;
|
||||
custom_task = std::move(old_task);
|
||||
last_function = old_func;
|
||||
|
||||
//if (custom_task)
|
||||
//{
|
||||
@ -357,6 +371,10 @@ const ppu_decoder<ppu_itype> s_ppu_itype;
|
||||
extern u64 get_timebased_time();
|
||||
extern void ppu_execute_syscall(PPUThread& ppu, u64 code);
|
||||
extern void ppu_execute_function(PPUThread& ppu, u32 index);
|
||||
extern ppu_function_t ppu_get_syscall(u64 code);
|
||||
extern std::string ppu_get_syscall_name(u64 code);
|
||||
extern ppu_function_t ppu_get_function(u32 index);
|
||||
extern std::string ppu_get_module_function_name(u32 index);
|
||||
|
||||
extern __m128 sse_exp2_ps(__m128 A);
|
||||
extern __m128 sse_log2_ps(__m128 A);
|
||||
@ -378,23 +396,6 @@ static void ppu_trace(u64 addr)
|
||||
LOG_NOTICE(PPU, "Trace: 0x%llx", addr);
|
||||
}
|
||||
|
||||
static void ppu_hlecall(PPUThread& ppu, u32 index)
|
||||
{
|
||||
ppu_execute_function(ppu, index);
|
||||
if (ppu.state.load() && ppu.check_status()) throw cpu_state::ret; // Temporarily
|
||||
}
|
||||
|
||||
static void ppu_syscall(PPUThread& ppu, u64 code)
|
||||
{
|
||||
ppu_execute_syscall(ppu, code);
|
||||
if (ppu.state.load() && ppu.check_status()) throw cpu_state::ret; // Temporarily
|
||||
}
|
||||
|
||||
static u32 ppu_tbl()
|
||||
{
|
||||
return (u32)get_timebased_time();
|
||||
}
|
||||
|
||||
static void ppu_call(PPUThread& ppu, u32 addr)
|
||||
{
|
||||
const auto found = s_ppu_compiled.find(addr);
|
||||
@ -410,7 +411,7 @@ static void ppu_call(PPUThread& ppu, u32 addr)
|
||||
// Allow HLE callbacks without compiling them
|
||||
if (itype == ppu_itype::HACK && vm::read32(addr + 4) == ppu_instructions::BLR())
|
||||
{
|
||||
return ppu_hlecall(ppu, op & 0x3ffffff);
|
||||
return ppu_execute_function(ppu, op & 0x3ffffff);
|
||||
}
|
||||
|
||||
ppu_trap(addr);
|
||||
@ -506,9 +507,9 @@ extern void ppu_initialize(const std::string& name, const std::vector<std::pair<
|
||||
{ "__memptr", (u64)&vm::g_base_addr },
|
||||
{ "__trap", (u64)&ppu_trap },
|
||||
{ "__trace", (u64)&ppu_trace },
|
||||
{ "__hlecall", (u64)&ppu_hlecall },
|
||||
{ "__syscall", (u64)&ppu_syscall },
|
||||
{ "__get_tbl", (u64)&ppu_tbl },
|
||||
{ "__hlecall", (u64)&ppu_execute_function },
|
||||
{ "__syscall", (u64)&ppu_execute_syscall },
|
||||
{ "__get_tbl", (u64)&get_timebased_time },
|
||||
{ "__call", (u64)&ppu_call },
|
||||
{ "__lwarx", (u64)&ppu_lwarx },
|
||||
{ "__ldarx", (u64)&ppu_ldarx },
|
||||
@ -576,7 +577,6 @@ extern void ppu_initialize(const std::string& name, const std::vector<std::pair<
|
||||
pm.add(createGVNPass());
|
||||
pm.add(createDeadStoreEliminationPass());
|
||||
pm.add(createSCCPPass());
|
||||
//pm.addPass(new SyscallAnalysisPass()); // Requires constant propagation
|
||||
pm.add(createInstructionCombiningPass());
|
||||
pm.add(createInstructionSimplifierPass());
|
||||
pm.add(createAggressiveDCEPass());
|
||||
@ -588,7 +588,55 @@ extern void ppu_initialize(const std::string& name, const std::vector<std::pair<
|
||||
{
|
||||
if (info.second)
|
||||
{
|
||||
pm.run(*translator->TranslateToIR(info.first, info.first + info.second, vm::_ptr<u32>(info.first)));
|
||||
const auto func = translator->TranslateToIR(info.first, info.first + info.second, vm::_ptr<u32>(info.first));
|
||||
|
||||
// Run optimization passes
|
||||
pm.run(*func);
|
||||
|
||||
const auto _syscall = module->getFunction("__syscall");
|
||||
const auto _hlecall = module->getFunction("__hlecall");
|
||||
|
||||
for (auto i = inst_begin(*func), end = inst_end(*func); i != end;)
|
||||
{
|
||||
const auto inst = &*i++;
|
||||
|
||||
if (const auto ci = dyn_cast<CallInst>(inst))
|
||||
{
|
||||
const auto cif = ci->getCalledFunction();
|
||||
const auto op1 = ci->getNumArgOperands() > 1 ? ci->getArgOperand(1) : nullptr;
|
||||
|
||||
if (cif == _syscall && op1 && isa<ConstantInt>(op1))
|
||||
{
|
||||
// Try to determine syscall using the value from r11 (requires constant propagation)
|
||||
const u64 index = cast<ConstantInt>(op1)->getZExtValue();
|
||||
|
||||
if (const auto ptr = ppu_get_syscall(index))
|
||||
{
|
||||
const auto n = ppu_get_syscall_name(index);
|
||||
const auto f = cast<Function>(module->getOrInsertFunction(n, _func));
|
||||
link_table.emplace(n, reinterpret_cast<std::uintptr_t>(ptr));
|
||||
|
||||
// Call the syscall directly
|
||||
ReplaceInstWithInst(ci, CallInst::Create(f, {ci->getArgOperand(0)}));
|
||||
}
|
||||
}
|
||||
|
||||
if (cif == _hlecall && op1 && isa<ConstantInt>(op1))
|
||||
{
|
||||
const u32 index = static_cast<u32>(cast<ConstantInt>(op1)->getZExtValue());
|
||||
|
||||
if (const auto ptr = ppu_get_function(index))
|
||||
{
|
||||
const auto n = ppu_get_module_function_name(index);
|
||||
const auto f = cast<Function>(module->getOrInsertFunction(n, _func));
|
||||
link_table.emplace(n, reinterpret_cast<std::uintptr_t>(ptr));
|
||||
|
||||
// Call the function directly
|
||||
ReplaceInstWithInst(ci, CallInst::Create(f, {ci->getArgOperand(0)}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -599,9 +647,6 @@ extern void ppu_initialize(const std::string& name, const std::vector<std::pair<
|
||||
//mpm.add(createFunctionInliningPass());
|
||||
mpm.run(*module);
|
||||
|
||||
// TODO: replacing __syscall/__hlecall
|
||||
// TODO: improve __call and s_ppu_compiled
|
||||
|
||||
std::string result;
|
||||
raw_string_ostream out(result);
|
||||
|
||||
@ -638,7 +683,7 @@ extern void ppu_initialize(const std::string& name, const std::vector<std::pair<
|
||||
if (info.second)
|
||||
{
|
||||
const std::uintptr_t link = jit->get(fmt::format("__sub_%x", info.first));
|
||||
s_ppu_compiled.emplace(info.first, (void(*)(PPUThread&))link);
|
||||
s_ppu_compiled.emplace(info.first, (ppu_function_t)link);
|
||||
|
||||
LOG_NOTICE(PPU, "** Function __sub_%x -> 0x%llx (addr=0x%x, size=0x%x)", info.first, link, info.first, info.second);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ public:
|
||||
virtual std::string dump() const override;
|
||||
virtual void cpu_init() override;
|
||||
virtual void cpu_task() override;
|
||||
virtual void cpu_task_main();
|
||||
virtual bool handle_interrupt() override;
|
||||
virtual ~PPUThread() override;
|
||||
|
||||
|
@ -910,40 +910,36 @@ std::array<ppu_function_t, 1024> g_ppu_syscall_table
|
||||
|
||||
extern void ppu_execute_syscall(PPUThread& ppu, u64 code)
|
||||
{
|
||||
if (code >= g_ppu_syscall_table.size())
|
||||
if (code < g_ppu_syscall_table.size())
|
||||
{
|
||||
throw fmt::exception("Invalid syscall number (%llu)", code);
|
||||
}
|
||||
// If autopause occures, check_status() will hold the thread till unpaused.
|
||||
if (debug::autopause::pause_syscall(code) && ppu.check_status()) throw cpu_state::ret;
|
||||
|
||||
// If autopause occures, check_status() will hold the thread till unpaused.
|
||||
if (debug::autopause::pause_syscall(code) && ppu.check_status())
|
||||
{
|
||||
throw cpu_state::ret;
|
||||
}
|
||||
|
||||
const auto previous_function = ppu.last_function; // TODO: use gsl::finally or something
|
||||
|
||||
try
|
||||
{
|
||||
if (auto func = g_ppu_syscall_table[code])
|
||||
{
|
||||
func(ppu);
|
||||
LOG_TRACE(PPU, "Syscall '%s' (%llu) finished, r3=0x%llx", ppu_get_syscall_name(code), code, ppu.GPR[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_TODO(HLE, "Unimplemented syscall %s -> CELL_OK", ppu_get_syscall_name(code));
|
||||
ppu.GPR[3] = 0;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
logs::PPU.format(Emu.IsStopped() ? logs::level::warning : logs::level::error, "Syscall '%s' (%llu) aborted", ppu_get_syscall_name(code), code);
|
||||
ppu.last_function = previous_function;
|
||||
throw;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_TRACE(PPU, "Syscall '%s' (%llu) finished, r3=0x%llx", ppu_get_syscall_name(code), code, ppu.GPR[3]);
|
||||
ppu.last_function = previous_function;
|
||||
throw fmt::exception("Invalid syscall number (%llu)", code);
|
||||
}
|
||||
|
||||
extern ppu_function_t ppu_get_syscall(u64 code)
|
||||
{
|
||||
if (code < g_ppu_syscall_table.size())
|
||||
{
|
||||
return g_ppu_syscall_table[code];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DECLARE(lv2_lock_t::mutex);
|
||||
|
@ -271,6 +271,7 @@ s32 sys_timer_sleep(u32 sleep_time)
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(useconds - passed));
|
||||
}
|
||||
|
||||
CHECK_EMU_STATUS;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@ -294,5 +295,6 @@ s32 sys_timer_usleep(const u64 sleep_time)
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(sleep_time - passed));
|
||||
}
|
||||
|
||||
CHECK_EMU_STATUS;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
@ -89,9 +89,11 @@ void idm::clear()
|
||||
// Call recorded finalization functions for all IDs
|
||||
for (std::size_t i = 0; i < g_map.size(); i++)
|
||||
{
|
||||
const auto on_stop = id_manager::typeinfo::get()[i].on_stop;
|
||||
|
||||
for (auto& id : g_map[i])
|
||||
{
|
||||
id_manager::typeinfo::get()[i].on_stop(id.second.get());
|
||||
on_stop(id.second.get());
|
||||
}
|
||||
|
||||
g_map[i].clear();
|
||||
|
@ -37,6 +37,8 @@ namespace id_manager
|
||||
{
|
||||
static inline void func(T*)
|
||||
{
|
||||
// Forbid forward declarations
|
||||
static constexpr auto size = sizeof(std::conditional_t<std::is_void<T>::value, void*, T>);
|
||||
}
|
||||
};
|
||||
|
||||
@ -45,7 +47,7 @@ namespace id_manager
|
||||
{
|
||||
static inline void func(T* ptr)
|
||||
{
|
||||
ptr->on_init();
|
||||
if (ptr) ptr->on_init();
|
||||
}
|
||||
};
|
||||
|
||||
@ -55,6 +57,8 @@ namespace id_manager
|
||||
{
|
||||
static inline void func(T*)
|
||||
{
|
||||
// Forbid forward declarations
|
||||
static constexpr auto size = sizeof(std::conditional_t<std::is_void<T>::value, void*, T>);
|
||||
}
|
||||
};
|
||||
|
||||
@ -63,7 +67,7 @@ namespace id_manager
|
||||
{
|
||||
static inline void func(T* ptr)
|
||||
{
|
||||
ptr->on_stop();
|
||||
if (ptr) ptr->on_stop();
|
||||
}
|
||||
};
|
||||
|
||||
@ -97,9 +101,6 @@ namespace id_manager
|
||||
template<typename T>
|
||||
static inline void update()
|
||||
{
|
||||
// Forbid forward declarations
|
||||
static constexpr auto size = sizeof(std::conditional_t<std::is_void<T>::value, void*, T>);
|
||||
|
||||
auto& info = access()[get_index<T>()];
|
||||
|
||||
info.on_init = [](void* ptr) { return_ id_manager::on_init<T>::func(static_cast<T*>(ptr)); };
|
||||
@ -111,12 +112,6 @@ namespace id_manager
|
||||
{
|
||||
return access();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline auto get_stop()
|
||||
{
|
||||
return access()[get_index<T>()].on_stop;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -257,6 +252,7 @@ public:
|
||||
if (auto pair = create_id<T>(WRAP_EXPR(std::make_shared<Make>(std::forward<Args>(args)...))))
|
||||
{
|
||||
id_manager::on_init<T>::func(static_cast<T*>(pair->second.get()));
|
||||
id_manager::on_stop<T>::func(nullptr);
|
||||
return{ pair->second, static_cast<T*>(pair->second.get()) };
|
||||
}
|
||||
|
||||
@ -270,6 +266,7 @@ public:
|
||||
if (auto pair = create_id<T>(WRAP_EXPR(std::make_shared<Make>(std::forward<Args>(args)...))))
|
||||
{
|
||||
id_manager::on_init<T>::func(static_cast<T*>(pair->second.get()));
|
||||
id_manager::on_stop<T>::func(nullptr);
|
||||
return pair->first;
|
||||
}
|
||||
|
||||
@ -283,6 +280,7 @@ public:
|
||||
if (auto pair = create_id<T>(WRAP_EXPR(ptr)))
|
||||
{
|
||||
id_manager::on_init<T>::func(static_cast<T*>(pair->second.get()));
|
||||
id_manager::on_stop<T>::func(nullptr);
|
||||
return pair->first;
|
||||
}
|
||||
|
||||
@ -296,6 +294,7 @@ public:
|
||||
if (auto pair = create_id<T>(std::forward<F>(provider)))
|
||||
{
|
||||
id_manager::on_init<T>::func(static_cast<T*>(pair->second.get()));
|
||||
id_manager::on_stop<T>::func(nullptr);
|
||||
return { pair->second, static_cast<T*>(pair->second.get()) };
|
||||
}
|
||||
|
||||
@ -389,7 +388,7 @@ public:
|
||||
|
||||
if (LIKELY(ptr))
|
||||
{
|
||||
id_manager::typeinfo::get_stop<T>()(static_cast<T*>(ptr.get()));
|
||||
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
|
||||
}
|
||||
|
||||
return ptr.operator bool();
|
||||
@ -403,7 +402,7 @@ public:
|
||||
|
||||
if (LIKELY(ptr))
|
||||
{
|
||||
id_manager::typeinfo::get_stop<T>()(static_cast<T*>(ptr.get()));
|
||||
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
|
||||
}
|
||||
|
||||
return{ ptr, static_cast<T*>(ptr.get()) };
|
||||
@ -429,7 +428,7 @@ public:
|
||||
g_map[get_type<T>()].erase(id);
|
||||
}
|
||||
|
||||
id_manager::typeinfo::get_stop<T>()(static_cast<T*>(ptr.get()));
|
||||
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
|
||||
|
||||
return{ ptr, static_cast<T*>(ptr.get()) };
|
||||
}
|
||||
@ -479,6 +478,7 @@ public:
|
||||
if (ptr)
|
||||
{
|
||||
id_manager::on_init<T>::func(ptr.get());
|
||||
id_manager::on_stop<T>::func(nullptr);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
@ -531,6 +531,7 @@ public:
|
||||
if (ptr)
|
||||
{
|
||||
id_manager::on_init<T>::func(ptr.get());
|
||||
id_manager::on_stop<T>::func(nullptr);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
@ -585,6 +586,7 @@ public:
|
||||
}
|
||||
|
||||
id_manager::on_init<T>::func(ptr.get());
|
||||
id_manager::on_stop<T>::func(nullptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@ -616,7 +618,7 @@ public:
|
||||
|
||||
if (ptr)
|
||||
{
|
||||
id_manager::typeinfo::get_stop<T>()(static_cast<T*>(ptr.get()));
|
||||
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
|
||||
}
|
||||
|
||||
return ptr.operator bool();
|
||||
@ -630,7 +632,7 @@ public:
|
||||
|
||||
if (ptr)
|
||||
{
|
||||
id_manager::typeinfo::get_stop<T>()(static_cast<T*>(ptr.get()));
|
||||
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
|
||||
}
|
||||
|
||||
return{ ptr, static_cast<T*>(ptr.get()) };
|
||||
|
@ -4,7 +4,12 @@
|
||||
|
||||
using arm_function_t = void(*)(ARMv7Thread&);
|
||||
|
||||
#define BIND_FUNC(func) [](ARMv7Thread& cpu){ cpu.last_function = #func; arm_func_detail::do_call(cpu, func); }
|
||||
#define BIND_FUNC(func) static_cast<arm_function_t>([](ARMv7Thread& cpu){\
|
||||
const auto old_f = cpu.last_function;\
|
||||
cpu.last_function = #func;\
|
||||
arm_func_detail::do_call(cpu, func);\
|
||||
cpu.last_function = old_f;\
|
||||
})
|
||||
|
||||
struct arm_va_args_t
|
||||
{
|
||||
|
@ -79,27 +79,26 @@ extern std::string arm_get_variable_name(const std::string& module, u32 vnid);
|
||||
// Function lookup table. Not supposed to grow after emulation start.
|
||||
std::vector<arm_function_t> g_arm_function_cache;
|
||||
|
||||
std::vector<std::string> g_arm_function_names;
|
||||
|
||||
extern std::string arm_get_module_function_name(u32 index)
|
||||
{
|
||||
if (index < g_arm_function_names.size())
|
||||
{
|
||||
return g_arm_function_names[index];
|
||||
}
|
||||
|
||||
return fmt::format(".%u", index);
|
||||
}
|
||||
|
||||
extern void arm_execute_function(ARMv7Thread& cpu, u32 index)
|
||||
{
|
||||
if (index < g_arm_function_cache.size())
|
||||
{
|
||||
if (const auto func = g_arm_function_cache[index])
|
||||
{
|
||||
const auto previous_function = cpu.last_function; // TODO: use gsl::finally or something
|
||||
|
||||
try
|
||||
{
|
||||
func(cpu);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
logs::ARMv7.format(Emu.IsStopped() ? logs::level::warning : logs::level::error, "Function '%s' aborted", cpu.last_function);
|
||||
cpu.last_function = previous_function;
|
||||
throw;
|
||||
}
|
||||
|
||||
LOG_TRACE(ARMv7, "Function '%s' finished, r0=0x%x", cpu.last_function, cpu.GPR[0]);
|
||||
cpu.last_function = previous_function;
|
||||
func(cpu);
|
||||
LOG_TRACE(ARMv7, "Function '%s' finished, r0=0x%x", arm_get_module_function_name(index), cpu.GPR[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -220,6 +219,8 @@ static void arm_initialize_modules()
|
||||
|
||||
// Reinitialize function cache
|
||||
g_arm_function_cache = arm_function_manager::get();
|
||||
g_arm_function_names.clear();
|
||||
g_arm_function_names.resize(g_arm_function_cache.size());
|
||||
|
||||
// "Use" all the modules for correct linkage
|
||||
for (auto& module : registered)
|
||||
@ -229,6 +230,7 @@ static void arm_initialize_modules()
|
||||
for (auto& function : module->functions)
|
||||
{
|
||||
LOG_TRACE(LOADER, "** 0x%08X: %s", function.first, function.second.name);
|
||||
g_arm_function_names.at(function.second.index) = fmt::format("%s.%s", module->name, function.second.name);
|
||||
}
|
||||
|
||||
for (auto& variable : module->variables)
|
||||
@ -555,6 +557,7 @@ void arm_exec_loader::load() const
|
||||
// TODO
|
||||
index = ::size32(g_arm_function_cache);
|
||||
g_arm_function_cache.emplace_back();
|
||||
g_arm_function_names.emplace_back(fmt::format("%s.%s", module_name, fname));
|
||||
|
||||
LOG_ERROR(LOADER, "** Unknown function '%s' in module '%s' (*0x%x) -> index %u", fname, module_name, faddr, index);
|
||||
}
|
||||
|
@ -126,13 +126,11 @@ extern thread_local std::string(*g_tls_log_prefix)();
|
||||
|
||||
void ARMv7Thread::cpu_task()
|
||||
{
|
||||
if (custom_task)
|
||||
{
|
||||
if (check_status()) return;
|
||||
|
||||
return custom_task(*this);
|
||||
}
|
||||
return custom_task ? custom_task(*this) : fast_call(PC);
|
||||
}
|
||||
|
||||
void ARMv7Thread::cpu_task_main()
|
||||
{
|
||||
g_tls_log_prefix = []
|
||||
{
|
||||
const auto cpu = static_cast<ARMv7Thread*>(get_current_cpu_thread());
|
||||
@ -191,34 +189,49 @@ ARMv7Thread::ARMv7Thread(const std::string& name)
|
||||
|
||||
void ARMv7Thread::fast_call(u32 addr)
|
||||
{
|
||||
auto old_PC = PC;
|
||||
auto old_stack = SP;
|
||||
auto old_LR = LR;
|
||||
auto old_task = std::move(custom_task);
|
||||
const auto old_PC = PC;
|
||||
const auto old_SP = SP;
|
||||
const auto old_LR = LR;
|
||||
const auto old_task = std::move(custom_task);
|
||||
const auto old_func = last_function;
|
||||
|
||||
PC = addr;
|
||||
LR = Emu.GetCPUThreadStop();
|
||||
custom_task = nullptr;
|
||||
last_function = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
cpu_task();
|
||||
cpu_task_main();
|
||||
|
||||
if (SP != old_SP && !state.test(cpu_state::ret) && !state.test(cpu_state::exit)) // SP shouldn't change
|
||||
{
|
||||
throw fmt::exception("Stack inconsistency (addr=0x%x, SP=0x%x, old=0x%x)", addr, SP, old_SP);
|
||||
}
|
||||
}
|
||||
catch (cpu_state _s)
|
||||
{
|
||||
state += _s;
|
||||
if (_s != cpu_state::ret) throw;
|
||||
}
|
||||
catch (EmulationStopped)
|
||||
{
|
||||
if (last_function) LOG_WARNING(ARMv7, "'%s' aborted", last_function);
|
||||
last_function = old_func;
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (last_function) LOG_ERROR(ARMv7, "'%s' aborted", last_function);
|
||||
last_function = old_func;
|
||||
throw;
|
||||
}
|
||||
|
||||
state -= cpu_state::ret;
|
||||
|
||||
PC = old_PC;
|
||||
|
||||
if (SP != old_stack) // SP shouldn't change
|
||||
{
|
||||
throw EXCEPTION("Stack inconsistency (addr=0x%x, SP=0x%x, old=0x%x)", addr, SP, old_stack);
|
||||
}
|
||||
|
||||
SP = old_SP;
|
||||
LR = old_LR;
|
||||
custom_task = std::move(old_task);
|
||||
last_function = old_func;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ public:
|
||||
virtual std::string dump() const override;
|
||||
virtual void cpu_init() override;
|
||||
virtual void cpu_task() override;
|
||||
virtual void cpu_task_main();
|
||||
virtual ~ARMv7Thread() override;
|
||||
|
||||
ARMv7Thread(const std::string& name);
|
||||
|
Loading…
Reference in New Issue
Block a user