JIT: support immediate stores

This commit is contained in:
Fiora 2014-09-10 01:12:33 -07:00
parent 68b2d86daf
commit d02b7c7755
5 changed files with 126 additions and 36 deletions

View File

@ -136,6 +136,7 @@ bool DisassembleMov(const unsigned char *codePtr, InstructionInfo *info)
info->isMemoryWrite = true; info->isMemoryWrite = true;
info->hasImmediate = true; info->hasImmediate = true;
info->immediate = *codePtr; info->immediate = *codePtr;
info->operandSize = 1;
codePtr++; codePtr++;
break; break;

View File

@ -399,18 +399,25 @@ void Jit64::stX(UGeckoInstruction inst)
gpr.Lock(a, s); gpr.Lock(a, s);
gpr.BindToRegister(a, true, false); gpr.BindToRegister(a, true, false);
X64Reg reg_value; if (gpr.R(s).IsImm())
if (WriteClobbersRegValue(accessSize, /* swap */ true))
{ {
MOV(32, R(RSCRATCH2), gpr.R(s)); SafeWriteRegToReg(gpr.R(s), gpr.RX(a), accessSize, offset, CallerSavedRegistersInUse(), SAFE_LOADSTORE_CLOBBER_RSCRATCH_INSTEAD_OF_ADDR);
reg_value = RSCRATCH2;
} }
else else
{ {
gpr.BindToRegister(s, true, false); X64Reg reg_value;
reg_value = gpr.RX(s); 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) if (update && offset)
{ {
@ -474,18 +481,25 @@ void Jit64::stXx(UGeckoInstruction inst)
break; break;
} }
X64Reg reg_value; if (gpr.R(s).IsImm())
if (WriteClobbersRegValue(accessSize, /* swap */ true))
{ {
MOV(32, R(RSCRATCH), gpr.R(s)); SafeWriteRegToReg(gpr.R(s), RSCRATCH2, accessSize, 0, CallerSavedRegistersInUse());
reg_value = RSCRATCH;
} }
else else
{ {
gpr.BindToRegister(s, true, false); X64Reg reg_value;
reg_value = gpr.RX(s); 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) if (update && js.memcheck)
{ {
@ -530,8 +544,15 @@ void Jit64::stmw(UGeckoInstruction inst)
MOV(32, R(RSCRATCH), gpr.R(inst.RA)); MOV(32, R(RSCRATCH), gpr.R(inst.RA));
else else
XOR(32, R(RSCRATCH), R(RSCRATCH)); XOR(32, R(RSCRATCH), R(RSCRATCH));
MOV(32, R(RSCRATCH2), gpr.R(i)); if (gpr.R(i).IsImm())
SafeWriteRegToReg(RSCRATCH2, RSCRATCH, 32, (i - inst.RD) * 4 + (u32)(s32)inst.SIMM_16, CallerSavedRegistersInUse()); {
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(); gpr.UnlockAllX();
} }

View File

@ -115,8 +115,31 @@ const u8 *TrampolineCache::GetWriteTrampoline(const InstructionInfo &info, u32 r
ABI_PushRegistersAndAdjustStack(registersInUse, 8); 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) if (info.displacement)
{ {
ADD(32, R(ABI_PARAM2), Imm32(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; u32 pc = it->second;
u8 *start; 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. // The instruction is a MOVBE but it failed so the value is still in little-endian byte order.
start = codePtr; start = codePtr;

View File

@ -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_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. // Get ourselves a free register; try to pick one that doesn't involve pushing, if we can.
X64Reg scratch = RSCRATCH; X64Reg scratch = RSCRATCH;
@ -381,7 +382,7 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg & opAddress,
} }
FixupBranch slow, exit; 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); UnsafeLoadToReg(reg_value, R(reg_addr), accessSize, 0, signExtend);
if (farcode.Enabled()) if (farcode.Enabled())
SwitchToFarCode(); 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(); u8* result = GetWritableCodePtr();
OpArg dest = MComplex(RMEM, reg_addr, SCALE_1, offset); 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) if (cpu_info.bMOVBE)
{ {
MOVBE(accessSize, dest, R(reg_value)); MOVBE(accessSize, dest, reg_value);
} }
else else
{ {
if (accessSize > 8) if (accessSize > 8)
BSWAP(accessSize, reg_value); BSWAP(accessSize, reg_value.GetSimpleReg());
result = GetWritableCodePtr(); result = GetWritableCodePtr();
MOV(accessSize, dest, R(reg_value)); MOV(accessSize, dest, reg_value);
} }
} }
else else
{ {
MOV(accessSize, dest, R(reg_value)); MOV(accessSize, dest, reg_value);
} }
return result; 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 && if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bMMU &&
SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem && SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem &&
!(flags & (SAFE_LOADSTORE_NO_SWAP | SAFE_LOADSTORE_NO_FASTMEM)) !(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 else
exit = J(true); exit = J(true);
SetJumpTarget(slow); SetJumpTarget(slow);
// PC is used by memory watchpoints (if enabled) or to print accurate PC locations in debug logs // 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)); MOV(32, PPCSTATE(pc), Imm32(jit->js.compilerPC));
size_t rsp_alignment = (flags & SAFE_LOADSTORE_NO_PROLOG) ? 8 : 0; size_t rsp_alignment = (flags & SAFE_LOADSTORE_NO_PROLOG) ? 8 : 0;
ABI_PushRegistersAndAdjustStack(registersInUse, rsp_alignment); 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) switch (accessSize)
{ {
case 64: 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; break;
case 32: 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; break;
case 16: 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; break;
case 8: case 8:
ABI_CallFunctionRR((void *)&Memory::Write_U8, reg_value, reg_addr); ABI_CallFunctionRR((void *)&Memory::Write_U8, reg, reg_addr);
break; break;
} }
ABI_PopRegistersAndAdjustStack(registersInUse, rsp_alignment); ABI_PopRegistersAndAdjustStack(registersInUse, rsp_alignment);

View File

@ -76,11 +76,15 @@ public:
void LoadAndSwap(int size, Gen::X64Reg dst, const Gen::OpArg& src); void LoadAndSwap(int size, Gen::X64Reg dst, const Gen::OpArg& src);
void SwapAndStore(int size, const Gen::OpArg& dst, Gen::X64Reg 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 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); 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 // 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); 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 // 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); 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 // Clobbers RSCRATCH or reg_addr depending on the relevant flag. Preserves
// reg_value if the load fails and js.memcheck is enabled. // 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 // applies to safe and unsafe WriteRegToReg
bool WriteClobbersRegValue(int accessSize, bool swap) bool WriteClobbersRegValue(int accessSize, bool swap)