Jit64Common: Split Jit64Util contents into separate files

Much of Jit64Util consists of essentials, not utilities. Breaking these
out into their own files also prevents unrelated includes from being
present near other classes.

This also makes it easier to find and change certain components of the
x86-64 JIT, should it be necessary.
This commit is contained in:
Lioncash 2016-12-17 20:02:20 -05:00
parent 49384208cf
commit 94237b694b
28 changed files with 395 additions and 306 deletions

View File

@ -207,9 +207,10 @@ if(_M_X86)
PowerPC/Jit64/JitRegCache.cpp
PowerPC/Jit64/Jit_SystemRegisters.cpp
PowerPC/Jit64Common/BlockCache.cpp
PowerPC/Jit64Common/EmuCodeBlock.cpp
PowerPC/Jit64Common/FarCodeCache.cpp
PowerPC/Jit64Common/Jit64AsmCommon.cpp
PowerPC/Jit64Common/Jit64Base.cpp
PowerPC/Jit64Common/Jit64Util.cpp
PowerPC/Jit64Common/TrampolineCache.cpp)
elseif(_M_ARM_64)
set(SRCS ${SRCS}

View File

@ -238,9 +238,10 @@
<ClCompile Include="PowerPC\Jit64\Jit_Paired.cpp" />
<ClCompile Include="PowerPC\Jit64\Jit_SystemRegisters.cpp" />
<ClCompile Include="PowerPC\Jit64Common\BlockCache.cpp" />
<ClCompile Include="PowerPC\Jit64Common\EmuCodeBlock.cpp" />
<ClCompile Include="PowerPC\Jit64Common\FarCodeCache.cpp" />
<ClCompile Include="PowerPC\Jit64Common\Jit64AsmCommon.cpp" />
<ClCompile Include="PowerPC\Jit64Common\Jit64Base.cpp" />
<ClCompile Include="PowerPC\Jit64Common\Jit64Util.cpp" />
<ClCompile Include="PowerPC\Jit64Common\TrampolineCache.cpp" />
<ClCompile Include="PowerPC\JitCommon\JitAsmCommon.cpp" />
<ClCompile Include="PowerPC\JitCommon\JitBase.cpp" />
@ -431,10 +432,13 @@
<ClInclude Include="PowerPC\JitILCommon\IR.h" />
<ClInclude Include="PowerPC\JitILCommon\JitILBase.h" />
<ClInclude Include="PowerPC\Jit64Common\BlockCache.h" />
<ClInclude Include="PowerPC\Jit64Common\EmuCodeBlock.h" />
<ClInclude Include="PowerPC\Jit64Common\FarCodeCache.h" />
<ClInclude Include="PowerPC\Jit64Common\Jit64AsmCommon.h" />
<ClInclude Include="PowerPC\Jit64Common\Jit64Base.h" />
<ClInclude Include="PowerPC\Jit64Common\Jit64Util.h" />
<ClInclude Include="PowerPC\Jit64Common\Jit64PowerPCState.h" />
<ClInclude Include="PowerPC\Jit64Common\TrampolineCache.h" />
<ClInclude Include="PowerPC\Jit64Common\TrampolineInfo.h" />
<ClInclude Include="PowerPC\JitCommon\JitAsmCommon.h" />
<ClInclude Include="PowerPC\JitCommon\JitBase.h" />
<ClInclude Include="PowerPC\JitCommon\JitCache.h" />

View File

@ -738,15 +738,18 @@
<ClCompile Include="PowerPC\Jit64Common\BlockCache.cpp">
<Filter>PowerPC\Jit64Common</Filter>
</ClCompile>
<ClCompile Include="PowerPC\Jit64Common\EmuCodeBlock.cpp">
<Filter>PowerPC\Jit64Common</Filter>
</ClCompile>
<ClCompile Include="PowerPC\Jit64Common\FarCodeCache.cpp">
<Filter>PowerPC\Jit64Common</Filter>
</ClCompile>
<ClCompile Include="PowerPC\Jit64Common\Jit64AsmCommon.cpp">
<Filter>PowerPC\Jit64Common</Filter>
</ClCompile>
<ClCompile Include="PowerPC\Jit64Common\Jit64Base.cpp">
<Filter>PowerPC\Jit64Common</Filter>
</ClCompile>
<ClCompile Include="PowerPC\Jit64Common\Jit64Util.cpp">
<Filter>PowerPC\Jit64Common</Filter>
</ClCompile>
<ClCompile Include="PowerPC\Jit64Common\TrampolineCache.cpp">
<Filter>PowerPC\Jit64Common</Filter>
</ClCompile>
@ -1278,18 +1281,27 @@
<ClInclude Include="PowerPC\Jit64Common\BlockCache.h">
<Filter>PowerPC\Jit64Common</Filter>
</ClInclude>
<ClInclude Include="PowerPC\Jit64Common\EmuCodeBlock.h">
<Filter>PowerPC\Jit64Common</Filter>
</ClInclude>
<ClInclude Include="PowerPC\Jit64Common\FarCodeCache.h">
<Filter>PowerPC\Jit64Common</Filter>
</ClInclude>
<ClInclude Include="PowerPC\Jit64Common\Jit64AsmCommon.h">
<Filter>PowerPC\Jit64Common</Filter>
</ClInclude>
<ClInclude Include="PowerPC\Jit64Common\Jit64Base.h">
<Filter>PowerPC\Jit64Common</Filter>
</ClInclude>
<ClInclude Include="PowerPC\Jit64Common\Jit64Util.h">
<ClInclude Include="PowerPC\Jit64Common\Jit64PowerPCState.h">
<Filter>PowerPC\Jit64Common</Filter>
</ClInclude>
<ClInclude Include="PowerPC\Jit64Common\TrampolineCache.h">
<Filter>PowerPC\Jit64Common</Filter>
</ClInclude>
<ClInclude Include="PowerPC\Jit64Common\TrampolineInfo.h">
<Filter>PowerPC\Jit64Common</Filter>
</ClInclude>
<ClInclude Include="Analytics.h" />
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_usb_ven.h">
<Filter>IPC HLE %28IOS/Starlet%29\USB</Filter>

View File

@ -10,7 +10,7 @@
#include "Core/HLE/HLE.h"
#include "Core/HW/CPU.h"
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/Jit64Common/Jit64Util.h"
#include "Core/PowerPC/Jit64Common/Jit64Base.h"
#include "Core/PowerPC/PPCAnalyst.h"
#include "Core/PowerPC/PowerPC.h"

View File

@ -26,7 +26,9 @@
#include "Core/PowerPC/Jit64/Jit64_Tables.h"
#include "Core/PowerPC/Jit64/JitAsm.h"
#include "Core/PowerPC/Jit64/JitRegCache.h"
#include "Core/PowerPC/Jit64Common/Jit64Util.h"
#include "Core/PowerPC/Jit64Common/FarCodeCache.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
#include "Core/PowerPC/Jit64Common/TrampolineCache.h"
#include "Core/PowerPC/JitInterface.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/PowerPC/Profiler.h"

View File

@ -12,6 +12,7 @@
#include "Core/HW/CPU.h"
#include "Core/HW/Memmap.h"
#include "Core/PowerPC/Jit64/JitAsm.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
#include "Core/PowerPC/PowerPC.h"
using namespace Gen;

View File

@ -14,7 +14,7 @@
#include "Common/x64Emitter.h"
#include "Core/PowerPC/Jit64/Jit.h"
#include "Core/PowerPC/Jit64/JitRegCache.h"
#include "Core/PowerPC/Jit64Common/Jit64Util.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
#include "Core/PowerPC/PowerPC.h"
using namespace Gen;

View File

@ -9,6 +9,7 @@
#include "Core/CoreTiming.h"
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/Jit64/JitRegCache.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
#include "Core/PowerPC/PPCAnalyst.h"
#include "Core/PowerPC/PowerPC.h"

View File

@ -13,6 +13,7 @@
#include "Core/Core.h"
#include "Core/PowerPC/Jit64/Jit.h"
#include "Core/PowerPC/Jit64/JitRegCache.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
#include "Core/PowerPC/PPCAnalyst.h"
#include "Core/PowerPC/PowerPC.h"

View File

@ -6,12 +6,13 @@
#include <vector>
#include "Common/Assert.h"
#include "Common/CPUDetect.h"
#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
#include "Common/x64Emitter.h"
#include "Core/PowerPC/Jit64/Jit.h"
#include "Core/PowerPC/Jit64/JitRegCache.h"
#include "Core/PowerPC/Jit64Common/Jit64Util.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
#include "Core/PowerPC/PPCAnalyst.h"
#include "Core/PowerPC/PowerPC.h"

View File

@ -17,7 +17,6 @@
#include "Core/HW/DSP.h"
#include "Core/HW/Memmap.h"
#include "Core/PowerPC/Jit64/JitRegCache.h"
#include "Core/PowerPC/Jit64Common/Jit64Util.h"
#include "Core/PowerPC/JitInterface.h"
#include "Core/PowerPC/PowerPC.h"

View File

@ -7,7 +7,7 @@
#include "Common/CommonTypes.h"
#include "Common/x64Emitter.h"
#include "Core/PowerPC/Jit64/JitRegCache.h"
#include "Core/PowerPC/Jit64Common/Jit64Util.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
using namespace Gen;

View File

@ -10,7 +10,7 @@
#include "Common/CommonTypes.h"
#include "Common/x64Emitter.h"
#include "Core/PowerPC/Jit64/JitRegCache.h"
#include "Core/PowerPC/Jit64Common/Jit64Util.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
#include "Core/PowerPC/JitCommon/JitAsmCommon.h"
#include "Core/PowerPC/PowerPC.h"

View File

@ -4,12 +4,13 @@
#include "Core/PowerPC/Jit64/Jit.h"
#include "Common/BitSet.h"
#include "Common/CPUDetect.h"
#include "Common/CommonTypes.h"
#include "Common/x64Emitter.h"
#include "Core/CoreTiming.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/PowerPC/Jit64/JitRegCache.h"
#include "Core/PowerPC/Jit64Common/Jit64Util.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
#include "Core/PowerPC/PowerPC.h"
using namespace Gen;

View File

@ -1,22 +1,45 @@
// Copyright 2008 Dolphin Emulator Project
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/PowerPC/Jit64Common/Jit64Util.h"
#include "Common/BitSet.h"
#include "Common/CommonTypes.h"
#include "Core/PowerPC/Jit64Common/EmuCodeBlock.h"
#include "Common/Assert.h"
#include "Common/CPUDetect.h"
#include "Common/Intrinsics.h"
#include "Common/MathUtil.h"
#include "Common/x64ABI.h"
#include "Common/x64Emitter.h"
#include "Core/HW/MMIO.h"
#include "Core/HW/Memmap.h"
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/Jit64Common/Jit64Base.h"
#include "Core/PowerPC/Jit64Common/TrampolineCache.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
#include "Core/PowerPC/PowerPC.h"
using namespace Gen;
namespace
{
OpArg SwapImmediate(int access_size, const OpArg& reg_value)
{
if (access_size == 32)
return Imm32(Common::swap32(reg_value.Imm32()));
if (access_size == 16)
return Imm16(Common::swap16(reg_value.Imm16()));
return Imm8(reg_value.Imm8());
}
OpArg FixImmediate(int access_size, OpArg arg)
{
if (arg.IsImm())
{
arg = access_size == 8 ? arg.AsImm8() : access_size == 16 ? arg.AsImm16() : arg.AsImm32();
}
return arg;
}
} // Anonymous namespace
void EmuCodeBlock::MemoryExceptionCheck()
{
// TODO: We really should untangle the trampolines, exception handlers and
@ -47,6 +70,49 @@ void EmuCodeBlock::MemoryExceptionCheck()
}
}
void EmuCodeBlock::SwitchToFarCode()
{
nearcode = GetWritableCodePtr();
SetCodePtr(farcode.GetWritableCodePtr());
}
void EmuCodeBlock::SwitchToNearCode()
{
farcode.SetCodePtr(GetWritableCodePtr());
SetCodePtr(nearcode);
}
FixupBranch EmuCodeBlock::CheckIfSafeAddress(const OpArg& reg_value, X64Reg reg_addr,
BitSet32 registers_in_use)
{
registers_in_use[reg_addr] = true;
if (reg_value.IsSimpleReg())
registers_in_use[reg_value.GetSimpleReg()] = true;
// Get ourselves a free register; try to pick one that doesn't involve pushing, if we can.
X64Reg scratch = RSCRATCH;
if (!registers_in_use[RSCRATCH])
scratch = RSCRATCH;
else if (!registers_in_use[RSCRATCH_EXTRA])
scratch = RSCRATCH_EXTRA;
else
scratch = reg_addr;
if (scratch == reg_addr)
PUSH(scratch);
else
MOV(32, R(scratch), R(reg_addr));
// Perform lookup to see if we can use fast path.
SHR(32, R(scratch), Imm8(PowerPC::BAT_INDEX_SHIFT));
TEST(32, MScaled(scratch, SCALE_4, PtrOffset(&PowerPC::dbat_table[0])), Imm32(2));
if (scratch == reg_addr)
POP(scratch);
return J_CC(CC_Z, farcode.Enabled());
}
void EmuCodeBlock::UnsafeLoadRegToReg(X64Reg reg_addr, X64Reg reg_value, int accessSize, s32 offset,
bool signExtend)
{
@ -63,6 +129,38 @@ void EmuCodeBlock::UnsafeLoadRegToRegNoSwap(X64Reg reg_addr, X64Reg reg_value, i
MOVZX(32, accessSize, reg_value, MComplex(RMEM, reg_addr, SCALE_1, offset));
}
void EmuCodeBlock::UnsafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int accessSize, s32 offset,
bool swap, MovInfo* info)
{
if (info)
{
info->address = GetWritableCodePtr();
info->nonAtomicSwapStore = false;
}
OpArg dest = MComplex(RMEM, reg_addr, SCALE_1, offset);
if (reg_value.IsImm())
{
if (swap)
reg_value = SwapImmediate(accessSize, reg_value);
MOV(accessSize, dest, reg_value);
}
else if (swap)
{
SwapAndStore(accessSize, dest, reg_value.GetSimpleReg(), info);
}
else
{
MOV(accessSize, dest, reg_value);
}
}
void EmuCodeBlock::UnsafeWriteRegToReg(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, int accessSize,
s32 offset, bool swap, Gen::MovInfo* info)
{
UnsafeWriteRegToReg(R(reg_value), reg_addr, accessSize, offset, swap, info);
}
bool EmuCodeBlock::UnsafeLoadToReg(X64Reg reg_value, OpArg opAddress, int accessSize, s32 offset,
bool signExtend, MovInfo* info)
{
@ -104,6 +202,28 @@ bool EmuCodeBlock::UnsafeLoadToReg(X64Reg reg_value, OpArg opAddress, int access
return offsetAddedToAddress;
}
void EmuCodeBlock::UnsafeWriteGatherPipe(int accessSize)
{
// No need to protect these, they don't touch any state
// question - should we inline them instead? Pro: Lose a CALL Con: Code bloat
switch (accessSize)
{
case 8:
CALL(jit->GetAsmRoutines()->fifoDirectWrite8);
break;
case 16:
CALL(jit->GetAsmRoutines()->fifoDirectWrite16);
break;
case 32:
CALL(jit->GetAsmRoutines()->fifoDirectWrite32);
break;
case 64:
CALL(jit->GetAsmRoutines()->fifoDirectWrite64);
break;
}
jit->js.fifoBytesSinceCheck += accessSize >> 3;
}
// Visitor that generates code to read a MMIO value.
template <typename T>
class MMIOReadCodeGenerator : public MMIO::ReadHandlingMethodVisitor<T>
@ -212,37 +332,6 @@ void EmuCodeBlock::MMIOLoadToReg(MMIO::Mapping* mmio, Gen::X64Reg reg_value,
}
}
FixupBranch EmuCodeBlock::CheckIfSafeAddress(const OpArg& reg_value, X64Reg reg_addr,
BitSet32 registers_in_use)
{
registers_in_use[reg_addr] = true;
if (reg_value.IsSimpleReg())
registers_in_use[reg_value.GetSimpleReg()] = true;
// Get ourselves a free register; try to pick one that doesn't involve pushing, if we can.
X64Reg scratch = RSCRATCH;
if (!registers_in_use[RSCRATCH])
scratch = RSCRATCH;
else if (!registers_in_use[RSCRATCH_EXTRA])
scratch = RSCRATCH_EXTRA;
else
scratch = reg_addr;
if (scratch == reg_addr)
PUSH(scratch);
else
MOV(32, R(scratch), R(reg_addr));
// Perform lookup to see if we can use fast path.
SHR(32, R(scratch), Imm8(PowerPC::BAT_INDEX_SHIFT));
TEST(32, MScaled(scratch, SCALE_4, PtrOffset(&PowerPC::dbat_table[0])), Imm32(2));
if (scratch == reg_addr)
POP(scratch);
return J_CC(CC_Z, farcode.Enabled());
}
void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg& opAddress, int accessSize,
s32 offset, BitSet32 registersInUse, bool signExtend, int flags)
{
@ -399,119 +488,6 @@ void EmuCodeBlock::SafeLoadToRegImmediate(X64Reg reg_value, u32 address, int acc
}
}
static OpArg SwapImmediate(int accessSize, const OpArg& reg_value)
{
if (accessSize == 32)
return Imm32(Common::swap32(reg_value.Imm32()));
else if (accessSize == 16)
return Imm16(Common::swap16(reg_value.Imm16()));
else
return Imm8(reg_value.Imm8());
}
void EmuCodeBlock::UnsafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int accessSize, s32 offset,
bool swap, MovInfo* info)
{
if (info)
{
info->address = GetWritableCodePtr();
info->nonAtomicSwapStore = false;
}
OpArg dest = MComplex(RMEM, reg_addr, SCALE_1, offset);
if (reg_value.IsImm())
{
if (swap)
reg_value = SwapImmediate(accessSize, reg_value);
MOV(accessSize, dest, reg_value);
}
else if (swap)
{
SwapAndStore(accessSize, dest, reg_value.GetSimpleReg(), info);
}
else
{
MOV(accessSize, dest, reg_value);
}
}
static OpArg FixImmediate(int accessSize, OpArg arg)
{
if (arg.IsImm())
{
arg = accessSize == 8 ? arg.AsImm8() : accessSize == 16 ? arg.AsImm16() : arg.AsImm32();
}
return arg;
}
void EmuCodeBlock::UnsafeWriteGatherPipe(int accessSize)
{
// No need to protect these, they don't touch any state
// question - should we inline them instead? Pro: Lose a CALL Con: Code bloat
switch (accessSize)
{
case 8:
CALL(jit->GetAsmRoutines()->fifoDirectWrite8);
break;
case 16:
CALL(jit->GetAsmRoutines()->fifoDirectWrite16);
break;
case 32:
CALL(jit->GetAsmRoutines()->fifoDirectWrite32);
break;
case 64:
CALL(jit->GetAsmRoutines()->fifoDirectWrite64);
break;
}
jit->js.fifoBytesSinceCheck += accessSize >> 3;
}
bool EmuCodeBlock::WriteToConstAddress(int accessSize, OpArg arg, u32 address,
BitSet32 registersInUse)
{
arg = FixImmediate(accessSize, arg);
// If we already know the address through constant folding, we can do some
// fun tricks...
if (jit->jo.optimizeGatherPipe && PowerPC::IsOptimizableGatherPipeWrite(address))
{
if (!arg.IsSimpleReg(RSCRATCH))
MOV(accessSize, R(RSCRATCH), arg);
UnsafeWriteGatherPipe(accessSize);
return false;
}
else if (PowerPC::IsOptimizableRAMAddress(address))
{
WriteToConstRamAddress(accessSize, arg, address);
return false;
}
else
{
// Helps external systems know which instruction triggered the write
MOV(32, PPCSTATE(pc), Imm32(jit->js.compilerPC));
ABI_PushRegistersAndAdjustStack(registersInUse, 0);
switch (accessSize)
{
case 64:
ABI_CallFunctionAC(64, PowerPC::Write_U64, arg, address);
break;
case 32:
ABI_CallFunctionAC(32, PowerPC::Write_U32, arg, address);
break;
case 16:
ABI_CallFunctionAC(16, PowerPC::Write_U16, arg, address);
break;
case 8:
ABI_CallFunctionAC(8, PowerPC::Write_U8, arg, address);
break;
}
ABI_PopRegistersAndAdjustStack(registersInUse, 0);
return true;
}
}
void EmuCodeBlock::SafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int accessSize, s32 offset,
BitSet32 registersInUse, int flags)
{
@ -625,6 +601,63 @@ void EmuCodeBlock::SafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int acces
}
}
void EmuCodeBlock::SafeWriteRegToReg(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, int accessSize,
s32 offset, BitSet32 registersInUse, int flags)
{
SafeWriteRegToReg(R(reg_value), reg_addr, accessSize, offset, registersInUse, flags);
}
bool EmuCodeBlock::WriteClobbersRegValue(int accessSize, bool swap)
{
return swap && !cpu_info.bMOVBE && accessSize > 8;
}
bool EmuCodeBlock::WriteToConstAddress(int accessSize, OpArg arg, u32 address,
BitSet32 registersInUse)
{
arg = FixImmediate(accessSize, arg);
// If we already know the address through constant folding, we can do some
// fun tricks...
if (jit->jo.optimizeGatherPipe && PowerPC::IsOptimizableGatherPipeWrite(address))
{
if (!arg.IsSimpleReg(RSCRATCH))
MOV(accessSize, R(RSCRATCH), arg);
UnsafeWriteGatherPipe(accessSize);
return false;
}
else if (PowerPC::IsOptimizableRAMAddress(address))
{
WriteToConstRamAddress(accessSize, arg, address);
return false;
}
else
{
// Helps external systems know which instruction triggered the write
MOV(32, PPCSTATE(pc), Imm32(jit->js.compilerPC));
ABI_PushRegistersAndAdjustStack(registersInUse, 0);
switch (accessSize)
{
case 64:
ABI_CallFunctionAC(64, PowerPC::Write_U64, arg, address);
break;
case 32:
ABI_CallFunctionAC(32, PowerPC::Write_U32, arg, address);
break;
case 16:
ABI_CallFunctionAC(16, PowerPC::Write_U16, arg, address);
break;
case 8:
ABI_CallFunctionAC(8, PowerPC::Write_U8, arg, address);
break;
}
ABI_PopRegistersAndAdjustStack(registersInUse, 0);
return true;
}
}
void EmuCodeBlock::WriteToConstRamAddress(int accessSize, OpArg arg, u32 address, bool swap)
{
X64Reg reg;
@ -653,6 +686,30 @@ void EmuCodeBlock::WriteToConstRamAddress(int accessSize, OpArg arg, u32 address
MOV(accessSize, MRegSum(RMEM, RSCRATCH2), R(reg));
}
void EmuCodeBlock::JitGetAndClearCAOV(bool oe)
{
if (oe)
AND(8, PPCSTATE(xer_so_ov), Imm8(~XER_OV_MASK)); // XER.OV = 0
SHR(8, PPCSTATE(xer_ca), Imm8(1)); // carry = XER.CA, XER.CA = 0
}
void EmuCodeBlock::JitSetCA()
{
MOV(8, PPCSTATE(xer_ca), Imm8(1)); // XER.CA = 1
}
// Some testing shows CA is set roughly ~1/3 of the time (relative to clears), so
// branchless calculation of CA is probably faster in general.
void EmuCodeBlock::JitSetCAIf(CCFlags conditionCode)
{
SETcc(conditionCode, PPCSTATE(xer_ca));
}
void EmuCodeBlock::JitClearCA()
{
MOV(8, PPCSTATE(xer_ca), Imm8(0));
}
void EmuCodeBlock::ForceSinglePrecision(X64Reg output, const OpArg& input, bool packed,
bool duplicate)
{
@ -1083,30 +1140,6 @@ void EmuCodeBlock::SetFPRF(Gen::X64Reg xmm)
OR(32, PPCSTATE(fpscr), R(RSCRATCH));
}
void EmuCodeBlock::JitGetAndClearCAOV(bool oe)
{
if (oe)
AND(8, PPCSTATE(xer_so_ov), Imm8(~XER_OV_MASK)); // XER.OV = 0
SHR(8, PPCSTATE(xer_ca), Imm8(1)); // carry = XER.CA, XER.CA = 0
}
void EmuCodeBlock::JitSetCA()
{
MOV(8, PPCSTATE(xer_ca), Imm8(1)); // XER.CA = 1
}
// Some testing shows CA is set roughly ~1/3 of the time (relative to clears), so
// branchless calculation of CA is probably faster in general.
void EmuCodeBlock::JitSetCAIf(CCFlags conditionCode)
{
SETcc(conditionCode, PPCSTATE(xer_ca));
}
void EmuCodeBlock::JitClearCA()
{
MOV(8, PPCSTATE(xer_ca), Imm8(0));
}
void EmuCodeBlock::Clear()
{
backPatchInfo.clear();

View File

@ -1,106 +1,23 @@
// Copyright 2010 Dolphin Emulator Project
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included./
// Refer to the license.txt file included.
#pragma once
#include <unordered_map>
#include "Common/BitSet.h"
#include "Common/CPUDetect.h"
#include "Common/CommonTypes.h"
#include "Common/x64Emitter.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/PowerPC/Jit64Common/FarCodeCache.h"
#include "Core/PowerPC/Jit64Common/TrampolineInfo.h"
namespace MMIO
{
class Mapping;
}
// We offset by 0x80 because the range of one byte memory offsets is
// -0x80..0x7f.
#define PPCSTATE(x) \
MDisp(RPPCSTATE, (int)((char*)&PowerPC::ppcState.x - (char*)&PowerPC::ppcState) - 0x80)
// In case you want to disable the ppcstate register:
// #define PPCSTATE(x) M(&PowerPC::ppcState.x)
#define PPCSTATE_LR PPCSTATE(spr[SPR_LR])
#define PPCSTATE_CTR PPCSTATE(spr[SPR_CTR])
#define PPCSTATE_SRR0 PPCSTATE(spr[SPR_SRR0])
#define PPCSTATE_SRR1 PPCSTATE(spr[SPR_SRR1])
// A place to throw blocks of code we don't want polluting the cache, e.g. rarely taken
// exception branches.
class FarCodeCache : public Gen::X64CodeBlock
{
private:
bool m_enabled = false;
public:
bool Enabled() const { return m_enabled; }
void Init(int size)
{
AllocCodeSpace(size);
m_enabled = true;
}
void Shutdown()
{
FreeCodeSpace();
m_enabled = false;
}
};
constexpr int CODE_SIZE = 1024 * 1024 * 32;
// a bit of a hack; the MMU results in a vast amount more code ending up in the far cache,
// mostly exception handling, so give it a whole bunch more space if the MMU is on.
constexpr int FARCODE_SIZE = 1024 * 1024 * 8;
constexpr int FARCODE_SIZE_MMU = 1024 * 1024 * 48;
// same for the trampoline code cache, because fastmem results in far more backpatches in MMU mode
constexpr int TRAMPOLINE_CODE_SIZE = 1024 * 1024 * 8;
constexpr int TRAMPOLINE_CODE_SIZE_MMU = 1024 * 1024 * 32;
// Stores information we need to batch-patch a MOV with a call to the slow read/write path after
// it faults. There will be 10s of thousands of these structs live, so be wary of making this too
// big.
struct TrampolineInfo final
{
// The start of the store operation that failed -- we will patch a JMP here
u8* start;
// The start + len = end of the store operation (points to the next instruction)
u32 len;
// The PPC PC for the current load/store block
u32 pc;
// Saved because we need these to make the ABI call in the trampoline
BitSet32 registersInUse;
// The MOV operation
Gen::X64Reg nonAtomicSwapStoreSrc;
// src/dest for load/store
s32 offset;
Gen::X64Reg op_reg;
Gen::OpArg op_arg;
// Original SafeLoadXXX/SafeStoreXXX flags
u8 flags;
// Memory access size (in bytes)
u8 accessSize : 4;
// true if this is a read op vs a write
bool read : 1;
// for read operations, true if needs sign-extension after load
bool signExtend : 1;
// Set to true if we added the offset to the address and need to undo it
bool offsetAddedToAddress : 1;
};
// Like XCodeBlock but has some utilities for memory access.
class EmuCodeBlock : public Gen::X64CodeBlock
{
@ -111,17 +28,8 @@ public:
void MemoryExceptionCheck();
// Simple functions to switch between near and far code emitting
void SwitchToFarCode()
{
nearcode = GetWritableCodePtr();
SetCodePtr(farcode.GetWritableCodePtr());
}
void SwitchToNearCode()
{
farcode.SetCodePtr(GetWritableCodePtr());
SetCodePtr(nearcode);
}
void SwitchToFarCode();
void SwitchToNearCode();
Gen::FixupBranch CheckIfSafeAddress(const Gen::OpArg& reg_value, Gen::X64Reg reg_addr,
BitSet32 registers_in_use);
@ -133,10 +41,8 @@ public:
void UnsafeWriteRegToReg(Gen::OpArg reg_value, Gen::X64Reg reg_addr, int accessSize,
s32 offset = 0, bool swap = true, Gen::MovInfo* info = nullptr);
void UnsafeWriteRegToReg(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, int accessSize,
s32 offset = 0, bool swap = true, Gen::MovInfo* info = nullptr)
{
UnsafeWriteRegToReg(R(reg_value), reg_addr, accessSize, offset, swap, info);
}
s32 offset = 0, bool swap = true, Gen::MovInfo* info = nullptr);
bool UnsafeLoadToReg(Gen::X64Reg reg_value, Gen::OpArg opAddress, int accessSize, s32 offset,
bool signExtend, Gen::MovInfo* info = nullptr);
void UnsafeWriteGatherPipe(int accessSize);
@ -169,20 +75,15 @@ public:
void SafeWriteRegToReg(Gen::OpArg reg_value, Gen::X64Reg reg_addr, int accessSize, s32 offset,
BitSet32 registersInUse, int flags = 0);
void SafeWriteRegToReg(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, int accessSize, s32 offset,
BitSet32 registersInUse, int flags = 0)
{
SafeWriteRegToReg(R(reg_value), reg_addr, accessSize, offset, registersInUse, flags);
}
BitSet32 registersInUse, int flags = 0);
// applies to safe and unsafe WriteRegToReg
bool WriteClobbersRegValue(int accessSize, bool swap)
{
return swap && !cpu_info.bMOVBE && accessSize > 8;
}
bool WriteClobbersRegValue(int accessSize, bool swap);
void WriteToConstRamAddress(int accessSize, Gen::OpArg arg, u32 address, bool swap = true);
// returns true if an exception could have been caused
bool WriteToConstAddress(int accessSize, Gen::OpArg arg, u32 address, BitSet32 registersInUse);
void WriteToConstRamAddress(int accessSize, Gen::OpArg arg, u32 address, bool swap = true);
void JitGetAndClearCAOV(bool oe);
void JitSetCA();
void JitSetCAIf(Gen::CCFlags conditionCode);

View File

@ -0,0 +1,22 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/PowerPC/Jit64Common/FarCodeCache.h"
void FarCodeCache::Init(int size)
{
AllocCodeSpace(size);
m_enabled = true;
}
void FarCodeCache::Shutdown()
{
FreeCodeSpace();
m_enabled = false;
}
bool FarCodeCache::Enabled() const
{
return m_enabled;
}

View File

@ -0,0 +1,26 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Common/x64Emitter.h"
// a bit of a hack; the MMU results in a vast amount more code ending up in the far cache,
// mostly exception handling, so give it a whole bunch more space if the MMU is on.
constexpr int FARCODE_SIZE = 1024 * 1024 * 8;
constexpr int FARCODE_SIZE_MMU = 1024 * 1024 * 48;
// A place to throw blocks of code we don't want polluting the cache, e.g. rarely taken
// exception branches.
class FarCodeCache : public Gen::X64CodeBlock
{
public:
void Init(int size);
void Shutdown();
bool Enabled() const;
private:
bool m_enabled = false;
};

View File

@ -4,6 +4,7 @@
#include "Core/PowerPC/Jit64Common/Jit64AsmCommon.h"
#include "Common/Assert.h"
#include "Common/CPUDetect.h"
#include "Common/CommonTypes.h"
#include "Common/JitRegister.h"
#include "Common/MathUtil.h"
@ -12,7 +13,7 @@
#include "Core/HW/GPFifo.h"
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/Jit64Common/Jit64Base.h"
#include "Core/PowerPC/Jit64Common/Jit64Util.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
#include "Core/PowerPC/PowerPC.h"
#define QUANTIZED_REGS_TO_SAVE \

View File

@ -5,7 +5,7 @@
#pragma once
#include "Common/CommonTypes.h"
#include "Core/PowerPC/Jit64Common/Jit64Util.h"
#include "Core/PowerPC/Jit64Common/EmuCodeBlock.h"
#include "Core/PowerPC/JitCommon/JitAsmCommon.h"
enum EQuantizeType : u32;

View File

@ -32,6 +32,8 @@ constexpr Gen::X64Reg RMEM = Gen::RBX;
// to address as much as possible in a one-byte offset form.
constexpr Gen::X64Reg RPPCSTATE = Gen::RBP;
constexpr int CODE_SIZE = 1024 * 1024 * 32;
class Jitx86Base : public JitBase, public QuantizedMemoryRoutines
{
protected:

View File

@ -0,0 +1,19 @@
// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included./
#pragma once
#include "Common/CommonTypes.h"
#include "Core/PowerPC/PowerPC.h"
// We offset by 0x80 because the range of one byte memory offsets is
// -0x80..0x7f.
#define PPCSTATE(x) \
MDisp(RPPCSTATE, (int)((char*)&PowerPC::ppcState.x - (char*)&PowerPC::ppcState) - 0x80)
// In case you want to disable the ppcstate register:
// #define PPCSTATE(x) M(&PowerPC::ppcState.x)
#define PPCSTATE_LR PPCSTATE(spr[SPR_LR])
#define PPCSTATE_CTR PPCSTATE(spr[SPR_CTR])
#define PPCSTATE_SRR0 PPCSTATE(spr[SPR_SRR0])
#define PPCSTATE_SRR1 PPCSTATE(spr[SPR_SRR1])

View File

@ -12,7 +12,8 @@
#include "Common/MsgHandler.h"
#include "Common/x64Emitter.h"
#include "Core/PowerPC/Jit64Common/Jit64Base.h"
#include "Core/PowerPC/Jit64Common/Jit64Util.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
#include "Core/PowerPC/Jit64Common/TrampolineInfo.h"
#include "Core/PowerPC/JitCommon/JitBase.h"
#include "Core/PowerPC/PowerPC.h"

View File

@ -5,7 +5,14 @@
#pragma once
#include "Common/CommonTypes.h"
#include "Core/PowerPC/Jit64Common/Jit64Util.h"
#include "Core/PowerPC/Jit64Common/EmuCodeBlock.h"
struct TrampolineInfo;
// a bit of a hack; the MMU results in more code ending up in the trampoline cache,
// because fastmem results in far more backpatches in MMU mode
constexpr int TRAMPOLINE_CODE_SIZE = 1024 * 1024 * 8;
constexpr int TRAMPOLINE_CODE_SIZE_MMU = 1024 * 1024 * 32;
// We need at least this many bytes for backpatching.
constexpr int BACKPATCH_SIZE = 5;

View File

@ -0,0 +1,50 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Common/BitSet.h"
#include "Common/CommonTypes.h"
#include "Common/x64Emitter.h"
// Stores information we need to batch-patch a MOV with a call to the slow read/write path after
// it faults. There will be 10s of thousands of these structs live, so be wary of making this too
// big.
struct TrampolineInfo final
{
// The start of the store operation that failed -- we will patch a JMP here
u8* start;
// The start + len = end of the store operation (points to the next instruction)
u32 len;
// The PPC PC for the current load/store block
u32 pc;
// Saved because we need these to make the ABI call in the trampoline
BitSet32 registersInUse;
// The MOV operation
Gen::X64Reg nonAtomicSwapStoreSrc;
// src/dest for load/store
s32 offset;
Gen::X64Reg op_reg;
Gen::OpArg op_arg;
// Original SafeLoadXXX/SafeStoreXXX flags
u8 flags;
// Memory access size (in bytes)
u8 accessSize : 4;
// true if this is a read op vs a write
bool read : 1;
// for read operations, true if needs sign-extension after load
bool signExtend : 1;
// Set to true if we added the offset to the address and need to undo it
bool offsetAddedToAddress : 1;
};

View File

@ -39,6 +39,7 @@ The register allocation is linear scan allocation.
#include "Core/HW/CPU.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
#include "Core/PowerPC/Jit64IL/JitIL.h"
#include "Core/PowerPC/PowerPC.h"

View File

@ -18,6 +18,7 @@
#include "Core/HLE/HLE.h"
#include "Core/HW/CPU.h"
#include "Core/PatchEngine.h"
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
#include "Core/PowerPC/Jit64IL/JitIL.h"
#include "Core/PowerPC/Jit64IL/JitIL_Tables.h"
#include "Core/PowerPC/PowerPC.h"

View File

@ -3,7 +3,9 @@
// Refer to the license.txt file included.
#include "Core/PowerPC/JitILCommon/JitILBase.h"
#include "Common/CommonTypes.h"
#include "Core/PowerPC/PowerPC.h"
void JitILBase::psq_st(UGeckoInstruction inst)
{