JitUtil: Optimize MMIO loads by generating code to directly access a given MMIO.

This commit is contained in:
Pierre Bourdon 2014-02-27 22:53:06 +01:00
parent edba8096bf
commit 8802770a85
2 changed files with 140 additions and 0 deletions

View File

@ -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);

View File

@ -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,