[Title] Negative zero checks cause unnecessary speculation failures on SunSpider on ARMv7.
[Issue#] N/A
[Problem] While testing SunSpider math-spectral-norm on x86-64 and ARMv7 Linux,
there are a lot of "Speculation failures" on ARM, but not on x86.
[Cause] N/A
[Solution] Check nodeCanIgnoreNegativeZero() for DoubleAsInt32 nodes, and create zero-check only when needed.
Change-Id: I50384d628a7ed8928d1378c52fe100f6de02197d
// If the result is not representable as a 32 bit value, branch.
// May also branch for some values that are representable in 32 bits
// (specifically, in this case, 0).
- void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp)
+ void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID, bool negZeroCheck = true)
{
m_assembler.vcvt_s32_f64_r(ARMRegisters::SD0 << 1, src);
m_assembler.vmov_arm32_r(dest, ARMRegisters::SD0 << 1);
failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, ARMRegisters::SD0));
// If the result is zero, it might have been -0.0, and 0.0 equals to -0.0
- failureCases.append(branchTest32(Zero, dest));
+ if (negZeroCheck)
+ failureCases.append(branchTest32(Zero, dest));
}
Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch)
// If the result is not representable as a 32 bit value, branch.
// May also branch for some values that are representable in 32 bits
// (specifically, in this case, 0).
- void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID)
+ void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID, bool negZeroCheck = true)
{
m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src);
m_assembler.vmov(dest, fpTempRegisterAsSingle());
failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, fpTempRegister));
// If the result is zero, it might have been -0.0, and the double comparison won't catch this!
- failureCases.append(branchTest32(Zero, dest));
+ if (negZeroCheck)
+ failureCases.append(branchTest32(Zero, dest));
}
Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID)
// If the result is not representable as a 32 bit value, branch.
// May also branch for some values that are representable in 32 bits
// (specifically, in this case, 0).
- void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp)
+ void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp, bool negZeroCheck = true)
{
m_assembler.cvtwd(fpTempRegister, src);
m_assembler.mfc1(dest, fpTempRegister);
// If the result is zero, it might have been -0.0, and the double comparison won't catch this!
- failureCases.append(branch32(Equal, dest, MIPSRegisters::zero));
+ if (negZeroCheck)
+ failureCases.append(branch32(Equal, dest, MIPSRegisters::zero));
// Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump.
convertInt32ToDouble(dest, fpTemp);
return branchTrue();
}
- void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp)
+ void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp, bool negZeroCheck = true)
{
m_assembler.ftrcdrmfpul(src);
m_assembler.stsfpulReg(dest);
convertInt32ToDouble(dest, fscratch);
failureCases.append(branchDouble(DoubleNotEqualOrUnordered, fscratch, src));
- if (dest == SH4Registers::r0)
- m_assembler.cmpEqImmR0(0, dest);
- else {
- m_assembler.movImm8(0, scratchReg3);
- m_assembler.cmplRegReg(scratchReg3, dest, SH4Condition(Equal));
+ if (negZeroCheck) {
+ if (dest == SH4Registers::r0)
+ m_assembler.cmpEqImmR0(0, dest);
+ else {
+ m_assembler.movImm8(0, scratchReg3);
+ m_assembler.cmplRegReg(scratchReg3, dest, SH4Condition(Equal));
+ }
+ failureCases.append(branchTrue());
}
- failureCases.append(branchTrue());
}
void neg32(RegisterID dst)
// If the result is not representable as a 32 bit value, branch.
// May also branch for some values that are representable in 32 bits
// (specifically, in this case, 0).
- void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp)
+ void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp, bool negZeroCheck = true)
{
ASSERT(isSSE2Present());
m_assembler.cvttsd2si_rr(src, dest);
// If the result is zero, it might have been -0.0, and the double comparison won't catch this!
- failureCases.append(branchTest32(Zero, dest));
+ if (negZeroCheck)
+ failureCases.append(branchTest32(Zero, dest));
// Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump.
convertInt32ToDouble(dest, fpTemp);
NodeFlags arithNodeFlags()
{
NodeFlags result = m_flags;
- if (op() == ArithMul || op() == ArithDiv || op() == ArithMod)
+ if (op() == ArithMul || op() == ArithDiv || op() == ArithMod || op() == ArithNegate || op() == DoubleAsInt32)
return result;
return result & ~NodeNeedsNegZero;
}
if (!m_graph.isNumberConstant(nodeIndex))
return false;
double value = m_graph.valueOfNumberConstant(nodeIndex);
- return !value && 1.0 / value < 0.0;
+ return (value || 1.0 / value > 0.0);
}
- bool isNotZero(NodeIndex nodeIndex)
+ bool isNotPosZero(NodeIndex nodeIndex)
{
if (!m_graph.isNumberConstant(nodeIndex))
return false;
- return !!m_graph.valueOfNumberConstant(nodeIndex);
+ double value = m_graph.valueOfNumberConstant(nodeIndex);
+ return (value || 1.0 / value < 0.0);
}
void propagate(Node& node)
changed |= mergePrediction(SpecDouble);
}
- if (isNotZero(node.child1().index()) || isNotZero(node.child2().index()))
+ if (isNotNegZero(node.child1().index()) || isNotPosZero(node.child2().index()))
flags &= ~NodeNeedsNegZero;
changed |= m_graph[node.child1()].mergeFlags(flags);
GPRReg resultGPR = result.gpr();
JITCompiler::JumpList failureCases;
- m_jit.branchConvertDoubleToInt32(valueFPR, resultGPR, failureCases, scratchFPR);
+ bool negZeroCheck = !nodeCanIgnoreNegativeZero(node.arithNodeFlags());
+ m_jit.branchConvertDoubleToInt32(valueFPR, resultGPR, failureCases, scratchFPR, negZeroCheck);
forwardSpeculationCheck(Overflow, JSValueRegs(), NoNode, failureCases, ValueRecovery::inFPR(valueFPR));
integerResult(resultGPR, m_compileIndex);
speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branch32(JITCompiler::Equal, eax.gpr(), TrustedImm32(-2147483647-1)));
m_jit.assembler().cdq();
m_jit.assembler().idivl_r(scratchGPR);
- // Check that we're not about to create negative zero.
- // FIXME: if the node use doesn't care about neg zero, we can do this more easily.
- JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1SaveGPR, TrustedImm32(0));
- speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchTest32(JITCompiler::Zero, edx.gpr()));
- numeratorPositive.link(&m_jit);
-
+ if (!nodeCanIgnoreNegativeZero(node.arithNodeFlags())) {
+ // Check that we're not about to create negative zero.
+ JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1SaveGPR, TrustedImm32(0));
+ speculationCheck(NegativeZero, JSValueRegs(), NoNode, m_jit.branchTest32(JITCompiler::Zero, edx.gpr()));
+ numeratorPositive.link(&m_jit);
+ }
if (op1SaveGPR != op1Gpr)
unlock(op1SaveGPR);
return;
}
}
+#elif CPU(ARM_THUMB2)
+ if (isInt32Constant(node.child2().index())) {
+ int32_t divisor = valueOfInt32Constant(node.child2().index());
+ if (divisor > 0 && !(divisor & (divisor - 1))) { // If power of 2 then just mask
+ GPRReg dividendGPR = op1.gpr();
+ GPRTemporary result(this);
+ GPRReg resultGPR = result.gpr();
+
+ m_jit.assembler().cmp(dividendGPR, ARMThumbImmediate::makeEncodedImm(0));
+ m_jit.assembler().it(ARMv7Assembler::ConditionLT, false);
+ m_jit.assembler().neg(resultGPR, dividendGPR);
+ m_jit.assembler().mov(resultGPR, dividendGPR);
+ m_jit.and32(TrustedImm32(divisor - 1), resultGPR);
+ m_jit.assembler().it(ARMv7Assembler::ConditionLT);
+ m_jit.assembler().neg(resultGPR, resultGPR);
+
+ if (!nodeCanIgnoreNegativeZero(node.arithNodeFlags())) {
+ // Check that we're not about to create negative zero.
+ JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, dividendGPR, TrustedImm32(0));
+ speculationCheck(NegativeZero, JSValueRegs(), NoNode, m_jit.branchTest32(JITCompiler::Zero, resultGPR));
+ numeratorPositive.link(&m_jit);
+ }
+
+ integerResult(resultGPR, m_compileIndex);
+ return;
+ }
+ }
#endif
SpeculateIntegerOperand op2(this, node.child2());
if (op2TempGPR != InvalidGPRReg)
unlock(op2TempGPR);
- // Check that we're not about to create negative zero.
- // FIXME: if the node use doesn't care about neg zero, we can do this more easily.
- JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1SaveGPR, TrustedImm32(0));
- speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchTest32(JITCompiler::Zero, edx.gpr()));
- numeratorPositive.link(&m_jit);
+ if (!nodeCanIgnoreNegativeZero(node.arithNodeFlags())) {
+ // Check that we're not about to create negative zero.
+ JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1SaveGPR, TrustedImm32(0));
+ speculationCheck(NegativeZero, JSValueRegs(), NoNode, m_jit.branchTest32(JITCompiler::Zero, edx.gpr()));
+ numeratorPositive.link(&m_jit);
+ }
if (op1SaveGPR != op1GPR)
unlock(op1SaveGPR);
FPRTemporary scratch(this);
GPRTemporary intResult(this);
JITCompiler::JumpList failureCases;
- m_jit.branchConvertDoubleToInt32(result.fpr(), intResult.gpr(), failureCases, scratch.fpr());
+ m_jit.branchConvertDoubleToInt32(result.fpr(), intResult.gpr(), failureCases, scratch.fpr(), false);
speculationCheck(Overflow, JSValueRegs(), NoNode, failureCases);
+ if (!nodeCanIgnoreNegativeZero(node.arithNodeFlags())) {
+ // Check that we're not about to create negative zero.
+ JITCompiler::Jump numeratorPositive = m_jit.branch32(JITCompiler::GreaterThanOrEqual, op1GPR, TrustedImm32(0));
+ speculationCheck(NegativeZero, JSValueRegs(), NoNode, m_jit.branchTest32(JITCompiler::Zero, intResult.gpr()));
+ numeratorPositive.link(&m_jit);
+ }
integerResult(intResult.gpr(), m_compileIndex);
#endif // CPU(X86) || CPU(X86_64)
else {
speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchNeg32(MacroAssembler::Overflow, result.gpr()));
if (!nodeCanIgnoreNegativeZero(node.arithNodeFlags()))
- speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchTest32(MacroAssembler::Zero, result.gpr()));
+ speculationCheck(NegativeZero, JSValueRegs(), NoNode, m_jit.branchTest32(MacroAssembler::Zero, result.gpr()));
}
integerResult(result.gpr(), m_compileIndex);