[DFG][FTL][B3] Support floor and ceil
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 1 Mar 2016 02:30:46 +0000 (02:30 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 1 Mar 2016 02:30:46 +0000 (02:30 +0000)
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: https://svn.webkit.org/repository/webkit/trunk@197380 268f45cc-cd09-0410-ab3c-d52691b4dbfc

54 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/assembler/MacroAssemblerARM.h
Source/JavaScriptCore/assembler/MacroAssemblerARM64.h
Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h
Source/JavaScriptCore/assembler/MacroAssemblerMIPS.h
Source/JavaScriptCore/assembler/MacroAssemblerSH4.h
Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h
Source/JavaScriptCore/b3/B3ConstDoubleValue.cpp
Source/JavaScriptCore/b3/B3ConstDoubleValue.h
Source/JavaScriptCore/b3/B3ConstFloatValue.cpp
Source/JavaScriptCore/b3/B3ConstFloatValue.h
Source/JavaScriptCore/b3/B3LowerMacrosAfterOptimizations.cpp
Source/JavaScriptCore/b3/B3LowerToAir.cpp
Source/JavaScriptCore/b3/B3Opcode.cpp
Source/JavaScriptCore/b3/B3Opcode.h
Source/JavaScriptCore/b3/B3ReduceDoubleToFloat.cpp
Source/JavaScriptCore/b3/B3ReduceStrength.cpp
Source/JavaScriptCore/b3/B3Validate.cpp
Source/JavaScriptCore/b3/B3Value.cpp
Source/JavaScriptCore/b3/B3Value.h
Source/JavaScriptCore/b3/air/AirFixPartialRegisterStalls.cpp
Source/JavaScriptCore/b3/air/AirOpcode.opcodes
Source/JavaScriptCore/b3/testb3.cpp
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGArithMode.cpp
Source/JavaScriptCore/dfg/DFGArithMode.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGGraph.h
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/ftl/FTLOutput.h
Source/JavaScriptCore/jit/ThunkGenerators.cpp
Source/JavaScriptCore/tests/stress/math-ceil-arith-rounding-mode.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/math-ceil-basics.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/math-floor-arith-rounding-mode.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/math-floor-basics.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/math-round-should-not-use-truncate.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/math-rounding-infinity.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/math-rounding-nan.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/math-rounding-negative-zero.js [new file with mode: 0644]
Websites/webkit.org/ChangeLog
Websites/webkit.org/docs/b3/intermediate-representation.html

index bff059a..7c1392c 100644 (file)
@@ -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
index c58cf8a..97d210e 100644 (file)
@@ -1139,7 +1139,7 @@ public:
         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 @@ public:
 
     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();
     }
 
index a590b44..5977613 100644 (file)
@@ -1343,7 +1343,7 @@ public:
     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 @@ public:
         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
index c075852..fc94b88 100644 (file)
@@ -870,7 +870,7 @@ public:
     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 @@ public:
 
     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();
     }
 
index 9b37564..7c4bd08 100644 (file)
@@ -649,7 +649,13 @@ public:
 
     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 @@ public:
 #endif
     }
     static bool supportsFloatingPointAbs() { return false; }
-    static bool supportsFloatingPointCeil() { return false; }
+    static bool supportsFloatingPointRounding() { return false; }
 
     // Stack manipulation operations:
     //
index 0f46f8f..7575609 100644 (file)
@@ -1091,7 +1091,7 @@ public:
     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 @@ public:
 
     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();
     }
 
index 08ea575..8085530 100644 (file)
@@ -696,6 +696,26 @@ public:
         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 @@ public:
         return X86Assembler::maxJumpReplacementSize();
     }
 
-    static bool supportsFloatingPointCeil()
+    static bool supportsFloatingPointRounding()
     {
         if (s_sse4_1CheckState == CPUIDCheckState::NotChecked) {
             int flags = 0;
index f9ce296..46183f3 100644 (file)
@@ -97,6 +97,11 @@ Value* ConstDoubleValue::ceilConstant(Procedure& proc) const
     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));
index b2cb246..e8ea539 100644 (file)
@@ -52,6 +52,7 @@ public:
     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;
index 44f6403..7c1b395 100644 (file)
@@ -97,6 +97,11 @@ Value* ConstFloatValue::ceilConstant(Procedure& proc) const
     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)));
index 440bd61..36673ca 100644 (file)
@@ -51,6 +51,7 @@ public:
     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;
index 5b2d90b..e03d5f4 100644 (file)
@@ -88,13 +88,12 @@ private:
                 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 @@ private:
                 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;
