summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorskidau <skidau@gmail.com>2014-09-16 13:31:39 +1000
committerskidau <skidau@gmail.com>2014-09-16 13:31:39 +1000
commit8361d2b1daf40fb5ce726355aacf146debe7fea7 (patch)
treef17d60000541a08237c844648919d0b0cf393bd4 /Source
parentbef2016909e90d250d500a0f89036f4aa7730876 (diff)
parentd02b7c7755721a9e05958e0945e4cce68fbcd02b (diff)
Merge pull request #805 from FioraAeterna/storerefactor
JIT: support immediate stores
Diffstat (limited to 'Source')
-rw-r--r--Source/Core/Common/x64Analyzer.cpp1
-rw-r--r--Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp53
-rw-r--r--Source/Core/Core/PowerPC/JitCommon/JitBackpatch.cpp29
-rw-r--r--Source/Core/Core/PowerPC/JitCommon/Jit_Util.cpp64
-rw-r--r--Source/Core/Core/PowerPC/JitCommon/Jit_Util.h15
5 files changed, 126 insertions, 36 deletions
diff --git a/Source/Core/Common/x64Analyzer.cpp b/Source/Core/Common/x64Analyzer.cpp
index c7db683f1e..5be91ea585 100644
--- a/Source/Core/Common/x64Analyzer.cpp
+++ b/Source/Core/Common/x64Analyzer.cpp
@@ -136,6 +136,7 @@ bool DisassembleMov(const unsigned char *codePtr, InstructionInfo *info)
info->isMemoryWrite = true;
info->hasImmediate = true;
info->immediate = *codePtr;
+ info->operandSize = 1;
codePtr++;
break;
diff --git a/Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp b/Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp
index 1dd12b0971..7e01afb1de 100644
--- a/Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp
+++ b/Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp
@@ -399,18 +399,25 @@ void Jit64::stX(UGeckoInstruction inst)
gpr.Lock(a, s);
gpr.BindToRegister(a, true, false);
- X64Reg reg_value;
- if (WriteClobbersRegValue(accessSize, /* swap */ true))
+ if (gpr.R(s).IsImm())
{
- MOV(32, R(RSCRATCH2), gpr.R(s));
- reg_value = RSCRATCH2;
+ SafeWriteRegToReg(gpr.R(s), gpr.RX(a), accessSize, offset, CallerSavedRegistersInUse(), SAFE_LOADSTORE_CLOBBER_RSCRATCH_INSTEAD_OF_ADDR);
}
else
{
- gpr.BindToRegister(s, true, false);
- reg_value = gpr.RX(s);
+ X64Reg reg_value;
+ if (WriteClobbersRegValue(accessSize, /* swap */ true))
+ {
+ MOV(32, R(RSCRATCH2), gpr.R(s));
+ reg_value = RSCRATCH2;
+ }
+ else
+ {
+ gpr.BindToRegister(s, true, false);
+ reg_value = gpr.RX(s);
+ }
+ SafeWriteRegToReg(reg_value, gpr.RX(a), accessSize, offset, CallerSavedRegistersInUse(), SAFE_LOADSTORE_CLOBBER_RSCRATCH_INSTEAD_OF_ADDR);
}
- SafeWriteRegToReg(reg_value, gpr.RX(a), accessSize, offset, CallerSavedRegistersInUse(), SAFE_LOADSTORE_CLOBBER_RSCRATCH_INSTEAD_OF_ADDR);
if (update && offset)
{
@@ -474,18 +481,25 @@ void Jit64::stXx(UGeckoInstruction inst)
break;
}
- X64Reg reg_value;
- if (WriteClobbersRegValue(accessSize, /* swap */ true))
+ if (gpr.R(s).IsImm())
{
- MOV(32, R(RSCRATCH), gpr.R(s));
- reg_value = RSCRATCH;
+ SafeWriteRegToReg(gpr.R(s), RSCRATCH2, accessSize, 0, CallerSavedRegistersInUse());
}
else
{
- gpr.BindToRegister(s, true, false);
- reg_value = gpr.RX(s);
+ X64Reg reg_value;
+ if (WriteClobbersRegValue(accessSize, /* swap */ true))
+ {
+ MOV(32, R(RSCRATCH), gpr.R(s));
+ reg_value = RSCRATCH;
+ }
+ else
+ {
+ gpr.BindToRegister(s, true, false);
+ reg_value = gpr.RX(s);
+ }
+ SafeWriteRegToReg(reg_value, RSCRATCH2, accessSize, 0, CallerSavedRegistersInUse());
}
- SafeWriteRegToReg(reg_value, RSCRATCH2, accessSize, 0, CallerSavedRegistersInUse());
if (update && js.memcheck)
{
@@ -530,8 +544,15 @@ void Jit64::stmw(UGeckoInstruction inst)
MOV(32, R(RSCRATCH), gpr.R(inst.RA));
else
XOR(32, R(RSCRATCH), R(RSCRATCH));
- MOV(32, R(RSCRATCH2), gpr.R(i));
- SafeWriteRegToReg(RSCRATCH2, RSCRATCH, 32, (i - inst.RD) * 4 + (u32)(s32)inst.SIMM_16, CallerSavedRegistersInUse());
+ if (gpr.R(i).IsImm())
+ {
+ SafeWriteRegToReg(gpr.R(i), RSCRATCH, 32, (i - inst.RD) * 4 + (u32)(s32)inst.SIMM_16, CallerSavedRegistersInUse());
+ }
+ else
+ {
+ MOV(32, R(RSCRATCH2), gpr.R(i));
+ SafeWriteRegToReg(RSCRATCH2, RSCRATCH, 32, (i - inst.RD) * 4 + (u32)(s32)inst.SIMM_16, CallerSavedRegistersInUse());
+ }
}
gpr.UnlockAllX();
}
diff --git a/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.cpp b/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.cpp
index 9a7fdc7989..6e89ae32eb 100644
--- a/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.cpp
+++ b/Source/Core/Core/PowerPC/JitCommon/JitBackpatch.cpp
@@ -115,8 +115,31 @@ const u8 *TrampolineCache::GetWriteTrampoline(const InstructionInfo &info, u32 r
ABI_PushRegistersAndAdjustStack(registersInUse, 8);
- MOVTwo(64, ABI_PARAM1, dataReg, ABI_PARAM2, addrReg);
-
+ if (info.hasImmediate)
+ {
+ if (addrReg != ABI_PARAM2)
+ MOV(64, R(ABI_PARAM2), R(addrReg));
+ // we have to swap back the immediate to pass it to the write functions
+ switch (info.operandSize)
+ {
+ case 8:
+ PanicAlert("Invalid 64-bit immediate!");
+ break;
+ case 4:
+ MOV(32, R(ABI_PARAM1), Imm32(Common::swap32((u32)info.immediate)));
+ break;
+ case 2:
+ MOV(16, R(ABI_PARAM1), Imm16(Common::swap16((u16)info.immediate)));
+ break;
+ case 1:
+ MOV(8, R(ABI_PARAM1), Imm8((u8)info.immediate));
+ break;
+ }
+ }
+ else
+ {
+ MOVTwo(64, ABI_PARAM1, dataReg, ABI_PARAM2, addrReg);
+ }
if (info.displacement)
{
ADD(32, R(ABI_PARAM2), Imm32(info.displacement));
@@ -220,7 +243,7 @@ const u8 *Jitx86Base::BackPatch(u8 *codePtr, u32 emAddress, void *ctx_void)
u32 pc = it->second;
u8 *start;
- if (info.byteSwap)
+ if (info.byteSwap || info.hasImmediate)
{
// The instruction is a MOVBE but it failed so the value is still in little-endian byte order.
start = codePtr;
diff --git a/Source/Core/Core/PowerPC/JitCommon/Jit_Util.cpp b/Source/Core/Core/PowerPC/JitCommon/Jit_Util.cpp
index 2ded60e859..f2313579fe 100644
--- a/Source/Core/Core/PowerPC/JitCommon/Jit_Util.cpp
+++ b/Source/Core/Core/PowerPC/JitCommon/Jit_Util.cpp
@@ -250,10 +250,11 @@ void EmuCodeBlock::MMIOLoadToReg(MMIO::Mapping* mmio, Gen::X64Reg reg_value,
}
}
-FixupBranch EmuCodeBlock::CheckIfSafeAddress(X64Reg reg_value, X64Reg reg_addr, u32 registers_in_use, u32 mem_mask)
+FixupBranch EmuCodeBlock::CheckIfSafeAddress(OpArg reg_value, X64Reg reg_addr, u32 registers_in_use, u32 mem_mask)
{
registers_in_use |= (1 << reg_addr);
- registers_in_use |= (1 << reg_value);
+ if (reg_value.IsSimpleReg())
+ registers_in_use |= (1 << reg_value.GetSimpleReg());
// Get ourselves a free register; try to pick one that doesn't involve pushing, if we can.
X64Reg scratch = RSCRATCH;
@@ -381,7 +382,7 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg & opAddress,
}
FixupBranch slow, exit;
- slow = CheckIfSafeAddress(reg_value, reg_addr, registersInUse, mem_mask);
+ slow = CheckIfSafeAddress(R(reg_value), reg_addr, registersInUse, mem_mask);
UnsafeLoadToReg(reg_value, R(reg_addr), accessSize, 0, signExtend);
if (farcode.Enabled())
SwitchToFarCode();
@@ -429,34 +430,55 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg & opAddress,
}
}
-u8 *EmuCodeBlock::UnsafeWriteRegToReg(X64Reg reg_value, X64Reg reg_addr, int accessSize, s32 offset, bool swap)
+u8 *EmuCodeBlock::UnsafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int accessSize, s32 offset, bool swap)
{
u8* result = GetWritableCodePtr();
OpArg dest = MComplex(RMEM, reg_addr, SCALE_1, offset);
- if (swap)
+ if (reg_value.IsImm())
+ {
+ if (swap)
+ {
+ if (accessSize == 32)
+ reg_value = Imm32(Common::swap32((u32)reg_value.offset));
+ else if (accessSize == 16)
+ reg_value = Imm16(Common::swap16((u16)reg_value.offset));
+ else
+ reg_value = Imm8((u8)reg_value.offset);
+ }
+ MOV(accessSize, dest, reg_value);
+ }
+ else if (swap)
{
if (cpu_info.bMOVBE)
{
- MOVBE(accessSize, dest, R(reg_value));
+ MOVBE(accessSize, dest, reg_value);
}
else
{
if (accessSize > 8)
- BSWAP(accessSize, reg_value);
+ BSWAP(accessSize, reg_value.GetSimpleReg());
result = GetWritableCodePtr();
- MOV(accessSize, dest, R(reg_value));
+ MOV(accessSize, dest, reg_value);
}
}
else
{
- MOV(accessSize, dest, R(reg_value));
+ MOV(accessSize, dest, reg_value);
}
return result;
}
-void EmuCodeBlock::SafeWriteRegToReg(X64Reg reg_value, X64Reg reg_addr, int accessSize, s32 offset, u32 registersInUse, int flags)
+void EmuCodeBlock::SafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int accessSize, s32 offset, u32 registersInUse, int flags)
{
+ // set the correct immediate format
+ if (reg_value.IsImm())
+ {
+ reg_value = accessSize == 32 ? Imm32((u32)reg_value.offset) :
+ accessSize == 16 ? Imm16((u16)reg_value.offset) :
+ Imm8((u8)reg_value.offset);
+ }
+
if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bMMU &&
SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem &&
!(flags & (SAFE_LOADSTORE_NO_SWAP | SAFE_LOADSTORE_NO_FASTMEM))
@@ -515,24 +537,38 @@ void EmuCodeBlock::SafeWriteRegToReg(X64Reg reg_value, X64Reg reg_addr, int acce
else
exit = J(true);
SetJumpTarget(slow);
+
// PC is used by memory watchpoints (if enabled) or to print accurate PC locations in debug logs
MOV(32, PPCSTATE(pc), Imm32(jit->js.compilerPC));
size_t rsp_alignment = (flags & SAFE_LOADSTORE_NO_PROLOG) ? 8 : 0;
ABI_PushRegistersAndAdjustStack(registersInUse, rsp_alignment);
+
+ // If the input is an immediate, we need to put it in a register.
+ X64Reg reg;
+ if (reg_value.IsImm())
+ {
+ reg = reg_addr == ABI_PARAM1 ? RSCRATCH : ABI_PARAM1;
+ MOV(accessSize, R(reg), reg_value);
+ }
+ else
+ {
+ reg = reg_value.GetSimpleReg();
+ }
+
switch (accessSize)
{
case 64:
- ABI_CallFunctionRR(swap ? ((void *)&Memory::Write_U64) : ((void *)&Memory::Write_U64_Swap), reg_value, reg_addr);
+ ABI_CallFunctionRR(swap ? ((void *)&Memory::Write_U64) : ((void *)&Memory::Write_U64_Swap), reg, reg_addr);
break;
case 32:
- ABI_CallFunctionRR(swap ? ((void *)&Memory::Write_U32) : ((void *)&Memory::Write_U32_Swap), reg_value, reg_addr);
+ ABI_CallFunctionRR(swap ? ((void *)&Memory::Write_U32) : ((void *)&Memory::Write_U32_Swap), reg, reg_addr);
break;
case 16:
- ABI_CallFunctionRR(swap ? ((void *)&Memory::Write_U16) : ((void *)&Memory::Write_U16_Swap), reg_value, reg_addr);
+ ABI_CallFunctionRR(swap ? ((void *)&Memory::Write_U16) : ((void *)&Memory::Write_U16_Swap), reg, reg_addr);
break;
case 8:
- ABI_CallFunctionRR((void *)&Memory::Write_U8, reg_value, reg_addr);
+ ABI_CallFunctionRR((void *)&Memory::Write_U8, reg, reg_addr);
break;
}
ABI_PopRegistersAndAdjustStack(registersInUse, rsp_alignment);
diff --git a/Source/Core/Core/PowerPC/JitCommon/Jit_Util.h b/Source/Core/Core/PowerPC/JitCommon/Jit_Util.h
index 7a2051f742..21a25d7213 100644
--- a/Source/Core/Core/PowerPC/JitCommon/Jit_Util.h
+++ b/Source/Core/Core/PowerPC/JitCommon/Jit_Util.h
@@ -76,11 +76,15 @@ public:
void LoadAndSwap(int size, Gen::X64Reg dst, const Gen::OpArg& src);
void SwapAndStore(int size, const Gen::OpArg& dst, Gen::X64Reg src);
- Gen::FixupBranch CheckIfSafeAddress(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, u32 registers_in_use, u32 mem_mask);
+ Gen::FixupBranch CheckIfSafeAddress(Gen::OpArg reg_value, Gen::X64Reg reg_addr, u32 registers_in_use, u32 mem_mask);
void UnsafeLoadRegToReg(Gen::X64Reg reg_addr, Gen::X64Reg reg_value, int accessSize, s32 offset = 0, bool signExtend = false);
void UnsafeLoadRegToRegNoSwap(Gen::X64Reg reg_addr, Gen::X64Reg reg_value, int accessSize, s32 offset, bool signExtend = false);
// 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 *UnsafeWriteRegToReg(Gen::OpArg reg_value, Gen::X64Reg reg_addr, int accessSize, s32 offset = 0, bool swap = true);
+ u8 *UnsafeWriteRegToReg(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, int accessSize, s32 offset = 0, bool swap = true)
+ {
+ UnsafeWriteRegToReg(R(reg_value), reg_addr, accessSize, offset, swap);
+ }
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
@@ -98,7 +102,12 @@ public:
void SafeLoadToReg(Gen::X64Reg reg_value, const Gen::OpArg & opAddress, int accessSize, s32 offset, u32 registersInUse, bool signExtend, int flags = 0);
// Clobbers RSCRATCH or reg_addr depending on the relevant flag. Preserves
// reg_value if the load fails and js.memcheck is enabled.
- void SafeWriteRegToReg(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, int accessSize, s32 offset, u32 registersInUse, int flags = 0);
+ // Works with immediate inputs and simple registers only.
+ void SafeWriteRegToReg(Gen::OpArg reg_value, Gen::X64Reg reg_addr, int accessSize, s32 offset, u32 registersInUse, int flags = 0);
+ void SafeWriteRegToReg(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, int accessSize, s32 offset, u32 registersInUse, int flags = 0)
+ {
+ SafeWriteRegToReg(R(reg_value), reg_addr, accessSize, offset, registersInUse, flags);
+ }
// applies to safe and unsafe WriteRegToReg
bool WriteClobbersRegValue(int accessSize, bool swap)