Merge pull request #10847 from unknownbrackets/arm64jit

arm64jit: Update rounding mode on thread switch
This commit is contained in:
Henrik Rydgård 2018-04-01 22:11:26 +02:00 committed by GitHub
commit 0d677fff87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 76 additions and 61 deletions

View File

@ -31,6 +31,7 @@
#include "Core/MIPS/MIPS.h"
#include "Core/CoreTiming.h"
#include "Core/MemMapHelpers.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "Core/Reporting.h"
#include "Common/ChunkFile.h"
@ -1460,6 +1461,10 @@ void __KernelLoadContext(ThreadContext *ctx, bool vfpuEnabled)
}
memcpy(currentMIPS->other, ctx->other, sizeof(ctx->other));
if (MIPSComp::jit) {
// When thread switching, we must update the rounding mode if cached in the jit.
MIPSComp::jit->UpdateFCR31();
}
// Reset the llBit, the other thread may have touched memory.
currentMIPS->llBit = 0;

View File

@ -123,20 +123,6 @@ void ArmJit::GenerateFixedCode() {
POP(2, SCRATCHREG1, R_PC);
}
// Must preserve SCRATCHREG1 (R0), destroys SCRATCHREG2 (LR)
updateRoundingMode = AlignCode16(); {
PUSH(2, SCRATCHREG1, R_LR);
LDR(SCRATCHREG2, CTXREG, offsetof(MIPSState, fcr31));
MOVI2R(SCRATCHREG1, 0x1000003);
TST(SCRATCHREG2, SCRATCHREG1);
FixupBranch skip = B_CC(CC_EQ); // zero
MOVI2R(SCRATCHREG2, 1);
MOVP2R(SCRATCHREG1, &js.hasSetRounding);
STRB(SCRATCHREG2, SCRATCHREG1, 0);
SetJumpTarget(skip);
POP(2, SCRATCHREG1, R_PC);
}
FlushLitPool();
enterDispatcher = AlignCode16();

View File

@ -416,8 +416,10 @@ void ArmJit::Comp_mxc1(MIPSOpcode op)
// Must clear before setting, since ApplyRoundingMode() assumes it was cleared.
RestoreRoundingMode();
bool wasImm = gpr.IsImm(rt);
u32 immVal = -1;
if (wasImm) {
gpr.SetImm(MIPS_REG_FPCOND, (gpr.GetImm(rt) >> 23) & 1);
immVal = gpr.GetImm(rt);
gpr.SetImm(MIPS_REG_FPCOND, (immVal >> 23) & 1);
gpr.MapReg(rt);
} else {
gpr.MapDirtyIn(MIPS_REG_FPCOND, rt);
@ -433,8 +435,10 @@ void ArmJit::Comp_mxc1(MIPSOpcode op)
MOV(SCRATCHREG1, Operand2(gpr.R(rt), ST_LSR, 23));
AND(gpr.R(MIPS_REG_FPCOND), SCRATCHREG1, Operand2(1));
#endif
UpdateRoundingMode();
} else {
UpdateRoundingMode(immVal);
}
UpdateRoundingMode();
ApplyRoundingMode();
} else {
Comp_Generic(op);

View File

@ -106,6 +106,9 @@ void ArmJit::DoState(PointerWrap &p)
}
}
void ArmJit::UpdateFCR31() {
}
void ArmJit::FlushAll()
{
gpr.FlushAll();
@ -637,8 +640,12 @@ void ArmJit::ApplyRoundingMode(bool force) {
}
// Does (must!) not destroy R0 (SCRATCHREG1). Destroys R14 (SCRATCHREG2).
void ArmJit::UpdateRoundingMode() {
QuickCallFunction(R1, updateRoundingMode);
void ArmJit::UpdateRoundingMode(u32 fcr31) {
// We must set js.hasSetRounding at compile time, or this block will use the wrong rounding mode.
// The fcr31 parameter is -1 when not known at compile time, so we just assume it was changed.
if (fcr31 & 0x01000003) {
js.hasSetRounding = true;
}
}
// IDEA - could have a WriteDualExit that takes two destinations and two condition flags,

View File

@ -175,6 +175,7 @@ public:
void ClearCache() override;
void InvalidateCacheAt(u32 em_address, int length = 4) override;
void UpdateFCR31() override;
void EatPrefix() override { js.EatPrefix(); }
@ -202,7 +203,7 @@ private:
void WriteDownCountR(ArmGen::ARMReg reg);
void RestoreRoundingMode(bool force = false);
void ApplyRoundingMode(bool force = false);
void UpdateRoundingMode();
void UpdateRoundingMode(u32 fcr31 = -1);
void MovFromPC(ArmGen::ARMReg r);
void MovToPC(ArmGen::ARMReg r);
@ -310,7 +311,6 @@ public:
const u8 *restoreRoundingMode;
const u8 *applyRoundingMode;
const u8 *updateRoundingMode;
const u8 *breakpointBailout;
};

View File

@ -177,17 +177,6 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) {
ADDI2R(SCRATCH2, SCRATCH2, 4);
SetJumpTarget(skip);
// We need both SCRATCH1 and SCRATCH2 for updating hasSetRounding.
PUSH(SCRATCH2);
// We can only skip if the rounding mode is zero and flush is not set.
CMPI2R(SCRATCH2, 0);
FixupBranch skip2 = B(CC_EQ);
MOVI2R(SCRATCH2, 1);
MOVP2R(SCRATCH1_64, &js.hasSetRounding);
STRB(INDEX_UNSIGNED, SCRATCH2, SCRATCH1_64, 0);
SetJumpTarget(skip2);
POP(SCRATCH2);
// Let's update js.currentRoundingFunc with the right convertS0ToSCRATCH1 func.
MOVP2R(SCRATCH1_64, convertS0ToSCRATCH1);
LSL(SCRATCH2, SCRATCH2, 3);

View File

@ -375,8 +375,10 @@ void Arm64Jit::Comp_mxc1(MIPSOpcode op)
// Must clear before setting, since ApplyRoundingMode() assumes it was cleared.
RestoreRoundingMode();
bool wasImm = gpr.IsImm(rt);
u32 immVal = -1;
if (wasImm) {
gpr.SetImm(MIPS_REG_FPCOND, (gpr.GetImm(rt) >> 23) & 1);
immVal = gpr.GetImm(rt);
gpr.SetImm(MIPS_REG_FPCOND, (immVal >> 23) & 1);
gpr.MapReg(rt);
} else {
gpr.MapDirtyIn(MIPS_REG_FPCOND, rt);
@ -387,9 +389,11 @@ void Arm64Jit::Comp_mxc1(MIPSOpcode op)
STR(INDEX_UNSIGNED, gpr.R(rt), CTXREG, offsetof(MIPSState, fcr31));
if (!wasImm) {
UBFX(gpr.R(MIPS_REG_FPCOND), gpr.R(rt), 23, 1);
// TODO: We do have the fcr31 value in a register here, could use that in UpdateRoundingMode to avoid reloading it.
UpdateRoundingMode();
} else {
UpdateRoundingMode(immVal);
}
// TODO: We do have the fcr31 value in a register here, could use that in UpdateRoundingMode to avoid reloading it.
UpdateRoundingMode();
ApplyRoundingMode();
} else {
Comp_Generic(op);

View File

@ -79,7 +79,7 @@ Arm64Jit::Arm64Jit(MIPSState *mips) : blocks(mips, this), gpr(mips, &js, &jo), f
AllocCodeSpace(1024 * 1024 * 16); // 32MB is the absolute max because that's what an ARM branch instruction can reach, backwards and forwards.
GenerateFixedCode(jo);
js.startDefaultPrefix = mips_->HasDefaultPrefix();
js.currentRoundingFunc = convertS0ToSCRATCH1[0];
js.currentRoundingFunc = convertS0ToSCRATCH1[mips_->fcr31 & 3];
}
Arm64Jit::~Arm64Jit() {
@ -98,13 +98,14 @@ void Arm64Jit::DoState(PointerWrap &p) {
js.hasSetRounding = 1;
}
if (p.GetMode() == PointerWrap::MODE_READ) {
js.currentRoundingFunc = convertS0ToSCRATCH1[(mips_->fcr31) & 3];
}
// Note: we can't update the currentRoundingFunc here because fcr31 wasn't loaded yet.
}
void Arm64Jit::FlushAll()
{
void Arm64Jit::UpdateFCR31() {
js.currentRoundingFunc = convertS0ToSCRATCH1[mips_->fcr31 & 3];
}
void Arm64Jit::FlushAll() {
gpr.FlushAll();
fpr.FlushAll();
FlushPrefixV();
@ -601,7 +602,12 @@ void Arm64Jit::ApplyRoundingMode(bool force) {
}
// Destroys SCRATCH1 and SCRATCH2
void Arm64Jit::UpdateRoundingMode() {
void Arm64Jit::UpdateRoundingMode(u32 fcr31) {
// We must set js.hasSetRounding at compile time, or this block will use the wrong rounding mode.
// The fcr31 parameter is -1 when not known at compile time, so we just assume it was changed.
if (fcr31 & 0x01000003) {
js.hasSetRounding = true;
}
QuickCallFunction(SCRATCH2_64, updateRoundingMode);
}

View File

@ -176,6 +176,7 @@ public:
void ClearCache() override;
void InvalidateCacheAt(u32 em_address, int length = 4) override;
void UpdateFCR31() override;
void EatPrefix() override { js.EatPrefix(); }
@ -201,7 +202,7 @@ private:
void WriteDownCountR(Arm64Gen::ARM64Reg reg, bool updateFlags = true);
void RestoreRoundingMode(bool force = false);
void ApplyRoundingMode(bool force = false);
void UpdateRoundingMode();
void UpdateRoundingMode(u32 fcr31 = -1);
void MovFromPC(Arm64Gen::ARM64Reg r);
void MovToPC(Arm64Gen::ARM64Reg r);

View File

@ -200,6 +200,8 @@ void IRFrontend::ApplyRoundingMode(bool force) {
// Destroys SCRATCH1 and SCRATCH2
void IRFrontend::UpdateRoundingMode() {
// We must set js.hasSetRounding at compile time, or this block will use the wrong rounding mode.
js.hasSetRounding = true;
ir.Write(IROp::UpdateRoundingMode);
}

View File

@ -55,6 +55,9 @@ void IRJit::DoState(PointerWrap &p) {
frontend_.DoState(p);
}
void IRJit::UpdateFCR31() {
}
void IRJit::ClearCache() {
ILOG("IRJit: Clearing the cache!");
blocks_.Clear();

View File

@ -158,6 +158,7 @@ public:
void ClearCache() override;
void InvalidateCacheAt(u32 em_address, int length = 4) override;
void UpdateFCR31() override;
const u8 *GetDispatcher() const override { return nullptr; }

View File

@ -131,6 +131,7 @@ namespace MIPSComp {
virtual void Compile(u32 em_address) = 0;
virtual void CompileFunction(u32 start_address, u32 length) { }
virtual void ClearCache() = 0;
virtual void UpdateFCR31() = 0;
virtual MIPSOpcode GetOriginalOp(MIPSOpcode op) = 0;
// No jit operations may be run between these calls.

View File

@ -293,6 +293,11 @@ void MIPSState::DoState(PointerWrap &p) {
p.Do(inDelaySlot);
p.Do(llBit);
p.Do(debugCount);
if (p.mode == p.MODE_READ && MIPSComp::jit) {
// Now that we've loaded fcr31, update any jit state associated.
MIPSComp::jit->UpdateFCR31();
}
}
void MIPSState::SingleStep() {

View File

@ -66,6 +66,9 @@ void MipsJit::DoState(PointerWrap &p)
}
}
void MipsJit::UpdateFCR31() {
}
void MipsJit::FlushAll()
{
//gpr.FlushAll();

View File

@ -136,6 +136,7 @@ public:
void ClearCache() override;
void InvalidateCacheAt(u32 em_address, int length = 4) override;
void UpdateFCR31() override;
void EatPrefix() override { js.EatPrefix(); }

View File

@ -528,6 +528,10 @@ namespace MIPSInt
if (fs == 31) {
currentMIPS->fcr31 = value & 0x0181FFFF;
currentMIPS->fpcond = (value >> 23) & 1;
if (MIPSComp::jit) {
// In case of DISABLE, we need to tell jit we updated FCR31.
MIPSComp::jit->UpdateFCR31();
}
} else {
WARN_LOG_REPORT(CPU, "WriteFCR: Unexpected reg %d (value %08x)", fs, value);
}

View File

@ -109,18 +109,6 @@ void Jit::GenerateFixedCode(JitOptions &jo) {
RET();
}
updateRoundingMode = AlignCode16(); {
// If it's only ever 0, we don't actually bother applying or restoring it.
// This is the most common situation.
TEST(32, MIPSSTATE_VAR(fcr31), Imm32(0x01000003));
FixupBranch skip = J_CC(CC_Z);
// TODO: Move the hasSetRounding flag somewhere we can reach it through the context pointer, or something.
MOV(PTRBITS, R(RAX), ImmPtr(&js.hasSetRounding));
MOV(8, MatR(RAX), Imm8(1));
SetJumpTarget(skip);
RET();
}
enterDispatcher = AlignCode16();
ABI_PushAllCalleeSavedRegsAndAdjustStack();
#ifdef _M_X64

View File

@ -426,7 +426,7 @@ void Jit::Comp_mxc1(MIPSOpcode op) {
if ((gpr.GetImm(rt) & 0x1000003) == 0) {
// Default nearest / no-flush mode, just leave it cleared.
} else {
UpdateRoundingMode();
UpdateRoundingMode(gpr.GetImm(rt));
ApplyRoundingMode();
}
} else {

View File

@ -142,6 +142,9 @@ void Jit::DoState(PointerWrap &p) {
CBreakPoints::SetSkipFirst(0);
}
void Jit::UpdateFCR31() {
}
void Jit::GetStateAndFlushAll(RegCacheState &state) {
gpr.GetState(state.gpr);
fpr.GetState(state.fpr);
@ -195,8 +198,12 @@ void Jit::ApplyRoundingMode(bool force) {
}
}
void Jit::UpdateRoundingMode() {
CALL(updateRoundingMode);
void Jit::UpdateRoundingMode(u32 fcr31) {
// We must set js.hasSetRounding at compile time, or this block will use the wrong rounding mode.
// The fcr31 parameter is -1 when not known at compile time, so we just assume it was changed.
if (fcr31 & 0x01000003) {
js.hasSetRounding = true;
}
}
void Jit::ClearCache()
@ -415,8 +422,6 @@ void Jit::AddContinuedBlock(u32 dest) {
bool Jit::DescribeCodePtr(const u8 *ptr, std::string &name) {
if (ptr == applyRoundingMode)
name = "applyRoundingMode";
else if (ptr == updateRoundingMode)
name = "updateRoundingMode";
else if (ptr == dispatcher)
name = "dispatcher";
else if (ptr == dispatcherInEAXNoCheck)

View File

@ -155,7 +155,7 @@ public:
void RestoreRoundingMode(bool force = false);
void ApplyRoundingMode(bool force = false);
void UpdateRoundingMode();
void UpdateRoundingMode(u32 fcr31 = -1);
JitBlockCache *GetBlockCache() override { return &blocks; }
JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() override { return &blocks; }
@ -171,6 +171,7 @@ public:
blocks.InvalidateICache(em_address, length);
}
}
void UpdateFCR31() override;
const u8 *GetDispatcher() const override {
return dispatcher;
@ -323,7 +324,6 @@ private:
const u8 *restoreRoundingMode;
const u8 *applyRoundingMode;
const u8 *updateRoundingMode;
const u8 *endOfPregeneratedCode;