index 7175de4..1cd557b 100644 (file)
@@ -1827,6 +1827,11 @@ private:
             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;
index 7c925ad..ae0b948 100644 (file)
@@ -161,6 +161,9 @@ void printInternal(PrintStream& out, Opcode opcode)
     case Ceil:
         out.print("Ceil");
         return;
+    case Floor:
+        out.print("Floor");
+        return;
     case Sqrt:
         out.print("Sqrt");
         return;
index 9d9bf18..76b91b6 100644 (file)
@@ -97,6 +97,7 @@ enum Opcode : int16_t {
     // Floating point math.
     Abs,
     Ceil,
+    Floor,
     Sqrt,
 
     // Casts and such.
index cb41f74..d8e43b4 100644 (file)
@@ -50,6 +50,7 @@ void attemptSimplification(Value* candidate)
         break;
     case Abs:
     case Ceil:
+    case Floor:
     case Sqrt:
         if (candidate->child(0)->opcode() == FloatToDouble) {
             candidate->child(0) = candidate->child(0)->child(0);
index 83cb21f..4363b9e 100644 (file)
@@ -510,8 +510,7 @@ private:
             //    -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 @@ private:
             // 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 @@ private:
                 // 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 @@ private:
                 // 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 @@ private:
                     // 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 @@ private:
                             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 @@ private:
                                     m_index, m_value->origin(), 31))));
                     break;
                 }
-
-                m_changed = true;
                 break;
             }
             break;
@@ -746,7 +740,7 @@ private:
                     // 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 @@ private:
                         break;
                     }
 
-                    m_value->replaceWithIdentity(
+                    replaceWithIdentity(
                         m_insertionSet.insert<Value>(
                             m_index, Sub, m_value->origin(),
                             m_value->child(0),
@@ -794,8 +788,6 @@ private:
                                 m_value->child(1))));
                     break;
                 }
-                
-                m_changed = true;
                 break;
             }
             
@@ -826,16 +818,14 @@ private:
             // 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 @@ private:
             // 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 @@ private:
                 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 @@ private:
             // 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 @@ private:
             // 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 @@ private:
             // 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 @@ private:
             // 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 @@ private:
                     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 @@ private:
                 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;
 
-            // 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));
+        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: Floor(roundedValue)
+            // Into this: roundedValue
+            if (m_value->child(0)->isRounded()) {
+                replaceWithIdentity(m_value->child(0));
                 break;
             }
             break;
@@ -1171,8 +1161,7 @@ private:
             // 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 @@ private:
             // 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 @@ private:
             // 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 @@ private:
             // 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 @@ private:
             // 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 @@ private:
             // 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 @@ private:
             // 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 @@ private:
                 // 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 @@ private:
             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 @@ private:
                 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 @@ private:
                     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 @@ private:
         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;
         }
 
index 47a494d..25f55c6 100644 (file)
@@ -241,6 +241,7 @@ public:
                 break;
             case Abs:
             case Ceil:
+            case Floor:
             case Sqrt:
                 VALIDATE(value->numChildren() == 1, ("At ", *value));
                 VALIDATE(isFloat(value->child(0)->type()), ("At ", *value));
index c028dad..ee23808 100644 (file)
@@ -287,6 +287,11 @@ Value* Value::ceilConstant(Procedure&) const
     return nullptr;
 }
 
+Value* Value::floorConstant(Procedure&) const
+{
+    return nullptr;
+}
+
 Value* Value::sqrtConstant(Procedure&) const
 {
     return nullptr;
@@ -356,6 +361,30 @@ Value* Value::invertedCompare(Procedure& proc) const
     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 @@ Effects Value::effects() const
     case Clz:
     case Abs:
     case Ceil:
+    case Floor:
     case Sqrt:
     case BitwiseCast:
     case SExt8:
@@ -514,6 +544,7 @@ ValueKey Value::key() const
     case Identity:
     case Abs:
     case Ceil:
+    case Floor:
     case Sqrt:
     case SExt8:
     case SExt16:
@@ -629,6 +660,7 @@ Type Value::typeFor(Opcode opcode, Value* firstChild, Value* secondChild)
     case Clz:
     case Abs:
     case Ceil:
+    case Floor:
     case Sqrt:
     case CheckAdd:
     case CheckSub:
index cbbf63c..981f007 100644 (file)
@@ -137,6 +137,7 @@ public:
     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 @@ public:
 
     bool isNegativeZero() const;
 
+    bool isRounded() const;
+
     TriState asTriState() const;
     bool isLikeZero() const { return asTriState() == FalseTriState; }
     bool isLikeNonZero() const { return asTriState() == TrueTriState; }
index b489308..c3640d6 100644 (file)
@@ -54,6 +54,8 @@ bool hasPartialXmmRegUpdate(const Inst& inst)
     case SqrtFloat:
     case CeilDouble:
     case CeilFloat:
+    case FloorDouble:
+    case FloorFloat:
         return true;
     default:
         break;
index 5b506d8..1cf91c2 100644 (file)
@@ -432,6 +432,14 @@ CeilFloat U:F:32, D:F:32
     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
index 631543e..074d91c 100644 (file)
@@ -3377,6 +3377,18 @@ void testCeilCeilArg(double a)
     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 @@ void testCeilCeilArg(float a)
     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 @@ void testCeilArgWithEffectfulDoubleConversion(float a)
     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 @@ void run(const char* filter)
     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>());
