mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 11:36:13 +00:00
llvm: Make Compiler class a module builder only class.
This commit is contained in:
parent
7ae1b51753
commit
8e451126ac
4 changed files with 209 additions and 211 deletions
|
@ -101,10 +101,13 @@ namespace ppu_recompiler_llvm {
|
|||
class Compiler;
|
||||
}
|
||||
|
||||
class ppu_llvm_test_class;
|
||||
|
||||
class PPUInterpreter : public PPUOpcodes
|
||||
{
|
||||
#ifdef PPU_LLVM_RECOMPILER
|
||||
friend class ppu_recompiler_llvm::Compiler;
|
||||
friend class ppu_llvm_test_class;
|
||||
#endif
|
||||
private:
|
||||
PPUThread& CPU;
|
||||
|
|
|
@ -45,25 +45,62 @@ using namespace ppu_recompiler_llvm;
|
|||
u64 Compiler::s_rotate_mask[64][64];
|
||||
bool Compiler::s_rotate_mask_inited = false;
|
||||
|
||||
Compiler::Compiler(RecompilationEngine & recompilation_engine, const Executable execute_unknown_function,
|
||||
const Executable execute_unknown_block, bool(*poll_status_function)(PPUThread * ppu_state))
|
||||
: m_recompilation_engine(recompilation_engine)
|
||||
, m_poll_status_function(poll_status_function) {
|
||||
InitializeNativeTarget();
|
||||
InitializeNativeTargetAsmPrinter();
|
||||
InitializeNativeTargetDisassembler();
|
||||
std::unique_ptr<Module> Compiler::create_module(LLVMContext &llvm_context)
|
||||
{
|
||||
const std::vector<Type *> arg_types = { Type::getInt8PtrTy(llvm_context), Type::getInt64Ty(llvm_context) };
|
||||
FunctionType *compiled_function_type = FunctionType::get(Type::getInt32Ty(llvm_context), arg_types, false);
|
||||
|
||||
m_llvm_context = new LLVMContext();
|
||||
m_ir_builder = new IRBuilder<>(*m_llvm_context);
|
||||
std::unique_ptr<llvm::Module> result(new llvm::Module("Module", llvm_context));
|
||||
Function *execute_unknown_function = (Function *)result->getOrInsertFunction("execute_unknown_function", compiled_function_type);
|
||||
execute_unknown_function->setCallingConv(CallingConv::X86_64_Win64);
|
||||
|
||||
Function *execute_unknown_block = (Function *)result->getOrInsertFunction("execute_unknown_block", compiled_function_type);
|
||||
execute_unknown_block->setCallingConv(CallingConv::X86_64_Win64);
|
||||
|
||||
std::string targetTriple = "x86_64-pc-windows-elf";
|
||||
result->setTargetTriple(targetTriple);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Compiler::optimise_module(llvm::Module *module)
|
||||
{
|
||||
llvm::FunctionPassManager fpm(module);
|
||||
fpm.add(createNoAAPass());
|
||||
fpm.add(createBasicAliasAnalysisPass());
|
||||
fpm.add(createNoTargetTransformInfoPass());
|
||||
fpm.add(createEarlyCSEPass());
|
||||
fpm.add(createTailCallEliminationPass());
|
||||
fpm.add(createReassociatePass());
|
||||
fpm.add(createInstructionCombiningPass());
|
||||
fpm.add(new DominatorTreeWrapperPass());
|
||||
fpm.add(new MemoryDependenceAnalysis());
|
||||
fpm.add(createGVNPass());
|
||||
fpm.add(createInstructionCombiningPass());
|
||||
fpm.add(new MemoryDependenceAnalysis());
|
||||
fpm.add(createDeadStoreEliminationPass());
|
||||
fpm.add(new LoopInfo());
|
||||
fpm.add(new ScalarEvolution());
|
||||
fpm.add(createSLPVectorizerPass());
|
||||
fpm.add(createInstructionCombiningPass());
|
||||
fpm.add(createCFGSimplificationPass());
|
||||
fpm.doInitialization();
|
||||
|
||||
for (auto I = module->begin(), E = module->end(); I != E; ++I)
|
||||
fpm.run(*I);
|
||||
}
|
||||
|
||||
|
||||
Compiler::Compiler(LLVMContext *context, llvm::IRBuilder<> *builder, std::unordered_map<std::string, void*> &function_ptrs)
|
||||
: m_llvm_context(context),
|
||||
m_ir_builder(builder),
|
||||
m_executable_map(function_ptrs) {
|
||||
|
||||
std::vector<Type *> arg_types;
|
||||
arg_types.push_back(m_ir_builder->getInt8PtrTy());
|
||||
arg_types.push_back(m_ir_builder->getInt64Ty());
|
||||
m_compiled_function_type = FunctionType::get(m_ir_builder->getInt32Ty(), arg_types, false);
|
||||
|
||||
m_executableMap["execute_unknown_function"] = execute_unknown_function;
|
||||
m_executableMap["execute_unknown_block"] = execute_unknown_block;
|
||||
|
||||
if (!s_rotate_mask_inited) {
|
||||
InitRotateMask();
|
||||
s_rotate_mask_inited = true;
|
||||
|
@ -71,54 +108,10 @@ Compiler::Compiler(RecompilationEngine & recompilation_engine, const Executable
|
|||
}
|
||||
|
||||
Compiler::~Compiler() {
|
||||
delete m_ir_builder;
|
||||
delete m_llvm_context;
|
||||
}
|
||||
|
||||
std::pair<Executable, llvm::ExecutionEngine *> Compiler::Compile(const std::string & name, u32 start_address, u32 instruction_count) {
|
||||
auto compilation_start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
m_module = new llvm::Module("Module", *m_llvm_context);
|
||||
m_execute_unknown_function = (Function *)m_module->getOrInsertFunction("execute_unknown_function", m_compiled_function_type);
|
||||
m_execute_unknown_function->setCallingConv(CallingConv::X86_64_Win64);
|
||||
|
||||
m_execute_unknown_block = (Function *)m_module->getOrInsertFunction("execute_unknown_block", m_compiled_function_type);
|
||||
m_execute_unknown_block->setCallingConv(CallingConv::X86_64_Win64);
|
||||
|
||||
std::string targetTriple = "x86_64-pc-windows-elf";
|
||||
m_module->setTargetTriple(targetTriple);
|
||||
|
||||
llvm::ExecutionEngine *execution_engine =
|
||||
EngineBuilder(std::unique_ptr<llvm::Module>(m_module))
|
||||
.setEngineKind(EngineKind::JIT)
|
||||
.setMCJITMemoryManager(std::unique_ptr<llvm::SectionMemoryManager>(new CustomSectionMemoryManager(m_executableMap)))
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setMCPU("nehalem")
|
||||
.create();
|
||||
m_module->setDataLayout(execution_engine->getDataLayout());
|
||||
|
||||
llvm::FunctionPassManager *fpm = new llvm::FunctionPassManager(m_module);
|
||||
fpm->add(createNoAAPass());
|
||||
fpm->add(createBasicAliasAnalysisPass());
|
||||
fpm->add(createNoTargetTransformInfoPass());
|
||||
fpm->add(createEarlyCSEPass());
|
||||
fpm->add(createTailCallEliminationPass());
|
||||
fpm->add(createReassociatePass());
|
||||
fpm->add(createInstructionCombiningPass());
|
||||
fpm->add(new DominatorTreeWrapperPass());
|
||||
fpm->add(new MemoryDependenceAnalysis());
|
||||
fpm->add(createGVNPass());
|
||||
fpm->add(createInstructionCombiningPass());
|
||||
fpm->add(new MemoryDependenceAnalysis());
|
||||
fpm->add(createDeadStoreEliminationPass());
|
||||
fpm->add(new LoopInfo());
|
||||
fpm->add(new ScalarEvolution());
|
||||
fpm->add(createSLPVectorizerPass());
|
||||
fpm->add(createInstructionCombiningPass());
|
||||
fpm->add(createCFGSimplificationPass());
|
||||
fpm->doInitialization();
|
||||
|
||||
// Create the function
|
||||
void Compiler::initiate_function(const std::string &name)
|
||||
{
|
||||
m_state.function = (Function *)m_module->getOrInsertFunction(name, m_compiled_function_type);
|
||||
m_state.function->setCallingConv(CallingConv::X86_64_Win64);
|
||||
auto arg_i = m_state.function->arg_begin();
|
||||
|
@ -126,6 +119,16 @@ std::pair<Executable, llvm::ExecutionEngine *> Compiler::Compile(const std::stri
|
|||
m_state.args[CompileTaskState::Args::State] = arg_i;
|
||||
(++arg_i)->setName("context");
|
||||
m_state.args[CompileTaskState::Args::Context] = arg_i;
|
||||
}
|
||||
|
||||
void ppu_recompiler_llvm::Compiler::translate_to_llvm_ir(llvm::Module *module, const std::string & name, u32 start_address, u32 instruction_count)
|
||||
{
|
||||
m_module = module;
|
||||
|
||||
m_execute_unknown_function = module->getFunction("execute_unknown_function");
|
||||
m_execute_unknown_block = module->getFunction("execute_unknown_block");
|
||||
|
||||
initiate_function(name);
|
||||
|
||||
// Create the entry block and add code to branch to the first instruction
|
||||
m_ir_builder->SetInsertPoint(GetBasicBlockFromAddress(0));
|
||||
|
@ -135,7 +138,7 @@ std::pair<Executable, llvm::ExecutionEngine *> Compiler::Compile(const std::stri
|
|||
PPUDisAsm dis_asm(CPUDisAsm_DumpMode);
|
||||
dis_asm.offset = vm::ps3::_ptr<u8>(start_address);
|
||||
|
||||
m_recompilation_engine.Log() << "Recompiling block :\n\n";
|
||||
// m_recompilation_engine.Log() << "Recompiling block :\n\n";
|
||||
|
||||
// Convert each instruction in the CFG to LLVM IR
|
||||
std::vector<PHINode *> exit_instr_list;
|
||||
|
@ -150,7 +153,7 @@ std::pair<Executable, llvm::ExecutionEngine *> Compiler::Compile(const std::stri
|
|||
// Dump PPU opcode
|
||||
dis_asm.dump_pc = instructionAddress;
|
||||
(*PPU_instr::main_list)(&dis_asm, instr);
|
||||
m_recompilation_engine.Log() << dis_asm.last_opcode;
|
||||
// m_recompilation_engine.Log() << dis_asm.last_opcode;
|
||||
|
||||
Decode(instr);
|
||||
if (!m_state.hit_branch_instruction)
|
||||
|
@ -194,51 +197,17 @@ std::pair<Executable, llvm::ExecutionEngine *> Compiler::Compile(const std::stri
|
|||
}
|
||||
}
|
||||
|
||||
m_recompilation_engine.Log() << "LLVM bytecode:\n";
|
||||
m_recompilation_engine.Log() << *m_module;
|
||||
// m_recompilation_engine.Log() << "LLVM bytecode:\n";
|
||||
// m_recompilation_engine.Log() << *m_module;
|
||||
|
||||
std::string verify;
|
||||
raw_string_ostream verify_ostream(verify);
|
||||
if (verifyFunction(*m_state.function, &verify_ostream)) {
|
||||
m_recompilation_engine.Log() << "Verification failed: " << verify_ostream.str() << "\n";
|
||||
// m_recompilation_engine.Log() << "Verification failed: " << verify_ostream.str() << "\n";
|
||||
}
|
||||
|
||||
auto ir_build_end = std::chrono::high_resolution_clock::now();
|
||||
m_stats.ir_build_time += std::chrono::duration_cast<std::chrono::nanoseconds>(ir_build_end - compilation_start);
|
||||
|
||||
// Optimize this function
|
||||
fpm->run(*m_state.function);
|
||||
auto optimize_end = std::chrono::high_resolution_clock::now();
|
||||
m_stats.optimization_time += std::chrono::duration_cast<std::chrono::nanoseconds>(optimize_end - ir_build_end);
|
||||
|
||||
// Translate to machine code
|
||||
execution_engine->finalizeObject();
|
||||
void *function = execution_engine->getPointerToFunction(m_state.function);
|
||||
auto translate_end = std::chrono::high_resolution_clock::now();
|
||||
m_stats.translation_time += std::chrono::duration_cast<std::chrono::nanoseconds>(translate_end - optimize_end);
|
||||
|
||||
/* m_recompilation_engine.Log() << "\nDisassembly:\n";
|
||||
auto disassembler = LLVMCreateDisasm(sys::getProcessTriple().c_str(), nullptr, 0, nullptr, nullptr);
|
||||
for (size_t pc = 0; pc < mci.size();) {
|
||||
char str[1024];
|
||||
|
||||
auto size = LLVMDisasmInstruction(disassembler, ((u8 *)mci.address()) + pc, mci.size() - pc, (uint64_t)(((u8 *)mci.address()) + pc), str, sizeof(str));
|
||||
m_recompilation_engine.Log() << fmt::format("0x%08X: ", (u64)(((u8 *)mci.address()) + pc)) << str << '\n';
|
||||
pc += size;
|
||||
}
|
||||
|
||||
LLVMDisasmDispose(disassembler);*/
|
||||
|
||||
auto compilation_end = std::chrono::high_resolution_clock::now();
|
||||
m_stats.total_time += std::chrono::duration_cast<std::chrono::nanoseconds>(compilation_end - compilation_start);
|
||||
delete fpm;
|
||||
|
||||
assert(function != nullptr);
|
||||
return std::make_pair((Executable)function, execution_engine);
|
||||
}
|
||||
|
||||
Compiler::Stats Compiler::GetStats() {
|
||||
return m_stats;
|
||||
m_module = nullptr;
|
||||
m_state.function = nullptr;
|
||||
}
|
||||
|
||||
void Compiler::Decode(const u32 code) {
|
||||
|
@ -252,7 +221,12 @@ RecompilationEngine::RecompilationEngine()
|
|||
: m_log(nullptr)
|
||||
, m_currentId(0)
|
||||
, m_last_cache_clear_time(std::chrono::high_resolution_clock::now())
|
||||
, m_compiler(*this, CPUHybridDecoderRecompiler::ExecuteFunction, CPUHybridDecoderRecompiler::ExecuteTillReturn, CPUHybridDecoderRecompiler::PollStatus) {
|
||||
, m_llvm_context(getGlobalContext())
|
||||
, m_ir_builder(getGlobalContext()) {
|
||||
InitializeNativeTarget();
|
||||
InitializeNativeTargetAsmPrinter();
|
||||
InitializeNativeTargetDisassembler();
|
||||
|
||||
FunctionCache = (ExecutableStorageType *)memory_helper::reserve_memory(VIRTUAL_INSTRUCTION_COUNT * sizeof(ExecutableStorageType));
|
||||
// Each char can store 8 page status
|
||||
FunctionCachePagesCommited = (char *)malloc(VIRTUAL_INSTRUCTION_COUNT / (8 * PAGE_SIZE));
|
||||
|
@ -350,20 +324,6 @@ void RecompilationEngine::on_task() {
|
|||
}
|
||||
}
|
||||
|
||||
std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
|
||||
auto total_time = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);
|
||||
auto compiler_stats = m_compiler.GetStats();
|
||||
|
||||
Log() << "Total time = " << total_time.count() / 1000000 << "ms\n";
|
||||
Log() << " Time spent compiling = " << compiler_stats.total_time.count() / 1000000 << "ms\n";
|
||||
Log() << " Time spent building IR = " << compiler_stats.ir_build_time.count() / 1000000 << "ms\n";
|
||||
Log() << " Time spent optimizing = " << compiler_stats.optimization_time.count() / 1000000 << "ms\n";
|
||||
Log() << " Time spent translating = " << compiler_stats.translation_time.count() / 1000000 << "ms\n";
|
||||
Log() << " Time spent recompiling = " << recompiling_time.count() / 1000000 << "ms\n";
|
||||
Log() << " Time spent idling = " << idling_time.count() / 1000000 << "ms\n";
|
||||
Log() << " Time spent doing misc tasks = " << (total_time.count() - idling_time.count() - compiler_stats.total_time.count()) / 1000000 << "ms\n";
|
||||
|
||||
LOG_NOTICE(PPU, "PPU LLVM Recompilation thread exiting.");
|
||||
s_the_instance = nullptr; // Can cause deadlock if this is the last instance. Need to fix this.
|
||||
}
|
||||
|
||||
|
@ -382,6 +342,92 @@ bool RecompilationEngine::IncreaseHitCounterAndBuild(u32 address) {
|
|||
return false;
|
||||
}
|
||||
|
||||
extern void execute_ppu_func_by_index(PPUThread& ppu, u32 id);
|
||||
extern void execute_syscall_by_index(PPUThread& ppu, u64 code);
|
||||
|
||||
static u32
|
||||
wrappedExecutePPUFuncByIndex(PPUThread &CPU, u32 index) noexcept {
|
||||
try
|
||||
{
|
||||
execute_ppu_func_by_index(CPU, index);
|
||||
return ExecutionStatus::ExecutionStatusBlockEnded;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CPU.pending_exception = std::current_exception();
|
||||
return ExecutionStatus::ExecutionStatusPropagateException;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 wrappedDoSyscall(PPUThread &CPU, u64 code) noexcept {
|
||||
try
|
||||
{
|
||||
execute_syscall_by_index(CPU, code);
|
||||
return ExecutionStatus::ExecutionStatusBlockEnded;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CPU.pending_exception = std::current_exception();
|
||||
return ExecutionStatus::ExecutionStatusPropagateException;
|
||||
}
|
||||
}
|
||||
|
||||
static void wrapped_fast_stop(PPUThread &CPU)
|
||||
{
|
||||
CPU.fast_stop();
|
||||
}
|
||||
|
||||
std::pair<Executable, llvm::ExecutionEngine *> RecompilationEngine::compile(const std::string & name, u32 start_address, u32 instruction_count) {
|
||||
std::unique_ptr<llvm::Module> module = Compiler::create_module(m_llvm_context);
|
||||
|
||||
std::unordered_map<std::string, void*> function_ptrs;
|
||||
function_ptrs["execute_unknown_function"] = reinterpret_cast<void*>(CPUHybridDecoderRecompiler::ExecuteFunction);
|
||||
function_ptrs["execute_unknown_block"] = reinterpret_cast<void*>(CPUHybridDecoderRecompiler::ExecuteTillReturn);
|
||||
function_ptrs["PollStatus"] = reinterpret_cast<void*>(CPUHybridDecoderRecompiler::PollStatus);
|
||||
function_ptrs["PPUThread.fast_stop"] = reinterpret_cast<void*>(wrapped_fast_stop);
|
||||
function_ptrs["vm.reservation_acquire"] = reinterpret_cast<void*>(vm::reservation_acquire);
|
||||
function_ptrs["vm.reservation_update"] = reinterpret_cast<void*>(vm::reservation_update);
|
||||
function_ptrs["get_timebased_time"] = reinterpret_cast<void*>(get_timebased_time);
|
||||
function_ptrs["wrappedExecutePPUFuncByIndex"] = reinterpret_cast<void*>(wrappedExecutePPUFuncByIndex);
|
||||
function_ptrs["wrappedDoSyscall"] = reinterpret_cast<void*>(wrappedDoSyscall);
|
||||
|
||||
Compiler(&m_llvm_context, &m_ir_builder, function_ptrs)
|
||||
.translate_to_llvm_ir(module.get(), name, start_address, instruction_count);
|
||||
Compiler::optimise_module(module.get());
|
||||
llvm::Module *module_ptr = module.get();
|
||||
|
||||
|
||||
llvm::ExecutionEngine *execution_engine =
|
||||
EngineBuilder(std::move(module))
|
||||
.setEngineKind(EngineKind::JIT)
|
||||
.setMCJITMemoryManager(std::unique_ptr<llvm::SectionMemoryManager>(new CustomSectionMemoryManager(function_ptrs)))
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setMCPU("nehalem")
|
||||
.create();
|
||||
module_ptr->setDataLayout(execution_engine->getDataLayout());
|
||||
|
||||
// Translate to machine code
|
||||
execution_engine->finalizeObject();
|
||||
|
||||
Function *llvm_function = module_ptr->getFunction(name);
|
||||
void *function = execution_engine->getPointerToFunction(llvm_function);
|
||||
|
||||
/* m_recompilation_engine.Log() << "\nDisassembly:\n";
|
||||
auto disassembler = LLVMCreateDisasm(sys::getProcessTriple().c_str(), nullptr, 0, nullptr, nullptr);
|
||||
for (size_t pc = 0; pc < mci.size();) {
|
||||
char str[1024];
|
||||
|
||||
auto size = LLVMDisasmInstruction(disassembler, ((u8 *)mci.address()) + pc, mci.size() - pc, (uint64_t)(((u8 *)mci.address()) + pc), str, sizeof(str));
|
||||
m_recompilation_engine.Log() << fmt::format("0x%08X: ", (u64)(((u8 *)mci.address()) + pc)) << str << '\n';
|
||||
pc += size;
|
||||
}
|
||||
|
||||
LLVMDisasmDispose(disassembler);*/
|
||||
|
||||
assert(function != nullptr);
|
||||
return std::make_pair((Executable)function, execution_engine);
|
||||
}
|
||||
|
||||
/**
|
||||
* This code is inspired from Dolphin PPC Analyst
|
||||
*/
|
||||
|
@ -464,7 +510,7 @@ void RecompilationEngine::CompileBlock(BlockEntry & block_entry) {
|
|||
std::unique_lock<std::mutex> lock(local_mutex);
|
||||
|
||||
const std::pair<Executable, llvm::ExecutionEngine *> &compileResult =
|
||||
m_compiler.Compile(fmt::format("fn_0x%08X", block_entry.address), block_entry.address, block_entry.instructionCount);
|
||||
compile(fmt::format("fn_0x%08X", block_entry.address), block_entry.address, block_entry.instructionCount);
|
||||
|
||||
if (!isAddressCommited(block_entry.address / 4))
|
||||
commitAddress(block_entry.address / 4);
|
||||
|
|
|
@ -46,38 +46,22 @@ namespace ppu_recompiler_llvm {
|
|||
/// Pointer to an executable
|
||||
typedef u32(*Executable)(PPUThread * ppu_state, u64 context);
|
||||
|
||||
/// PPU compiler that uses LLVM for code generation and optimization
|
||||
/// Parses PPU opcodes and translate them into llvm ir.
|
||||
class Compiler : protected PPUOpcodes, protected PPCDecoder {
|
||||
public:
|
||||
struct Stats {
|
||||
/// Time spent building the LLVM IR
|
||||
std::chrono::nanoseconds ir_build_time;
|
||||
|
||||
/// Time spent optimizing
|
||||
std::chrono::nanoseconds optimization_time;
|
||||
|
||||
/// Time spent translating LLVM IR to machine code
|
||||
std::chrono::nanoseconds translation_time;
|
||||
|
||||
/// Total time
|
||||
std::chrono::nanoseconds total_time;
|
||||
};
|
||||
|
||||
Compiler(RecompilationEngine & recompilation_engine, const Executable execute_unknown_function,
|
||||
const Executable execute_unknown_block, bool(*poll_status_function)(PPUThread * ppu_state));
|
||||
Compiler(llvm::LLVMContext *context, llvm::IRBuilder<> *builder, std::unordered_map<std::string, void*> &function_ptrs);
|
||||
|
||||
Compiler(const Compiler&) = delete; // Delete copy/move constructors and copy/move operators
|
||||
|
||||
virtual ~Compiler();
|
||||
|
||||
/**
|
||||
* Compile a code fragment described by a cfg and return an executable and the ExecutionEngine storing it
|
||||
* Pointer to function can be retrieved with getPointerToFunction
|
||||
*/
|
||||
std::pair<Executable, llvm::ExecutionEngine *> Compile(const std::string & name, u32 start_address, u32 instruction_count);
|
||||
/// Create a module setting target triples and some callbacks
|
||||
static std::unique_ptr<llvm::Module> create_module(llvm::LLVMContext &llvm_context);
|
||||
|
||||
/// Retrieve compiler stats
|
||||
Stats GetStats();
|
||||
/// Create a function called name in module and populates it by translating block at start_address with instruction_count length.
|
||||
void translate_to_llvm_ir(llvm::Module *module, const std::string & name, u32 start_address, u32 instruction_count);
|
||||
|
||||
static void optimise_module(llvm::Module *module);
|
||||
|
||||
protected:
|
||||
void Decode(const u32 code) override;
|
||||
|
@ -484,7 +468,10 @@ namespace ppu_recompiler_llvm {
|
|||
|
||||
void UNK(const u32 code, const u32 opcode, const u32 gcode) override;
|
||||
|
||||
private:
|
||||
/// Utility function creating a function called name with Executable signature
|
||||
void initiate_function(const std::string &name);
|
||||
|
||||
protected:
|
||||
/// State of a compilation task
|
||||
struct CompileTaskState {
|
||||
enum Args {
|
||||
|
@ -508,12 +495,6 @@ namespace ppu_recompiler_llvm {
|
|||
bool hit_branch_instruction;
|
||||
};
|
||||
|
||||
/// Recompilation engine
|
||||
RecompilationEngine & m_recompilation_engine;
|
||||
|
||||
/// The function that should be called to check the status of the thread
|
||||
bool(*m_poll_status_function)(PPUThread * ppu_state);
|
||||
|
||||
/// The function that will be called to execute unknown functions
|
||||
llvm::Function * m_execute_unknown_function;
|
||||
|
||||
|
@ -521,7 +502,7 @@ namespace ppu_recompiler_llvm {
|
|||
llvm::Function * m_execute_unknown_block;
|
||||
|
||||
/// Maps function name to executable memory pointer
|
||||
std::unordered_map<std::string, Executable> m_executableMap;
|
||||
std::unordered_map<std::string, void*> &m_executable_map;
|
||||
|
||||
/// LLVM context
|
||||
llvm::LLVMContext * m_llvm_context;
|
||||
|
@ -538,9 +519,6 @@ namespace ppu_recompiler_llvm {
|
|||
/// State of the current compilation task
|
||||
CompileTaskState m_state;
|
||||
|
||||
/// Compiler stats
|
||||
Stats m_stats;
|
||||
|
||||
/// Get the name of the basic block for the specified address
|
||||
std::string GetBasicBlockNameFromAddress(u32 address, const std::string & suffix = "") const;
|
||||
|
||||
|
@ -726,29 +704,22 @@ namespace ppu_recompiler_llvm {
|
|||
}
|
||||
|
||||
/// Call a function
|
||||
template<class ReturnType, class Func, class... Args>
|
||||
llvm::Value * Call(const char * name, Func function, Args... args) {
|
||||
template<class ReturnType, class... Args>
|
||||
llvm::Value * Call(const char * name, Args... args) {
|
||||
auto fn = m_module->getFunction(name);
|
||||
if (!fn) {
|
||||
std::vector<llvm::Type *> fn_args_type = { args->getType()... };
|
||||
auto fn_type = llvm::FunctionType::get(CppToLlvmType<ReturnType>(), fn_args_type, false);
|
||||
fn = llvm::cast<llvm::Function>(m_module->getOrInsertFunction(name, fn_type));
|
||||
fn->setCallingConv(llvm::CallingConv::X86_64_Win64);
|
||||
// Note: not threadsafe
|
||||
m_executableMap[name] = (Executable)(void *&)function;
|
||||
// Create an entry in m_executable_map that will be populated outside of compiler
|
||||
(void)m_executable_map[name];
|
||||
}
|
||||
|
||||
std::vector<llvm::Value *> fn_args = { args... };
|
||||
return m_ir_builder->CreateCall(fn, fn_args);
|
||||
}
|
||||
|
||||
/// Test an instruction against the interpreter
|
||||
template <class... Args>
|
||||
void VerifyInstructionAgainstInterpreter(const char * name, void (Compiler::*recomp_fn)(Args...), void (PPUInterpreter::*interp_fn)(Args...), PPUState & input_state, Args... args);
|
||||
|
||||
/// Excute a test
|
||||
void RunTest(const char * name, std::function<void()> test_case, std::function<void()> input, std::function<bool(std::string & msg)> check_result);
|
||||
|
||||
/// Handle compilation errors
|
||||
void CompilationError(const std::string & error);
|
||||
|
||||
|
@ -870,12 +841,22 @@ namespace ppu_recompiler_llvm {
|
|||
/// vector storing all exec engine
|
||||
std::vector<std::unique_ptr<llvm::ExecutionEngine> > m_executable_storage;
|
||||
|
||||
|
||||
/// LLVM context
|
||||
llvm::LLVMContext &m_llvm_context;
|
||||
|
||||
/// LLVM IR builder
|
||||
llvm::IRBuilder<> m_ir_builder;
|
||||
|
||||
/**
|
||||
* Compile a code fragment described by a cfg and return an executable and the ExecutionEngine storing it
|
||||
* Pointer to function can be retrieved with getPointerToFunction
|
||||
*/
|
||||
std::pair<Executable, llvm::ExecutionEngine *> compile(const std::string & name, u32 start_address, u32 instruction_count);
|
||||
|
||||
/// The time at which the m_address_to_ordinal cache was last cleared
|
||||
std::chrono::high_resolution_clock::time_point m_last_cache_clear_time;
|
||||
|
||||
/// PPU Compiler
|
||||
Compiler m_compiler;
|
||||
|
||||
RecompilationEngine();
|
||||
|
||||
RecompilationEngine(const RecompilationEngine&) = delete; // Delete copy/move constructors and copy/move operators
|
||||
|
@ -944,16 +925,16 @@ namespace ppu_recompiler_llvm {
|
|||
|
||||
class CustomSectionMemoryManager : public llvm::SectionMemoryManager {
|
||||
private:
|
||||
std::unordered_map<std::string, Executable> &executableMap;
|
||||
std::unordered_map<std::string, void*> &executableMap;
|
||||
public:
|
||||
CustomSectionMemoryManager(std::unordered_map<std::string, Executable> &map) :
|
||||
CustomSectionMemoryManager(std::unordered_map<std::string, void*> &map) :
|
||||
executableMap(map)
|
||||
{}
|
||||
~CustomSectionMemoryManager() override {}
|
||||
|
||||
virtual uint64_t getSymbolAddress(const std::string &Name) override
|
||||
{
|
||||
std::unordered_map<std::string, Executable>::const_iterator It = executableMap.find(Name);
|
||||
std::unordered_map<std::string, void*>::const_iterator It = executableMap.find(Name);
|
||||
if (It != executableMap.end())
|
||||
return (uint64_t)It->second;
|
||||
return getSymbolAddressInProcess(Name);
|
||||
|
|
|
@ -29,9 +29,6 @@
|
|||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
extern void execute_ppu_func_by_index(PPUThread& ppu, u32 id);
|
||||
extern void execute_syscall_by_index(PPUThread& ppu, u64 code);
|
||||
|
||||
using namespace llvm;
|
||||
using namespace ppu_recompiler_llvm;
|
||||
|
||||
|
@ -1755,24 +1752,8 @@ void Compiler::BC(u32 bo, u32 bi, s32 bd, u32 aa, u32 lk) {
|
|||
CreateBranch(CheckBranchCondition(bo, bi), target_i32, lk ? true : false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static u32
|
||||
wrappedExecutePPUFuncByIndex(PPUThread &CPU, u32 index) noexcept {
|
||||
try
|
||||
{
|
||||
execute_ppu_func_by_index(CPU, index);
|
||||
return ExecutionStatus::ExecutionStatusBlockEnded;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CPU.pending_exception = std::current_exception();
|
||||
return ExecutionStatus::ExecutionStatusPropagateException;
|
||||
}
|
||||
}
|
||||
|
||||
void Compiler::HACK(u32 index) {
|
||||
llvm::Value *status = Call<u32>("wrappedExecutePPUFuncByIndex", &wrappedExecutePPUFuncByIndex, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt32(index & EIF_USE_BRANCH ? index : index & ~EIF_PERFORM_BLR));
|
||||
llvm::Value *status = Call<u32>("wrappedExecutePPUFuncByIndex", m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt32(index & EIF_USE_BRANCH ? index : index & ~EIF_PERFORM_BLR));
|
||||
llvm::BasicBlock *cputhreadexitblock = GetBasicBlockFromAddress(m_state.current_instruction_address, "early_exit");
|
||||
llvm::Value *isCPUThreadExit = m_ir_builder->CreateICmpEQ(status, m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusPropagateException));
|
||||
llvm::BasicBlock *normal_execution = GetBasicBlockFromAddress(m_state.current_instruction_address, "normal_execution");
|
||||
|
@ -1787,24 +1768,11 @@ void Compiler::HACK(u32 index) {
|
|||
}
|
||||
}
|
||||
|
||||
static u32 wrappedDoSyscall(PPUThread &CPU, u64 code) noexcept {
|
||||
try
|
||||
{
|
||||
execute_syscall_by_index(CPU, code);
|
||||
return ExecutionStatus::ExecutionStatusBlockEnded;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CPU.pending_exception = std::current_exception();
|
||||
return ExecutionStatus::ExecutionStatusPropagateException;
|
||||
}
|
||||
}
|
||||
|
||||
void Compiler::SC(u32 lev) {
|
||||
switch (lev) {
|
||||
case 0:
|
||||
{
|
||||
llvm::Value *status = Call<u32>("wrappedDoSyscall", &wrappedDoSyscall, m_state.args[CompileTaskState::Args::State], GetGpr(11));
|
||||
llvm::Value *status = Call<u32>("wrappedDoSyscall", m_state.args[CompileTaskState::Args::State], GetGpr(11));
|
||||
llvm::BasicBlock *cputhreadexitblock = GetBasicBlockFromAddress(m_state.current_instruction_address, "early_exit");
|
||||
llvm::Value *isCPUThreadExit = m_ir_builder->CreateICmpEQ(status, m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusPropagateException));
|
||||
llvm::BasicBlock *normal_execution = GetBasicBlockFromAddress(m_state.current_instruction_address, "normal_execution");
|
||||
|
@ -1815,14 +1783,14 @@ void Compiler::SC(u32 lev) {
|
|||
}
|
||||
break;
|
||||
case 3:
|
||||
Call<void>("PPUThread.fast_stop", &PPUThread::fast_stop, m_state.args[CompileTaskState::Args::State]);
|
||||
Call<void>("PPUThread.fast_stop", m_state.args[CompileTaskState::Args::State]);
|
||||
break;
|
||||
default:
|
||||
CompilationError(fmt::format("SC %u", lev));
|
||||
break;
|
||||
}
|
||||
|
||||
auto ret_i1 = Call<bool>("PollStatus", m_poll_status_function, m_state.args[CompileTaskState::Args::State]);
|
||||
auto ret_i1 = Call<bool>("PollStatus", m_state.args[CompileTaskState::Args::State]);
|
||||
auto cmp_i1 = m_ir_builder->CreateICmpEQ(ret_i1, m_ir_builder->getInt1(true));
|
||||
auto then_bb = GetBasicBlockFromAddress(m_state.current_instruction_address, "then_true");
|
||||
auto merge_bb = GetBasicBlockFromAddress(m_state.current_instruction_address, "merge_true");
|
||||
|
@ -2293,7 +2261,7 @@ void Compiler::LWARX(u32 rd, u32 ra, u32 rb) {
|
|||
auto addr_i32 = m_ir_builder->CreateTrunc(addr_i64, m_ir_builder->getInt32Ty());
|
||||
auto val_i32_ptr = m_ir_builder->CreateAlloca(m_ir_builder->getInt32Ty());
|
||||
val_i32_ptr->setAlignment(4);
|
||||
Call<bool>("vm.reservation_acquire", vm::reservation_acquire, m_ir_builder->CreateBitCast(val_i32_ptr, m_ir_builder->getInt8PtrTy()), addr_i32, m_ir_builder->getInt32(4));
|
||||
Call<bool>("vm.reservation_acquire", m_ir_builder->CreateBitCast(val_i32_ptr, m_ir_builder->getInt8PtrTy()), addr_i32, m_ir_builder->getInt32(4));
|
||||
auto val_i32 = (Value *)m_ir_builder->CreateLoad(val_i32_ptr);
|
||||
val_i32 = m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::bswap, m_ir_builder->getInt32Ty()), val_i32);
|
||||
auto val_i64 = m_ir_builder->CreateZExt(val_i32, m_ir_builder->getInt64Ty());
|
||||
|
@ -2565,7 +2533,7 @@ void Compiler::LDARX(u32 rd, u32 ra, u32 rb) {
|
|||
auto addr_i32 = m_ir_builder->CreateTrunc(addr_i64, m_ir_builder->getInt32Ty());
|
||||
auto val_i64_ptr = m_ir_builder->CreateAlloca(m_ir_builder->getInt64Ty());
|
||||
val_i64_ptr->setAlignment(8);
|
||||
Call<bool>("vm.reservation_acquire", vm::reservation_acquire, m_ir_builder->CreateBitCast(val_i64_ptr, m_ir_builder->getInt8PtrTy()), addr_i32, m_ir_builder->getInt32(8));
|
||||
Call<bool>("vm.reservation_acquire", m_ir_builder->CreateBitCast(val_i64_ptr, m_ir_builder->getInt8PtrTy()), addr_i32, m_ir_builder->getInt32(8));
|
||||
auto val_i64 = (Value *)m_ir_builder->CreateLoad(val_i64_ptr);
|
||||
val_i64 = m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::bswap, m_ir_builder->getInt64Ty()), val_i64);
|
||||
SetGpr(rd, val_i64);
|
||||
|
@ -2744,7 +2712,7 @@ void Compiler::STWCX_(u32 rs, u32 ra, u32 rb) {
|
|||
auto rs_i32_ptr = m_ir_builder->CreateAlloca(m_ir_builder->getInt32Ty());
|
||||
rs_i32_ptr->setAlignment(4);
|
||||
m_ir_builder->CreateStore(rs_i32, rs_i32_ptr);
|
||||
auto success_i1 = Call<bool>("vm.reservation_update", vm::reservation_update, addr_i32, m_ir_builder->CreateBitCast(rs_i32_ptr, m_ir_builder->getInt8PtrTy()), m_ir_builder->getInt32(4));
|
||||
auto success_i1 = Call<bool>("vm.reservation_update", addr_i32, m_ir_builder->CreateBitCast(rs_i32_ptr, m_ir_builder->getInt8PtrTy()), m_ir_builder->getInt32(4));
|
||||
|
||||
auto cr_i32 = GetCr();
|
||||
cr_i32 = SetBit(cr_i32, 2, success_i1);
|
||||
|
@ -2863,7 +2831,7 @@ void Compiler::STDCX_(u32 rs, u32 ra, u32 rb) {
|
|||
auto rs_i64_ptr = m_ir_builder->CreateAlloca(m_ir_builder->getInt64Ty());
|
||||
rs_i64_ptr->setAlignment(8);
|
||||
m_ir_builder->CreateStore(rs_i64, rs_i64_ptr);
|
||||
auto success_i1 = Call<bool>("vm.reservation_update", vm::reservation_update, addr_i32, m_ir_builder->CreateBitCast(rs_i64_ptr, m_ir_builder->getInt8PtrTy()), m_ir_builder->getInt32(8));
|
||||
auto success_i1 = Call<bool>("vm.reservation_update", addr_i32, m_ir_builder->CreateBitCast(rs_i64_ptr, m_ir_builder->getInt8PtrTy()), m_ir_builder->getInt32(8));
|
||||
|
||||
auto cr_i32 = GetCr();
|
||||
cr_i32 = SetBit(cr_i32, 2, success_i1);
|
||||
|
@ -3084,10 +3052,10 @@ void Compiler::MFSPR(u32 rd, u32 spr) {
|
|||
rd_i64 = GetVrsave();
|
||||
break;
|
||||
case 0x10C:
|
||||
rd_i64 = Call<u64>("get_timebased_time", get_timebased_time);
|
||||
rd_i64 = Call<u64>("get_timebased_time");
|
||||
break;
|
||||
case 0x10D:
|
||||
rd_i64 = Call<u64>("get_timebased_time", get_timebased_time);
|
||||
rd_i64 = Call<u64>("get_timebased_time");
|
||||
rd_i64 = m_ir_builder->CreateLShr(rd_i64, 32);
|
||||
break;
|
||||
default:
|
||||
|
@ -3132,7 +3100,7 @@ void Compiler::LVXL(u32 vd, u32 ra, u32 rb) {
|
|||
}
|
||||
|
||||
void Compiler::MFTB(u32 rd, u32 spr) {
|
||||
auto tb_i64 = Call<u64>("get_timebased_time", get_timebased_time);
|
||||
auto tb_i64 = Call<u64>("get_timebased_time");
|
||||
|
||||
u32 n = (spr >> 5) | ((spr & 0x1f) << 5);
|
||||
if (n == 0x10D) {
|
||||
|
@ -5244,7 +5212,7 @@ void Compiler::CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i32, bool
|
|||
// if (fn)
|
||||
// execStatus = m_ir_builder->CreateCall2(fn, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt64(0));
|
||||
// else
|
||||
execStatus = Call<u32>("execute_unknown_function", nullptr, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt64(0));
|
||||
execStatus = Call<u32>("execute_unknown_function", m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt64(0));
|
||||
|
||||
llvm::BasicBlock *cputhreadexitblock = GetBasicBlockFromAddress(m_state.current_instruction_address, "early_exit");
|
||||
llvm::Value *isCPUThreadExit = m_ir_builder->CreateICmpEQ(execStatus, m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusPropagateException));
|
||||
|
|
Loading…
Add table
Reference in a new issue