summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorPierre Bourdon <delroth@gmail.com>2014-02-27 22:53:06 +0100
committerPierre Bourdon <delroth@gmail.com>2014-03-08 23:32:43 +0100
commit8802770a85f7bc452c15b754beef7f28fe2a2033 (patch)
treea8a014ad9dcd33a22ed5f23196cec7fdaca22b71 /Source/Core
parentedba8096bf2b2b251cb8f6813eb45c6005de4b9e (diff)
JitUtil: Optimize MMIO loads by generating code to directly access a given MMIO.
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/PowerPC/JitCommon/Jit_Util.cpp133
-rw-r--r--Source/Core/Core/PowerPC/JitCommon/Jit_Util.h7
2 files changed, 140 insertions, 0 deletions
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 <typename T>
+class MMIOReadCodeGenerator : public MMIO::ReadHandlingMethodVisitor<T>
+{
+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<T(u32)>* 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<T(u32)>* 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<u8> gen(this, registers_in_use, reg_value,
+ address, sign_extend);
+ mmio->GetHandlerForRead8(address).Visit(gen);
+ break;
+ }
+ case 16:
+ {
+ MMIOReadCodeGenerator<u16> gen(this, registers_in_use, reg_value,
+ address, sign_extend);
+ mmio->GetHandlerForRead16(address).Visit(gen);
+ break;
+ }
+ case 32:
+ {
+ MMIOReadCodeGenerator<u32> 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,