index d7c1bff..a541ef2 100644 (file)
@@ -833,10 +833,20 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         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);
index c1dfe7f..84ddae1 100644 (file)
@@ -55,6 +55,22 @@ void printInternal(PrintStream& out, JSC::DFG::Arith::Mode mode)
     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)
index 4e09ac3..aecfe3e 100644 (file)
@@ -146,6 +146,7 @@ namespace WTF {
 
 class PrintStream;
 void printInternal(PrintStream&, JSC::DFG::Arith::Mode);
+void printInternal(PrintStream&, JSC::DFG::Arith::RoundingMode);
 
 } // namespace WTF
 
index f098b64..bbc0521 100644 (file)
@@ -2188,7 +2188,9 @@ bool ByteCodeParser::handleIntrinsicCall(int resultOperand, Intrinsic intrinsic,
         
         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 @@ bool ByteCodeParser::handleIntrinsicCall(int resultOperand, Intrinsic intrinsic,
         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;
         }
index 343a6de..458086e 100644 (file)
@@ -297,6 +297,8 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         }
 
     case ArithRound:
+    case ArithFloor:
+    case ArithCeil:
         def(PureValue(node, static_cast<uintptr_t>(node->arithRoundingMode())));
         return;
 
index 702da5b..4521adb 100644 (file)
@@ -87,6 +87,8 @@ bool doesGC(Graph& graph, Node* node)
     case ArithSqrt:
     case ArithRandom:
     case ArithRound:
+    case ArithFloor:
+    case ArithCeil:
     case ArithFRound:
     case ArithSin:
     case ArithCos:
index 2265c10..f01c146 100644 (file)
@@ -363,7 +363,9 @@ private:
             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());
index 4c9a38b..541c84e 100644 (file)
@@ -222,6 +222,8 @@ void Graph::dump(PrintStream& out, const char* prefix, Node* node, DumpContext*
         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())
index 60d146b..1a4e2f7 100644 (file)
@@ -316,7 +316,7 @@ public:
 
     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);
     }
     
index eeab257..5499deb 100644 (file)
@@ -928,7 +928,7 @@ struct Node {
     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 @@ struct Node {
     {
         switch (op()) {
         case ArithRound:
+        case ArithFloor:
+        case ArithCeil:
         case GetDirectPname:
         case GetById:
         case GetByIdFlush:
@@ -1715,7 +1717,7 @@ struct Node {
 
     bool hasArithRoundingMode()
     {
-        return op() == ArithRound;
+        return op() == ArithRound || op() == ArithFloor || op() == ArithCeil;
     }
 
     Arith::RoundingMode arithRoundingMode()
index ddd0b97..71c075a 100644 (file)
@@ -154,6 +154,8 @@ namespace JSC { namespace DFG {
     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) \
index fa48248..4596053 100644 (file)
@@ -386,7 +386,9 @@ private:
             break;
         }
 
-        case ArithRound: {
+        case ArithRound:
+        case ArithFloor:
+        case ArithCeil: {
             if (isInt32OrBooleanSpeculation(node->getHeapPrediction()) && m_graph.roundShouldSpeculateInt32(node, m_pass))
                 changed |= setPrediction(SpecInt32);
             else
index 230efed..98592c7 100644 (file)
@@ -181,6 +181,8 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case ArithSqrt:
     case ArithFRound:
     case ArithRound:
+    case ArithFloor:
+    case ArithCeil:
     case ArithSin:
     case ArithCos:
     case ArithLog:
index 6174603..f03af97 100644 (file)
@@ -4412,46 +4412,93 @@ void SpeculativeJIT::compileArithMod(Node* node)
     }
 }
 
-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);
+
+            int32Result(resultGPR, node);
+        } else
+            doubleResult(resultFPR, node);
+    };
 
-        static const double halfConstant = 0.5;
-        m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&halfConstant), oneHalfFPR);
-        m_jit.addDouble(valueFPR, oneHalfFPR);
+    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);
 
