// Copyright 2013 Dolphin Emulator Project // Licensed under GPLv2 // Refer to the license.txt file included. #include #include #include // Bochs #include #if defined(HAS_LLVM) // PowerPC.h defines PC. // This conflicts with a function that has an argument named PC #undef PC #include #include #endif #include #include #include #include #include #include "Common/CommonTypes.h" #include "Common/GekkoDisassembler.h" #include "Common/StringUtil.h" #include "Core/PowerPC/Gekko.h" #include "Core/PowerPC/PPCAnalyst.h" #include "Core/PowerPC/JitCommon/JitBase.h" #include "Core/PowerPC/JitCommon/JitCache.h" #include "DolphinWX/Globals.h" #include "DolphinWX/WxUtils.h" #include "DolphinWX/Debugger/JitWindow.h" #if defined(HAS_LLVM) // This class declaration should be in the header // Due to the conflict with the PC define and the function with PC as an argument // it has to be in this file instead. // Once that conflict is resolved this can be moved to the header class HostDisassemblerLLVM : public HostDisassembler { public: HostDisassemblerLLVM(const std::string host_disasm, int inst_size = -1, const std::string cpu = ""); ~HostDisassemblerLLVM() { if (m_can_disasm) LLVMDisasmDispose(m_llvm_context); } private: bool m_can_disasm; LLVMDisasmContextRef m_llvm_context; int m_instruction_size; std::string DisassembleHostBlock(const u8* code_start, const u32 code_size, u32* host_instructions_count) override; }; HostDisassemblerLLVM::HostDisassemblerLLVM(const std::string host_disasm, int inst_size, const std::string cpu) : m_can_disasm(false), m_instruction_size(inst_size) { LLVMInitializeAllTargetInfos(); LLVMInitializeAllTargetMCs(); LLVMInitializeAllDisassemblers(); m_llvm_context = LLVMCreateDisasmCPU(host_disasm.c_str(), cpu.c_str(), nullptr, 0, 0, nullptr); // Couldn't create llvm context if (!m_llvm_context) return; LLVMSetDisasmOptions(m_llvm_context, LLVMDisassembler_Option_AsmPrinterVariant | LLVMDisassembler_Option_PrintLatency); m_can_disasm = true; } std::string HostDisassemblerLLVM::DisassembleHostBlock(const u8* code_start, const u32 code_size, u32 *host_instructions_count) { if (!m_can_disasm) return "(No LLVM context)"; u8* disasmPtr = (u8*)code_start; const u8 *end = code_start + code_size; std::ostringstream x86_disasm; while ((u8*)disasmPtr < end) { char inst_disasm[256]; size_t inst_size = LLVMDisasmInstruction(m_llvm_context, disasmPtr, (u64)(end - disasmPtr), (u64)disasmPtr, inst_disasm, 256); if (!inst_size) { x86_disasm << "Invalid inst:"; if (m_instruction_size != -1) { // If we are on an architecture that has a fixed instruction size // We can continue onward past this bad instruction. std::string inst_str = ""; for (int i = 0; i < m_instruction_size; ++i) inst_str += StringFromFormat("%02x", disasmPtr[i]); x86_disasm << inst_str << std::endl; disasmPtr += m_instruction_size; } else { // We can't continue if we are on an architecture that has flexible instruction sizes // Dump the rest of the block instead std::string code_block = ""; for (int i = 0; (disasmPtr + i) < end; ++i) code_block += StringFromFormat("%02x", disasmPtr[i]); x86_disasm << code_block << std::endl; break; } } else { x86_disasm << inst_disasm << std::endl; disasmPtr += inst_size; } (*host_instructions_count)++; } return x86_disasm.str(); } #endif std::string HostDisassembler::DisassembleBlock(u32* address, u32* host_instructions_count, u32* code_size) { if (!jit) { *host_instructions_count = 0; *code_size = 0; return "(No JIT active)"; } int block_num = jit->GetBlockCache()->GetBlockNumberFromStartAddress(*address); if (block_num < 0) { for (int i = 0; i < 500; i++) { block_num = jit->GetBlockCache()->GetBlockNumberFromStartAddress(*address - 4 * i); if (block_num >= 0) break; } if (block_num >= 0) { JitBlock* block = jit->GetBlockCache()->GetBlock(block_num); if (!(block->originalAddress <= *address && block->originalSize + block->originalAddress >= *address)) block_num = -1; } // Do not merge this "if" with the above - block_num changes inside it. if (block_num < 0) { host_instructions_count = 0; code_size = 0; return "(No translation)"; } } JitBlock* block = jit->GetBlockCache()->GetBlock(block_num); const u8* code = (const u8*)jit->GetBlockCache()->GetCompiledCodeFromBlock(block_num); *code_size = block->codeSize; *address = block->originalAddress; return DisassembleHostBlock(code, block->codeSize, host_instructions_count); } HostDisassemblerX86::HostDisassemblerX86() { m_disasm.set_syntax_intel(); } std::string HostDisassemblerX86::DisassembleHostBlock(const u8* code_start, const u32 code_size, u32* host_instructions_count) { u64 disasmPtr = (u64)code_start; const u8* end = code_start + code_size; std::ostringstream x86_disasm; while ((u8*)disasmPtr < end) { char inst_disasm[256]; disasmPtr += m_disasm.disasm64(disasmPtr, disasmPtr, (u8*)disasmPtr, inst_disasm); x86_disasm << inst_disasm << std::endl; (*host_instructions_count)++; } return x86_disasm.str(); } CJitWindow::CJitWindow(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name) : wxPanel(parent, id, pos, size, style, name) { wxBoxSizer* sizerBig = new wxBoxSizer(wxVERTICAL); wxBoxSizer* sizerSplit = new wxBoxSizer(wxHORIZONTAL); sizerSplit->Add(ppc_box = new wxTextCtrl(this, wxID_ANY, "(ppc)", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE), 1, wxEXPAND); sizerSplit->Add(x86_box = new wxTextCtrl(this, wxID_ANY, "(x86)", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE), 1, wxEXPAND); sizerBig->Add(block_list = new JitBlockList(this, wxID_ANY, wxDefaultPosition, wxSize(100, 140), wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT | wxLC_SINGLE_SEL | wxLC_SORT_ASCENDING), 0, wxEXPAND); sizerBig->Add(sizerSplit, 2, wxEXPAND); sizerBig->Add(button_refresh = new wxButton(this, wxID_ANY, _("&Refresh"))); button_refresh->Bind(wxEVT_BUTTON, &CJitWindow::OnRefresh, this); SetSizer(sizerBig); sizerSplit->Fit(this); sizerBig->Fit(this); #if defined(_M_X86) && defined(HAS_LLVM) m_disassembler.reset(new HostDisassemblerLLVM("x86_64-none-unknown")); #elif defined(_M_X86) m_disassembler.reset(new HostDisassemblerX86()); #elif defined(_M_ARM_64) && defined(HAS_LLVM) m_disassembler.reset(new HostDisassemblerLLVM("aarch64-none-unknown", 4, "cortex-a57")); #elif defined(_M_ARM_32) && defined(HAS_LLVM) m_disassembler.reset(new HostDisassemblerLLVM("armv7-none-unknown", 4, "cortex-a15")); #else m_disassembler.reset(new HostDisassembler()); #endif } void CJitWindow::OnRefresh(wxCommandEvent& /*event*/) { block_list->Update(); } void CJitWindow::ViewAddr(u32 em_address) { Show(true); Compare(em_address); SetFocus(); } void CJitWindow::Compare(u32 em_address) { // Get host side code disassembly u32 host_instructions_count = 0; u32 host_code_size = 0; std::string host_instructions_disasm; host_instructions_disasm = m_disassembler->DisassembleBlock(&em_address, &host_instructions_count, &host_code_size); x86_box->SetValue(host_instructions_disasm); // == Fill in ppc box u32 ppc_addr = em_address; PPCAnalyst::CodeBuffer code_buffer(32000); PPCAnalyst::BlockStats st; PPCAnalyst::BlockRegStats gpa; PPCAnalyst::BlockRegStats fpa; PPCAnalyst::CodeBlock code_block; PPCAnalyst::PPCAnalyzer analyzer; analyzer.SetOption(PPCAnalyst::PPCAnalyzer::OPTION_CONDITIONAL_CONTINUE); code_block.m_stats = &st; code_block.m_gpa = &gpa; code_block.m_fpa = &fpa; if (analyzer.Analyze(ppc_addr, &code_block, &code_buffer, 32000) != 0xFFFFFFFF) { std::ostringstream ppc_disasm; for (u32 i = 0; i < code_block.m_num_instructions; i++) { const PPCAnalyst::CodeOp &op = code_buffer.codebuffer[i]; std::string opcode = GekkoDisassembler::Disassemble(op.inst.hex, op.address); ppc_disasm << std::setfill('0') << std::setw(8) << std::hex << op.address; ppc_disasm << " " << opcode << std::endl; } // Add stats to the end of the ppc box since it's generally the shortest. ppc_disasm << std::dec << std::endl; // Add some generic analysis if (st.isFirstBlockOfFunction) ppc_disasm << "(first block of function)" << std::endl; if (st.isLastBlockOfFunction) ppc_disasm << "(last block of function)" << std::endl; ppc_disasm << st.numCycles << " estimated cycles" << std::endl; ppc_disasm << "Num instr: PPC: " << code_block.m_num_instructions << " x86: " << host_instructions_count << " (blowup: " << 100 * host_instructions_count / code_block.m_num_instructions - 100 << "%)" << std::endl; ppc_disasm << "Num bytes: PPC: " << code_block.m_num_instructions * 4 << " x86: " << host_code_size << " (blowup: " << 100 * host_code_size / (4 * code_block.m_num_instructions) - 100 << "%)" << std::endl; ppc_box->SetValue(ppc_disasm.str()); } else { ppc_box->SetValue(StringFromFormat("(non-code address: %08x)", em_address)); x86_box->SetValue("---"); } } void CJitWindow::Update() { } void CJitWindow::OnHostMessage(wxCommandEvent& event) { switch (event.GetId()) { case IDM_NOTIFY_MAP_LOADED: //NotifyMapLoaded(); break; } } // JitBlockList //================ enum { COLUMN_ADDRESS, COLUMN_PPCSIZE, COLUMN_X86SIZE, COLUMN_NAME, COLUMN_FLAGS, COLUMN_NUMEXEC, COLUMN_COST, // (estimated as x86size * numexec) }; JitBlockList::JitBlockList(wxWindow* parent, const wxWindowID id, const wxPoint& pos, const wxSize& size, long style) : wxListCtrl(parent, id, pos, size, style) // | wxLC_VIRTUAL) { Init(); } void JitBlockList::Init() { InsertColumn(COLUMN_ADDRESS, _("Address")); InsertColumn(COLUMN_PPCSIZE, _("PPC Size")); InsertColumn(COLUMN_X86SIZE, _("x86 Size")); InsertColumn(COLUMN_NAME, _("Symbol")); InsertColumn(COLUMN_FLAGS, _("Flags")); InsertColumn(COLUMN_NUMEXEC, _("NumExec")); InsertColumn(COLUMN_COST, _("Cost")); } void JitBlockList::Update() { }