From 8802770a85f7bc452c15b754beef7f28fe2a2033 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Thu, 27 Feb 2014 22:53:06 +0100 Subject: [PATCH] JitUtil: Optimize MMIO loads by generating code to directly access a given MMIO. --- .../Core/Core/PowerPC/JitCommon/Jit_Util.cpp | 133 ++++++++++++++++++ Source/Core/Core/PowerPC/JitCommon/Jit_Util.h | 7 + 2 files changed, 140 insertions(+) diff --git a/Source/Core/Core/PowerPC/JitCommon/Jit_Util.cpp b/Source/Core/Core/PowerPC/JitCommon/Jit_Util.cpp index 8140c235eb..ebe89b0e5b 100644 --- a/Source/Core/Core/PowerPC/JitCommon/Jit_Util.cpp +++ b/Source/Core/Core/PowerPC/JitCommon/Jit_Util.cpp @@ -6,6 +6,8 @@ #include "Common/Common.h" #include "Common/CPUDetect.h" + +#include "Core/HW/MMIO.h" #include "Core/PowerPC/JitCommon/Jit_Util.h" #include "Core/PowerPC/JitCommon/JitBase.h" @@ -118,6 +120,122 @@ u8 *EmuCodeBlock::UnsafeLoadToReg(X64Reg reg_value, Gen::OpArg opAddress, int ac return result; } +// Visitor that generates code to read a MMIO value to EAX. +template +class MMIOReadCodeGenerator : public MMIO::ReadHandlingMethodVisitor +{ +public: + MMIOReadCodeGenerator(Gen::XCodeBlock* code, u32 registers_in_use, + Gen::X64Reg dst_reg, u32 address, bool sign_extend) + : m_code(code), m_registers_in_use(registers_in_use), m_dst_reg(dst_reg), + m_address(address), m_sign_extend(sign_extend) + { + } + + virtual void VisitConstant(T value) + { + LoadConstantToReg(8 * sizeof (T), value); + } + virtual void VisitDirect(const T* addr, u32 mask) + { + LoadAddrMaskToReg(8 * sizeof (T), addr, mask); + } + virtual void VisitComplex(const std::function* lambda) + { + CallLambda(8 * sizeof (T), lambda); + } + +private: + // Generates code to load a constant to the destination register. In + // practice it would be better to avoid using a register for this, but it + // would require refactoring a lot of JIT code. + void LoadConstantToReg(int sbits, u32 value) + { + if (m_sign_extend) + { + u32 sign = !!(value & (1 << (sbits - 1))); + value |= sign * ((0xFFFFFFFF >> sbits) << sbits); + } + m_code->MOV(32, R(m_dst_reg), Gen::Imm32(value)); + } + + // Generate the proper MOV instruction depending on whether the read should + // be sign extended or zero extended. + void MoveOpArgToReg(int sbits, Gen::OpArg arg) + { + if (m_sign_extend) + m_code->MOVSX(32, sbits, m_dst_reg, arg); + else + m_code->MOVZX(32, sbits, m_dst_reg, arg); + } + + void LoadAddrMaskToReg(int sbits, const void* ptr, u32 mask) + { +#ifdef _ARCH_64 + m_code->MOV(64, R(EAX), ImmPtr(ptr)); +#else + m_code->MOV(32, R(EAX), ImmPtr(ptr)); +#endif + // If we do not need to mask, we can do the sign extend while loading + // from memory. If masking is required, we have to first zero extend, + // then mask, then sign extend if needed (1 instr vs. 2/3). + u32 all_ones = (1ULL << sbits) - 1; + if ((all_ones & mask) == all_ones) + MoveOpArgToReg(sbits, MDisp(EAX, 0)); + else + { + m_code->MOVZX(32, sbits, m_dst_reg, MDisp(EAX, 0)); + m_code->AND(32, R(m_dst_reg), Imm32(mask)); + if (m_sign_extend) + m_code->MOVSX(32, sbits, m_dst_reg, R(m_dst_reg)); + } + } + + void CallLambda(int sbits, const std::function* lambda) + { + m_code->ABI_PushRegistersAndAdjustStack(m_registers_in_use, false); + m_code->ABI_CallLambdaC(lambda, m_address); + m_code->ABI_PopRegistersAndAdjustStack(m_registers_in_use, false); + MoveOpArgToReg(sbits, R(EAX)); + } + + Gen::XCodeBlock* m_code; + u32 m_registers_in_use; + Gen::X64Reg m_dst_reg; + u32 m_address; + bool m_sign_extend; +}; + +void EmuCodeBlock::MMIOLoadToReg(MMIO::Mapping* mmio, Gen::X64Reg reg_value, + u32 registers_in_use, u32 address, + int access_size, bool sign_extend) +{ + switch (access_size) + { + case 8: + { + MMIOReadCodeGenerator gen(this, registers_in_use, reg_value, + address, sign_extend); + mmio->GetHandlerForRead8(address).Visit(gen); + break; + } + case 16: + { + MMIOReadCodeGenerator gen(this, registers_in_use, reg_value, + address, sign_extend); + mmio->GetHandlerForRead16(address).Visit(gen); + break; + } + case 32: + { + MMIOReadCodeGenerator gen(this, registers_in_use, reg_value, + address, sign_extend); + mmio->GetHandlerForRead32(address).Visit(gen); + break; + } + } +} + void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg & opAddress, int accessSize, s32 offset, u32 registersInUse, bool signExtend, int flags) { if (!jit->js.memcheck) @@ -157,10 +275,25 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg & opAddress, if (opAddress.IsImm()) { u32 address = (u32)opAddress.offset + offset; + + // If we know the address, try the following loading methods in + // order: + // + // 1. If the address is in RAM, generate an unsafe load (directly + // access the RAM buffer and load from there). + // 2. If the address is in the MMIO range, find the appropriate + // MMIO handler and generate the code to load using the handler. + // 3. Otherwise, just generate a call to Memory::Read_* with the + // address hardcoded. if ((address & mem_mask) == 0) { UnsafeLoadToReg(reg_value, opAddress, accessSize, offset, signExtend); } + else if (!Core::g_CoreStartupParameter.bMMU && MMIO::IsMMIOAddress(address)) + { + MMIOLoadToReg(Memory::mmio_mapping, reg_value, registersInUse, + address, accessSize, signExtend); + } else { ABI_PushRegistersAndAdjustStack(registersInUse, false); diff --git a/Source/Core/Core/PowerPC/JitCommon/Jit_Util.h b/Source/Core/Core/PowerPC/JitCommon/Jit_Util.h index 52d0b12d39..cb67a2efbc 100644 --- a/Source/Core/Core/PowerPC/JitCommon/Jit_Util.h +++ b/Source/Core/Core/PowerPC/JitCommon/Jit_Util.h @@ -8,6 +8,8 @@ #include "Common/x64Emitter.h" +namespace MMIO { class Mapping; } + #define MEMCHECK_START \ FixupBranch memException; \ if (jit->js.memcheck) \ @@ -28,6 +30,11 @@ public: // these return the address of the MOV, for backpatching u8 *UnsafeWriteRegToReg(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, int accessSize, s32 offset = 0, bool swap = true); u8 *UnsafeLoadToReg(Gen::X64Reg reg_value, Gen::OpArg opAddress, int accessSize, s32 offset, bool signExtend); + + // Generate a load/write from the MMIO handler for a given address. Only + // call for known addresses in MMIO range (MMIO::IsMMIOAddress). + void MMIOLoadToReg(MMIO::Mapping* mmio, Gen::X64Reg reg_value, u32 registers_in_use, u32 address, int access_size, bool sign_extend); + enum SafeLoadStoreFlags { SAFE_LOADSTORE_NO_SWAP = 1,