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->hasImmediate = true;
info->immediate = *codePtr;
info->operandSize = 1;
codePtr++;
break;

View File

@ -399,6 +399,12 @@ void Jit64::stX(UGeckoInstruction inst)
gpr.Lock(a, s);
gpr.BindToRegister(a, true, false);
if (gpr.R(s).IsImm())
{
SafeWriteRegToReg(gpr.R(s), gpr.RX(a), accessSize, offset, CallerSavedRegistersInUse(), SAFE_LOADSTORE_CLOBBER_RSCRATCH_INSTEAD_OF_ADDR);
}
else
{
X64Reg reg_value;
if (WriteClobbersRegValue(accessSize, /* swap */ true))
{
@ -411,6 +417,7 @@ void Jit64::stX(UGeckoInstruction inst)
reg_value = gpr.RX(s);
}
SafeWriteRegToReg(reg_value, gpr.RX(a), accessSize, offset, CallerSavedRegistersInUse(), SAFE_LOADSTORE_CLOBBER_RSCRATCH_INSTEAD_OF_ADDR);
}
if (update && offset)
{
@ -474,6 +481,12 @@ void Jit64::stXx(UGeckoInstruction inst)
break;
}
if (gpr.R(s).IsImm())
{
SafeWriteRegToReg(gpr.R(s), RSCRATCH2, accessSize, 0, CallerSavedRegistersInUse());
}
else
{
X64Reg reg_value;
if (WriteClobbersRegValue(accessSize, /* swap */ true))
{
@ -486,6 +499,7 @@ void Jit64::stXx(UGeckoInstruction inst)
reg_value = gpr.RX(s);
}
SafeWriteRegToReg(reg_value, RSCRATCH2, accessSize, 0, CallerSavedRegistersInUse());
}
if (update && js.memcheck)
{
@ -530,9 +544,16 @@ void Jit64::stmw(UGeckoInstruction inst)
MOV(32, R(RSCRATCH), gpr.R(inst.RA));
else
XOR(32, R(RSCRATCH), R(RSCRATCH));
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();
}

View File

@ -115,8 +115,31 @@ const u8 *TrampolineCache::GetWriteTrampoline(const InstructionInfo &info, u32 r
ABI_PushRegistersAndAdjustStack(registersInUse, 8);
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;

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_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 (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);

View File

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