[DFG][FTL][B3] Support floor and ceil
https://bugs.webkit.org/show_bug.cgi?id=154683
Reviewed by Filip Pizlo.
Source/JavaScriptCore:
This patch implements and fixes the following things.
1. Implement Ceil and Floor in DFG, FTL and B3
x86 SSE 4.2 and ARM64 have round instructions that can directly perform Ceil or Floor.
This patch leverages this functionality. We introduce ArithFloor and ArithCeil.
During DFG phase, these nodes attempt to convert itself to Identity (in Fixup phase).
As the same to ArithRound, it tracks arith rounding mode.
And if these nodes are required to emit machine codes, we emit rounding machine code
if it is supported in the current machine. For example, in x86, we emit `round`.
This `Floor` functionality is nice for @toInteger in builtin.
That is used for Array.prototype.{forEach, map, every, some, reduce...}
And according to the benchmark results, Kraken audio-oscillator is slightly improved
due to its frequent Math.round and Math.floor calls.
2. Implement Floor in B3 and Air
As the same to Ceil in B3, we add a new B3 IR and Air opcode, Floor.
This Floor is leveraged to implement ArithFloor in DFG.
3. Fix ArithRound operation
Currently, we used cvtsd2si (in x86) to convert double value to int32.
And we also used this to implement Math.round, like, cvtsd2si(value + 0.5).
However, this implementation is not correct. Because cvtsd2si is not floor operation.
It is trucate operation. This is OK for positive numbers. But NG for negative numbers.
For example, the current implementation accidentally rounds `-0.6` to `-0.0`. This should be `-1.0`.
Using Ceil and Floor instructions, we implement correct ArithRound.
* assembler/MacroAssemblerARM.h:
(JSC::MacroAssemblerARM::supportsFloatingPointRounding):
(JSC::MacroAssemblerARM::ceilDouble):
(JSC::MacroAssemblerARM::floorDouble):
(JSC::MacroAssemblerARM::supportsFloatingPointCeil): Deleted.
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::supportsFloatingPointRounding):
(JSC::MacroAssemblerARM64::floorFloat):
(JSC::MacroAssemblerARM64::supportsFloatingPointCeil): Deleted.
* assembler/MacroAssemblerARMv7.h:
(JSC::MacroAssemblerARMv7::supportsFloatingPointRounding):
(JSC::MacroAssemblerARMv7::ceilDouble):
(JSC::MacroAssemblerARMv7::floorDouble):
(JSC::MacroAssemblerARMv7::supportsFloatingPointCeil): Deleted.
* assembler/MacroAssemblerMIPS.h:
(JSC::MacroAssemblerMIPS::ceilDouble):
(JSC::MacroAssemblerMIPS::floorDouble):
(JSC::MacroAssemblerMIPS::supportsFloatingPointRounding):
(JSC::MacroAssemblerMIPS::supportsFloatingPointCeil): Deleted.
* assembler/MacroAssemblerSH4.h:
(JSC::MacroAssemblerSH4::supportsFloatingPointRounding):
(JSC::MacroAssemblerSH4::ceilDouble):
(JSC::MacroAssemblerSH4::floorDouble):
(JSC::MacroAssemblerSH4::supportsFloatingPointCeil): Deleted.
* assembler/MacroAssemblerX86Common.h:
(JSC::MacroAssemblerX86Common::floorDouble):
(JSC::MacroAssemblerX86Common::floorFloat):
(JSC::MacroAssemblerX86Common::supportsFloatingPointRounding):
(JSC::MacroAssemblerX86Common::supportsFloatingPointCeil): Deleted.
* b3/B3ConstDoubleValue.cpp:
(JSC::B3::ConstDoubleValue::floorConstant):
* b3/B3ConstDoubleValue.h:
* b3/B3ConstFloatValue.cpp:
(JSC::B3::ConstFloatValue::floorConstant):
* b3/B3ConstFloatValue.h:
* b3/B3LowerMacrosAfterOptimizations.cpp:
* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::lower):
* b3/B3Opcode.cpp:
(WTF::printInternal):
* b3/B3Opcode.h:
* b3/B3ReduceDoubleToFloat.cpp:
* b3/B3ReduceStrength.cpp:
* b3/B3Validate.cpp:
* b3/B3Value.cpp:
(JSC::B3::Value::floorConstant):
(JSC::B3::Value::isRounded):
(JSC::B3::Value::effects):
(JSC::B3::Value::key):
(JSC::B3::Value::typeFor):
* b3/B3Value.h:
* b3/air/AirFixPartialRegisterStalls.cpp:
* b3/air/AirOpcode.opcodes:
* b3/testb3.cpp:
(JSC::B3::testFloorCeilArg):
(JSC::B3::testFloorArg):
(JSC::B3::testFloorImm):
(JSC::B3::testFloorMem):
(JSC::B3::testFloorFloorArg):
(JSC::B3::testCeilFloorArg):
(JSC::B3::testFloorIToD64):
(JSC::B3::testFloorIToD32):
(JSC::B3::testFloorArgWithUselessDoubleConversion):
(JSC::B3::testFloorArgWithEffectfulDoubleConversion):
(JSC::B3::run):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGArithMode.cpp:
(WTF::printInternal):
* dfg/DFGArithMode.h:
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::roundShouldSpeculateInt32):
* dfg/DFGNode.h:
(JSC::DFG::Node::arithNodeFlags):
(JSC::DFG::Node::hasHeapPrediction):
(JSC::DFG::Node::hasArithRoundingMode):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileArithRounding):
(JSC::DFG::SpeculativeJIT::compileArithRound): Deleted.
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileArithRound):
(JSC::FTL::DFG::LowerDFGToB3::compileArithFloor):
(JSC::FTL::DFG::LowerDFGToB3::compileArithCeil):
* ftl/FTLOutput.h:
(JSC::FTL::Output::doubleFloor):
* jit/ThunkGenerators.cpp:
(JSC::ceilThunkGenerator):
* tests/stress/math-ceil-arith-rounding-mode.js: Added.
(firstCareAboutZeroSecondDoesNot):
(firstDoNotCareAboutZeroSecondDoes):
(warmup):
(verifyNegativeZeroIsPreserved):
* tests/stress/math-ceil-basics.js: Added.
(mathCeilOnIntegers):
(mathCeilOnDoubles):
(mathCeilOnBooleans):
(uselessMathCeil):
(mathCeilWithOverflow):
(mathCeilConsumedAsDouble):
(mathCeilDoesNotCareAboutMinusZero):
(mathCeilNoArguments):
(mathCeilTooManyArguments):
(testMathCeilOnConstants):
(mathCeilStructTransition):
(Math.ceil):
* tests/stress/math-floor-arith-rounding-mode.js: Added.
(firstCareAboutZeroSecondDoesNot):
(firstDoNotCareAboutZeroSecondDoes):
(warmup):
(verifyNegativeZeroIsPreserved):
* tests/stress/math-floor-basics.js: Added.
(mathFloorOnIntegers):
(mathFloorOnDoubles):
(mathFloorOnBooleans):
(uselessMathFloor):
(mathFloorWithOverflow):
(mathFloorConsumedAsDouble):
(mathFloorDoesNotCareAboutMinusZero):
(mathFloorNoArguments):
(mathFloorTooManyArguments):
(testMathFloorOnConstants):
(mathFloorStructTransition):
(Math.floor):
* tests/stress/math-round-should-not-use-truncate.js: Added.
(mathRoundDoesNotCareAboutMinusZero):
* tests/stress/math-rounding-infinity.js: Added.
(shouldBe):
(testRound):
(testFloor):
(testCeil):
* tests/stress/math-rounding-nan.js: Added.
(shouldBe):
(testRound):
(testFloor):
(testCeil):
* tests/stress/math-rounding-negative-zero.js: Added.
(shouldBe):
(testRound):
(testFloor):
(testCeil):
(testRoundNonNegativeZero):
(testRoundNonNegativeZero2):
Websites/webkit.org:
* docs/b3/intermediate-representation.html:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@197380 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index bff059a..7c1392c 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,206 @@
+2016-02-29 Yusuke Suzuki <utatane.tea@gmail.com>
+
+ [DFG][FTL][B3] Support floor and ceil
+ https://bugs.webkit.org/show_bug.cgi?id=154683
+
+ Reviewed by Filip Pizlo.
+
+ This patch implements and fixes the following things.
+
+ 1. Implement Ceil and Floor in DFG, FTL and B3
+
+ x86 SSE 4.2 and ARM64 have round instructions that can directly perform Ceil or Floor.
+ This patch leverages this functionality. We introduce ArithFloor and ArithCeil.
+ During DFG phase, these nodes attempt to convert itself to Identity (in Fixup phase).
+ As the same to ArithRound, it tracks arith rounding mode.
+ And if these nodes are required to emit machine codes, we emit rounding machine code
+ if it is supported in the current machine. For example, in x86, we emit `round`.
+
+ This `Floor` functionality is nice for @toInteger in builtin.
+ That is used for Array.prototype.{forEach, map, every, some, reduce...}
+ And according to the benchmark results, Kraken audio-oscillator is slightly improved
+ due to its frequent Math.round and Math.floor calls.
+
+ 2. Implement Floor in B3 and Air
+
+ As the same to Ceil in B3, we add a new B3 IR and Air opcode, Floor.
+ This Floor is leveraged to implement ArithFloor in DFG.
+
+ 3. Fix ArithRound operation
+
+ Currently, we used cvtsd2si (in x86) to convert double value to int32.
+ And we also used this to implement Math.round, like, cvtsd2si(value + 0.5).
+ However, this implementation is not correct. Because cvtsd2si is not floor operation.
+ It is trucate operation. This is OK for positive numbers. But NG for negative numbers.
+ For example, the current implementation accidentally rounds `-0.6` to `-0.0`. This should be `-1.0`.
+ Using Ceil and Floor instructions, we implement correct ArithRound.
+
+ * assembler/MacroAssemblerARM.h:
+ (JSC::MacroAssemblerARM::supportsFloatingPointRounding):
+ (JSC::MacroAssemblerARM::ceilDouble):
+ (JSC::MacroAssemblerARM::floorDouble):
+ (JSC::MacroAssemblerARM::supportsFloatingPointCeil): Deleted.
+ * assembler/MacroAssemblerARM64.h:
+ (JSC::MacroAssemblerARM64::supportsFloatingPointRounding):
+ (JSC::MacroAssemblerARM64::floorFloat):
+ (JSC::MacroAssemblerARM64::supportsFloatingPointCeil): Deleted.
+ * assembler/MacroAssemblerARMv7.h:
+ (JSC::MacroAssemblerARMv7::supportsFloatingPointRounding):
+ (JSC::MacroAssemblerARMv7::ceilDouble):
+ (JSC::MacroAssemblerARMv7::floorDouble):
+ (JSC::MacroAssemblerARMv7::supportsFloatingPointCeil): Deleted.
+ * assembler/MacroAssemblerMIPS.h:
+ (JSC::MacroAssemblerMIPS::ceilDouble):
+ (JSC::MacroAssemblerMIPS::floorDouble):
+ (JSC::MacroAssemblerMIPS::supportsFloatingPointRounding):
+ (JSC::MacroAssemblerMIPS::supportsFloatingPointCeil): Deleted.
+ * assembler/MacroAssemblerSH4.h:
+ (JSC::MacroAssemblerSH4::supportsFloatingPointRounding):
+ (JSC::MacroAssemblerSH4::ceilDouble):
+ (JSC::MacroAssemblerSH4::floorDouble):
+ (JSC::MacroAssemblerSH4::supportsFloatingPointCeil): Deleted.
+ * assembler/MacroAssemblerX86Common.h:
+ (JSC::MacroAssemblerX86Common::floorDouble):
+ (JSC::MacroAssemblerX86Common::floorFloat):
+ (JSC::MacroAssemblerX86Common::supportsFloatingPointRounding):
+ (JSC::MacroAssemblerX86Common::supportsFloatingPointCeil): Deleted.
+ * b3/B3ConstDoubleValue.cpp:
+ (JSC::B3::ConstDoubleValue::floorConstant):
+ * b3/B3ConstDoubleValue.h:
+ * b3/B3ConstFloatValue.cpp:
+ (JSC::B3::ConstFloatValue::floorConstant):
+ * b3/B3ConstFloatValue.h:
+ * b3/B3LowerMacrosAfterOptimizations.cpp:
+ * b3/B3LowerToAir.cpp:
+ (JSC::B3::Air::LowerToAir::lower):
+ * b3/B3Opcode.cpp:
+ (WTF::printInternal):
+ * b3/B3Opcode.h:
+ * b3/B3ReduceDoubleToFloat.cpp:
+ * b3/B3ReduceStrength.cpp:
+ * b3/B3Validate.cpp:
+ * b3/B3Value.cpp:
+ (JSC::B3::Value::floorConstant):
+ (JSC::B3::Value::isRounded):
+ (JSC::B3::Value::effects):
+ (JSC::B3::Value::key):
+ (JSC::B3::Value::typeFor):
+ * b3/B3Value.h:
+ * b3/air/AirFixPartialRegisterStalls.cpp:
+ * b3/air/AirOpcode.opcodes:
+ * b3/testb3.cpp:
+ (JSC::B3::testFloorCeilArg):
+ (JSC::B3::testFloorArg):
+ (JSC::B3::testFloorImm):
+ (JSC::B3::testFloorMem):
+ (JSC::B3::testFloorFloorArg):
+ (JSC::B3::testCeilFloorArg):
+ (JSC::B3::testFloorIToD64):
+ (JSC::B3::testFloorIToD32):
+ (JSC::B3::testFloorArgWithUselessDoubleConversion):
+ (JSC::B3::testFloorArgWithEffectfulDoubleConversion):
+ (JSC::B3::run):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGArithMode.cpp:
+ (WTF::printInternal):
+ * dfg/DFGArithMode.h:
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGGraph.cpp:
+ (JSC::DFG::Graph::dump):
+ * dfg/DFGGraph.h:
+ (JSC::DFG::Graph::roundShouldSpeculateInt32):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::arithNodeFlags):
+ (JSC::DFG::Node::hasHeapPrediction):
+ (JSC::DFG::Node::hasArithRoundingMode):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate):
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileArithRounding):
+ (JSC::DFG::SpeculativeJIT::compileArithRound): Deleted.
+ * dfg/DFGSpeculativeJIT.h:
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+ (JSC::FTL::DFG::LowerDFGToB3::compileArithRound):
+ (JSC::FTL::DFG::LowerDFGToB3::compileArithFloor):
+ (JSC::FTL::DFG::LowerDFGToB3::compileArithCeil):
+ * ftl/FTLOutput.h:
+ (JSC::FTL::Output::doubleFloor):
+ * jit/ThunkGenerators.cpp:
+ (JSC::ceilThunkGenerator):
+ * tests/stress/math-ceil-arith-rounding-mode.js: Added.
+ (firstCareAboutZeroSecondDoesNot):
+ (firstDoNotCareAboutZeroSecondDoes):
+ (warmup):
+ (verifyNegativeZeroIsPreserved):
+ * tests/stress/math-ceil-basics.js: Added.
+ (mathCeilOnIntegers):
+ (mathCeilOnDoubles):
+ (mathCeilOnBooleans):
+ (uselessMathCeil):
+ (mathCeilWithOverflow):
+ (mathCeilConsumedAsDouble):
+ (mathCeilDoesNotCareAboutMinusZero):
+ (mathCeilNoArguments):
+ (mathCeilTooManyArguments):
+ (testMathCeilOnConstants):
+ (mathCeilStructTransition):
+ (Math.ceil):
+ * tests/stress/math-floor-arith-rounding-mode.js: Added.
+ (firstCareAboutZeroSecondDoesNot):
+ (firstDoNotCareAboutZeroSecondDoes):
+ (warmup):
+ (verifyNegativeZeroIsPreserved):
+ * tests/stress/math-floor-basics.js: Added.
+ (mathFloorOnIntegers):
+ (mathFloorOnDoubles):
+ (mathFloorOnBooleans):
+ (uselessMathFloor):
+ (mathFloorWithOverflow):
+ (mathFloorConsumedAsDouble):
+ (mathFloorDoesNotCareAboutMinusZero):
+ (mathFloorNoArguments):
+ (mathFloorTooManyArguments):
+ (testMathFloorOnConstants):
+ (mathFloorStructTransition):
+ (Math.floor):
+ * tests/stress/math-round-should-not-use-truncate.js: Added.
+ (mathRoundDoesNotCareAboutMinusZero):
+ * tests/stress/math-rounding-infinity.js: Added.
+ (shouldBe):
+ (testRound):
+ (testFloor):
+ (testCeil):
+ * tests/stress/math-rounding-nan.js: Added.
+ (shouldBe):
+ (testRound):
+ (testFloor):
+ (testCeil):
+ * tests/stress/math-rounding-negative-zero.js: Added.
+ (shouldBe):
+ (testRound):
+ (testFloor):
+ (testCeil):
+ (testRoundNonNegativeZero):
+ (testRoundNonNegativeZero2):
+
2016-02-29 Joseph Pecoraro <pecoraro@apple.com>
Add new MethodTable method to get an estimated size for a cell
diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerARM.h b/Source/JavaScriptCore/assembler/MacroAssemblerARM.h
index c58cf8a..97d210e 100644
--- a/Source/JavaScriptCore/assembler/MacroAssemblerARM.h
+++ b/Source/JavaScriptCore/assembler/MacroAssemblerARM.h
@@ -1139,7 +1139,7 @@
return s_isVFPPresent;
}
static bool supportsFloatingPointAbs() { return false; }
- static bool supportsFloatingPointCeil() { return false; }
+ static bool supportsFloatingPointRounding() { return false; }
void loadFloat(BaseIndex address, FPRegisterID dest)
{
@@ -1164,7 +1164,13 @@
NO_RETURN_DUE_TO_CRASH void ceilDouble(FPRegisterID, FPRegisterID)
{
- ASSERT(!supportsFloatingPointCeil());
+ ASSERT(!supportsFloatingPointRounding());
+ CRASH();
+ }
+
+ NO_RETURN_DUE_TO_CRASH void floorDouble(FPRegisterID, FPRegisterID)
+ {
+ ASSERT(!supportsFloatingPointRounding());
CRASH();
}
diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerARM64.h b/Source/JavaScriptCore/assembler/MacroAssemblerARM64.h
index a590b44..5977613 100644
--- a/Source/JavaScriptCore/assembler/MacroAssemblerARM64.h
+++ b/Source/JavaScriptCore/assembler/MacroAssemblerARM64.h
@@ -1343,7 +1343,7 @@
static bool supportsFloatingPointTruncate() { return true; }
static bool supportsFloatingPointSqrt() { return true; }
static bool supportsFloatingPointAbs() { return true; }
- static bool supportsFloatingPointCeil() { return true; }
+ static bool supportsFloatingPointRounding() { return true; }
enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful };
@@ -1399,6 +1399,11 @@
m_assembler.frintm<64>(dest, src);
}
+ void floorFloat(FPRegisterID src, FPRegisterID dest)
+ {
+ m_assembler.frintm<32>(dest, src);
+ }
+
// Convert 'src' to an integer, and places the resulting 'dest'.
// If the result is not representable as a 32 bit value, branch.
// May also branch for some values that are representable in 32 bits
diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h b/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h
index c075852..fc94b88 100644
--- a/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h
+++ b/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h
@@ -870,7 +870,7 @@
static bool supportsFloatingPointTruncate() { return true; }
static bool supportsFloatingPointSqrt() { return true; }
static bool supportsFloatingPointAbs() { return true; }
- static bool supportsFloatingPointCeil() { return false; }
+ static bool supportsFloatingPointRounding() { return false; }
void loadDouble(ImplicitAddress address, FPRegisterID dest)
{
@@ -1063,7 +1063,13 @@
NO_RETURN_DUE_TO_CRASH void ceilDouble(FPRegisterID, FPRegisterID)
{
- ASSERT(!supportsFloatingPointCeil());
+ ASSERT(!supportsFloatingPointRounding());
+ CRASH();
+ }
+
+ NO_RETURN_DUE_TO_CRASH void floorDouble(FPRegisterID, FPRegisterID)
+ {
+ ASSERT(!supportsFloatingPointRounding());
CRASH();
}
diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerMIPS.h b/Source/JavaScriptCore/assembler/MacroAssemblerMIPS.h
index 9b37564..7c4bd08 100644
--- a/Source/JavaScriptCore/assembler/MacroAssemblerMIPS.h
+++ b/Source/JavaScriptCore/assembler/MacroAssemblerMIPS.h
@@ -649,7 +649,13 @@
NO_RETURN_DUE_TO_CRASH void ceilDouble(FPRegisterID, FPRegisterID)
{
- ASSERT(!supportsFloatingPointCeil());
+ ASSERT(!supportsFloatingPointRounding());
+ CRASH();
+ }
+
+ NO_RETURN_DUE_TO_CRASH void floorDouble(FPRegisterID, FPRegisterID)
+ {
+ ASSERT(!supportsFloatingPointRounding());
CRASH();
}
@@ -1237,7 +1243,7 @@
#endif
}
static bool supportsFloatingPointAbs() { return false; }
- static bool supportsFloatingPointCeil() { return false; }
+ static bool supportsFloatingPointRounding() { return false; }
// Stack manipulation operations:
//
diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerSH4.h b/Source/JavaScriptCore/assembler/MacroAssemblerSH4.h
index 0f46f8f..7575609 100644
--- a/Source/JavaScriptCore/assembler/MacroAssemblerSH4.h
+++ b/Source/JavaScriptCore/assembler/MacroAssemblerSH4.h
@@ -1091,7 +1091,7 @@
static bool supportsFloatingPointTruncate() { return true; }
static bool supportsFloatingPointSqrt() { return true; }
static bool supportsFloatingPointAbs() { return true; }
- static bool supportsFloatingPointCeil() { return false; }
+ static bool supportsFloatingPointRounding() { return false; }
void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2)
{
@@ -1577,7 +1577,13 @@
NO_RETURN_DUE_TO_CRASH void ceilDouble(FPRegisterID, FPRegisterID)
{
- ASSERT(!supportsFloatingPointCeil());
+ ASSERT(!supportsFloatingPointRounding());
+ CRASH();
+ }
+
+ NO_RETURN_DUE_TO_CRASH void floorDouble(FPRegisterID, FPRegisterID)
+ {
+ ASSERT(!supportsFloatingPointRounding());
CRASH();
}
diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h b/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h
index 08ea575..8085530 100644
--- a/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h
+++ b/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h
@@ -696,6 +696,26 @@
m_assembler.roundss_mr(src.offset, src.base, dst, X86Assembler::RoundingType::TowardInfiniti);
}
+ void floorDouble(FPRegisterID src, FPRegisterID dst)
+ {
+ m_assembler.roundsd_rr(src, dst, X86Assembler::RoundingType::TowardNegativeInfiniti);
+ }
+
+ void floorDouble(Address src, FPRegisterID dst)
+ {
+ m_assembler.roundsd_mr(src.offset, src.base, dst, X86Assembler::RoundingType::TowardNegativeInfiniti);
+ }
+
+ void floorFloat(FPRegisterID src, FPRegisterID dst)
+ {
+ m_assembler.roundss_rr(src, dst, X86Assembler::RoundingType::TowardNegativeInfiniti);
+ }
+
+ void floorFloat(Address src, FPRegisterID dst)
+ {
+ m_assembler.roundss_mr(src.offset, src.base, dst, X86Assembler::RoundingType::TowardNegativeInfiniti);
+ }
+
// Memory access operations:
//
// Loads are of the form load(address, destination) and stores of the form
@@ -2300,7 +2320,7 @@
return X86Assembler::maxJumpReplacementSize();
}
- static bool supportsFloatingPointCeil()
+ static bool supportsFloatingPointRounding()
{
if (s_sse4_1CheckState == CPUIDCheckState::NotChecked) {
int flags = 0;
diff --git a/Source/JavaScriptCore/b3/B3ConstDoubleValue.cpp b/Source/JavaScriptCore/b3/B3ConstDoubleValue.cpp
index f9ce296..46183f3 100644
--- a/Source/JavaScriptCore/b3/B3ConstDoubleValue.cpp
+++ b/Source/JavaScriptCore/b3/B3ConstDoubleValue.cpp
@@ -97,6 +97,11 @@
return proc.add<ConstDoubleValue>(origin(), ceil(m_value));
}
+Value* ConstDoubleValue::floorConstant(Procedure& proc) const
+{
+ return proc.add<ConstDoubleValue>(origin(), floor(m_value));
+}
+
Value* ConstDoubleValue::sqrtConstant(Procedure& proc) const
{
return proc.add<ConstDoubleValue>(origin(), sqrt(m_value));
diff --git a/Source/JavaScriptCore/b3/B3ConstDoubleValue.h b/Source/JavaScriptCore/b3/B3ConstDoubleValue.h
index b2cb246..e8ea539 100644
--- a/Source/JavaScriptCore/b3/B3ConstDoubleValue.h
+++ b/Source/JavaScriptCore/b3/B3ConstDoubleValue.h
@@ -52,6 +52,7 @@
Value* doubleToFloatConstant(Procedure&) const override;
Value* absConstant(Procedure&) const override;
Value* ceilConstant(Procedure&) const override;
+ Value* floorConstant(Procedure&) const override;
Value* sqrtConstant(Procedure&) const override;
TriState equalConstant(const Value* other) const override;
diff --git a/Source/JavaScriptCore/b3/B3ConstFloatValue.cpp b/Source/JavaScriptCore/b3/B3ConstFloatValue.cpp
index 44f6403..7c1b395 100644
--- a/Source/JavaScriptCore/b3/B3ConstFloatValue.cpp
+++ b/Source/JavaScriptCore/b3/B3ConstFloatValue.cpp
@@ -97,6 +97,11 @@
return proc.add<ConstFloatValue>(origin(), ceilf(m_value));
}
+Value* ConstFloatValue::floorConstant(Procedure& proc) const
+{
+ return proc.add<ConstFloatValue>(origin(), floorf(m_value));
+}
+
Value* ConstFloatValue::sqrtConstant(Procedure& proc) const
{
return proc.add<ConstFloatValue>(origin(), static_cast<float>(sqrt(m_value)));
diff --git a/Source/JavaScriptCore/b3/B3ConstFloatValue.h b/Source/JavaScriptCore/b3/B3ConstFloatValue.h
index 440bd61..36673ca 100644
--- a/Source/JavaScriptCore/b3/B3ConstFloatValue.h
+++ b/Source/JavaScriptCore/b3/B3ConstFloatValue.h
@@ -51,6 +51,7 @@
Value* floatToDoubleConstant(Procedure&) const override;
Value* absConstant(Procedure&) const override;
Value* ceilConstant(Procedure&) const override;
+ Value* floorConstant(Procedure&) const override;
Value* sqrtConstant(Procedure&) const override;
TriState equalConstant(const Value* other) const override;
diff --git a/Source/JavaScriptCore/b3/B3LowerMacrosAfterOptimizations.cpp b/Source/JavaScriptCore/b3/B3LowerMacrosAfterOptimizations.cpp
index 5b2d90b..e03d5f4 100644
--- a/Source/JavaScriptCore/b3/B3LowerMacrosAfterOptimizations.cpp
+++ b/Source/JavaScriptCore/b3/B3LowerMacrosAfterOptimizations.cpp
@@ -88,13 +88,12 @@
break;
}
case Ceil: {
- if (MacroAssembler::supportsFloatingPointCeil())
+ if (MacroAssembler::supportsFloatingPointRounding())
break;
Value* functionAddress = nullptr;
- double (*ceilDouble)(double) = ceil;
if (m_value->type() == Double)
- functionAddress = m_insertionSet.insert<ConstPtrValue>(m_index, m_origin, ceilDouble);
+ functionAddress = m_insertionSet.insert<ConstPtrValue>(m_index, m_origin, ceil);
else if (m_value->type() == Float)
functionAddress = m_insertionSet.insert<ConstPtrValue>(m_index, m_origin, ceilf);
else
@@ -109,6 +108,27 @@
m_value->replaceWithIdentity(result);
break;
}
+ case Floor: {
+ if (MacroAssembler::supportsFloatingPointRounding())
+ break;
+
+ Value* functionAddress = nullptr;
+ if (m_value->type() == Double)
+ functionAddress = m_insertionSet.insert<ConstPtrValue>(m_index, m_origin, floor);
+ else if (m_value->type() == Float)
+ functionAddress = m_insertionSet.insert<ConstPtrValue>(m_index, m_origin, floorf);
+ else
+ RELEASE_ASSERT_NOT_REACHED();
+
+ Value* result = m_insertionSet.insert<CCallValue>(m_index,
+ m_value->type(),
+ m_origin,
+ Effects::none(),
+ functionAddress,
+ m_value->child(0));
+ m_value->replaceWithIdentity(result);
+ break;
+ }
case Neg: {
if (!isFloat(m_value->type()))
break;
diff --git a/Source/JavaScriptCore/b3/B3LowerToAir.cpp b/Source/JavaScriptCore/b3/B3LowerToAir.cpp
index 7175de4..1cd557b 100644
--- a/Source/JavaScriptCore/b3/B3LowerToAir.cpp
+++ b/Source/JavaScriptCore/b3/B3LowerToAir.cpp
@@ -1827,6 +1827,11 @@
return;
}
+ case Floor: {
+ appendUnOp<Air::Oops, Air::Oops, FloorDouble, FloorFloat>(m_value->child(0));
+ return;
+ }
+
case Sqrt: {
appendUnOp<Air::Oops, Air::Oops, SqrtDouble, SqrtFloat>(m_value->child(0));
return;
diff --git a/Source/JavaScriptCore/b3/B3Opcode.cpp b/Source/JavaScriptCore/b3/B3Opcode.cpp
index 7c925ad..ae0b948 100644
--- a/Source/JavaScriptCore/b3/B3Opcode.cpp
+++ b/Source/JavaScriptCore/b3/B3Opcode.cpp
@@ -161,6 +161,9 @@
case Ceil:
out.print("Ceil");
return;
+ case Floor:
+ out.print("Floor");
+ return;
case Sqrt:
out.print("Sqrt");
return;
diff --git a/Source/JavaScriptCore/b3/B3Opcode.h b/Source/JavaScriptCore/b3/B3Opcode.h
index 9d9bf18..76b91b6 100644
--- a/Source/JavaScriptCore/b3/B3Opcode.h
+++ b/Source/JavaScriptCore/b3/B3Opcode.h
@@ -97,6 +97,7 @@
// Floating point math.
Abs,
Ceil,
+ Floor,
Sqrt,
// Casts and such.
diff --git a/Source/JavaScriptCore/b3/B3ReduceDoubleToFloat.cpp b/Source/JavaScriptCore/b3/B3ReduceDoubleToFloat.cpp
index cb41f74..d8e43b4 100644
--- a/Source/JavaScriptCore/b3/B3ReduceDoubleToFloat.cpp
+++ b/Source/JavaScriptCore/b3/B3ReduceDoubleToFloat.cpp
@@ -50,6 +50,7 @@
break;
case Abs:
case Ceil:
+ case Floor:
case Sqrt:
if (candidate->child(0)->opcode() == FloatToDouble) {
candidate->child(0) = candidate->child(0)->child(0);
diff --git a/Source/JavaScriptCore/b3/B3ReduceStrength.cpp b/Source/JavaScriptCore/b3/B3ReduceStrength.cpp
index 83cb21f..4363b9e 100644
--- a/Source/JavaScriptCore/b3/B3ReduceStrength.cpp
+++ b/Source/JavaScriptCore/b3/B3ReduceStrength.cpp
@@ -510,8 +510,7 @@
// -0 + 0 = 0
// -0 + -0 = -0
if (m_value->child(1)->isInt(0) || m_value->child(1)->isNegativeZero()) {
- m_value->replaceWithIdentity(m_value->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0));
break;
}
@@ -566,8 +565,7 @@
// Turn this: Neg(Neg(value))
// Into this: value
if (m_value->child(0)->opcode() == Neg) {
- m_value->replaceWithIdentity(m_value->child(0)->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0)->child(0));
break;
}
@@ -591,16 +589,14 @@
// Note that we don't do this for doubles because that's wrong. For example, -1 * 0
// and 1 * 0 yield different results.
if (!factor) {
- m_value->replaceWithIdentity(m_value->child(1));
- m_changed = true;
+ replaceWithIdentity(m_value->child(1));
break;
}
// Turn this: Mul(value, 1)
// Into this: value
if (factor == 1) {
- m_value->replaceWithIdentity(m_value->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0));
break;
}
@@ -632,7 +628,7 @@
// Turn this: Mul(value, 1)
// Into this: value
if (factor == 1) {
- m_value->replaceWithIdentity(m_value->child(0));
+ replaceWithIdentity(m_value->child(0));
break;
}
}
@@ -663,13 +659,13 @@
// Into this: 0
// We can do this because it's precisely correct for ChillDiv and for Div we
// are allowed to do whatever we want.
- m_value->replaceWithIdentity(m_value->child(1));
+ replaceWithIdentity(m_value->child(1));
break;
case 1:
// Turn this: Div(value, 1)
// Into this: value
- m_value->replaceWithIdentity(m_value->child(0));
+ replaceWithIdentity(m_value->child(0));
break;
default:
@@ -715,7 +711,7 @@
m_insertionSet.insert<Const32Value>(
m_index, m_value->origin(), magic.shift));
}
- m_value->replaceWithIdentity(
+ replaceWithIdentity(
m_insertionSet.insert<Value>(
m_index, Add, m_value->origin(), magicQuotient,
m_insertionSet.insert<Value>(
@@ -724,8 +720,6 @@
m_index, m_value->origin(), 31))));
break;
}
-
- m_changed = true;
break;
}
break;
@@ -746,7 +740,7 @@
// Turn this: Mod(value, 0)
// Into this: 0
// This is correct according to ChillMod semantics.
- m_value->replaceWithIdentity(m_value->child(1));
+ replaceWithIdentity(m_value->child(1));
break;
default:
@@ -782,7 +776,7 @@
break;
}
- m_value->replaceWithIdentity(
+ replaceWithIdentity(
m_insertionSet.insert<Value>(
m_index, Sub, m_value->origin(),
m_value->child(0),
@@ -794,8 +788,6 @@
m_value->child(1))));
break;
}
-
- m_changed = true;
break;
}
@@ -826,16 +818,14 @@
// Turn this: BitAnd(valueX, valueX)
// Into this: valueX.
if (m_value->child(0) == m_value->child(1)) {
- m_value->replaceWithIdentity(m_value->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0));
break;
}
// Turn this: BitAnd(value, zero-constant)
// Into this: zero-constant.
if (m_value->child(1)->isInt(0)) {
- m_value->replaceWithIdentity(m_value->child(1));
- m_changed = true;
+ replaceWithIdentity(m_value->child(1));
break;
}
@@ -843,8 +833,7 @@
// Into this: value.
if ((m_value->type() == Int64 && m_value->child(1)->isInt(0xffffffffffffffff))
|| (m_value->type() == Int32 && m_value->child(1)->isInt(0xffffffff))) {
- m_value->replaceWithIdentity(m_value->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0));
break;
}
@@ -854,8 +843,7 @@
Value* newValue = m_insertionSet.insert<Value>(
m_index, ZExt32, m_value->origin(),
m_insertionSet.insert<Value>(m_index, Trunc, m_value->origin(), m_value->child(0)));
- m_value->replaceWithIdentity(newValue);
- m_changed = true;
+ replaceWithIdentity(newValue);
break;
}
@@ -932,16 +920,14 @@
// Turn this: BitOr(valueX, valueX)
// Into this: valueX.
if (m_value->child(0) == m_value->child(1)) {
- m_value->replaceWithIdentity(m_value->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0));
break;
}
// Turn this: BitOr(value, zero-constant)
// Into this: value.
if (m_value->child(1)->isInt(0)) {
- m_value->replaceWithIdentity(m_value->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0));
break;
}
@@ -949,8 +935,7 @@
// Into this: all-ones.
if ((m_value->type() == Int64 && m_value->child(1)->isInt(0xffffffffffffffff))
|| (m_value->type() == Int32 && m_value->child(1)->isInt(0xffffffff))) {
- m_value->replaceWithIdentity(m_value->child(1));
- m_changed = true;
+ replaceWithIdentity(m_value->child(1));
break;
}
@@ -997,8 +982,7 @@
// Turn this: BitXor(value, zero-constant)
// Into this: value.
if (m_value->child(1)->isInt(0)) {
- m_value->replaceWithIdentity(m_value->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0));
break;
}
@@ -1103,7 +1087,7 @@
// Turn this: Abs(Abs(value))
// Into this: Abs(value)
if (m_value->child(0)->opcode() == Abs) {
- m_value->replaceWithIdentity(m_value->child(0));
+ replaceWithIdentity(m_value->child(0));
break;
}
@@ -1120,7 +1104,7 @@
m_value->child(0)->child(0),
mask);
Value* cast = m_insertionSet.insert<Value>(m_index, BitwiseCast, m_value->origin(), bitAnd);
- m_value->replaceWithIdentity(cast);
+ replaceWithIdentity(cast);
break;
}
break;
@@ -1133,20 +1117,26 @@
break;
}
- // Turn this: Ceil(Ceil(value))
- // Into this: Ceil(value)
- if (m_value->child(0)->opcode() == Ceil) {
- m_value->replaceWithIdentity(m_value->child(0));
+ // Turn this: Ceil(roundedValue)
+ // Into this: roundedValue
+ if (m_value->child(0)->isRounded()) {
+ replaceWithIdentity(m_value->child(0));
+ break;
+ }
+ break;
+
+ case Floor:
+ // Turn this: Floor(constant)
+ // Into this: floor<value->type()>(constant)
+ if (Value* constant = m_value->child(0)->floorConstant(m_proc)) {
+ replaceWithNewValue(constant);
break;
}
- // Turn this: Ceil(IToD(value))
- // Into this: IToD(value)
- //
- // That works for Int64 because both ARM64 and x86_64
- // perform rounding when converting a 64bit integer to double.
- if (m_value->child(0)->opcode() == IToD) {
- m_value->replaceWithIdentity(m_value->child(0));
+ // Turn this: Floor(roundedValue)
+ // Into this: roundedValue
+ if (m_value->child(0)->isRounded()) {
+ replaceWithIdentity(m_value->child(0));
break;
}
break;
@@ -1171,8 +1161,7 @@
// Turn this: BitwiseCast(BitwiseCast(value))
// Into this: value
if (m_value->child(0)->opcode() == BitwiseCast) {
- m_value->replaceWithIdentity(m_value->child(0)->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0)->child(0));
break;
}
break;
@@ -1238,8 +1227,7 @@
// Turn this: SExt16(SExt8(value))
// Into this: SExt8(value)
if (m_value->child(0)->opcode() == SExt8) {
- m_value->replaceWithIdentity(m_value->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0));
break;
}
@@ -1311,8 +1299,7 @@
// Turn this: Trunc(SExt32(value)) or Trunc(ZExt32(value))
// Into this: value
if (m_value->child(0)->opcode() == SExt32 || m_value->child(0)->opcode() == ZExt32) {
- m_value->replaceWithIdentity(m_value->child(0)->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0)->child(0));
break;
}
@@ -1350,8 +1337,7 @@
// Turn this: DoubleToFloat(FloatToDouble(value))
// Into this: value
if (m_value->child(0)->opcode() == FloatToDouble) {
- m_value->replaceWithIdentity(m_value->child(0)->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0)->child(0));
break;
}
@@ -1367,9 +1353,8 @@
// Turn this: Select(constant, a, b)
// Into this: constant ? a : b
if (m_value->child(0)->hasInt32()) {
- m_value->replaceWithIdentity(
+ replaceWithIdentity(
m_value->child(0)->asInt32() ? m_value->child(1) : m_value->child(2));
- m_changed = true;
break;
}
@@ -1407,8 +1392,7 @@
// Turn this: Select(stuff, x, x)
// Into this: x
if (m_value->child(1) == m_value->child(2)) {
- m_value->replaceWithIdentity(m_value->child(1));
- m_changed = true;
+ replaceWithIdentity(m_value->child(1));
break;
}
break;
@@ -1491,8 +1475,7 @@
// Turn this Equal(bool, 1)
// Into this: bool
if (m_value->child(0)->returnsBool() && m_value->child(1)->isInt32(1)) {
- m_value->replaceWithIdentity(m_value->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0));
break;
}
@@ -1511,8 +1494,7 @@
// Turn this: NotEqual(bool, 0)
// Into this: bool
if (m_value->child(1)->isInt32(0)) {
- m_value->replaceWithIdentity(m_value->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0));
break;
}
@@ -1613,8 +1595,7 @@
handleCommutativity();
if (m_value->child(1)->isInt(0)) {
- m_value->replaceWithIdentity(m_value->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0));
break;
}
@@ -1633,8 +1614,7 @@
break;
if (m_value->child(1)->isInt(0)) {
- m_value->replaceWithIdentity(m_value->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0));
break;
}
@@ -1669,8 +1649,7 @@
replaceWithNewValue(m_proc.addIntConstant(m_value, 0));
break;
case 1:
- m_value->replaceWithIdentity(m_value->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0));
break;
case 2:
m_value->as<CheckValue>()->convertToAdd();
@@ -2110,12 +2089,17 @@
return true;
}
+ void replaceWithIdentity(Value* newValue)
+ {
+ m_value->replaceWithIdentity(newValue);
+ m_changed = true;
+ }
+
bool handleShiftAmount()
{
// Shift anything by zero is identity.
if (m_value->child(1)->isInt32(0)) {
- m_value->replaceWithIdentity(m_value->child(0));
- m_changed = true;
+ replaceWithIdentity(m_value->child(0));
return true;
}
diff --git a/Source/JavaScriptCore/b3/B3Validate.cpp b/Source/JavaScriptCore/b3/B3Validate.cpp
index 47a494d..25f55c6 100644
--- a/Source/JavaScriptCore/b3/B3Validate.cpp
+++ b/Source/JavaScriptCore/b3/B3Validate.cpp
@@ -241,6 +241,7 @@
break;
case Abs:
case Ceil:
+ case Floor:
case Sqrt:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(isFloat(value->child(0)->type()), ("At ", *value));
diff --git a/Source/JavaScriptCore/b3/B3Value.cpp b/Source/JavaScriptCore/b3/B3Value.cpp
index c028dad..ee23808 100644
--- a/Source/JavaScriptCore/b3/B3Value.cpp
+++ b/Source/JavaScriptCore/b3/B3Value.cpp
@@ -287,6 +287,11 @@
return nullptr;
}
+Value* Value::floorConstant(Procedure&) const
+{
+ return nullptr;
+}
+
Value* Value::sqrtConstant(Procedure&) const
{
return nullptr;
@@ -356,6 +361,30 @@
return nullptr;
}
+bool Value::isRounded() const
+{
+ ASSERT(isFloat(type()));
+ switch (opcode()) {
+ case Floor:
+ case Ceil:
+ case IToD:
+ return true;
+
+ case ConstDouble: {
+ double value = asDouble();
+ return std::isfinite(value) && value == ceil(value);
+ }
+
+ case ConstFloat: {
+ float value = asFloat();
+ return std::isfinite(value) && value == ceilf(value);
+ }
+
+ default:
+ return false;
+ }
+}
+
bool Value::returnsBool() const
{
if (type() != Int32)
@@ -432,6 +461,7 @@
case Clz:
case Abs:
case Ceil:
+ case Floor:
case Sqrt:
case BitwiseCast:
case SExt8:
@@ -514,6 +544,7 @@
case Identity:
case Abs:
case Ceil:
+ case Floor:
case Sqrt:
case SExt8:
case SExt16:
@@ -629,6 +660,7 @@
case Clz:
case Abs:
case Ceil:
+ case Floor:
case Sqrt:
case CheckAdd:
case CheckSub:
diff --git a/Source/JavaScriptCore/b3/B3Value.h b/Source/JavaScriptCore/b3/B3Value.h
index cbbf63c..981f007 100644
--- a/Source/JavaScriptCore/b3/B3Value.h
+++ b/Source/JavaScriptCore/b3/B3Value.h
@@ -137,6 +137,7 @@
virtual Value* floatToDoubleConstant(Procedure&) const;
virtual Value* absConstant(Procedure&) const;
virtual Value* ceilConstant(Procedure&) const;
+ virtual Value* floorConstant(Procedure&) const;
virtual Value* sqrtConstant(Procedure&) const;
virtual TriState equalConstant(const Value* other) const;
@@ -190,6 +191,8 @@
bool isNegativeZero() const;
+ bool isRounded() const;
+
TriState asTriState() const;
bool isLikeZero() const { return asTriState() == FalseTriState; }
bool isLikeNonZero() const { return asTriState() == TrueTriState; }
diff --git a/Source/JavaScriptCore/b3/air/AirFixPartialRegisterStalls.cpp b/Source/JavaScriptCore/b3/air/AirFixPartialRegisterStalls.cpp
index b489308..c3640d6 100644
--- a/Source/JavaScriptCore/b3/air/AirFixPartialRegisterStalls.cpp
+++ b/Source/JavaScriptCore/b3/air/AirFixPartialRegisterStalls.cpp
@@ -54,6 +54,8 @@
case SqrtFloat:
case CeilDouble:
case CeilFloat:
+ case FloorDouble:
+ case FloorFloat:
return true;
default:
break;
diff --git a/Source/JavaScriptCore/b3/air/AirOpcode.opcodes b/Source/JavaScriptCore/b3/air/AirOpcode.opcodes
index 5b506d8..1cf91c2 100644
--- a/Source/JavaScriptCore/b3/air/AirOpcode.opcodes
+++ b/Source/JavaScriptCore/b3/air/AirOpcode.opcodes
@@ -432,6 +432,14 @@
Tmp, Tmp
x86: Addr, Tmp
+FloorDouble U:F:64, D:F:64
+ Tmp, Tmp
+ x86: Addr, Tmp
+
+FloorFloat U:F:32, D:F:32
+ Tmp, Tmp
+ x86: Addr, Tmp
+
SqrtDouble U:F:64, D:F:64
Tmp, Tmp
x86: Addr, Tmp
diff --git a/Source/JavaScriptCore/b3/testb3.cpp b/Source/JavaScriptCore/b3/testb3.cpp
index 631543e..074d91c 100644
--- a/Source/JavaScriptCore/b3/testb3.cpp
+++ b/Source/JavaScriptCore/b3/testb3.cpp
@@ -3377,6 +3377,18 @@
CHECK(isIdentical(compileAndRun<double>(proc, a), ceil(a)));
}
+void testFloorCeilArg(double a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* firstCeil = root->appendNew<Value>(proc, Ceil, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0));
+ Value* wrappingFloor = root->appendNew<Value>(proc, Floor, Origin(), firstCeil);
+ root->appendNew<ControlValue>(proc, Return, Origin(), wrappingFloor);
+
+ CHECK(isIdentical(compileAndRun<double>(proc, a), ceil(a)));
+}
+
void testCeilIToD64(int64_t a)
{
Procedure proc;
@@ -3459,6 +3471,20 @@
CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a)), ceilf(a)));
}
+void testFloorCeilArg(float a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
+ Value* firstCeil = root->appendNew<Value>(proc, Ceil, Origin(), argument);
+ Value* wrappingFloor = root->appendNew<Value>(proc, Floor, Origin(), firstCeil);
+ root->appendNew<ControlValue>(proc, Return, Origin(), wrappingFloor);
+
+ CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a)), ceilf(a)));
+}
+
void testCeilArgWithUselessDoubleConversion(float a)
{
Procedure proc;
@@ -3496,6 +3522,201 @@
CHECK(isIdentical(effect, ceilf(a)));
}
+void testFloorArg(double a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(
+ proc, Floor, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0)));
+
+ CHECK(isIdentical(compileAndRun<double>(proc, a), floor(a)));
+}
+
+void testFloorImm(double a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* argument = root->appendNew<ConstDoubleValue>(proc, Origin(), a);
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, Floor, Origin(), argument));
+
+ CHECK(isIdentical(compileAndRun<double>(proc), floor(a)));
+}
+
+void testFloorMem(double a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ MemoryValue* loadDouble = root->appendNew<MemoryValue>(proc, Load, Double, Origin(), address);
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, Floor, Origin(), loadDouble));
+
+ CHECK(isIdentical(compileAndRun<double>(proc, &a), floor(a)));
+}
+
+void testFloorFloorArg(double a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* firstFloor = root->appendNew<Value>(proc, Floor, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0));
+ Value* secondFloor = root->appendNew<Value>(proc, Floor, Origin(), firstFloor);
+ root->appendNew<ControlValue>(proc, Return, Origin(), secondFloor);
+
+ CHECK(isIdentical(compileAndRun<double>(proc, a), floor(a)));
+}
+
+void testCeilFloorArg(double a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* firstFloor = root->appendNew<Value>(proc, Floor, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0));
+ Value* wrappingCeil = root->appendNew<Value>(proc, Ceil, Origin(), firstFloor);
+ root->appendNew<ControlValue>(proc, Return, Origin(), wrappingCeil);
+
+ CHECK(isIdentical(compileAndRun<double>(proc, a), floor(a)));
+}
+
+void testFloorIToD64(int64_t a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* argumentAsDouble = root->appendNew<Value>(proc, IToD, Origin(), argument);
+
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, Floor, Origin(), argumentAsDouble));
+
+ CHECK(isIdentical(compileAndRun<double>(proc, a), floor(static_cast<double>(a))));
+}
+
+void testFloorIToD32(int64_t a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* argument = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* argumentAsDouble = root->appendNew<Value>(proc, IToD, Origin(), argument);
+
+ root->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ root->appendNew<Value>(proc, Floor, Origin(), argumentAsDouble));
+
+ CHECK(isIdentical(compileAndRun<double>(proc, a), floor(static_cast<double>(a))));
+}
+
+void testFloorArg(float a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
+ Value* result = root->appendNew<Value>(proc, Floor, Origin(), argument);
+ Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
+ root->appendNew<ControlValue>(proc, Return, Origin(), result32);
+
+ CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(floorf(a))));
+}
+
+void testFloorImm(float a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* argument = root->appendNew<ConstFloatValue>(proc, Origin(), a);
+ Value* result = root->appendNew<Value>(proc, Floor, Origin(), argument);
+ Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
+ root->appendNew<ControlValue>(proc, Return, Origin(), result32);
+
+ CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(floorf(a))));
+}
+
+void testFloorMem(float a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* address = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ MemoryValue* loadFloat = root->appendNew<MemoryValue>(proc, Load, Float, Origin(), address);
+ Value* result = root->appendNew<Value>(proc, Floor, Origin(), loadFloat);
+ Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), result);
+ root->appendNew<ControlValue>(proc, Return, Origin(), result32);
+
+ CHECK(isIdentical(compileAndRun<int32_t>(proc, &a), bitwise_cast<int32_t>(floorf(a))));
+}
+
+void testFloorFloorArg(float a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
+ Value* firstFloor = root->appendNew<Value>(proc, Floor, Origin(), argument);
+ Value* secondFloor = root->appendNew<Value>(proc, Floor, Origin(), firstFloor);
+ root->appendNew<ControlValue>(proc, Return, Origin(), secondFloor);
+
+ CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a)), floorf(a)));
+}
+
+void testCeilFloorArg(float a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* argument = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
+ Value* firstFloor = root->appendNew<Value>(proc, Floor, Origin(), argument);
+ Value* wrappingCeil = root->appendNew<Value>(proc, Ceil, Origin(), firstFloor);
+ root->appendNew<ControlValue>(proc, Return, Origin(), wrappingCeil);
+
+ CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a)), floorf(a)));
+}
+
+void testFloorArgWithUselessDoubleConversion(float a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
+ Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
+ Value* result = root->appendNew<Value>(proc, Floor, Origin(), asDouble);
+ Value* floatResult = root->appendNew<Value>(proc, DoubleToFloat, Origin(), result);
+ Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), floatResult);
+ root->appendNew<ControlValue>(proc, Return, Origin(), result32);
+
+ CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a)), bitwise_cast<int32_t>(floorf(a))));
+}
+
+void testFloorArgWithEffectfulDoubleConversion(float a)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
+ Value* asDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
+ Value* result = root->appendNew<Value>(proc, Floor, Origin(), asDouble);
+ Value* floatResult = root->appendNew<Value>(proc, DoubleToFloat, Origin(), result);
+ Value* result32 = root->appendNew<Value>(proc, BitwiseCast, Origin(), floatResult);
+ Value* doubleAddress = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
+ root->appendNew<MemoryValue>(proc, Store, Origin(), result, doubleAddress);
+ root->appendNew<ControlValue>(proc, Return, Origin(), result32);
+
+ double effect = 0;
+ int32_t resultValue = compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a), &effect);
+ CHECK(isIdentical(resultValue, bitwise_cast<int32_t>(floorf(a))));
+ CHECK(isIdentical(effect, floorf(a)));
+}
+
void testSqrtArg(double a)
{
Procedure proc;
@@ -11142,15 +11363,32 @@
RUN_UNARY(testCeilImm, floatingPointOperands<double>());
RUN_UNARY(testCeilMem, floatingPointOperands<double>());
RUN_UNARY(testCeilCeilArg, floatingPointOperands<double>());
+ RUN_UNARY(testFloorCeilArg, floatingPointOperands<double>());
RUN_UNARY(testCeilIToD64, int64Operands());
RUN_UNARY(testCeilIToD32, int32Operands());
RUN_UNARY(testCeilArg, floatingPointOperands<float>());
RUN_UNARY(testCeilImm, floatingPointOperands<float>());
RUN_UNARY(testCeilMem, floatingPointOperands<float>());
RUN_UNARY(testCeilCeilArg, floatingPointOperands<float>());
+ RUN_UNARY(testFloorCeilArg, floatingPointOperands<float>());
RUN_UNARY(testCeilArgWithUselessDoubleConversion, floatingPointOperands<float>());
RUN_UNARY(testCeilArgWithEffectfulDoubleConversion, floatingPointOperands<float>());
+ RUN_UNARY(testFloorArg, floatingPointOperands<double>());
+ RUN_UNARY(testFloorImm, floatingPointOperands<double>());
+ RUN_UNARY(testFloorMem, floatingPointOperands<double>());
+ RUN_UNARY(testFloorFloorArg, floatingPointOperands<double>());
+ RUN_UNARY(testCeilFloorArg, floatingPointOperands<double>());
+ RUN_UNARY(testFloorIToD64, int64Operands());
+ RUN_UNARY(testFloorIToD32, int32Operands());
+ RUN_UNARY(testFloorArg, floatingPointOperands<float>());
+ RUN_UNARY(testFloorImm, floatingPointOperands<float>());
+ RUN_UNARY(testFloorMem, floatingPointOperands<float>());
+ RUN_UNARY(testFloorFloorArg, floatingPointOperands<float>());
+ RUN_UNARY(testCeilFloorArg, floatingPointOperands<float>());
+ RUN_UNARY(testFloorArgWithUselessDoubleConversion, floatingPointOperands<float>());
+ RUN_UNARY(testFloorArgWithEffectfulDoubleConversion, floatingPointOperands<float>());
+
RUN_UNARY(testSqrtArg, floatingPointOperands<double>());
RUN_UNARY(testSqrtImm, floatingPointOperands<double>());
RUN_UNARY(testSqrtMem, floatingPointOperands<double>());
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index d7c1bff..a541ef2 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -833,10 +833,20 @@
break;
}
- case ArithRound: {
+ case ArithRound:
+ case ArithFloor:
+ case ArithCeil: {
JSValue operand = forNode(node->child1()).value();
if (operand && operand.isNumber()) {
- double roundedValue = jsRound(operand.asNumber());
+ double roundedValue = 0;
+ if (node->op() == ArithRound)
+ roundedValue = jsRound(operand.asNumber());
+ else if (node->op() == ArithFloor)
+ roundedValue = floor(operand.asNumber());
+ else {
+ ASSERT(node->op() == ArithCeil);
+ roundedValue = ceil(operand.asNumber());
+ }
if (producesInteger(node->arithRoundingMode())) {
int32_t roundedValueAsInt32 = static_cast<int32_t>(roundedValue);
diff --git a/Source/JavaScriptCore/dfg/DFGArithMode.cpp b/Source/JavaScriptCore/dfg/DFGArithMode.cpp
index c1dfe7f..84ddae1 100644
--- a/Source/JavaScriptCore/dfg/DFGArithMode.cpp
+++ b/Source/JavaScriptCore/dfg/DFGArithMode.cpp
@@ -55,6 +55,22 @@
RELEASE_ASSERT_NOT_REACHED();
}
+void printInternal(PrintStream& out, JSC::DFG::Arith::RoundingMode mode)
+{
+ switch (mode) {
+ case JSC::DFG::Arith::RoundingMode::Int32:
+ out.print("Int32");
+ return;
+ case JSC::DFG::Arith::RoundingMode::Int32WithNegativeZeroCheck:
+ out.print("Int32WithNegativeZeroCheck");
+ return;
+ case JSC::DFG::Arith::RoundingMode::Double:
+ out.print("Double");
+ return;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+}
+
} // namespace WTF
#endif // ENABLE(DFG_JIT)
diff --git a/Source/JavaScriptCore/dfg/DFGArithMode.h b/Source/JavaScriptCore/dfg/DFGArithMode.h
index 4e09ac3..aecfe3e 100644
--- a/Source/JavaScriptCore/dfg/DFGArithMode.h
+++ b/Source/JavaScriptCore/dfg/DFGArithMode.h
@@ -146,6 +146,7 @@
class PrintStream;
void printInternal(PrintStream&, JSC::DFG::Arith::Mode);
+void printInternal(PrintStream&, JSC::DFG::Arith::RoundingMode);
} // namespace WTF
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index f098b64..bbc0521 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -2188,7 +2188,9 @@
return true;
}
- case RoundIntrinsic: {
+ case RoundIntrinsic:
+ case FloorIntrinsic:
+ case CeilIntrinsic: {
if (argumentCountIncludingThis == 1) {
insertChecks();
set(VirtualRegister(resultOperand), addToGraph(JSConstant, OpInfo(m_constantNaN)));
@@ -2197,7 +2199,16 @@
if (argumentCountIncludingThis == 2) {
insertChecks();
Node* operand = get(virtualRegisterForArgument(1, registerOffset));
- Node* roundNode = addToGraph(ArithRound, OpInfo(0), OpInfo(prediction), operand);
+ NodeType op;
+ if (intrinsic == RoundIntrinsic)
+ op = ArithRound;
+ else if (intrinsic == FloorIntrinsic)
+ op = ArithFloor;
+ else {
+ ASSERT(intrinsic == CeilIntrinsic);
+ op = ArithCeil;
+ }
+ Node* roundNode = addToGraph(op, OpInfo(0), OpInfo(prediction), operand);
set(VirtualRegister(resultOperand), roundNode);
return true;
}
diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h
index 343a6de..458086e 100644
--- a/Source/JavaScriptCore/dfg/DFGClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h
@@ -297,6 +297,8 @@
}
case ArithRound:
+ case ArithFloor:
+ case ArithCeil:
def(PureValue(node, static_cast<uintptr_t>(node->arithRoundingMode())));
return;
diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
index 702da5b..4521adb 100644
--- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
@@ -87,6 +87,8 @@
case ArithSqrt:
case ArithRandom:
case ArithRound:
+ case ArithFloor:
+ case ArithCeil:
case ArithFRound:
case ArithSin:
case ArithCos:
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 2265c10..f01c146 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -363,7 +363,9 @@
break;
}
- case ArithRound: {
+ case ArithRound:
+ case ArithFloor:
+ case ArithCeil: {
if (m_graph.unaryArithShouldSpeculateInt32(node, FixupPass)) {
fixIntOrBooleanEdge(node->child1());
insertCheck<Int32Use>(m_indexInBlock, node->child1().node());
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp
index 4c9a38b..541c84e 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.cpp
+++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp
@@ -222,6 +222,8 @@
out.print(comma, node->arrayMode());
if (node->hasArithMode())
out.print(comma, node->arithMode());
+ if (node->hasArithRoundingMode())
+ out.print(comma, "Rounding:", node->arithRoundingMode());
if (node->hasScopeOffset())
out.print(comma, node->scopeOffset());
if (node->hasDirectArgumentsOffset())
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h
index 60d146b..1a4e2f7 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.h
+++ b/Source/JavaScriptCore/dfg/DFGGraph.h
@@ -316,7 +316,7 @@
bool roundShouldSpeculateInt32(Node* arithRound, PredictionPass pass)
{
- ASSERT(arithRound->op() == ArithRound);
+ ASSERT(arithRound->op() == ArithRound || arithRound->op() == ArithFloor || arithRound->op() == ArithCeil);
return arithRound->canSpeculateInt32(pass) && !hasExitSite(arithRound->origin.semantic, Overflow) && !hasExitSite(arithRound->origin.semantic, NegativeZero);
}
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index eeab257..5499deb 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -928,7 +928,7 @@
NodeFlags arithNodeFlags()
{
NodeFlags result = m_flags & NodeArithFlagsMask;
- if (op() == ArithMul || op() == ArithDiv || op() == ArithMod || op() == ArithNegate || op() == ArithPow || op() == ArithRound || op() == DoubleAsInt32)
+ if (op() == ArithMul || op() == ArithDiv || op() == ArithMod || op() == ArithNegate || op() == ArithPow || op() == ArithRound || op() == ArithFloor || op() == ArithCeil || op() == DoubleAsInt32)
return result;
return result & ~NodeBytecodeNeedsNegZero;
}
@@ -1334,6 +1334,8 @@
{
switch (op()) {
case ArithRound:
+ case ArithFloor:
+ case ArithCeil:
case GetDirectPname:
case GetById:
case GetByIdFlush:
@@ -1715,7 +1717,7 @@
bool hasArithRoundingMode()
{
- return op() == ArithRound;
+ return op() == ArithRound || op() == ArithFloor || op() == ArithCeil;
}
Arith::RoundingMode arithRoundingMode()
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index ddd0b97..71c075a 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -154,6 +154,8 @@
macro(ArithPow, NodeResultNumber) \
macro(ArithRandom, NodeResultDouble | NodeMustGenerate) \
macro(ArithRound, NodeResultNumber) \
+ macro(ArithFloor, NodeResultNumber) \
+ macro(ArithCeil, NodeResultNumber) \
macro(ArithSqrt, NodeResultNumber) \
macro(ArithSin, NodeResultNumber) \
macro(ArithCos, NodeResultNumber) \
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index fa48248..4596053 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -386,7 +386,9 @@
break;
}
- case ArithRound: {
+ case ArithRound:
+ case ArithFloor:
+ case ArithCeil: {
if (isInt32OrBooleanSpeculation(node->getHeapPrediction()) && m_graph.roundShouldSpeculateInt32(node, m_pass))
changed |= setPrediction(SpecInt32);
else
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index 230efed..98592c7 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -181,6 +181,8 @@
case ArithSqrt:
case ArithFRound:
case ArithRound:
+ case ArithFloor:
+ case ArithCeil:
case ArithSin:
case ArithCos:
case ArithLog:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index 6174603..f03af97 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -4412,46 +4412,93 @@
}
}
-void SpeculativeJIT::compileArithRound(Node* node)
+void SpeculativeJIT::compileArithRounding(Node* node)
{
ASSERT(node->child1().useKind() == DoubleRepUse);
SpeculateDoubleOperand value(this, node->child1());
FPRReg valueFPR = value.fpr();
- if (producesInteger(node->arithRoundingMode()) && !shouldCheckNegativeZero(node->arithRoundingMode())) {
- FPRTemporary oneHalf(this);
- GPRTemporary roundedResultAsInt32(this);
- FPRReg oneHalfFPR = oneHalf.fpr();
- GPRReg resultGPR = roundedResultAsInt32.gpr();
+ auto setResult = [&] (FPRReg resultFPR) {
+ if (producesInteger(node->arithRoundingMode())) {
+ GPRTemporary roundedResultAsInt32(this);
+ FPRTemporary scratch(this);
+ FPRReg scratchFPR = scratch.fpr();
+ GPRReg resultGPR = roundedResultAsInt32.gpr();
+ JITCompiler::JumpList failureCases;
+ m_jit.branchConvertDoubleToInt32(resultFPR, resultGPR, failureCases, scratchFPR, shouldCheckNegativeZero(node->arithRoundingMode()));
+ speculationCheck(Overflow, JSValueRegs(), node, failureCases);
- static const double halfConstant = 0.5;
- m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&halfConstant), oneHalfFPR);
- m_jit.addDouble(valueFPR, oneHalfFPR);
+ int32Result(resultGPR, node);
+ } else
+ doubleResult(resultFPR, node);
+ };
- JITCompiler::Jump truncationFailed = m_jit.branchTruncateDoubleToInt32(oneHalfFPR, resultGPR);
- speculationCheck(Overflow, JSValueRegs(), node, truncationFailed);
- int32Result(resultGPR, node);
- return;
+ if (m_jit.supportsFloatingPointRounding()) {
+ switch (node->op()) {
+ case ArithRound: {
+ FPRTemporary result(this);
+ FPRReg resultFPR = result.fpr();
+ if (producesInteger(node->arithRoundingMode()) && !shouldCheckNegativeZero(node->arithRoundingMode())) {
+ static const double halfConstant = 0.5;
+ m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&halfConstant), resultFPR);
+ m_jit.addDouble(valueFPR, resultFPR);
+ m_jit.floorDouble(resultFPR, resultFPR);
+ } else {
+ m_jit.ceilDouble(valueFPR, resultFPR);
+ FPRTemporary realPart(this);
+ FPRReg realPartFPR = realPart.fpr();
+ m_jit.subDouble(resultFPR, valueFPR, realPartFPR);
+
+ FPRTemporary scratch(this);
+ FPRReg scratchFPR = scratch.fpr();
+ static const double halfConstant = 0.5;
+ m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&halfConstant), scratchFPR);
+
+ JITCompiler::Jump shouldUseCeiled = m_jit.branchDouble(JITCompiler::DoubleLessThanOrEqual, realPartFPR, scratchFPR);
+ static const double oneConstant = -1.0;
+ m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&oneConstant), scratchFPR);
+ m_jit.addDouble(scratchFPR, resultFPR);
+ shouldUseCeiled.link(&m_jit);
+ }
+ setResult(resultFPR);
+ return;
+ }
+
+ case ArithFloor: {
+ FPRTemporary rounded(this);
+ FPRReg resultFPR = rounded.fpr();
+ m_jit.floorDouble(valueFPR, resultFPR);
+ setResult(resultFPR);
+ return;
+ }
+
+ case ArithCeil: {
+ FPRTemporary rounded(this);
+ FPRReg resultFPR = rounded.fpr();
+ m_jit.ceilDouble(valueFPR, resultFPR);
+ setResult(resultFPR);
+ return;
+ }
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ } else {
+ flushRegisters();
+ FPRResult roundedResultAsDouble(this);
+ FPRReg resultFPR = roundedResultAsDouble.fpr();
+ if (node->op() == ArithRound)
+ callOperation(jsRound, resultFPR, valueFPR);
+ else if (node->op() == ArithFloor)
+ callOperation(floor, resultFPR, valueFPR);
+ else {
+ ASSERT(node->op() == ArithCeil);
+ callOperation(ceil, resultFPR, valueFPR);
+ }
+ m_jit.exceptionCheck();
+ setResult(resultFPR);
}
-
- flushRegisters();
- FPRResult roundedResultAsDouble(this);
- FPRReg resultFPR = roundedResultAsDouble.fpr();
- callOperation(jsRound, resultFPR, valueFPR);
- m_jit.exceptionCheck();
- if (producesInteger(node->arithRoundingMode())) {
- GPRTemporary roundedResultAsInt32(this);
- FPRTemporary scratch(this);
- FPRReg scratchFPR = scratch.fpr();
- GPRReg resultGPR = roundedResultAsInt32.gpr();
- JITCompiler::JumpList failureCases;
- m_jit.branchConvertDoubleToInt32(resultFPR, resultGPR, failureCases, scratchFPR);
- speculationCheck(Overflow, JSValueRegs(), node, failureCases);
-
- int32Result(resultGPR, node);
- } else
- doubleResult(resultFPR, node);
}
void SpeculativeJIT::compileArithSqrt(Node* node)
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index 9188a83..5d6fba4d 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -2267,7 +2267,9 @@
void compileArithDiv(Node*);
void compileArithMod(Node*);
void compileArithPow(Node*);
- void compileArithRound(Node*);
+ void compileArithRounding(Node*);
+ void compileArithFloor(Node*);
+ void compileArithCeil(Node*);
void compileArithRandom(Node*);
void compileArithSqrt(Node*);
void compileArithLog(Node*);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 8472b8c..2009a02 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -2292,7 +2292,9 @@
break;
case ArithRound:
- compileArithRound(node);
+ case ArithFloor:
+ case ArithCeil:
+ compileArithRounding(node);
break;
case ArithSin: {
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 83b6f5b..38cb2cd 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -2415,7 +2415,9 @@
break;
case ArithRound:
- compileArithRound(node);
+ case ArithFloor:
+ case ArithCeil:
+ compileArithRounding(node);
break;
case ArithSin: {
diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
index 01c8e87..d715693 100644
--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
@@ -97,6 +97,8 @@
case ArithPow:
case ArithRandom:
case ArithRound:
+ case ArithFloor:
+ case ArithCeil:
case ArithSqrt:
case ArithLog:
case ArithFRound:
diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
index 53e926e..dd76f26 100644
--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
@@ -523,6 +523,12 @@
case ArithRound:
compileArithRound();
break;
+ case ArithFloor:
+ compileArithFloor();
+ break;
+ case ArithCeil:
+ compileArithCeil();
+ break;
case ArithSqrt:
compileArithSqrt();
break;
@@ -1890,24 +1896,31 @@
void compileArithRound()
{
- LBasicBlock realPartIsMoreThanHalf = FTL_NEW_BLOCK(m_out, ("ArithRound should round down"));
- LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("ArithRound continuation"));
+ LValue result = nullptr;
- LValue value = lowDouble(m_node->child1());
- LValue integerValue = m_out.doubleCeil(value);
- ValueFromBlock integerValueResult = m_out.anchor(integerValue);
+ if (producesInteger(m_node->arithRoundingMode()) && !shouldCheckNegativeZero(m_node->arithRoundingMode())) {
+ LValue value = lowDouble(m_node->child1());
+ result = m_out.doubleFloor(m_out.doubleAdd(value, m_out.constDouble(0.5)));
+ } else {
+ LBasicBlock realPartIsMoreThanHalf = FTL_NEW_BLOCK(m_out, ("ArithRound should round down"));
+ LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("ArithRound continuation"));
- LValue realPart = m_out.doubleSub(integerValue, value);
+ LValue value = lowDouble(m_node->child1());
+ LValue integerValue = m_out.doubleCeil(value);
+ ValueFromBlock integerValueResult = m_out.anchor(integerValue);
- m_out.branch(m_out.doubleGreaterThanOrUnordered(realPart, m_out.constDouble(0.5)), unsure(realPartIsMoreThanHalf), unsure(continuation));
+ LValue realPart = m_out.doubleSub(integerValue, value);
- LBasicBlock lastNext = m_out.appendTo(realPartIsMoreThanHalf, continuation);
- LValue integerValueRoundedDown = m_out.doubleSub(integerValue, m_out.constDouble(1));
- ValueFromBlock integerValueRoundedDownResult = m_out.anchor(integerValueRoundedDown);
- m_out.jump(continuation);
- m_out.appendTo(continuation, lastNext);
+ m_out.branch(m_out.doubleGreaterThanOrUnordered(realPart, m_out.constDouble(0.5)), unsure(realPartIsMoreThanHalf), unsure(continuation));
- LValue result = m_out.phi(m_out.doubleType, integerValueResult, integerValueRoundedDownResult);
+ LBasicBlock lastNext = m_out.appendTo(realPartIsMoreThanHalf, continuation);
+ LValue integerValueRoundedDown = m_out.doubleSub(integerValue, m_out.constDouble(1));
+ ValueFromBlock integerValueRoundedDownResult = m_out.anchor(integerValueRoundedDown);
+ m_out.jump(continuation);
+ m_out.appendTo(continuation, lastNext);
+
+ result = m_out.phi(m_out.doubleType, integerValueResult, integerValueRoundedDownResult);
+ }
if (producesInteger(m_node->arithRoundingMode())) {
LValue integerValue = convertDoubleToInt32(result, shouldCheckNegativeZero(m_node->arithRoundingMode()));
@@ -1916,6 +1929,26 @@
setDouble(result);
}
+ void compileArithFloor()
+ {
+ LValue value = lowDouble(m_node->child1());
+ LValue integerValue = m_out.doubleFloor(value);
+ if (producesInteger(m_node->arithRoundingMode()))
+ setInt32(convertDoubleToInt32(integerValue, shouldCheckNegativeZero(m_node->arithRoundingMode())));
+ else
+ setDouble(integerValue);
+ }
+
+ void compileArithCeil()
+ {
+ LValue value = lowDouble(m_node->child1());
+ LValue integerValue = m_out.doubleCeil(value);
+ if (producesInteger(m_node->arithRoundingMode()))
+ setInt32(convertDoubleToInt32(integerValue, shouldCheckNegativeZero(m_node->arithRoundingMode())));
+ else
+ setDouble(integerValue);
+ }
+
void compileArithSqrt() { setDouble(m_out.doubleSqrt(lowDouble(m_node->child1()))); }
void compileArithLog() { setDouble(m_out.doubleLog(lowDouble(m_node->child1()))); }
diff --git a/Source/JavaScriptCore/ftl/FTLOutput.h b/Source/JavaScriptCore/ftl/FTLOutput.h
index 78a5ca5..a6487e5 100644
--- a/Source/JavaScriptCore/ftl/FTLOutput.h
+++ b/Source/JavaScriptCore/ftl/FTLOutput.h
@@ -155,6 +155,7 @@
LValue mulWithOverflow64(LValue left, LValue right) { CRASH(); }
LValue doubleAbs(LValue value) { return m_block->appendNew<B3::Value>(m_proc, B3::Abs, origin(), value); }
LValue doubleCeil(LValue operand) { return m_block->appendNew<B3::Value>(m_proc, B3::Ceil, origin(), operand); }
+ LValue doubleFloor(LValue operand) { return m_block->appendNew<B3::Value>(m_proc, B3::Floor, origin(), operand); }
LValue doubleSin(LValue value)
{
diff --git a/Source/JavaScriptCore/jit/ThunkGenerators.cpp b/Source/JavaScriptCore/jit/ThunkGenerators.cpp
index 7f5469a..5c03d70 100644
--- a/Source/JavaScriptCore/jit/ThunkGenerators.cpp
+++ b/Source/JavaScriptCore/jit/ThunkGenerators.cpp
@@ -828,7 +828,7 @@
jit.returnInt32(SpecializedThunkJIT::regT0);
nonIntJump.link(&jit);
jit.loadDoubleArgument(0, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0);
- if (jit.supportsFloatingPointCeil())
+ if (jit.supportsFloatingPointRounding())
jit.ceilDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT0);
else
jit.callDoubleToDoublePreservingReturn(UnaryDoubleOpWrapper(ceil));
diff --git a/Source/JavaScriptCore/tests/stress/math-ceil-arith-rounding-mode.js b/Source/JavaScriptCore/tests/stress/math-ceil-arith-rounding-mode.js
new file mode 100644
index 0000000..3ebfceb
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/math-ceil-arith-rounding-mode.js
@@ -0,0 +1,85 @@
+function firstCareAboutZeroSecondDoesNot(a) {
+ var resultA = Math.ceil(a);
+ var resultB = Math.ceil(a)|0;
+ return { resultA:resultA, resultB:resultB };
+}
+noInline(firstCareAboutZeroSecondDoesNot);
+
+function firstDoNotCareAboutZeroSecondDoes(a) {
+ var resultA = Math.ceil(a)|0;
+ var resultB = Math.ceil(a);
+ return { resultA:resultA, resultB:resultB };
+}
+noInline(firstDoNotCareAboutZeroSecondDoes);
+
+// Warmup with doubles, but nothing that would ceil to -0 to ensure we never
+// see a double as result. The result must be integers, the input is kept to small values.
+function warmup() {
+ for (var i = 0; i < 1e4; ++i) {
+ firstCareAboutZeroSecondDoesNot(42.6 + i);
+ firstDoNotCareAboutZeroSecondDoes(42.4 + i);
+ }
+}
+warmup();
+
+function verifyNegativeZeroIsPreserved() {
+ for (var i = 0; i < 1e4; ++i) {
+ var result1 = firstCareAboutZeroSecondDoesNot(-0.1);
+ if (1 / result1.resultA !== -Infinity) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(-0.1), resultA = " + result1.resultA);
+ }
+ if (1 / result1.resultB !== Infinity) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(-0.1), resultB = " + result1.resultB);
+ }
+ var result2 = firstDoNotCareAboutZeroSecondDoes(-0.1);
+ if (1 / result2.resultA !== Infinity) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(-0.1), resultA = " + result1.resultA);
+ }
+ if (1 / result2.resultB !== -Infinity) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(-0.1), resultB = " + result1.resultB);
+ }
+ var result3 = firstCareAboutZeroSecondDoesNot(-0.0);
+ if (1 / result3.resultA !== -Infinity) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(-0.0), resultA = " + result3.resultA);
+ }
+ if (1 / result3.resultB !== Infinity) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(-0.0), resultB = " + result3.resultB);
+ }
+ var result4 = firstDoNotCareAboutZeroSecondDoes(-0.0);
+ if (1 / result4.resultA !== Infinity) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(-0.0), resultA = " + result4.resultA);
+ }
+ if (1 / result4.resultB !== -Infinity) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(-0.0), resultB = " + result4.resultB);
+ }
+ var result5 = firstCareAboutZeroSecondDoesNot(0.0);
+ if (1 / result5.resultA !== Infinity) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(0.0), resultA = " + result5.resultA);
+ }
+ if (1 / result5.resultB !== Infinity) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(0.0), resultB = " + result5.resultB);
+ }
+ var result6 = firstDoNotCareAboutZeroSecondDoes(0.0);
+ if (1 / result6.resultA !== Infinity) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(0.0), resultA = " + result6.resultA);
+ }
+ if (1 / result6.resultB !== Infinity) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(0.0), resultB = " + result6.resultB);
+ }
+ var result7 = firstCareAboutZeroSecondDoesNot(0.1);
+ if (1 / result7.resultA !== 1) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(0.1), resultA = " + result7.resultA);
+ }
+ if (1 / result7.resultB !== 1) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(0.1), resultB = " + result7.resultB);
+ }
+ var result8 = firstDoNotCareAboutZeroSecondDoes(0.1);
+ if (1 / result8.resultA !== 1) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(0.1), resultA = " + result8.resultA);
+ }
+ if (1 / result8.resultB !== 1) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(0.1), resultB = " + result8.resultB);
+ }
+ }
+}
+verifyNegativeZeroIsPreserved();
diff --git a/Source/JavaScriptCore/tests/stress/math-ceil-basics.js b/Source/JavaScriptCore/tests/stress/math-ceil-basics.js
new file mode 100644
index 0000000..22d9f26
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/math-ceil-basics.js
@@ -0,0 +1,257 @@
+
+function mathCeilOnIntegers(value)
+{
+ return Math.ceil(value);
+}
+noInline(mathCeilOnIntegers);
+
+function mathCeilOnDoubles(value)
+{
+ return Math.ceil(value);
+}
+noInline(mathCeilOnDoubles);
+
+function mathCeilOnBooleans(value)
+{
+ return Math.ceil(value);
+}
+noInline(mathCeilOnBooleans);
+
+// The trivial cases first.
+for (var i = 1; i < 1e4; ++i) {
+ var ceiledValue = mathCeilOnIntegers(i);
+ if (ceiledValue !== i)
+ throw new Error("mathCeilOnIntegers(" + i + ") = " + ceiledValue);
+
+ var ceiledValue = mathCeilOnIntegers(-i);
+ if (ceiledValue !== -i)
+ throw new Error("mathCeilOnIntegers(" + -i + ") = " + ceiledValue);
+
+ var doubleLow = i + 0.4;
+ var ceiledValue = mathCeilOnDoubles(doubleLow);
+ if (ceiledValue !== i + 1.0)
+ throw new Error("mathCeilOnDoubles(" + doubleLow + ") = " + ceiledValue);
+
+ var doubleHigh = i + 0.6;
+ var ceiledValue = mathCeilOnDoubles(doubleHigh);
+ if (ceiledValue !== i + 1)
+ throw new Error("mathCeilOnDoubles(" + doubleHigh + ") = " + ceiledValue);
+
+ var doubleMid = i + 0.5;
+ var ceiledValue = mathCeilOnDoubles(doubleMid);
+ if (ceiledValue !== i + 1)
+ throw new Error("mathCeilOnDoubles(" + doubleMid + ") = " + ceiledValue);
+
+ var ceiledValue = mathCeilOnDoubles(-0.6);
+ if (ceiledValue !== -0.0)
+ throw new Error("mathCeilOnDoubles(-0.6) = " + ceiledValue);
+}
+
+// Some more interesting cases, some of them well OSR exit when the return value is zero.
+for (var i = 0; i < 1e4; ++i) {
+ var ceiledValue = mathCeilOnIntegers(i);
+ if (ceiledValue !== i)
+ throw new Error("mathCeilOnIntegers(" + i + ") = " + ceiledValue);
+
+ var ceiledValue = mathCeilOnIntegers(-i);
+ if (ceiledValue !== -i)
+ throw new Error("mathCeilOnIntegers(-" + i + ") = " + ceiledValue);
+
+ var ceiledValue = mathCeilOnDoubles(-0.4);
+ if (ceiledValue !== 0)
+ throw new Error("mathCeilOnDoubles(-0.4) = " + ceiledValue);
+
+ var ceiledValue = mathCeilOnDoubles(-0.5);
+ if (ceiledValue !== 0)
+ throw new Error("mathCeilOnDoubles(-0.5) = " + ceiledValue);
+
+ var ceiledValue = mathCeilOnDoubles(-0);
+ if (!(ceiledValue === 0 && (1/ceiledValue) === -Infinity))
+ throw new Error("mathCeilOnDoubles(-0) = " + ceiledValue);
+
+ var ceiledValue = mathCeilOnDoubles(NaN);
+ if (ceiledValue === ceiledValue)
+ throw new Error("mathCeilOnDoubles(NaN) = " + ceiledValue);
+
+ var ceiledValue = mathCeilOnDoubles(Number.POSITIVE_INFINITY);
+ if (ceiledValue !== Number.POSITIVE_INFINITY)
+ throw new Error("mathCeilOnDoubles(Number.POSITIVE_INFINITY) = " + ceiledValue);
+
+ var ceiledValue = mathCeilOnDoubles(Number.NEGATIVE_INFINITY);
+ if (ceiledValue !== Number.NEGATIVE_INFINITY)
+ throw new Error("mathCeilOnDoubles(Number.NEGATIVE_INFINITY) = " + ceiledValue);
+
+ var boolean = !!(i % 2);
+ var ceiledBoolean = mathCeilOnBooleans(boolean);
+ if (ceiledBoolean != boolean)
+ throw new Error("mathCeilOnDoubles(" + boolean + ") = " + ceiledBoolean);
+}
+
+function uselessMathCeil(value)
+{
+ return Math.ceil(value|0);
+}
+noInline(uselessMathCeil);
+
+for (var i = 0; i < 1e4; ++i) {
+ var ceiledValue = uselessMathCeil(i);
+ if (ceiledValue !== i)
+ throw new Error("uselessMathCeil(" + i + ") = " + ceiledValue);
+
+ var doubleLow = i + 0.4;
+ var ceiledValue = uselessMathCeil(doubleLow);
+ if (ceiledValue !== i)
+ throw new Error("uselessMathCeil(" + doubleLow + ") = " + ceiledValue);
+
+ var doubleHigh = i + 0.6;
+ var ceiledValue = uselessMathCeil(doubleHigh);
+ if (ceiledValue !== i)
+ throw new Error("uselessMathCeil(" + doubleHigh + ") = " + ceiledValue);
+
+ var doubleMid = i + 0.5;
+ var ceiledValue = uselessMathCeil(doubleMid);
+ if (ceiledValue !== i)
+ throw new Error("uselessMathCeil(" + doubleMid + ") = " + ceiledValue);
+
+ var ceiledValue = uselessMathCeil(-0.4);
+ if (ceiledValue !== 0)
+ throw new Error("uselessMathCeil(-0.4) = " + ceiledValue);
+
+ var ceiledValue = uselessMathCeil(-0.5);
+ if (ceiledValue !== 0)
+ throw new Error("uselessMathCeil(-0.5) = " + ceiledValue);
+
+ var ceiledValue = uselessMathCeil(-0.6);
+ if (ceiledValue !== 0)
+ throw new Error("uselessMathCeil(-0.6) = " + ceiledValue);
+}
+
+function mathCeilWithOverflow(value)
+{
+ return Math.ceil(value);
+}
+noInline(mathCeilWithOverflow);
+
+for (var i = 0; i < 1e4; ++i) {
+ var bigValue = 1000000000000;
+ var ceiledValue = mathCeilWithOverflow(bigValue);
+ if (ceiledValue !== bigValue)
+ throw new Error("mathCeilWithOverflow(" + bigValue + ") = " + ceiledValue);
+}
+
+function mathCeilConsumedAsDouble(value)
+{
+ return Math.ceil(value) * 0.5;
+}
+noInline(mathCeilConsumedAsDouble);
+
+for (var i = 0; i < 1e4; ++i) {
+ var doubleValue = i + 0.1;
+ var ceiledValue = mathCeilConsumedAsDouble(doubleValue);
+ if (ceiledValue !== ((i + 1) * 0.5))
+ throw new Error("mathCeilConsumedAsDouble(" + doubleValue + ") = " + ceiledValue);
+
+ var doubleValue = i + 0.6;
+ var ceiledValue = mathCeilConsumedAsDouble(doubleValue);
+ if (ceiledValue !== ((i + 1) * 0.5))
+ throw new Error("mathCeilConsumedAsDouble(" + doubleValue + ") = " + ceiledValue);
+
+}
+
+function mathCeilDoesNotCareAboutMinusZero(value)
+{
+ return Math.ceil(value)|0;
+}
+noInline(mathCeilDoesNotCareAboutMinusZero);
+
+for (var i = 0; i < 1e4; ++i) {
+ var doubleMid = i + 0.5;
+ var ceiledValue = mathCeilDoesNotCareAboutMinusZero(doubleMid);
+ if (ceiledValue !== i + 1)
+ throw new Error("mathCeilDoesNotCareAboutMinusZero(" + doubleMid + ") = " + ceiledValue);
+}
+
+
+// *** Function arguments. ***
+function mathCeilNoArguments()
+{
+ return Math.ceil();
+}
+noInline(mathCeilNoArguments);
+
+function mathCeilTooManyArguments(a, b, c)
+{
+ return Math.ceil(a, b, c);
+}
+noInline(mathCeilTooManyArguments);
+
+for (var i = 0; i < 1e4; ++i) {
+ var value = mathCeilNoArguments();
+ if (value === value)
+ throw new Error("mathCeilNoArguments() = " + value);
+
+ var value = mathCeilTooManyArguments(2.1, 3, 5);
+ if (value !== 3)
+ throw new Error("mathCeilTooManyArguments() = " + value);
+}
+
+
+// *** Constant as arguments. ***
+function testMathCeilOnConstants()
+{
+ var value = Math.ceil(0);
+ if (value !== 0)
+ throw new Error("Math.ceil(0) = " + value);
+ var value = Math.ceil(-0);
+ if (!(value === 0 && (1/value) === -Infinity))
+ throw new Error("Math.ceil(-0) = " + value);
+ var value = Math.ceil(1);
+ if (value !== 1)
+ throw new Error("Math.ceil(1) = " + value);
+ var value = Math.ceil(-1);
+ if (value !== -1)
+ throw new Error("Math.ceil(-1) = " + value);
+ var value = Math.ceil(42);
+ if (value !== 42)
+ throw new Error("Math.ceil(42) = " + value);
+ var value = Math.ceil(-42.2);
+ if (value !== -42)
+ throw new Error("Math.ceil(-42.2) = " + value);
+ var value = Math.ceil(NaN);
+ if (value === value)
+ throw new Error("Math.ceil(NaN) = " + value);
+ var value = Math.ceil(Number.POSITIVE_INFINITI);
+ if (value === value)
+ throw new Error("Math.ceil(Number.POSITIVE_INFINITI) = " + value);
+ var value = Math.ceil(Number.NEGATIVE_INFINITI);
+ if (value === value)
+ throw new Error("Math.ceil(Number.NEGATIVE_INFINITI) = " + value);
+ var value = Math.ceil(Math.E);
+ if (value !== 3)
+ throw new Error("Math.ceil(Math.E) = " + value);
+}
+noInline(testMathCeilOnConstants);
+
+for (var i = 0; i < 1e4; ++i) {
+ testMathCeilOnConstants();
+}
+
+
+// *** Struct transition. ***
+function mathCeilStructTransition(value)
+{
+ return Math.ceil(value);
+}
+noInline(mathCeilStructTransition);
+
+for (var i = 0; i < 1e4; ++i) {
+ var value = mathCeilStructTransition(42.5);
+ if (value !== 43)
+ throw new Error("mathCeilStructTransition(42.5) = " + value);
+}
+
+Math.ceil = function() { return arguments[0] + 5; }
+
+var value = mathCeilStructTransition(42);
+if (value !== 47)
+ throw new Error("mathCeilStructTransition(42) after transition = " + value);
diff --git a/Source/JavaScriptCore/tests/stress/math-floor-arith-rounding-mode.js b/Source/JavaScriptCore/tests/stress/math-floor-arith-rounding-mode.js
new file mode 100644
index 0000000..a30ed89
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/math-floor-arith-rounding-mode.js
@@ -0,0 +1,85 @@
+function firstCareAboutZeroSecondDoesNot(a) {
+ var resultA = Math.floor(a);
+ var resultB = Math.floor(a)|0;
+ return { resultA:resultA, resultB:resultB };
+}
+noInline(firstCareAboutZeroSecondDoesNot);
+
+function firstDoNotCareAboutZeroSecondDoes(a) {
+ var resultA = Math.floor(a)|0;
+ var resultB = Math.floor(a);
+ return { resultA:resultA, resultB:resultB };
+}
+noInline(firstDoNotCareAboutZeroSecondDoes);
+
+// Warmup with doubles, but nothing that would floor to -0 to ensure we never
+// see a double as result. The result must be integers, the input is kept to small values.
+function warmup() {
+ for (var i = 0; i < 1e4; ++i) {
+ firstCareAboutZeroSecondDoesNot(42.6 + i);
+ firstDoNotCareAboutZeroSecondDoes(42.4 + i);
+ }
+}
+warmup();
+
+function verifyNegativeZeroIsPreserved() {
+ for (var i = 0; i < 1e4; ++i) {
+ var result1 = firstCareAboutZeroSecondDoesNot(-0.1);
+ if (1 / result1.resultA !== -1) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(-0.1), resultA = " + result1.resultA);
+ }
+ if (1 / result1.resultB !== -1) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(-0.1), resultB = " + result1.resultB);
+ }
+ var result2 = firstDoNotCareAboutZeroSecondDoes(-0.1);
+ if (1 / result2.resultA !== -1) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(-0.1), resultA = " + result1.resultA);
+ }
+ if (1 / result2.resultB !== -1) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(-0.1), resultB = " + result1.resultB);
+ }
+ var result3 = firstCareAboutZeroSecondDoesNot(-0.0);
+ if (1 / result3.resultA !== -Infinity) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(-0.0), resultA = " + result3.resultA);
+ }
+ if (1 / result3.resultB !== Infinity) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(-0.0), resultB = " + result3.resultB);
+ }
+ var result4 = firstDoNotCareAboutZeroSecondDoes(-0.0);
+ if (1 / result4.resultA !== Infinity) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(-0.0), resultA = " + result4.resultA);
+ }
+ if (1 / result4.resultB !== -Infinity) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(-0.0), resultB = " + result4.resultB);
+ }
+ var result5 = firstCareAboutZeroSecondDoesNot(0.0);
+ if (1 / result5.resultA !== Infinity) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(0.0), resultA = " + result5.resultA);
+ }
+ if (1 / result5.resultB !== Infinity) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(0.0), resultB = " + result5.resultB);
+ }
+ var result6 = firstDoNotCareAboutZeroSecondDoes(0.0);
+ if (1 / result6.resultA !== Infinity) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(0.0), resultA = " + result6.resultA);
+ }
+ if (1 / result6.resultB !== Infinity) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(0.0), resultB = " + result6.resultB);
+ }
+ var result7 = firstCareAboutZeroSecondDoesNot(0.1);
+ if (1 / result7.resultA !== Infinity) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(0.1), resultA = " + result7.resultA);
+ }
+ if (1 / result7.resultB !== Infinity) {
+ throw new Error("Failed firstCareAboutZeroSecondDoesNot(0.1), resultB = " + result7.resultB);
+ }
+ var result8 = firstDoNotCareAboutZeroSecondDoes(0.1);
+ if (1 / result8.resultA !== Infinity) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(0.1), resultA = " + result8.resultA);
+ }
+ if (1 / result8.resultB !== Infinity) {
+ throw new Error("Failed firstDoNotCareAboutZeroSecondDoes(0.1), resultB = " + result8.resultB);
+ }
+ }
+}
+verifyNegativeZeroIsPreserved();
diff --git a/Source/JavaScriptCore/tests/stress/math-floor-basics.js b/Source/JavaScriptCore/tests/stress/math-floor-basics.js
new file mode 100644
index 0000000..7448eb9
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/math-floor-basics.js
@@ -0,0 +1,257 @@
+
+function mathFloorOnIntegers(value)
+{
+ return Math.floor(value);
+}
+noInline(mathFloorOnIntegers);
+
+function mathFloorOnDoubles(value)
+{
+ return Math.floor(value);
+}
+noInline(mathFloorOnDoubles);
+
+function mathFloorOnBooleans(value)
+{
+ return Math.floor(value);
+}
+noInline(mathFloorOnBooleans);
+
+// The trivial cases first.
+for (var i = 1; i < 1e4; ++i) {
+ var flooredValue = mathFloorOnIntegers(i);
+ if (flooredValue !== i)
+ throw new Error("mathFloorOnIntegers(" + i + ") = " + flooredValue);
+
+ var flooredValue = mathFloorOnIntegers(-i);
+ if (flooredValue !== -i)
+ throw new Error("mathFloorOnIntegers(" + -i + ") = " + flooredValue);
+
+ var doubleLow = i + 0.4;
+ var flooredValue = mathFloorOnDoubles(doubleLow);
+ if (flooredValue !== i)
+ throw new Error("mathFloorOnDoubles(" + doubleLow + ") = " + flooredValue);
+
+ var doubleHigh = i + 0.6;
+ var flooredValue = mathFloorOnDoubles(doubleHigh);
+ if (flooredValue !== i)
+ throw new Error("mathFloorOnDoubles(" + doubleHigh + ") = " + flooredValue);
+
+ var doubleMid = i + 0.5;
+ var flooredValue = mathFloorOnDoubles(doubleMid);
+ if (flooredValue !== i)
+ throw new Error("mathFloorOnDoubles(" + doubleMid + ") = " + flooredValue);
+
+ var flooredValue = mathFloorOnDoubles(-0.6);
+ if (flooredValue !== -1.0)
+ throw new Error("mathFloorOnDoubles(-0.6) = " + flooredValue);
+}
+
+// Some more interesting cases, some of them well OSR exit when the return value is zero.
+for (var i = 0; i < 1e4; ++i) {
+ var flooredValue = mathFloorOnIntegers(i);
+ if (flooredValue !== i)
+ throw new Error("mathFloorOnIntegers(" + i + ") = " + flooredValue);
+
+ var flooredValue = mathFloorOnIntegers(-i);
+ if (flooredValue !== -i)
+ throw new Error("mathFloorOnIntegers(-" + i + ") = " + flooredValue);
+
+ var flooredValue = mathFloorOnDoubles(-0.4);
+ if (flooredValue !== -1.00)
+ throw new Error("mathFloorOnDoubles(-0.4) = " + flooredValue);
+
+ var flooredValue = mathFloorOnDoubles(-0.5);
+ if (flooredValue !== -1.0)
+ throw new Error("mathFloorOnDoubles(-0.5) = " + flooredValue);
+
+ var flooredValue = mathFloorOnDoubles(-0);
+ if (!(flooredValue === 0 && (1/flooredValue) === -Infinity))
+ throw new Error("mathFloorOnDoubles(-0) = " + flooredValue);
+
+ var flooredValue = mathFloorOnDoubles(NaN);
+ if (flooredValue === flooredValue)
+ throw new Error("mathFloorOnDoubles(NaN) = " + flooredValue);
+
+ var flooredValue = mathFloorOnDoubles(Number.POSITIVE_INFINITY);
+ if (flooredValue !== Number.POSITIVE_INFINITY)
+ throw new Error("mathFloorOnDoubles(Number.POSITIVE_INFINITY) = " + flooredValue);
+
+ var flooredValue = mathFloorOnDoubles(Number.NEGATIVE_INFINITY);
+ if (flooredValue !== Number.NEGATIVE_INFINITY)
+ throw new Error("mathFloorOnDoubles(Number.NEGATIVE_INFINITY) = " + flooredValue);
+
+ var boolean = !!(i % 2);
+ var flooredBoolean = mathFloorOnBooleans(boolean);
+ if (flooredBoolean != boolean)
+ throw new Error("mathFloorOnDoubles(" + boolean + ") = " + flooredBoolean);
+}
+
+function uselessMathFloor(value)
+{
+ return Math.floor(value|0);
+}
+noInline(uselessMathFloor);
+
+for (var i = 0; i < 1e4; ++i) {
+ var flooredValue = uselessMathFloor(i);
+ if (flooredValue !== i)
+ throw new Error("uselessMathFloor(" + i + ") = " + flooredValue);
+
+ var doubleLow = i + 0.4;
+ var flooredValue = uselessMathFloor(doubleLow);
+ if (flooredValue !== i)
+ throw new Error("uselessMathFloor(" + doubleLow + ") = " + flooredValue);
+
+ var doubleHigh = i + 0.6;
+ var flooredValue = uselessMathFloor(doubleHigh);
+ if (flooredValue !== i)
+ throw new Error("uselessMathFloor(" + doubleHigh + ") = " + flooredValue);
+
+ var doubleMid = i + 0.5;
+ var flooredValue = uselessMathFloor(doubleMid);
+ if (flooredValue !== i)
+ throw new Error("uselessMathFloor(" + doubleMid + ") = " + flooredValue);
+
+ var flooredValue = uselessMathFloor(-0.4);
+ if (flooredValue !== 0)
+ throw new Error("uselessMathFloor(-0.4) = " + flooredValue);
+
+ var flooredValue = uselessMathFloor(-0.5);
+ if (flooredValue !== 0)
+ throw new Error("uselessMathFloor(-0.5) = " + flooredValue);
+
+ var flooredValue = uselessMathFloor(-0.6);
+ if (flooredValue !== 0)
+ throw new Error("uselessMathFloor(-0.6) = " + flooredValue);
+}
+
+function mathFloorWithOverflow(value)
+{
+ return Math.floor(value);
+}
+noInline(mathFloorWithOverflow);
+
+for (var i = 0; i < 1e4; ++i) {
+ var bigValue = 1000000000000;
+ var flooredValue = mathFloorWithOverflow(bigValue);
+ if (flooredValue !== bigValue)
+ throw new Error("mathFloorWithOverflow(" + bigValue + ") = " + flooredValue);
+}
+
+function mathFloorConsumedAsDouble(value)
+{
+ return Math.floor(value) * 0.5;
+}
+noInline(mathFloorConsumedAsDouble);
+
+for (var i = 0; i < 1e4; ++i) {
+ var doubleValue = i + 0.1;
+ var flooredValue = mathFloorConsumedAsDouble(doubleValue);
+ if (flooredValue !== (i * 0.5))
+ throw new Error("mathFloorConsumedAsDouble(" + doubleValue + ") = " + flooredValue);
+
+ var doubleValue = i + 0.6;
+ var flooredValue = mathFloorConsumedAsDouble(doubleValue);
+ if (flooredValue !== (i * 0.5))
+ throw new Error("mathFloorConsumedAsDouble(" + doubleValue + ") = " + flooredValue);
+
+}
+
+function mathFloorDoesNotCareAboutMinusZero(value)
+{
+ return Math.floor(value)|0;
+}
+noInline(mathFloorDoesNotCareAboutMinusZero);
+
+for (var i = 0; i < 1e4; ++i) {
+ var doubleMid = i + 0.5;
+ var flooredValue = mathFloorDoesNotCareAboutMinusZero(doubleMid);
+ if (flooredValue !== i)
+ throw new Error("mathFloorDoesNotCareAboutMinusZero(" + doubleMid + ") = " + flooredValue);
+}
+
+
+// *** Function arguments. ***
+function mathFloorNoArguments()
+{
+ return Math.floor();
+}
+noInline(mathFloorNoArguments);
+
+function mathFloorTooManyArguments(a, b, c)
+{
+ return Math.floor(a, b, c);
+}
+noInline(mathFloorTooManyArguments);
+
+for (var i = 0; i < 1e4; ++i) {
+ var value = mathFloorNoArguments();
+ if (value === value)
+ throw new Error("mathFloorNoArguments() = " + value);
+
+ var value = mathFloorTooManyArguments(2.1, 3, 5);
+ if (value !== 2)
+ throw new Error("mathFloorTooManyArguments() = " + value);
+}
+
+
+// *** Constant as arguments. ***
+function testMathFloorOnConstants()
+{
+ var value = Math.floor(0);
+ if (value !== 0)
+ throw new Error("Math.floor(0) = " + value);
+ var value = Math.floor(-0);
+ if (!(value === 0 && (1/value) === -Infinity))
+ throw new Error("Math.floor(-0) = " + value);
+ var value = Math.floor(1);
+ if (value !== 1)
+ throw new Error("Math.floor(1) = " + value);
+ var value = Math.floor(-1);
+ if (value !== -1)
+ throw new Error("Math.floor(-1) = " + value);
+ var value = Math.floor(42);
+ if (value !== 42)
+ throw new Error("Math.floor(42) = " + value);
+ var value = Math.floor(-42.2);
+ if (value !== -43)
+ throw new Error("Math.floor(-42.2) = " + value);
+ var value = Math.floor(NaN);
+ if (value === value)
+ throw new Error("Math.floor(NaN) = " + value);
+ var value = Math.floor(Number.POSITIVE_INFINITI);
+ if (value === value)
+ throw new Error("Math.floor(Number.POSITIVE_INFINITI) = " + value);
+ var value = Math.floor(Number.NEGATIVE_INFINITI);
+ if (value === value)
+ throw new Error("Math.floor(Number.NEGATIVE_INFINITI) = " + value);
+ var value = Math.floor(Math.E);
+ if (value !== 2)
+ throw new Error("Math.floor(Math.E) = " + value);
+}
+noInline(testMathFloorOnConstants);
+
+for (var i = 0; i < 1e4; ++i) {
+ testMathFloorOnConstants();
+}
+
+
+// *** Struct transition. ***
+function mathFloorStructTransition(value)
+{
+ return Math.floor(value);
+}
+noInline(mathFloorStructTransition);
+
+for (var i = 0; i < 1e4; ++i) {
+ var value = mathFloorStructTransition(42.5);
+ if (value !== 42)
+ throw new Error("mathFloorStructTransition(42.5) = " + value);
+}
+
+Math.floor = function() { return arguments[0] + 5; }
+
+var value = mathFloorStructTransition(42);
+if (value !== 47)
+ throw new Error("mathFloorStructTransition(42) after transition = " + value);
diff --git a/Source/JavaScriptCore/tests/stress/math-round-should-not-use-truncate.js b/Source/JavaScriptCore/tests/stress/math-round-should-not-use-truncate.js
new file mode 100644
index 0000000..bae0782
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/math-round-should-not-use-truncate.js
@@ -0,0 +1,12 @@
+function mathRoundDoesNotCareAboutMinusZero(value)
+{
+ return Math.round(value);
+}
+noInline(mathRoundDoesNotCareAboutMinusZero);
+
+for (var i = 0; i < 1e4; ++i) {
+ var doubleMid = -9901 - 0.6;
+ var roundedValue = mathRoundDoesNotCareAboutMinusZero(doubleMid);
+ if (roundedValue !== -9902)
+ throw "mathRoundDoesNotCareAboutMinusZero(" + doubleMid + ") = " + roundedValue;
+}
diff --git a/Source/JavaScriptCore/tests/stress/math-rounding-infinity.js b/Source/JavaScriptCore/tests/stress/math-rounding-infinity.js
new file mode 100644
index 0000000..21e021e
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/math-rounding-infinity.js
@@ -0,0 +1,31 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function testRound(value)
+{
+ return Math.round(value);
+}
+noInline(testRound);
+
+function testFloor(value)
+{
+ return Math.floor(value);
+}
+noInline(testFloor);
+
+function testCeil(value)
+{
+ return Math.ceil(value);
+}
+noInline(testCeil);
+
+for (var i = 0; i < 1e4; ++i) {
+ shouldBe(testRound(Infinity), Infinity);
+ shouldBe(testRound(-Infinity), -Infinity);
+ shouldBe(testFloor(Infinity), Infinity);
+ shouldBe(testFloor(-Infinity), -Infinity);
+ shouldBe(testCeil(Infinity), Infinity);
+ shouldBe(testCeil(-Infinity), -Infinity);
+}
diff --git a/Source/JavaScriptCore/tests/stress/math-rounding-nan.js b/Source/JavaScriptCore/tests/stress/math-rounding-nan.js
new file mode 100644
index 0000000..ecccdbc
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/math-rounding-nan.js
@@ -0,0 +1,28 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function testRound(value)
+{
+ return Math.round(value);
+}
+noInline(testRound);
+
+function testFloor(value)
+{
+ return Math.floor(value);
+}
+noInline(testFloor);
+
+function testCeil(value)
+{
+ return Math.ceil(value);
+}
+noInline(testCeil);
+
+for (var i = 0; i < 1e4; ++i) {
+ shouldBe(Number.isNaN(testRound(NaN)), true);
+ shouldBe(Number.isNaN(testFloor(NaN)), true);
+ shouldBe(Number.isNaN(testCeil(NaN)), true);
+}
diff --git a/Source/JavaScriptCore/tests/stress/math-rounding-negative-zero.js b/Source/JavaScriptCore/tests/stress/math-rounding-negative-zero.js
new file mode 100644
index 0000000..abae278
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/math-rounding-negative-zero.js
@@ -0,0 +1,70 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function testRound(value)
+{
+ return Math.round(value);
+}
+noInline(testRound);
+
+for (var i = 0; i < 1e4; ++i) {
+ shouldBe(1 / testRound(-0.4), -Infinity);
+ shouldBe(1 / testRound(-0.5), -Infinity);
+ shouldBe(1 / testRound(-0.6), -1.0);
+ shouldBe(1 / testRound(-0.0), -Infinity);
+ shouldBe(1 / testRound(0.1), Infinity);
+}
+
+function testFloor(value)
+{
+ return Math.floor(value);
+}
+noInline(testFloor);
+
+for (var i = 0; i < 1e4; ++i) {
+ shouldBe(1 / testFloor(-0.0), -Infinity);
+}
+
+function testCeil(value)
+{
+ return Math.ceil(value);
+}
+noInline(testCeil);
+
+for (var i = 0; i < 1e4; ++i) {
+ shouldBe(1 / testCeil(-0.0), -Infinity);
+ shouldBe(1 / testCeil(-0.9), -Infinity);
+}
+
+function testRoundNonNegativeZero(value)
+{
+ return Math.round(value) | 0;
+}
+noInline(testRoundNonNegativeZero);
+
+for (var i = 0; i < 1e4; ++i) {
+ shouldBe(testRoundNonNegativeZero(0.4), 0);
+ shouldBe(testRoundNonNegativeZero(0.5), 1);
+ shouldBe(testRoundNonNegativeZero(0.6), 1);
+ shouldBe(testRoundNonNegativeZero(0.0), 0);
+ shouldBe(testRoundNonNegativeZero(0.1), 0);
+}
+shouldBe(1 / testRoundNonNegativeZero(-0.4), Infinity);
+
+function testRoundNonNegativeZero2(value)
+{
+ return Math.round(value) | 0;
+}
+noInline(testRoundNonNegativeZero2);
+
+for (var i = 0; i < 1e4; ++i) {
+ shouldBe(1 / testRoundNonNegativeZero2(-0.4), Infinity);
+ shouldBe(1 / testRoundNonNegativeZero2(-0.5), Infinity);
+ shouldBe(1 / testRoundNonNegativeZero2(-0.6), -1.0);
+ shouldBe(1 / testRoundNonNegativeZero2(-0.0), Infinity);
+ shouldBe(1 / testRoundNonNegativeZero2(0.1), Infinity);
+}
+
+