diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index 3dfa186687..5ae0548f65 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -2004,19 +2004,10 @@ void Compiler::BC(u32 bo, u32 bi, s32 bd, u32 aa, u32 lk) { } void Compiler::HACK(u32 index) { - if (index & EIF_SAVE_RTOC) { - auto addr_i64 = (Value *)m_ir_builder->getInt64(0x28); - auto ra_i64 = GetGpr(1); - addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); - - WriteMemory(addr_i64, GetGpr(2, 64)); - } - Call("execute_ppu_func_by_index", &execute_ppu_func_by_index, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt32(index & ~EIF_FLAGS)); - if (index & EIF_PERFORM_BLR) { - auto lr_i64 = GetLr(); - lr_i64 = m_ir_builder->CreateAnd(lr_i64, ~0x3ULL); - auto lr_i32 = m_ir_builder->CreateTrunc(lr_i64, m_ir_builder->getInt32Ty()); - CreateBranch(nullptr, lr_i32, false, true); + Call("execute_ppu_func_by_index", &execute_ppu_func_by_index, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt32(index & EIF_USE_BRANCH ? index : index & ~EIF_PERFORM_BLR)); + if (index & EIF_PERFORM_BLR || index & EIF_USE_BRANCH) { + auto lr_i32 = index & EIF_USE_BRANCH ? GetPc() : m_ir_builder->CreateTrunc(m_ir_builder->CreateAnd(GetLr(), ~0x3ULL), m_ir_builder->getInt32Ty()); + CreateBranch(nullptr, lr_i32, false, (index & EIF_USE_BRANCH) == 0); } // copied from Compiler::SC() //auto ret_i1 = Call("PollStatus", m_poll_status_function, m_state.args[CompileTaskState::Args::State]); @@ -6089,9 +6080,10 @@ BranchType ppu_recompiler_llvm::GetBranchTypeFromInstruction(u32 instruction) { } else if (field2 == 528) { type = lk ? BranchType::FunctionCall : BranchType::LocalBranch; } - } else if (field1 == 1 && (instruction & EIF_PERFORM_BLR)) { - type = BranchType::Return; + } else if (field1 == 1 && (instruction & EIF_PERFORM_BLR)) { // classify HACK instruction + type = instruction & EIF_USE_BRANCH ? BranchType::FunctionCall : BranchType::Return; + } else if (field1 == 1 && (instruction & EIF_USE_BRANCH)) { + type = BranchType::LocalBranch; } - return type; } diff --git a/rpcs3/Emu/SysCalls/Modules.cpp b/rpcs3/Emu/SysCalls/Modules.cpp index 0e9f3561f9..73a095e37a 100644 --- a/rpcs3/Emu/SysCalls/Modules.cpp +++ b/rpcs3/Emu/SysCalls/Modules.cpp @@ -15,9 +15,19 @@ std::vector g_ppu_func_subs; u32 add_ppu_func(ModuleFunc func) { + if (g_ppu_func_list.empty()) + { + // prevent relocations if the array growths, must be sizeof(ModuleFunc) * 0x8000 ~~ about 1 MB of memory + g_ppu_func_list.reserve(0x8000); + } + for (auto& f : g_ppu_func_list) { - assert(f.id != func.id); + if (f.id == func.id) + { + // TODO: if NIDs overlap or if the same function is added twice + assert(!"add_ppu_func(): NID already exists"); + } } g_ppu_func_list.push_back(func); @@ -93,15 +103,56 @@ void execute_ppu_func_by_index(PPUThread& CPU, u32 index) { if (auto func = get_ppu_func_by_index(index)) { - auto old_last_syscall = CPU.m_last_syscall; - CPU.m_last_syscall = func->id; - + // save RTOC if necessary if (index & EIF_SAVE_RTOC) { - // save RTOC if necessary vm::write64(vm::cast(CPU.GPR[1] + 0x28), CPU.GPR[2]); } + // save old syscall/NID value + auto old_last_syscall = CPU.m_last_syscall; + + // branch directly to the LLE function + if (index & EIF_USE_BRANCH) + { + // for example, FastCall2 can't work with functions which do user level context switch + + if (old_last_syscall) + { + throw "Unfortunately, this function cannot be called from the callback."; + } + + if (!func->lle_func) + { + throw "Wrong usage: LLE function not set."; + } + + if (func->flags & MFF_FORCED_HLE) + { + throw "Wrong usage: Forced HLE enabled."; + } + + if (Ini.HLELogging.GetValue()) + { + LOG_NOTICE(HLE, "Branch to LLE function: %s", SysCalls::GetFuncName(func->id)); + } + + if (index & EIF_PERFORM_BLR) + { + throw "TODO: Branch with link"; + // CPU.LR = CPU.PC + 4; + } + + const auto data = vm::get_ptr>(func->lle_func.addr()); + CPU.SetBranch(data[0]); + CPU.GPR[2] = data[1]; // set rtoc + + return; + } + + // change current syscall/NID value + CPU.m_last_syscall = func->id; + if (func->lle_func && !(func->flags & MFF_FORCED_HLE)) { // call LLE function if available @@ -427,6 +478,15 @@ bool patch_ppu_import(u32 addr, u32 index) using namespace PPU_instr; + if (index >= g_ppu_func_list.size()) + { + return false; + } + + const u32 imm = (g_ppu_func_list[index].flags & MFF_NO_RETURN) && !(g_ppu_func_list[index].flags & MFF_FORCED_HLE) + ? index | EIF_USE_BRANCH + : index | EIF_PERFORM_BLR; + // check different patterns: if (vm::check_addr(addr, 32) && @@ -439,7 +499,7 @@ bool patch_ppu_import(u32 addr, u32 index) data[6] == MTCTR(r0) && data[7] == BCTR()) { - vm::write32(addr, HACK(index | EIF_SAVE_RTOC | EIF_PERFORM_BLR)); + vm::write32(addr, HACK(imm | EIF_SAVE_RTOC)); return true; } @@ -467,7 +527,7 @@ bool patch_ppu_import(u32 addr, u32 index) sub[0xd] == MTLR(r0) && sub[0xe] == BLR()) { - vm::write32(addr, HACK(index | EIF_PERFORM_BLR)); + vm::write32(addr, HACK(imm)); return true; } } @@ -490,7 +550,7 @@ bool patch_ppu_import(u32 addr, u32 index) data[0xe] == MTLR(r0) && data[0xf] == BLR()) { - vm::write32(addr, HACK(index | EIF_PERFORM_BLR)); + vm::write32(addr, HACK(imm)); return true; } @@ -511,7 +571,7 @@ bool patch_ppu_import(u32 addr, u32 index) data[0xd] == MTLR(r0) && data[0xe] == BLR()) { - vm::write32(addr, HACK(index | EIF_PERFORM_BLR)); + vm::write32(addr, HACK(imm)); return true; } @@ -531,7 +591,7 @@ bool patch_ppu_import(u32 addr, u32 index) data[0xc] == LD(r2, r1, 0x28) && data[0xd] == BLR()) { - vm::write32(addr, HACK(index | EIF_PERFORM_BLR)); + vm::write32(addr, HACK(imm)); return true; } diff --git a/rpcs3/Emu/SysCalls/Modules.h b/rpcs3/Emu/SysCalls/Modules.h index bc1ffe66d4..67f68567b8 100644 --- a/rpcs3/Emu/SysCalls/Modules.h +++ b/rpcs3/Emu/SysCalls/Modules.h @@ -9,6 +9,7 @@ class Module; enum : u32 { MFF_FORCED_HLE = (1 << 0), // always call HLE function + MFF_NO_RETURN = (1 << 1), // uses EIF_USE_BRANCH flag with LLE, ignored with MFF_FORCED_HLE }; // flags passed with index @@ -16,8 +17,9 @@ enum : u32 { EIF_SAVE_RTOC = (1 << 25), // save RTOC in [SP+0x28] before calling HLE/LLE function EIF_PERFORM_BLR = (1 << 24), // do BLR after calling HLE/LLE function + EIF_USE_BRANCH = (1 << 23), // do only branch, LLE must be set, last_syscall must be zero - EIF_FLAGS = 0x3000000, // all flags + EIF_FLAGS = 0x3800000, // all flags }; struct ModuleFunc @@ -121,6 +123,7 @@ bool patch_ppu_import(u32 addr, u32 index); #define REG_FUNC(module, name) add_ppu_func(ModuleFunc(get_function_id(#name), 0, &module, #name, bind_func(name))) #define REG_FUNC_FH(module, name) add_ppu_func(ModuleFunc(get_function_id(#name), MFF_FORCED_HLE, &module, #name, bind_func(name))) +#define REG_FUNC_NR(module, name) add_ppu_func(ModuleFunc(get_function_id(#name), MFF_NO_RETURN, &module, #name, bind_func(name))) #define REG_UNNAMED(module, nid) add_ppu_func(ModuleFunc(0x##nid, 0, &module, "_nid_"#nid, bind_func(_nid_##nid))) diff --git a/rpcs3/Emu/SysCalls/Modules/cellFiber.cpp b/rpcs3/Emu/SysCalls/Modules/cellFiber.cpp index 5b51c39312..77d1c9aa7b 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellFiber.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellFiber.cpp @@ -293,57 +293,57 @@ int cellFiberPpuUtilWorkerControlInitializeWithAttribute() Module cellFiber("cellFiber", []() { - REG_FUNC(cellFiber, _cellFiberPpuInitialize); + REG_FUNC_NR(cellFiber, _cellFiberPpuInitialize); - REG_FUNC(cellFiber, _cellFiberPpuSchedulerAttributeInitialize); - REG_FUNC(cellFiber, cellFiberPpuInitializeScheduler); - REG_FUNC(cellFiber, cellFiberPpuFinalizeScheduler); - REG_FUNC(cellFiber, cellFiberPpuRunFibers); - REG_FUNC(cellFiber, cellFiberPpuCheckFlags); - REG_FUNC(cellFiber, cellFiberPpuHasRunnableFiber); + REG_FUNC_NR(cellFiber, _cellFiberPpuSchedulerAttributeInitialize); + REG_FUNC_NR(cellFiber, cellFiberPpuInitializeScheduler); + REG_FUNC_NR(cellFiber, cellFiberPpuFinalizeScheduler); + REG_FUNC_NR(cellFiber, cellFiberPpuRunFibers); + REG_FUNC_NR(cellFiber, cellFiberPpuCheckFlags); + REG_FUNC_NR(cellFiber, cellFiberPpuHasRunnableFiber); - REG_FUNC(cellFiber, _cellFiberPpuAttributeInitialize); - REG_FUNC(cellFiber, cellFiberPpuCreateFiber); - REG_FUNC(cellFiber, cellFiberPpuExit); - REG_FUNC(cellFiber, cellFiberPpuYield); - REG_FUNC(cellFiber, cellFiberPpuJoinFiber); - REG_FUNC(cellFiber, cellFiberPpuSelf); - REG_FUNC(cellFiber, cellFiberPpuSendSignal); - REG_FUNC(cellFiber, cellFiberPpuWaitSignal); - REG_FUNC(cellFiber, cellFiberPpuWaitFlag); - REG_FUNC(cellFiber, cellFiberPpuGetScheduler); - REG_FUNC(cellFiber, cellFiberPpuSetPriority); - REG_FUNC(cellFiber, cellFiberPpuCheckStackLimit); + REG_FUNC_NR(cellFiber, _cellFiberPpuAttributeInitialize); + REG_FUNC_NR(cellFiber, cellFiberPpuCreateFiber); + REG_FUNC_NR(cellFiber, cellFiberPpuExit); + REG_FUNC_NR(cellFiber, cellFiberPpuYield); + REG_FUNC_NR(cellFiber, cellFiberPpuJoinFiber); + REG_FUNC_NR(cellFiber, cellFiberPpuSelf); + REG_FUNC_NR(cellFiber, cellFiberPpuSendSignal); + REG_FUNC_NR(cellFiber, cellFiberPpuWaitSignal); + REG_FUNC_NR(cellFiber, cellFiberPpuWaitFlag); + REG_FUNC_NR(cellFiber, cellFiberPpuGetScheduler); + REG_FUNC_NR(cellFiber, cellFiberPpuSetPriority); + REG_FUNC_NR(cellFiber, cellFiberPpuCheckStackLimit); - REG_FUNC(cellFiber, _cellFiberPpuContextAttributeInitialize); - REG_FUNC(cellFiber, cellFiberPpuContextInitialize); - REG_FUNC(cellFiber, cellFiberPpuContextFinalize); - REG_FUNC(cellFiber, cellFiberPpuContextRun); - REG_FUNC(cellFiber, cellFiberPpuContextSwitch); - REG_FUNC(cellFiber, cellFiberPpuContextSelf); - REG_FUNC(cellFiber, cellFiberPpuContextReturnToThread); - REG_FUNC(cellFiber, cellFiberPpuContextCheckStackLimit); + REG_FUNC_NR(cellFiber, _cellFiberPpuContextAttributeInitialize); + REG_FUNC_NR(cellFiber, cellFiberPpuContextInitialize); + REG_FUNC_NR(cellFiber, cellFiberPpuContextFinalize); + REG_FUNC_NR(cellFiber, cellFiberPpuContextRun); + REG_FUNC_NR(cellFiber, cellFiberPpuContextSwitch); + REG_FUNC_NR(cellFiber, cellFiberPpuContextSelf); + REG_FUNC_NR(cellFiber, cellFiberPpuContextReturnToThread); + REG_FUNC_NR(cellFiber, cellFiberPpuContextCheckStackLimit); - REG_FUNC(cellFiber, cellFiberPpuContextRunScheduler); - REG_FUNC(cellFiber, cellFiberPpuContextEnterScheduler); + REG_FUNC_NR(cellFiber, cellFiberPpuContextRunScheduler); + REG_FUNC_NR(cellFiber, cellFiberPpuContextEnterScheduler); - REG_FUNC(cellFiber, cellFiberPpuSchedulerTraceInitialize); - REG_FUNC(cellFiber, cellFiberPpuSchedulerTraceFinalize); - REG_FUNC(cellFiber, cellFiberPpuSchedulerTraceStart); - REG_FUNC(cellFiber, cellFiberPpuSchedulerTraceStop); + REG_FUNC_NR(cellFiber, cellFiberPpuSchedulerTraceInitialize); + REG_FUNC_NR(cellFiber, cellFiberPpuSchedulerTraceFinalize); + REG_FUNC_NR(cellFiber, cellFiberPpuSchedulerTraceStart); + REG_FUNC_NR(cellFiber, cellFiberPpuSchedulerTraceStop); - REG_FUNC(cellFiber, _cellFiberPpuUtilWorkerControlAttributeInitialize); - REG_FUNC(cellFiber, cellFiberPpuUtilWorkerControlRunFibers); - REG_FUNC(cellFiber, cellFiberPpuUtilWorkerControlInitialize); - REG_FUNC(cellFiber, cellFiberPpuUtilWorkerControlSetPollingMode); - REG_FUNC(cellFiber, cellFiberPpuUtilWorkerControlJoinFiber); - REG_FUNC(cellFiber, cellFiberPpuUtilWorkerControlDisconnectEventQueue); - REG_FUNC(cellFiber, cellFiberPpuUtilWorkerControlSendSignal); - REG_FUNC(cellFiber, cellFiberPpuUtilWorkerControlConnectEventQueueToSpurs); - REG_FUNC(cellFiber, cellFiberPpuUtilWorkerControlFinalize); - REG_FUNC(cellFiber, cellFiberPpuUtilWorkerControlWakeup); - REG_FUNC(cellFiber, cellFiberPpuUtilWorkerControlCreateFiber); - REG_FUNC(cellFiber, cellFiberPpuUtilWorkerControlShutdown); - REG_FUNC(cellFiber, cellFiberPpuUtilWorkerControlCheckFlags); - REG_FUNC(cellFiber, cellFiberPpuUtilWorkerControlInitializeWithAttribute); + REG_FUNC_NR(cellFiber, _cellFiberPpuUtilWorkerControlAttributeInitialize); + REG_FUNC_NR(cellFiber, cellFiberPpuUtilWorkerControlRunFibers); + REG_FUNC_NR(cellFiber, cellFiberPpuUtilWorkerControlInitialize); + REG_FUNC_NR(cellFiber, cellFiberPpuUtilWorkerControlSetPollingMode); + REG_FUNC_NR(cellFiber, cellFiberPpuUtilWorkerControlJoinFiber); + REG_FUNC_NR(cellFiber, cellFiberPpuUtilWorkerControlDisconnectEventQueue); + REG_FUNC_NR(cellFiber, cellFiberPpuUtilWorkerControlSendSignal); + REG_FUNC_NR(cellFiber, cellFiberPpuUtilWorkerControlConnectEventQueueToSpurs); + REG_FUNC_NR(cellFiber, cellFiberPpuUtilWorkerControlFinalize); + REG_FUNC_NR(cellFiber, cellFiberPpuUtilWorkerControlWakeup); + REG_FUNC_NR(cellFiber, cellFiberPpuUtilWorkerControlCreateFiber); + REG_FUNC_NR(cellFiber, cellFiberPpuUtilWorkerControlShutdown); + REG_FUNC_NR(cellFiber, cellFiberPpuUtilWorkerControlCheckFlags); + REG_FUNC_NR(cellFiber, cellFiberPpuUtilWorkerControlInitializeWithAttribute); });