JIT: support immediate stores
This commit is contained in:
parent
68b2d86daf
commit
d02b7c7755
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue