mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-11 00:29:11 +01:00
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->isMemoryWrite = true;
|
||||||
info->hasImmediate = true;
|
info->hasImmediate = true;
|
||||||
info->immediate = *codePtr;
|
info->immediate = *codePtr;
|
||||||
|
info->operandSize = 1;
|
||||||
codePtr++;
|
codePtr++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user