diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index 7bd273c920..f0de619fee 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -1265,4 +1265,37 @@ void ARMXEmitter::VCVT(ARMReg Dest, ARMReg Source, int flags) } } +// UNTESTED + +// See page A8-878 in ARMv7-A Architecture Reference Manual + +// Dest is a Q register, Src is a D register. +void ARMXEmitter::VCVTF32F16(ARMReg Dest, ARMReg Src) { + _assert_msg_(JIT, cpu_info.bVFPv4, "Can't use half-float conversions when you don't support VFPv4"); + if (Dest < Q0 || Dest > Q15 || Src < D0 || Src > D15) { + _assert_msg_(JIT, cpu_info.bNEON, "Bad inputs to VCVTF32F16"); + // Invalid! + } + + Dest = SubBase(Dest); + Src = SubBase(Src); + + int op = 1; + Write32((0xF3B6 << 16) | ((Dest & 0x10) << 18) | ((Dest & 0xF) << 12) | 0x600 | (op << 8) | ((Src & 0x10) << 1) | (Src & 0xF)); +} + +// UNTESTED +// Dest is a D register, Src is a Q register. +void ARMXEmitter::VCVTF16F32(ARMReg Dest, ARMReg Src) { + _assert_msg_(JIT, cpu_info.bVFPv4, "Can't use half-float conversions when you don't support VFPv4"); + if (Dest < D0 || Dest > D15 || Src < Q0 || Src > Q15) { + _assert_msg_(JIT, cpu_info.bNEON, "Bad inputs to VCVTF32F16"); + // Invalid! + } + Dest = SubBase(Dest); + Src = SubBase(Src); + int op = 0; + Write32((0xF3B6 << 16) | ((Dest & 0x10) << 18) | ((Dest & 0xF) << 12) | 0x600 | (op << 8) | ((Src & 0x10) << 1) | (Src & 0xF)); +} + } diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index d006df70fd..f876a198e1 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -572,6 +572,10 @@ public: void VMOV(ARMReg Dest, ARMReg Src); void VCVT(ARMReg Dest, ARMReg Src, int flags); + // NEON, need to check for this (supported if VFP4 is supported) + void VCVTF32F16(ARMReg Dest, ARMReg Src); + void VCVTF16F32(ARMReg Dest, ARMReg Src); + void VMRS_APSR(); void VMRS(ARMReg Rt); void VMSR(ARMReg Rt); diff --git a/Core/MIPS/JitCommon/JitBlockCache.h b/Core/MIPS/JitCommon/JitBlockCache.h index c7bd5288f0..f7937b128f 100644 --- a/Core/MIPS/JitCommon/JitBlockCache.h +++ b/Core/MIPS/JitCommon/JitBlockCache.h @@ -115,6 +115,8 @@ public: void InvalidateICache(u32 address, const u32 length); void DestroyBlock(int block_num, bool invalidate); + int GetNumBlocks() const { return num_blocks; } + private: void LinkBlockExits(int i); void LinkBlock(int i); diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index 7322ccd4e3..70eb10f34d 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -266,8 +266,8 @@ void Jit::Compile(u32 em_address) blocks.FinalizeBlock(block_num, jo.enableBlocklink); // Drat. The VFPU hit an uneaten prefix at the end of a block. - if (js.startDefaultPrefix && js.MayHavePrefix()) - { + if (js.startDefaultPrefix && js.MayHavePrefix()) { + WARN_LOG(JIT, "Uneaten prefix at end of block: %08x", js.compilerPC - 4); js.startDefaultPrefix = false; // Our assumptions are all wrong so it's clean-slate time. ClearCache(); diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index dc04e0fcb5..905c089a59 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -26,13 +26,16 @@ #include "UI/GameSettingsScreen.h" #include "Common/LogManager.h" #include "Core/Config.h" - +#include "Core/MIPS/MIPSTables.h" +#include "Core/MIPS/JitCommon/JitCommon.h" +#include "ext/disarm.h" void DevMenu::CreatePopupContents(UI::ViewGroup *parent) { using namespace UI; parent->Add(new Choice("Log Channels"))->OnClick.Handle(this, &DevMenu::OnLogConfig); parent->Add(new Choice("Developer Tools"))->OnClick.Handle(this, &DevMenu::OnDeveloperTools); + parent->Add(new Choice("Jit Compare"))->OnClick.Handle(this, &DevMenu::OnJitCompare); } UI::EventReturn DevMenu::OnLogConfig(UI::EventParams &e) { @@ -45,6 +48,11 @@ UI::EventReturn DevMenu::OnDeveloperTools(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn DevMenu::OnJitCompare(UI::EventParams &e) { + screenManager()->push(new JitCompareScreen()); + return UI::EVENT_DONE; +} + void DevMenu::dialogFinished(const Screen *dialog, DialogResult result) { // Close when a subscreen got closed. // TODO: a bug in screenmanager causes this not to work here. @@ -149,3 +157,117 @@ void SystemInfoScreen::CreateViews() { scroll->Add(new TextView(exts[i])); } } + + + +// Three panes: Block chooser, MIPS view, ARM/x86 view +void JitCompareScreen::CreateViews() { + I18NCategory *d = GetI18NCategory("Dialog"); + + using namespace UI; + + root_ = new LinearLayout(ORIENT_HORIZONTAL); + + ScrollView *leftColumnScroll = root_->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f))); + LinearLayout *leftColumn = leftColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL)); + + ScrollView *midColumnScroll = root_->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(2.0f))); + LinearLayout *midColumn = midColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL)); + leftDisasm_ = midColumn->Add(new LinearLayout(ORIENT_VERTICAL)); + leftDisasm_->SetSpacing(0.0f); + + ScrollView *rightColumnScroll = root_->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(2.0f))); + LinearLayout *rightColumn = rightColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL)); + rightDisasm_ = rightColumn->Add(new LinearLayout(ORIENT_VERTICAL)); + rightDisasm_->SetSpacing(0.0f); + + leftColumn->Add(new Choice("Current"))->OnClick.Handle(this, &JitCompareScreen::OnCurrentBlock); + leftColumn->Add(new Choice("Random"))->OnClick.Handle(this, &JitCompareScreen::OnRandomBlock); + leftColumn->Add(new Choice(d->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); +} + +std::vector DisassembleArm2(const u8 *data, int size) { + std::vector lines; + + char temp[256]; + for (int i = 0; i < size; i += 4) { + const u32 *codePtr = (const u32 *)(data + i); + u32 inst = codePtr[0]; + u32 next = (i < size - 4) ? codePtr[1] : 0; + // MAGIC SPECIAL CASE for MOVW/MOVT readability! + if ((inst & 0x0FF00000) == 0x03000000 && (next & 0x0FF00000) == 0x03400000) { + u32 low = ((inst & 0x000F0000) >> 4) | (inst & 0x0FFF); + u32 hi = ((next & 0x000F0000) >> 4) | (next & 0x0FFF); + int reg0 = (inst & 0x0000F000) >> 12; + int reg1 = (next & 0x0000F000) >> 12; + if (reg0 == reg1) { + sprintf(temp, "MOV32 %s, %04x%04x", ArmRegName(reg0), hi, low); + // sprintf(temp, "%08x MOV32? %s, %04x%04x", (u32)inst, ArmRegName(reg0), hi, low); + lines.push_back(temp); + i += 4; + continue; + } + } + ArmDis((u32)codePtr, inst, temp, false); + std::string buf = temp; + lines.push_back(buf); + } + return lines; +} + +void JitCompareScreen::UpdateDisasm() { + leftDisasm_->Clear(); + rightDisasm_->Clear(); + + using namespace UI; + + if (currentBlock_ == -1) { + leftDisasm_->Add(new TextView("No block")); + rightDisasm_->Add(new TextView("No block")); + return; + } + + JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache(); + JitBlock *block = blockCache->GetBlock(currentBlock_); + + // Alright. First generate the MIPS disassembly. + + for (u32 addr = block->originalAddress; addr <= block->originalAddress + block->originalSize * 4; addr += 4) { + char temp[256]; + MIPSDisAsm(Memory::Read_Instruction(addr), addr, temp, true); + std::string mipsDis = temp; + leftDisasm_->Add(new TextView(mipsDis)); + } + +#if defined(ARM) + std::vector targetDis = DisassembleArm2(block->normalEntry, block->codeSize); + for (size_t i = 0; i < targetDis.size(); i++) { + rightDisasm_->Add(new TextView(targetDis[i])); + } +#else + rightDisasm_->Add(new TextView("No x86 disassembler available")); +#endif +} + +UI::EventReturn JitCompareScreen::OnRandomBlock(UI::EventParams &e) { + JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache(); + int numBlocks = blockCache->GetNumBlocks(); + if (numBlocks > 0) { + currentBlock_ = rand() % numBlocks; + } + UpdateDisasm(); + return UI::EVENT_DONE; +} + +UI::EventReturn JitCompareScreen::OnCurrentBlock(UI::EventParams &e) { + JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache(); + std::vector blockNum; + blockCache->GetBlockNumbersFromAddress(currentMIPS->pc, &blockNum); + if (blockNum.size() > 0) { + currentBlock_ = blockNum[0]; + } else { + currentBlock_ = -1; + } + UpdateDisasm(); + return UI::EVENT_DONE; +} \ No newline at end of file diff --git a/UI/DevScreens.h b/UI/DevScreens.h index 9be6485040..b8af66d53c 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -37,6 +37,7 @@ public: protected: UI::EventReturn OnLogConfig(UI::EventParams &e); + UI::EventReturn OnJitCompare(UI::EventParams &e); UI::EventReturn OnDeveloperTools(UI::EventParams &e); }; @@ -53,4 +54,20 @@ class SystemInfoScreen : public UIDialogScreenWithBackground { public: SystemInfoScreen() {} virtual void CreateViews(); +}; + +class JitCompareScreen : public UIDialogScreenWithBackground { +public: + JitCompareScreen() : currentBlock_(-1) {} + virtual void CreateViews(); + +private: + void UpdateDisasm(); + UI::EventReturn OnRandomBlock(UI::EventParams &e); + UI::EventReturn OnCurrentBlock(UI::EventParams &e); + + int currentBlock_; + + UI::LinearLayout *leftDisasm_; + UI::LinearLayout *rightDisasm_; }; \ No newline at end of file diff --git a/ext/disarm.cpp b/ext/disarm.cpp index 767e112b1a..c41fa74abb 100644 --- a/ext/disarm.cpp +++ b/ext/disarm.cpp @@ -1023,10 +1023,15 @@ static sDisOptions options = { const char *ArmRegName(int r) { return reg_names[r]; } -void ArmDis(unsigned int addr, unsigned int w, char *output) { + +void ArmDis(unsigned int addr, unsigned int w, char *output, bool includeWord) { pInstruction instr = instr_disassemble(w, addr, &options); char temp[256]; - sprintf(output, "%08x\t%s", w, instr->text); + if (includeWord) { + sprintf(output, "%08x\t%s", w, instr->text); + } else { + sprintf(output, "%s", instr->text); + } if (instr->undefined || instr->badbits || instr->oddbits) { if (instr->undefined) sprintf(output, " [undefined instr %08x]", w); if (instr->badbits) sprintf(output, " [illegal bits %08x]", w); diff --git a/ext/disarm.h b/ext/disarm.h index 5032880a18..609b5ef542 100644 --- a/ext/disarm.h +++ b/ext/disarm.h @@ -31,4 +31,4 @@ // program with this function. const char *ArmRegName(int r); -void ArmDis(unsigned int addr, unsigned int w, char *output); +void ArmDis(unsigned int addr, unsigned int w, char *output, bool includeWord = true);