Prevent paired singles routines clobbering PC,SRR0
Paired single (ps) instructions can call asm_routines that try to update PowerPC::ppcState.pc. At the time the asm_routine is built, emulation has not started and the PC is invalid (0). If the ps instruction causes an exception (e.g. DSI), SRR0 gets clobbered with the invalid PC. This change makes the relevant ps instructions store PC before calling out to asm_routines, and prevents the asm_routine from trying to store PC itself.
This commit is contained in:
parent
3cca051850
commit
c880302c6b
|
@ -71,6 +71,8 @@ void Jit64::psq_stXX(UGeckoInstruction inst)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Stash PC in case asm_routine causes exception
|
||||||
|
MOV(32, PPCSTATE(pc), Imm32(g_jit->js.compilerPC));
|
||||||
// We know what GQR is here, so we can load RSCRATCH2 and call into the store method directly
|
// We know what GQR is here, so we can load RSCRATCH2 and call into the store method directly
|
||||||
// with just the scale bits.
|
// with just the scale bits.
|
||||||
MOV(32, R(RSCRATCH2), Imm32(gqrValue & 0x3F00));
|
MOV(32, R(RSCRATCH2), Imm32(gqrValue & 0x3F00));
|
||||||
|
@ -83,6 +85,8 @@ void Jit64::psq_stXX(UGeckoInstruction inst)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Stash PC incase asm_routine causes exception
|
||||||
|
MOV(32, PPCSTATE(pc), Imm32(g_jit->js.compilerPC));
|
||||||
// Some games (e.g. Dirt 2) incorrectly set the unused bits which breaks the lookup table code.
|
// Some games (e.g. Dirt 2) incorrectly set the unused bits which breaks the lookup table code.
|
||||||
// Hence, we need to mask out the unused bits. The layout of the GQR register is
|
// Hence, we need to mask out the unused bits. The layout of the GQR register is
|
||||||
// UU[SCALE]UUUUU[TYPE] where SCALE is 6 bits and TYPE is 3 bits, so we have to AND with
|
// UU[SCALE]UUUUU[TYPE] where SCALE is 6 bits and TYPE is 3 bits, so we have to AND with
|
||||||
|
@ -148,10 +152,11 @@ void Jit64::psq_lXX(UGeckoInstruction inst)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Stash PC in case asm_routine causes exception
|
||||||
|
MOV(32, PPCSTATE(pc), Imm32(g_jit->js.compilerPC));
|
||||||
// Get the high part of the GQR register
|
// Get the high part of the GQR register
|
||||||
OpArg gqr = PPCSTATE(spr[SPR_GQR0 + i]);
|
OpArg gqr = PPCSTATE(spr[SPR_GQR0 + i]);
|
||||||
gqr.AddMemOffset(2);
|
gqr.AddMemOffset(2);
|
||||||
|
|
||||||
MOV(32, R(RSCRATCH2), Imm32(0x3F07));
|
MOV(32, R(RSCRATCH2), Imm32(0x3F07));
|
||||||
AND(32, R(RSCRATCH2), gqr);
|
AND(32, R(RSCRATCH2), gqr);
|
||||||
LEA(64, RSCRATCH, M(w ? asm_routines.singleLoadQuantized : asm_routines.pairedLoadQuantized));
|
LEA(64, RSCRATCH, M(w ? asm_routines.singleLoadQuantized : asm_routines.pairedLoadQuantized));
|
||||||
|
|
|
@ -317,7 +317,8 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg& opAddress,
|
||||||
bool slowmem = (flags & SAFE_LOADSTORE_FORCE_SLOWMEM) != 0;
|
bool slowmem = (flags & SAFE_LOADSTORE_FORCE_SLOWMEM) != 0;
|
||||||
|
|
||||||
registersInUse[reg_value] = false;
|
registersInUse[reg_value] = false;
|
||||||
if (g_jit->jo.fastmem && !(flags & SAFE_LOADSTORE_NO_FASTMEM) && !slowmem)
|
if (g_jit->jo.fastmem && !(flags & (SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_UPDATE_PC)) &&
|
||||||
|
!slowmem)
|
||||||
{
|
{
|
||||||
u8* backpatchStart = GetWritableCodePtr();
|
u8* backpatchStart = GetWritableCodePtr();
|
||||||
MovInfo mov;
|
MovInfo mov;
|
||||||
|
@ -378,7 +379,11 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg& opAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helps external systems know which instruction triggered the read.
|
// Helps external systems know which instruction triggered the read.
|
||||||
MOV(32, PPCSTATE(pc), Imm32(g_jit->js.compilerPC));
|
// Invalid for calls from Jit64AsmCommon routines
|
||||||
|
if (!(flags & SAFE_LOADSTORE_NO_UPDATE_PC))
|
||||||
|
{
|
||||||
|
MOV(32, PPCSTATE(pc), Imm32(g_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);
|
||||||
|
@ -483,7 +488,8 @@ void EmuCodeBlock::SafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int acces
|
||||||
// set the correct immediate format
|
// set the correct immediate format
|
||||||
reg_value = FixImmediate(accessSize, reg_value);
|
reg_value = FixImmediate(accessSize, reg_value);
|
||||||
|
|
||||||
if (g_jit->jo.fastmem && !(flags & SAFE_LOADSTORE_NO_FASTMEM) && !slowmem)
|
if (g_jit->jo.fastmem && !(flags & (SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_UPDATE_PC)) &&
|
||||||
|
!slowmem)
|
||||||
{
|
{
|
||||||
u8* backpatchStart = GetWritableCodePtr();
|
u8* backpatchStart = GetWritableCodePtr();
|
||||||
MovInfo mov;
|
MovInfo mov;
|
||||||
|
@ -540,7 +546,11 @@ void EmuCodeBlock::SafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int acces
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(g_jit->js.compilerPC));
|
// Invalid for calls from Jit64AsmCommon routines
|
||||||
|
if (!(flags & SAFE_LOADSTORE_NO_UPDATE_PC))
|
||||||
|
{
|
||||||
|
MOV(32, PPCSTATE(pc), Imm32(g_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);
|
||||||
|
|
|
@ -77,6 +77,8 @@ public:
|
||||||
// Force slowmem (used when generating fallbacks in trampolines)
|
// Force slowmem (used when generating fallbacks in trampolines)
|
||||||
SAFE_LOADSTORE_FORCE_SLOWMEM = 16,
|
SAFE_LOADSTORE_FORCE_SLOWMEM = 16,
|
||||||
SAFE_LOADSTORE_DR_ON = 32,
|
SAFE_LOADSTORE_DR_ON = 32,
|
||||||
|
// Generated from a context that doesn't have the PC of the instruction that caused it
|
||||||
|
SAFE_LOADSTORE_NO_UPDATE_PC = 64,
|
||||||
};
|
};
|
||||||
|
|
||||||
void SafeLoadToReg(Gen::X64Reg reg_value, const Gen::OpArg& opAddress, int accessSize, s32 offset,
|
void SafeLoadToReg(Gen::X64Reg reg_value, const Gen::OpArg& opAddress, int accessSize, s32 offset,
|
||||||
|
|
|
@ -419,8 +419,9 @@ void QuantizedMemoryRoutines::GenQuantizedStore(bool single, EQuantizeType type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int flags =
|
int flags = isInline ? 0 :
|
||||||
isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | SAFE_LOADSTORE_DR_ON;
|
SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG |
|
||||||
|
SAFE_LOADSTORE_DR_ON | SAFE_LOADSTORE_NO_UPDATE_PC;
|
||||||
if (!single)
|
if (!single)
|
||||||
flags |= SAFE_LOADSTORE_NO_SWAP;
|
flags |= SAFE_LOADSTORE_NO_SWAP;
|
||||||
|
|
||||||
|
@ -478,8 +479,9 @@ void QuantizedMemoryRoutines::GenQuantizedLoad(bool single, EQuantizeType type,
|
||||||
if (g_jit->jo.memcheck)
|
if (g_jit->jo.memcheck)
|
||||||
{
|
{
|
||||||
BitSet32 regsToSave = QUANTIZED_REGS_TO_SAVE_LOAD;
|
BitSet32 regsToSave = QUANTIZED_REGS_TO_SAVE_LOAD;
|
||||||
int flags =
|
int flags = isInline ? 0 :
|
||||||
isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | SAFE_LOADSTORE_DR_ON;
|
SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG |
|
||||||
|
SAFE_LOADSTORE_DR_ON | SAFE_LOADSTORE_NO_UPDATE_PC;
|
||||||
SafeLoadToReg(RSCRATCH_EXTRA, R(RSCRATCH_EXTRA), size, 0, regsToSave, extend, flags);
|
SafeLoadToReg(RSCRATCH_EXTRA, R(RSCRATCH_EXTRA), size, 0, regsToSave, extend, flags);
|
||||||
if (!single && (type == QUANTIZE_U8 || type == QUANTIZE_S8))
|
if (!single && (type == QUANTIZE_U8 || type == QUANTIZE_S8))
|
||||||
{
|
{
|
||||||
|
@ -604,8 +606,9 @@ void QuantizedMemoryRoutines::GenQuantizedLoadFloat(bool single, bool isInline)
|
||||||
if (g_jit->jo.memcheck)
|
if (g_jit->jo.memcheck)
|
||||||
{
|
{
|
||||||
BitSet32 regsToSave = QUANTIZED_REGS_TO_SAVE;
|
BitSet32 regsToSave = QUANTIZED_REGS_TO_SAVE;
|
||||||
int flags =
|
int flags = isInline ? 0 :
|
||||||
isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | SAFE_LOADSTORE_DR_ON;
|
SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG |
|
||||||
|
SAFE_LOADSTORE_DR_ON | SAFE_LOADSTORE_NO_UPDATE_PC;
|
||||||
SafeLoadToReg(RSCRATCH_EXTRA, R(RSCRATCH_EXTRA), size, 0, regsToSave, extend, flags);
|
SafeLoadToReg(RSCRATCH_EXTRA, R(RSCRATCH_EXTRA), size, 0, regsToSave, extend, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,12 +32,14 @@ public:
|
||||||
// Out: XMM0: Bottom two 32-bit slots hold the read value,
|
// Out: XMM0: Bottom two 32-bit slots hold the read value,
|
||||||
// converted to a pair of floats.
|
// converted to a pair of floats.
|
||||||
// Trashes: all three RSCRATCH
|
// Trashes: all three RSCRATCH
|
||||||
|
// Note: Store PC if this could cause an exception
|
||||||
const u8** pairedLoadQuantized;
|
const u8** pairedLoadQuantized;
|
||||||
|
|
||||||
// In: array index: GQR to use.
|
// In: array index: GQR to use.
|
||||||
// In: ECX: Address to read from.
|
// In: ECX: Address to read from.
|
||||||
// Out: XMM0: Bottom 32-bit slot holds the read value.
|
// Out: XMM0: Bottom 32-bit slot holds the read value.
|
||||||
// Trashes: all three RSCRATCH
|
// Trashes: all three RSCRATCH
|
||||||
|
// Note: Store PC if this could cause an exception
|
||||||
const u8** singleLoadQuantized;
|
const u8** singleLoadQuantized;
|
||||||
|
|
||||||
// In: array index: GQR to use.
|
// In: array index: GQR to use.
|
||||||
|
@ -45,10 +47,12 @@ public:
|
||||||
// In: XMM0: Bottom two 32-bit slots hold the pair of floats to be written.
|
// In: XMM0: Bottom two 32-bit slots hold the pair of floats to be written.
|
||||||
// Out: Nothing.
|
// Out: Nothing.
|
||||||
// Trashes: all three RSCRATCH
|
// Trashes: all three RSCRATCH
|
||||||
|
// Note: Store PC if this could cause an exception
|
||||||
const u8** pairedStoreQuantized;
|
const u8** pairedStoreQuantized;
|
||||||
|
|
||||||
// In: array index: GQR to use.
|
// In: array index: GQR to use.
|
||||||
// In: ECX: Address to write to.
|
// In: ECX: Address to write to.
|
||||||
// In: XMM0: Bottom 32-bit slot holds the float to be written.
|
// In: XMM0: Bottom 32-bit slot holds the float to be written.
|
||||||
|
// Note: Store PC if this could cause an exception
|
||||||
const u8** singleStoreQuantized;
|
const u8** singleStoreQuantized;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue