armjit: Fix rounding mode, allow non flush-to-zero.

Default: force flush to zero (for RunFast mode.)  But now it's an ini
option so we can more easily compare armjit differences.
This commit is contained in:
Unknown W. Brackets 2014-09-11 07:58:51 -07:00
parent 432bdef368
commit 52b6f1095e
6 changed files with 61 additions and 31 deletions

View file

@ -313,6 +313,14 @@ static ConfigSetting generalSettings[] = {
ConfigSetting(false),
};
static bool DefaultForceFlushToZero() {
#ifdef ARM
return true;
#else
return false;
#endif
}
static ConfigSetting cpuSettings[] = {
ReportedConfigSetting("Jit", &g_Config.bJit, &DefaultJit),
ReportedConfigSetting("SeparateCPUThread", &g_Config.bSeparateCPUThread, false),
@ -323,6 +331,7 @@ static ConfigSetting cpuSettings[] = {
ReportedConfigSetting("FuncReplacements", &g_Config.bFuncReplacements, true),
ReportedConfigSetting("CPUSpeed", &g_Config.iLockedCPUSpeed, 0),
ReportedConfigSetting("SetRoundingMode", &g_Config.bSetRoundingMode, true),
ReportedConfigSetting("ForceFlushToZero", &g_Config.bForceFlushToZero, &DefaultForceFlushToZero),
ConfigSetting(false),
};

View file

@ -98,6 +98,7 @@ public:
bool bForceLagSync;
bool bFuncReplacements;
bool bSetRoundingMode;
bool bForceFlushToZero;
// Definitely cannot be changed while game is running.
bool bSeparateCPUThread;

View file

@ -399,6 +399,8 @@ void Jit::Comp_mxc1(MIPSOpcode op)
case 6: //ctc1
if (fs == 31) {
// Must clear before setting, since SetRoundingMode() assumes it was cleared.
ClearRoundingMode();
bool wasImm = gpr.IsImm(rt);
if (wasImm) {
gpr.SetImm(MIPS_REG_FPCOND, (gpr.GetImm(rt) >> 23) & 1);
@ -417,15 +419,8 @@ void Jit::Comp_mxc1(MIPSOpcode op)
MOV(SCRATCHREG1, Operand2(gpr.R(rt), ST_LSR, 23));
AND(gpr.R(MIPS_REG_FPCOND), SCRATCHREG1, Operand2(1));
#endif
SetRoundingMode();
} else {
if ((gpr.GetImm(rt) & 3) == 0) {
// Default nearest mode, let's do this the fast way.
ClearRoundingMode();
} else {
SetRoundingMode();
}
}
SetRoundingMode();
} else {
Comp_Generic(op);
}

View file

@ -508,8 +508,7 @@ void Jit::RestoreDowncount() {
LDR(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
}
void Jit::WriteDownCount(int offset)
{
void Jit::WriteDownCount(int offset) {
if (jo.downcountInRegister) {
int theDowncount = js.downcountAmount + offset;
Operand2 op2;
@ -538,8 +537,7 @@ void Jit::WriteDownCount(int offset)
}
// Abuses R2
void Jit::WriteDownCountR(ARMReg reg)
{
void Jit::WriteDownCountR(ARMReg reg) {
if (jo.downcountInRegister) {
SUBS(DOWNCOUNTREG, DOWNCOUNTREG, reg);
} else {
@ -549,42 +547,64 @@ void Jit::WriteDownCountR(ARMReg reg)
}
}
void Jit::ClearRoundingMode()
{
if (g_Config.bSetRoundingMode)
{
void Jit::ClearRoundingMode() {
if (g_Config.bSetRoundingMode) {
VMRS(SCRATCHREG2);
// Assume we're always in round-to-nearest mode beforehand.
// Also on ARM, we're always in flush-to-zero in C++, so stay that way.
if (!g_Config.bForceFlushToZero) {
ORR(SCRATCHREG2, SCRATCHREG2, AssumeMakeOperand2(4 << 22));
}
BIC(SCRATCHREG2, SCRATCHREG2, AssumeMakeOperand2(3 << 22));
VMSR(SCRATCHREG2);
}
}
void Jit::SetRoundingMode()
{
void Jit::SetRoundingMode() {
// NOTE: Must not destory R0.
if (g_Config.bSetRoundingMode)
{
if (g_Config.bSetRoundingMode) {
LDR(SCRATCHREG2, CTXREG, offsetof(MIPSState, fcr31));
AND(SCRATCHREG2, SCRATCHREG2, Operand2(3));
if (!g_Config.bForceFlushToZero) {
TST(SCRATCHREG2, AssumeMakeOperand2(1 << 24));
AND(SCRATCHREG2, SCRATCHREG2, Operand2(3));
SetCC(CC_NEQ);
ADD(SCRATCHREG2, SCRATCHREG2, Operand2(4));
SetCC(CC_AL);
// We can only skip if the rounding mode is zero and flush is set.
CMP(SCRATCHREG2, Operand2(4));
} else {
ANDS(SCRATCHREG2, SCRATCHREG2, Operand2(3));
}
// At this point, if it was zero, we can skip the rest.
FixupBranch skip = B_CC(CC_EQ);
PUSH(1, SCRATCHREG1);
// MIPS Rounding Mode: ARM Rounding Mode
// 0: Round nearest 0
// 1: Round to zero 3
// 2: Round up (ceil) 1
// 3: Round down (floor) 2
CMP(SCRATCHREG2, Operand2(1));
FixupBranch skip = B_CC(CC_LT);
if (!g_Config.bForceFlushToZero) {
AND(SCRATCHREG1, SCRATCHREG2, Operand2(3));
CMP(SCRATCHREG1, Operand2(1));
} else {
CMP(SCRATCHREG2, Operand2(1));
}
SetCC(CC_EQ); ADD(SCRATCHREG2, SCRATCHREG2, Operand2(2));
SetCC(CC_GT); SUB(SCRATCHREG2, SCRATCHREG2, Operand2(1));
SetCC(CC_AL);
PUSH(1, SCRATCHREG1);
VMRS(SCRATCHREG1);
// Assume we're always in round-to-nearest mode beforehand.
ORR(SCRATCHREG2, SCRATCHREG2, Operand2(SCRATCHREG1, ST_LSL, 22));
VMSR(SCRATCHREG2);
POP(1, SCRATCHREG1);
if (!g_Config.bForceFlushToZero) {
// But we need to clear flush to zero in this case anyway.
BIC(SCRATCHREG1, SCRATCHREG1, AssumeMakeOperand2(7 << 22));
}
ORR(SCRATCHREG1, SCRATCHREG1, Operand2(SCRATCHREG2, ST_LSL, 22));
VMSR(SCRATCHREG1);
POP(1, SCRATCHREG1);
SetJumpTarget(skip);
}
}

View file

@ -379,6 +379,7 @@ void Jit::Comp_mxc1(MIPSOpcode op)
case 6: //currentMIPS->WriteFCR(fs, R(rt)); break; //ctc1
if (fs == 31) {
// Must clear before setting, since SetRoundingMode() assumes it was cleared.
ClearRoundingMode();
if (gpr.IsImm(rt)) {
gpr.SetImm(MIPS_REG_FPCOND, (gpr.GetImm(rt) >> 23) & 1);

View file

@ -250,10 +250,14 @@ void Jit::SetRoundingMode(XEmitter *emitter)
emitter->SHL(32, R(EAX), Imm8(13));
emitter->OR(32, M(&currentMIPS->temp), R(EAX));
emitter->TEST(32, M(&mips_->fcr31), Imm32(1 << 24));
FixupBranch skip3 = emitter->J_CC(CC_Z);
emitter->OR(32, M(&currentMIPS->temp), Imm32(1 << 15));
emitter->SetJumpTarget(skip3);
if (g_Config.bForceFlushToZero) {
emitter->OR(32, M(&currentMIPS->temp), Imm32(1 << 15));
} else {
emitter->TEST(32, M(&mips_->fcr31), Imm32(1 << 24));
FixupBranch skip3 = emitter->J_CC(CC_Z);
emitter->OR(32, M(&currentMIPS->temp), Imm32(1 << 15));
emitter->SetJumpTarget(skip3);
}
emitter->LDMXCSR(M(&currentMIPS->temp));