-        JITCompiler::Jump truncationFailed = m_jit.branchTruncateDoubleToInt32(oneHalfFPR, resultGPR);
-        speculationCheck(Overflow, JSValueRegs(), node, truncationFailed);
-        int32Result(resultGPR, node);
-        return;
-    }
+                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;
+        }
 
-    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);
+        case ArithFloor: {
+            FPRTemporary rounded(this);
+            FPRReg resultFPR = rounded.fpr();
+            m_jit.floorDouble(valueFPR, resultFPR);
+            setResult(resultFPR);
+            return;
+        }
 
-        int32Result(resultGPR, node);
-    } else
-        doubleResult(resultFPR, node);
+        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);
+    }
 }
 
 void SpeculativeJIT::compileArithSqrt(Node* node)
index 9188a83..5d6fba4 100644 (file)
@@ -2267,7 +2267,9 @@ public:
     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*);
index 8472b8c..2009a02 100644 (file)
@@ -2292,7 +2292,9 @@ void SpeculativeJIT::compile(Node* node)
         break;
 
     case ArithRound:
-        compileArithRound(node);
+    case ArithFloor:
+    case ArithCeil:
+        compileArithRounding(node);
         break;
 
     case ArithSin: {
index 83b6f5b..38cb2cd 100644 (file)
@@ -2415,7 +2415,9 @@ void SpeculativeJIT::compile(Node* node)
         break;
 
     case ArithRound:
-        compileArithRound(node);
+    case ArithFloor:
+    case ArithCeil:
+        compileArithRounding(node);
         break;
 
     case ArithSin: {
index 01c8e87..d715693 100644 (file)
@@ -97,6 +97,8 @@ inline CapabilityLevel canCompile(Node* node)
     case ArithPow:
     case ArithRandom:
     case ArithRound:
+    case ArithFloor:
+    case ArithCeil:
     case ArithSqrt:
     case ArithLog:
     case ArithFRound:
index 53e926e..dd76f26 100644 (file)
@@ -523,6 +523,12 @@ private:
         case ArithRound:
             compileArithRound();
             break;
+        case ArithFloor:
+            compileArithFloor();
+            break;
+        case ArithCeil:
+            compileArithCeil();
+            break;
         case ArithSqrt:
             compileArithSqrt();
             break;
@@ -1890,24 +1896,31 @@ private:
 
     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));
+
+            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);
 
-        LValue result = m_out.phi(m_out.doubleType, integerValueResult, integerValueRoundedDownResult);
+            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 @@ private:
             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()))); }
index 78a5ca5..a6487e5 100644 (file)
@@ -155,6 +155,7 @@ public:
     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)
     {
index 7f5469a..5c03d70 100644 (file)
@@ -828,7 +828,7 @@ MacroAssemblerCodeRef ceilThunkGenerator(VM* vm)
     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 (file)
index 0000000..3ebfceb
--- /dev/null
@@ -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 (file)
index 0000000..22d9f26
--- /dev/null
@@ -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 (file)
index 0000000..a30ed89
--- /dev/null
@@ -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 (file)
index 0000000..7448eb9
--- /dev/null
@@ -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 (file)
index 0000000..bae0782
--- /dev/null
@@ -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 (file)
index 0000000..21e021e
--- /dev/null
@@ -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 (file)
index 0000000..ecccdbc
--- /dev/null
@@ -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 (file)
index 0000000..abae278
--- /dev/null
@@ -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);
+}
+
+
index b815eae..6771ab2 100644 (file)
@@ -1,3 +1,12 @@
+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.
+
+        * docs/b3/intermediate-representation.html:
+
 2016-02-16  Jonathan Davis  <jond@apple.com>
 
         Add a redirect for B3 documentation
index 6a53c54..8e84b48 100644 (file)
       <dt>T Ceil(T)</dt>
       <dd>Ceiling.  Valid for Float and Double.</dd>
 
+      <dt>T Floor(T)</dt>
+      <dd>Flooring.  Valid for Float and Double.</dd>
+
       <dt>T Sqrt(T)</dt>
       <dd>Square root.  Valid for Float and Double.</dd>