[JSC] Implement isFinite / isNaN in JS and make DFG ToNumber accept non number values
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 24 Jun 2016 03:41:52 +0000 (03:41 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 24 Jun 2016 03:41:52 +0000 (03:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=154022

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

We aim at optimizing @toInteger operation.
While it still has an unoptimized part[1], this patch should be a first step.

We introduce the @toNumber builtin intrinsic operation.
This converts the given value to the JS number by emitting op_to_number bytecode.
Previously @toInteger called C++ @Number constructor for that purpose.

And in DFG, op_to_number is converted to DFG ToNumber node.
During DFG, we attempt to convert this to edge filtering and Identity, but if we fail,
we just fall back to calling the C++ function.

To utilize ToNumber in user-land side, we add a path attempting to convert Number constructor calls
to ToNumber DFG nodes. This conversion is useful because `Number(value)` is used to convert a value to a number in JS.

Before this patch, we emit simple edge filtering (NumberUse) instead of emitting DFG node like ToNumber for op_to_number.
But emitting ToNumber is useful, because in the case of `Number(value)`, considering `value` may not be a number is reasonable.

By leveraging @toNumber operation, we rewrite Number.{isFinite, isNaN}, global.{isFinite, isNaN} and @toInteger.

ToNumber DFG node has a value profiling. This profiling is leveraged to determine the result number type of the ToNumber operation.
This value profiling is provided from either NumberConstructor's call operation or op_to_number.

The results (with the added performance tests) show that, while existing cases are performance neutral, the newly added cases gain the performance benefit.
And ASMBench/n-body.c also shows stable ~2% progression.

[1]: https://bugs.webkit.org/show_bug.cgi?id=153738

* CMakeLists.txt:
* DerivedSources.make:
* JavaScriptCore.xcodeproj/project.pbxproj:
* builtins/BuiltinNames.h:
* builtins/GlobalObject.js:
(globalPrivate.isFinite):
(globalPrivate.isNaN):
(globalPrivate.toInteger): Deleted.
(globalPrivate.toLength): Deleted.
(globalPrivate.isDictionary): Deleted.
(globalPrivate.speciesGetter): Deleted.
(globalPrivate.speciesConstructor): Deleted.
* builtins/GlobalOperations.js: Copied from Source/JavaScriptCore/builtins/GlobalObject.js.
(globalPrivate.toInteger):
(globalPrivate.toLength):
(globalPrivate.isDictionary):
(globalPrivate.speciesGetter):
(globalPrivate.speciesConstructor):
* builtins/NumberConstructor.js: Added.
(isFinite):
(isNaN):
* bytecode/BytecodeIntrinsicRegistry.cpp:
(JSC::BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry):
* bytecode/BytecodeIntrinsicRegistry.h:
* bytecode/BytecodeList.json:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::finishCreation):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitUnaryOp):
(JSC::BytecodeGenerator::emitUnaryOpProfiled):
* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::emitToNumber):
* bytecompiler/NodesCodegen.cpp:
(JSC::BytecodeIntrinsicNode::emit_intrinsic_toNumber):
(JSC::UnaryPlusNode::emitBytecode):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::attemptToInlineCall):
(JSC::DFG::ByteCodeParser::handleConstantInternalFunction):
(JSC::DFG::ByteCodeParser::parseBlock):
We use `getPrediction()` to retrieve the heap prediction from the to_number bytecode.
According to the benchmark results, choosing `getPredictionWithoutOSRExit()` causes performance regression (1.5%) in kraken stanford-crypto-aes.

* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::fixupToNumber):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
Alway rely on the heap prediction.

* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
As of 64bit version, we carefully manage the register reuse. The largest difference between 32bit and 64bit is
`branchIfNotNumber()` requires the temporary register. We should not use the result registers for that since
it may be reuse the argument registers and it can break the argument registers before using them to call the operation.
Currently, we allocate the additional temporary register for that scratch register.

* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
Reuse the argument register for the result if possible. And manually decrement the use count in the middle of the node.
This is similar technique used in ToPrimitive. Typically, the child of ToNumber is only used by this ToNumber node since
we would like to perform the type conversion onto this child node here. So this careful register reuse effectively removes
the spills to call the operation. The example of the actually emitted code is the following.

76:<!2:loc11>     ToNumber(Untyped:@68, JS|MustGen|UseAsOther, DoubleimpurenanTopEmpty, R:World, W:Heap, Exits, ClobbersExit, bc#48)  predicting DoubleimpurenanTopEmpty
    0x7f986d5fe693: test %rax, %r14
    0x7f986d5fe696: jz 0x7f986d5fe6a1
    0x7f986d5fe69c: jmp 0x7f986d5fe6d1
    0x7f986d5fe6a1: mov %rax, %rsi
    0x7f986d5fe6a4: mov %rbp, %rdi
    0x7f986d5fe6a7: mov $0x2, 0x24(%rbp)
    0x7f986d5fe6ae: mov $0x7f98711ea5f0, %r11
    0x7f986d5fe6b8: call *%r11
    0x7f986d5fe6bb: mov $0x7f982d3f72d0, %r11
    0x7f986d5fe6c5: mov (%r11), %r11
    0x7f986d5fe6c8: test %r11, %r11
    0x7f986d5fe6cb: jnz 0x7f986d5fe88c

It effectively removes the unnecessary spill to call the operation!

* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileToNumber):
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::branchIfNumber):
(JSC::AssemblyHelpers::branchIfNotNumber):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_to_number):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_to_number):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* parser/Nodes.h:
(JSC::UnaryOpNode::opcodeID):
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::globalFuncIsNaN): Deleted.
(JSC::globalFuncIsFinite): Deleted.
* runtime/JSGlobalObjectFunctions.h:
* runtime/MathCommon.h:
(JSC::maxSafeInteger):
(JSC::minSafeInteger):
* runtime/NumberConstructor.cpp:
(JSC::NumberConstructor::finishCreation):
(JSC::numberConstructorFuncIsFinite): Deleted.
(JSC::numberConstructorFuncIsNaN): Deleted.
* runtime/NumberConstructor.h:
* tests/stress/Number-isNaN-basics.js: Added.
(numberIsNaNOnInteger):
(testNumberIsNaNOnIntegers):
(verifyNumberIsNaNOnIntegerWithOtherTypes):
(numberIsNaNOnDouble):
(testNumberIsNaNOnDoubles):
(verifyNumberIsNaNOnDoublesWithOtherTypes):
(numberIsNaNNoArguments):
(numberIsNaNTooManyArguments):
(testNumberIsNaNOnConstants):
(numberIsNaNStructTransition):
(Number.isNaN):
* tests/stress/global-is-finite.js: Added.
(shouldBe):
* tests/stress/global-is-nan.js: Added.
(shouldBe):
* tests/stress/global-isNaN-basics.js: Added.
(isNaNOnInteger):
(testIsNaNOnIntegers):
(verifyIsNaNOnIntegerWithOtherTypes):
(isNaNOnDouble):
(testIsNaNOnDoubles):
(verifyIsNaNOnDoublesWithOtherTypes):
(verifyIsNaNOnCoercedTypes):
(isNaNNoArguments):
(isNaNTooManyArguments):
(testIsNaNOnConstants):
(isNaNTypeCoercionSideEffects):
(i.value.isNaNTypeCoercionSideEffects.valueOf):
(isNaNStructTransition):
(isNaN):
* tests/stress/number-is-finite.js: Added.
(shouldBe):
(test2):
(test3):
* tests/stress/number-is-nan.js: Added.
(shouldBe):
(test2):
(test3):
* tests/stress/to-number-basics.js: Added.
(shouldBe):
* tests/stress/to-number-convert-identity-without-execution.js: Added.
(shouldBe):
(object.valueOf):
(valueOf):
* tests/stress/to-number-int52.js: Added.
(shouldBe):
(object.valueOf):
* tests/stress/to-number-intrinsic-convert-to-identity-without-execution.js: Added.
(shouldBe):
(object.valueOf):
(valueOf):
* tests/stress/to-number-intrinsic-int52.js: Added.
(shouldBe):
(object.valueOf):
* tests/stress/to-number-intrinsic-object-without-execution.js: Added.
(shouldBe):
(object.valueOf):
* tests/stress/to-number-intrinsic-value-profiling.js: Added.
(shouldBe):
(object.valueOf):
* tests/stress/to-number-object-without-execution.js: Added.
(shouldBe):
(object.valueOf):
* tests/stress/to-number-object.js: Added.
(shouldBe):
(test12):
(object1.valueOf):
(test2):
(test22):
(object2.valueOf):
(test3):
(test32):
(object3.valueOf):
* tests/stress/to-number-value-profiling.js: Added.
(shouldBe):
(object.valueOf):

LayoutTests:

* js/regress/Number-isNaN-expected.txt: Added.
* js/regress/Number-isNaN.html: Added.
* js/regress/global-isNaN-expected.txt: Added.
* js/regress/global-isNaN.html: Added.
* js/regress/script-tests/Number-isNaN.js: Added.
* js/regress/script-tests/global-isNaN.js: Added.
* js/regress/script-tests/many-foreach-calls.js:
(i.4.forEach):
(i.array.forEach): Deleted.
* js/regress/script-tests/to-number-constructor-number-string-number-string.js: Added.
(test):
* js/regress/script-tests/to-number-constructor-only-number.js: Added.
(test):
* js/regress/script-tests/to-number-constructor-only-string.js: Added.
(test):
* js/regress/script-tests/to-number-constructor-string-number-string-number.js: Added.
(test):
* js/regress/script-tests/to-number-number-string-number-string.js: Added.
(test):
* js/regress/script-tests/to-number-only-number.js: Added.
(test):
* js/regress/script-tests/to-number-only-string.js: Added.
(test):
* js/regress/script-tests/to-number-string-number-string-number.js: Added.
(test):
* js/regress/to-number-constructor-number-string-number-string-expected.txt: Added.
* js/regress/to-number-constructor-number-string-number-string.html: Added.
* js/regress/to-number-constructor-only-number-expected.txt: Added.
* js/regress/to-number-constructor-only-number.html: Added.
* js/regress/to-number-constructor-only-string-expected.txt: Added.
* js/regress/to-number-constructor-only-string.html: Added.
* js/regress/to-number-constructor-string-number-string-number-expected.txt: Added.
* js/regress/to-number-constructor-string-number-string-number.html: Added.
* js/regress/to-number-number-string-number-string-expected.txt: Added.
* js/regress/to-number-number-string-number-string.html: Added.
* js/regress/to-number-only-number-expected.txt: Added.
* js/regress/to-number-only-number.html: Added.
* js/regress/to-number-only-string-expected.txt: Added.
* js/regress/to-number-only-string.html: Added.
* js/regress/to-number-string-number-string-number-expected.txt: Added.
* js/regress/to-number-string-number-string-number.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@202413 268f45cc-cd09-0410-ab3c-d52691b4dbfc

92 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/Number-isNaN-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/Number-isNaN.html [new file with mode: 0644]
LayoutTests/js/regress/global-isNaN-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/global-isNaN.html [new file with mode: 0644]
LayoutTests/js/regress/script-tests/Number-isNaN.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/global-isNaN.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/many-foreach-calls.js
LayoutTests/js/regress/script-tests/to-number-constructor-number-string-number-string.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/to-number-constructor-only-number.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/to-number-constructor-only-string.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/to-number-constructor-string-number-string-number.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/to-number-number-string-number-string.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/to-number-only-number.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/to-number-only-string.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/to-number-string-number-string-number.js [new file with mode: 0644]
LayoutTests/js/regress/to-number-constructor-number-string-number-string-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/to-number-constructor-number-string-number-string.html [new file with mode: 0644]
LayoutTests/js/regress/to-number-constructor-only-number-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/to-number-constructor-only-number.html [new file with mode: 0644]
LayoutTests/js/regress/to-number-constructor-only-string-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/to-number-constructor-only-string.html [new file with mode: 0644]
LayoutTests/js/regress/to-number-constructor-string-number-string-number-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/to-number-constructor-string-number-string-number.html [new file with mode: 0644]
LayoutTests/js/regress/to-number-number-string-number-string-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/to-number-number-string-number-string.html [new file with mode: 0644]
LayoutTests/js/regress/to-number-only-number-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/to-number-only-number.html [new file with mode: 0644]
LayoutTests/js/regress/to-number-only-string-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/to-number-only-string.html [new file with mode: 0644]
LayoutTests/js/regress/to-number-string-number-string-number-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/to-number-string-number-string-number.html [new file with mode: 0644]
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/DerivedSources.make
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/builtins/BuiltinNames.h
Source/JavaScriptCore/builtins/GlobalObject.js
Source/JavaScriptCore/builtins/GlobalOperations.js [new file with mode: 0644]
Source/JavaScriptCore/builtins/NumberConstructor.js [new file with mode: 0644]
Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.cpp
Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h
Source/JavaScriptCore/runtime/MathCommon.h
Source/JavaScriptCore/runtime/NumberConstructor.cpp
Source/JavaScriptCore/runtime/NumberConstructor.h
Source/JavaScriptCore/tests/stress/Number-isNaN-basics.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/global-is-finite.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/global-is-nan.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/global-isNaN-basics.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/number-is-finite.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/number-is-nan.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/to-number-basics.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/to-number-convert-identity-without-execution.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/to-number-int52.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/to-number-intrinsic-convert-to-identity-without-execution.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/to-number-intrinsic-int52.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/to-number-intrinsic-object-without-execution.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/to-number-intrinsic-value-profiling.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/to-number-object-without-execution.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/to-number-object.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/to-number-value-profiling.js [new file with mode: 0644]

index e2c7dea..a7f7af1 100644 (file)
@@ -1,3 +1,52 @@
+2016-06-23  Joseph Pecoraro  <pecoraro@apple.com> and Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Implement isFinite / isNaN in JS and make DFG ToNumber accept non number values
+        https://bugs.webkit.org/show_bug.cgi?id=154022
+
+        Reviewed by Filip Pizlo.
+
+        * js/regress/Number-isNaN-expected.txt: Added.
+        * js/regress/Number-isNaN.html: Added.
+        * js/regress/global-isNaN-expected.txt: Added.
+        * js/regress/global-isNaN.html: Added.
+        * js/regress/script-tests/Number-isNaN.js: Added.
+        * js/regress/script-tests/global-isNaN.js: Added.
+        * js/regress/script-tests/many-foreach-calls.js:
+        (i.4.forEach):
+        (i.array.forEach): Deleted.
+        * js/regress/script-tests/to-number-constructor-number-string-number-string.js: Added.
+        (test):
+        * js/regress/script-tests/to-number-constructor-only-number.js: Added.
+        (test):
+        * js/regress/script-tests/to-number-constructor-only-string.js: Added.
+        (test):
+        * js/regress/script-tests/to-number-constructor-string-number-string-number.js: Added.
+        (test):
+        * js/regress/script-tests/to-number-number-string-number-string.js: Added.
+        (test):
+        * js/regress/script-tests/to-number-only-number.js: Added.
+        (test):
+        * js/regress/script-tests/to-number-only-string.js: Added.
+        (test):
+        * js/regress/script-tests/to-number-string-number-string-number.js: Added.
+        (test):
+        * js/regress/to-number-constructor-number-string-number-string-expected.txt: Added.
+        * js/regress/to-number-constructor-number-string-number-string.html: Added.
+        * js/regress/to-number-constructor-only-number-expected.txt: Added.
+        * js/regress/to-number-constructor-only-number.html: Added.
+        * js/regress/to-number-constructor-only-string-expected.txt: Added.
+        * js/regress/to-number-constructor-only-string.html: Added.
+        * js/regress/to-number-constructor-string-number-string-number-expected.txt: Added.
+        * js/regress/to-number-constructor-string-number-string-number.html: Added.
+        * js/regress/to-number-number-string-number-string-expected.txt: Added.
+        * js/regress/to-number-number-string-number-string.html: Added.
+        * js/regress/to-number-only-number-expected.txt: Added.
+        * js/regress/to-number-only-number.html: Added.
+        * js/regress/to-number-only-string-expected.txt: Added.
+        * js/regress/to-number-only-string.html: Added.
+        * js/regress/to-number-string-number-string-number-expected.txt: Added.
+        * js/regress/to-number-string-number-string-number.html: Added.
+
 2016-06-23  Simon Fraser  <simon.fraser@apple.com>
 
         https://bugs.webkit.org/show_bug.cgi?id=159077
diff --git a/LayoutTests/js/regress/Number-isNaN-expected.txt b/LayoutTests/js/regress/Number-isNaN-expected.txt
new file mode 100644 (file)
index 0000000..ec6b938
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/Number-isNaN
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/Number-isNaN.html b/LayoutTests/js/regress/Number-isNaN.html
new file mode 100644 (file)
index 0000000..e2fa4c9
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/Number-isNaN.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/global-isNaN-expected.txt b/LayoutTests/js/regress/global-isNaN-expected.txt
new file mode 100644 (file)
index 0000000..ee3ae28
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/global-isNaN
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/global-isNaN.html b/LayoutTests/js/regress/global-isNaN.html
new file mode 100644 (file)
index 0000000..b943f9b
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/global-isNaN.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/script-tests/Number-isNaN.js b/LayoutTests/js/regress/script-tests/Number-isNaN.js
new file mode 100644 (file)
index 0000000..d0ff54b
--- /dev/null
@@ -0,0 +1,12 @@
+(function() {
+    var result = 0;
+    var values = [0, -1, 123.45, Infinity, NaN];
+    for (var i = 0; i < 1000000; ++i) {
+        for (var j = 0; j < values.length; ++j) {
+            if (Number.isNaN(values[j]))
+                result++;
+        }
+    }
+    if (result !== 1000000)
+        throw "Error: bad result: " + result;
+})();
diff --git a/LayoutTests/js/regress/script-tests/global-isNaN.js b/LayoutTests/js/regress/script-tests/global-isNaN.js
new file mode 100644 (file)
index 0000000..372ef14
--- /dev/null
@@ -0,0 +1,12 @@
+(function() {
+    var result = 0;
+    var values = [0, -1, 123.45, Infinity, NaN];
+    for (var i = 0; i < 1000000; ++i) {
+        for (var j = 0; j < values.length; ++j) {
+            if (isNaN(values[j]))
+                result++;
+        }
+    }
+    if (result !== 1000000)
+        throw "Error: bad result: " + result;
+})();
index d2b3825..cec0fae 100644 (file)
@@ -1,7 +1,6 @@
-var sum = 0;
-var array = [1, 2, 3];
-for (var i = 0; i < 1e5; ++i) {
-    array.forEach(function (value) {
+for (var i = 0; i < 1e4; ++i) {
+    var sum = 0;
+    [1, 2, 3, 4].forEach(function (value) {
         sum += value;
     });
 }
diff --git a/LayoutTests/js/regress/script-tests/to-number-constructor-number-string-number-string.js b/LayoutTests/js/regress/script-tests/to-number-constructor-number-string-number-string.js
new file mode 100644 (file)
index 0000000..fcae0b4
--- /dev/null
@@ -0,0 +1,20 @@
+function test(value)
+{
+    return Number(value);
+}
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i);
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i.toString());
+
+var result = 0;
+for (var i = 0; i < 4e4; ++i)
+    result = test(i);
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i.toString());
diff --git a/LayoutTests/js/regress/script-tests/to-number-constructor-only-number.js b/LayoutTests/js/regress/script-tests/to-number-constructor-only-number.js
new file mode 100644 (file)
index 0000000..04a945b
--- /dev/null
@@ -0,0 +1,10 @@
+function test(value)
+{
+    return Number(value);
+}
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i);
+if (result !== 9999)
+    throw new Error(`bad result ${result}`);
diff --git a/LayoutTests/js/regress/script-tests/to-number-constructor-only-string.js b/LayoutTests/js/regress/script-tests/to-number-constructor-only-string.js
new file mode 100644 (file)
index 0000000..14ad650
--- /dev/null
@@ -0,0 +1,10 @@
+function test(value)
+{
+    return Number(value);
+}
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i.toString());
+if (result !== 9999)
+    throw new Error(`bad result ${result}`);
diff --git a/LayoutTests/js/regress/script-tests/to-number-constructor-string-number-string-number.js b/LayoutTests/js/regress/script-tests/to-number-constructor-string-number-string-number.js
new file mode 100644 (file)
index 0000000..6b7892d
--- /dev/null
@@ -0,0 +1,20 @@
+function test(value)
+{
+    return Number(value);
+}
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i.toString());
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i);
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i.toString());
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i);
diff --git a/LayoutTests/js/regress/script-tests/to-number-number-string-number-string.js b/LayoutTests/js/regress/script-tests/to-number-number-string-number-string.js
new file mode 100644 (file)
index 0000000..4f97795
--- /dev/null
@@ -0,0 +1,20 @@
+function test(value)
+{
+    return +value;
+}
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i);
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i.toString());
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i);
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i.toString());
diff --git a/LayoutTests/js/regress/script-tests/to-number-only-number.js b/LayoutTests/js/regress/script-tests/to-number-only-number.js
new file mode 100644 (file)
index 0000000..965861f
--- /dev/null
@@ -0,0 +1,10 @@
+function test(value)
+{
+    return +value;
+}
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i);
+if (result !== 9999)
+    throw new Error(`bad result ${result}`);
diff --git a/LayoutTests/js/regress/script-tests/to-number-only-string.js b/LayoutTests/js/regress/script-tests/to-number-only-string.js
new file mode 100644 (file)
index 0000000..105ad3c
--- /dev/null
@@ -0,0 +1,10 @@
+function test(value)
+{
+    return +value;
+}
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i.toString());
+if (result !== 9999)
+    throw new Error(`bad result ${result}`);
diff --git a/LayoutTests/js/regress/script-tests/to-number-string-number-string-number.js b/LayoutTests/js/regress/script-tests/to-number-string-number-string-number.js
new file mode 100644 (file)
index 0000000..de3941d
--- /dev/null
@@ -0,0 +1,20 @@
+function test(value)
+{
+    return +value;
+}
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i.toString());
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i);
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i.toString());
+
+var result = 0;
+for (var i = 0; i < 1e4; ++i)
+    result = test(i);
diff --git a/LayoutTests/js/regress/to-number-constructor-number-string-number-string-expected.txt b/LayoutTests/js/regress/to-number-constructor-number-string-number-string-expected.txt
new file mode 100644 (file)
index 0000000..17e1f67
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/to-number-constructor-number-string-number-string
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/to-number-constructor-number-string-number-string.html b/LayoutTests/js/regress/to-number-constructor-number-string-number-string.html
new file mode 100644 (file)
index 0000000..2564405
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/to-number-constructor-number-string-number-string.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/to-number-constructor-only-number-expected.txt b/LayoutTests/js/regress/to-number-constructor-only-number-expected.txt
new file mode 100644 (file)
index 0000000..fa27862
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/to-number-constructor-only-number
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/to-number-constructor-only-number.html b/LayoutTests/js/regress/to-number-constructor-only-number.html
new file mode 100644 (file)
index 0000000..a1cc4f6
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/to-number-constructor-only-number.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/to-number-constructor-only-string-expected.txt b/LayoutTests/js/regress/to-number-constructor-only-string-expected.txt
new file mode 100644 (file)
index 0000000..3601a24
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/to-number-constructor-only-string
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/to-number-constructor-only-string.html b/LayoutTests/js/regress/to-number-constructor-only-string.html
new file mode 100644 (file)
index 0000000..04f558a
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/to-number-constructor-only-string.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/to-number-constructor-string-number-string-number-expected.txt b/LayoutTests/js/regress/to-number-constructor-string-number-string-number-expected.txt
new file mode 100644 (file)
index 0000000..a3bad6c
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/to-number-constructor-string-number-string-number
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/to-number-constructor-string-number-string-number.html b/LayoutTests/js/regress/to-number-constructor-string-number-string-number.html
new file mode 100644 (file)
index 0000000..20bab4d
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/to-number-constructor-string-number-string-number.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/to-number-number-string-number-string-expected.txt b/LayoutTests/js/regress/to-number-number-string-number-string-expected.txt
new file mode 100644 (file)
index 0000000..2e3e50a
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/to-number-number-string-number-string
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/to-number-number-string-number-string.html b/LayoutTests/js/regress/to-number-number-string-number-string.html
new file mode 100644 (file)
index 0000000..e368326
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/to-number-number-string-number-string.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/to-number-only-number-expected.txt b/LayoutTests/js/regress/to-number-only-number-expected.txt
new file mode 100644 (file)
index 0000000..07fa1d1
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/to-number-only-number
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/to-number-only-number.html b/LayoutTests/js/regress/to-number-only-number.html
new file mode 100644 (file)
index 0000000..bc65e22
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/to-number-only-number.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/to-number-only-string-expected.txt b/LayoutTests/js/regress/to-number-only-string-expected.txt
new file mode 100644 (file)
index 0000000..1612698
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/to-number-only-string
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/to-number-only-string.html b/LayoutTests/js/regress/to-number-only-string.html
new file mode 100644 (file)
index 0000000..75c0826
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/to-number-only-string.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/to-number-string-number-string-number-expected.txt b/LayoutTests/js/regress/to-number-string-number-string-number-expected.txt
new file mode 100644 (file)
index 0000000..2afd790
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/to-number-string-number-string-number
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/to-number-string-number-string-number.html b/LayoutTests/js/regress/to-number-string-number-string-number.html
new file mode 100644 (file)
index 0000000..04ef530
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/to-number-string-number-string-number.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 20751fd..0aac2f0 100644 (file)
@@ -1217,11 +1217,13 @@ set(JavaScriptCore_BUILTINS_SOURCES
     ${JAVASCRIPTCORE_DIR}/builtins/FunctionPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/GeneratorPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/GlobalObject.js
+    ${JAVASCRIPTCORE_DIR}/builtins/GlobalOperations.js
     ${JAVASCRIPTCORE_DIR}/builtins/InspectorInstrumentationObject.js
     ${JAVASCRIPTCORE_DIR}/builtins/InternalPromiseConstructor.js
     ${JAVASCRIPTCORE_DIR}/builtins/IteratorPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/MapPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/ModuleLoaderObject.js
+    ${JAVASCRIPTCORE_DIR}/builtins/NumberConstructor.js
     ${JAVASCRIPTCORE_DIR}/builtins/NumberPrototype.js
     ${JAVASCRIPTCORE_DIR}/builtins/ObjectConstructor.js
     ${JAVASCRIPTCORE_DIR}/builtins/PromiseConstructor.js
index e42db94..c156e6b 100644 (file)
@@ -1,3 +1,242 @@
+2016-06-23  Joseph Pecoraro  <pecoraro@apple.com> and Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Implement isFinite / isNaN in JS and make DFG ToNumber accept non number values
+        https://bugs.webkit.org/show_bug.cgi?id=154022
+
+        Reviewed by Filip Pizlo.
+
+        We aim at optimizing @toInteger operation.
+        While it still has an unoptimized part[1], this patch should be a first step.
+
+        We introduce the @toNumber builtin intrinsic operation.
+        This converts the given value to the JS number by emitting op_to_number bytecode.
+        Previously @toInteger called C++ @Number constructor for that purpose.
+
+        And in DFG, op_to_number is converted to DFG ToNumber node.
+        During DFG, we attempt to convert this to edge filtering and Identity, but if we fail,
+        we just fall back to calling the C++ function.
+
+        To utilize ToNumber in user-land side, we add a path attempting to convert Number constructor calls
+        to ToNumber DFG nodes. This conversion is useful because `Number(value)` is used to convert a value to a number in JS.
+
+        Before this patch, we emit simple edge filtering (NumberUse) instead of emitting DFG node like ToNumber for op_to_number.
+        But emitting ToNumber is useful, because in the case of `Number(value)`, considering `value` may not be a number is reasonable.
+
+        By leveraging @toNumber operation, we rewrite Number.{isFinite, isNaN}, global.{isFinite, isNaN} and @toInteger.
+
+        ToNumber DFG node has a value profiling. This profiling is leveraged to determine the result number type of the ToNumber operation.
+        This value profiling is provided from either NumberConstructor's call operation or op_to_number.
+
+        The results (with the added performance tests) show that, while existing cases are performance neutral, the newly added cases gain the performance benefit.
+        And ASMBench/n-body.c also shows stable ~2% progression.
+
+        [1]: https://bugs.webkit.org/show_bug.cgi?id=153738
+
+        * CMakeLists.txt:
+        * DerivedSources.make:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * builtins/BuiltinNames.h:
+        * builtins/GlobalObject.js:
+        (globalPrivate.isFinite):
+        (globalPrivate.isNaN):
+        (globalPrivate.toInteger): Deleted.
+        (globalPrivate.toLength): Deleted.
+        (globalPrivate.isDictionary): Deleted.
+        (globalPrivate.speciesGetter): Deleted.
+        (globalPrivate.speciesConstructor): Deleted.
+        * builtins/GlobalOperations.js: Copied from Source/JavaScriptCore/builtins/GlobalObject.js.
+        (globalPrivate.toInteger):
+        (globalPrivate.toLength):
+        (globalPrivate.isDictionary):
+        (globalPrivate.speciesGetter):
+        (globalPrivate.speciesConstructor):
+        * builtins/NumberConstructor.js: Added.
+        (isFinite):
+        (isNaN):
+        * bytecode/BytecodeIntrinsicRegistry.cpp:
+        (JSC::BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry):
+        * bytecode/BytecodeIntrinsicRegistry.h:
+        * bytecode/BytecodeList.json:
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::dumpBytecode):
+        (JSC::CodeBlock::finishCreation):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitUnaryOp):
+        (JSC::BytecodeGenerator::emitUnaryOpProfiled):
+        * bytecompiler/BytecodeGenerator.h:
+        (JSC::BytecodeGenerator::emitToNumber):
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::BytecodeIntrinsicNode::emit_intrinsic_toNumber):
+        (JSC::UnaryPlusNode::emitBytecode):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::attemptToInlineCall):
+        (JSC::DFG::ByteCodeParser::handleConstantInternalFunction):
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        We use `getPrediction()` to retrieve the heap prediction from the to_number bytecode.
+        According to the benchmark results, choosing `getPredictionWithoutOSRExit()` causes performance regression (1.5%) in kraken stanford-crypto-aes.
+
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        (JSC::DFG::FixupPhase::fixupToNumber):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasHeapPrediction):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        Alway rely on the heap prediction.
+
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        As of 64bit version, we carefully manage the register reuse. The largest difference between 32bit and 64bit is
+        `branchIfNotNumber()` requires the temporary register. We should not use the result registers for that since
+        it may be reuse the argument registers and it can break the argument registers before using them to call the operation.
+        Currently, we allocate the additional temporary register for that scratch register.
+
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        Reuse the argument register for the result if possible. And manually decrement the use count in the middle of the node.
+        This is similar technique used in ToPrimitive. Typically, the child of ToNumber is only used by this ToNumber node since
+        we would like to perform the type conversion onto this child node here. So this careful register reuse effectively removes
+        the spills to call the operation. The example of the actually emitted code is the following.
+
+        76:<!2:loc11>     ToNumber(Untyped:@68, JS|MustGen|UseAsOther, DoubleimpurenanTopEmpty, R:World, W:Heap, Exits, ClobbersExit, bc#48)  predicting DoubleimpurenanTopEmpty
+            0x7f986d5fe693: test %rax, %r14
+            0x7f986d5fe696: jz 0x7f986d5fe6a1
+            0x7f986d5fe69c: jmp 0x7f986d5fe6d1
+            0x7f986d5fe6a1: mov %rax, %rsi
+            0x7f986d5fe6a4: mov %rbp, %rdi
+            0x7f986d5fe6a7: mov $0x2, 0x24(%rbp)
+            0x7f986d5fe6ae: mov $0x7f98711ea5f0, %r11
+            0x7f986d5fe6b8: call *%r11
+            0x7f986d5fe6bb: mov $0x7f982d3f72d0, %r11
+            0x7f986d5fe6c5: mov (%r11), %r11
+            0x7f986d5fe6c8: test %r11, %r11
+            0x7f986d5fe6cb: jnz 0x7f986d5fe88c
+
+        It effectively removes the unnecessary spill to call the operation!
+
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileToNumber):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
+        * jit/AssemblyHelpers.h:
+        (JSC::AssemblyHelpers::branchIfNumber):
+        (JSC::AssemblyHelpers::branchIfNotNumber):
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_to_number):
+        * jit/JITOpcodes32_64.cpp:
+        (JSC::JIT::emit_op_to_number):
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * parser/Nodes.h:
+        (JSC::UnaryOpNode::opcodeID):
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        * runtime/JSGlobalObjectFunctions.cpp:
+        (JSC::globalFuncIsNaN): Deleted.
+        (JSC::globalFuncIsFinite): Deleted.
+        * runtime/JSGlobalObjectFunctions.h:
+        * runtime/MathCommon.h:
+        (JSC::maxSafeInteger):
+        (JSC::minSafeInteger):
+        * runtime/NumberConstructor.cpp:
+        (JSC::NumberConstructor::finishCreation):
+        (JSC::numberConstructorFuncIsFinite): Deleted.
+        (JSC::numberConstructorFuncIsNaN): Deleted.
+        * runtime/NumberConstructor.h:
+        * tests/stress/Number-isNaN-basics.js: Added.
+        (numberIsNaNOnInteger):
+        (testNumberIsNaNOnIntegers):
+        (verifyNumberIsNaNOnIntegerWithOtherTypes):
+        (numberIsNaNOnDouble):
+        (testNumberIsNaNOnDoubles):
+        (verifyNumberIsNaNOnDoublesWithOtherTypes):
+        (numberIsNaNNoArguments):
+        (numberIsNaNTooManyArguments):
+        (testNumberIsNaNOnConstants):
+        (numberIsNaNStructTransition):
+        (Number.isNaN):
+        * tests/stress/global-is-finite.js: Added.
+        (shouldBe):
+        * tests/stress/global-is-nan.js: Added.
+        (shouldBe):
+        * tests/stress/global-isNaN-basics.js: Added.
+        (isNaNOnInteger):
+        (testIsNaNOnIntegers):
+        (verifyIsNaNOnIntegerWithOtherTypes):
+        (isNaNOnDouble):
+        (testIsNaNOnDoubles):
+        (verifyIsNaNOnDoublesWithOtherTypes):
+        (verifyIsNaNOnCoercedTypes):
+        (isNaNNoArguments):
+        (isNaNTooManyArguments):
+        (testIsNaNOnConstants):
+        (isNaNTypeCoercionSideEffects):
+        (i.value.isNaNTypeCoercionSideEffects.valueOf):
+        (isNaNStructTransition):
+        (isNaN):
+        * tests/stress/number-is-finite.js: Added.
+        (shouldBe):
+        (test2):
+        (test3):
+        * tests/stress/number-is-nan.js: Added.
+        (shouldBe):
+        (test2):
+        (test3):
+        * tests/stress/to-number-basics.js: Added.
+        (shouldBe):
+        * tests/stress/to-number-convert-identity-without-execution.js: Added.
+        (shouldBe):
+        (object.valueOf):
+        (valueOf):
+        * tests/stress/to-number-int52.js: Added.
+        (shouldBe):
+        (object.valueOf):
+        * tests/stress/to-number-intrinsic-convert-to-identity-without-execution.js: Added.
+        (shouldBe):
+        (object.valueOf):
+        (valueOf):
+        * tests/stress/to-number-intrinsic-int52.js: Added.
+        (shouldBe):
+        (object.valueOf):
+        * tests/stress/to-number-intrinsic-object-without-execution.js: Added.
+        (shouldBe):
+        (object.valueOf):
+        * tests/stress/to-number-intrinsic-value-profiling.js: Added.
+        (shouldBe):
+        (object.valueOf):
+        * tests/stress/to-number-object-without-execution.js: Added.
+        (shouldBe):
+        (object.valueOf):
+        * tests/stress/to-number-object.js: Added.
+        (shouldBe):
+        (test12):
+        (object1.valueOf):
+        (test2):
+        (test22):
+        (object2.valueOf):
+        (test3):
+        (test32):
+        (object3.valueOf):
+        * tests/stress/to-number-value-profiling.js: Added.
+        (shouldBe):
+        (object.valueOf):
+
 2016-06-23  Saam Barati  <sbarati@apple.com>
 
         DFGSpeculativeJIT's m_slowPathLambdas should restore the current node field and DFG OSR entry functions should use DeferGCForAWhile instead of DeferGC
index 72e41f4..ecdd87e 100644 (file)
@@ -88,11 +88,13 @@ JavaScriptCore_BUILTINS_SOURCES = \
     $(JavaScriptCore)/builtins/FunctionPrototype.js \
     $(JavaScriptCore)/builtins/GeneratorPrototype.js \
     $(JavaScriptCore)/builtins/GlobalObject.js \
+    $(JavaScriptCore)/builtins/GlobalOperations.js \
     $(JavaScriptCore)/builtins/InspectorInstrumentationObject.js \
     $(JavaScriptCore)/builtins/InternalPromiseConstructor.js \
     $(JavaScriptCore)/builtins/IteratorPrototype.js \
     $(JavaScriptCore)/builtins/MapPrototype.js \
     $(JavaScriptCore)/builtins/ModuleLoaderObject.js \
+    $(JavaScriptCore)/builtins/NumberConstructor.js \
     $(JavaScriptCore)/builtins/NumberPrototype.js \
     $(JavaScriptCore)/builtins/ObjectConstructor.js \
     $(JavaScriptCore)/builtins/PromiseConstructor.js \
index ceb9f3a..ba1e7a1 100644 (file)
                A513E5C9185F9624007E95AD /* InjectedScriptManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InjectedScriptManager.h; sourceTree = "<group>"; };
                A514B2C0185A684400F3C7CB /* InjectedScriptBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InjectedScriptBase.cpp; sourceTree = "<group>"; };
                A514B2C1185A684400F3C7CB /* InjectedScriptBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InjectedScriptBase.h; sourceTree = "<group>"; };
+               A52704851D027C8800354C37 /* GlobalOperations.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = GlobalOperations.js; sourceTree = "<group>"; };
+               A52704861D027C8800354C37 /* NumberConstructor.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = NumberConstructor.js; sourceTree = "<group>"; };
                A5311C341C77CEAC00E6B1B6 /* HeapSnapshotBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HeapSnapshotBuilder.cpp; sourceTree = "<group>"; };
                A5311C351C77CEAC00E6B1B6 /* HeapSnapshotBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapSnapshotBuilder.h; sourceTree = "<group>"; };
                A532438118568317002ED692 /* InspectorBackendDispatchers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorBackendDispatchers.cpp; sourceTree = "<group>"; };
                                A7A979C418BE8D9E002C3733 /* FunctionPrototype.js */,
                                70B7918F1C0244EC002481E2 /* GeneratorPrototype.js */,
                                7CF9BC5A1B65D9A3009DB1EF /* GlobalObject.js */,
+                               A52704851D027C8800354C37 /* GlobalOperations.js */,
                                E35E03611B7AB4850073AD2A /* InspectorInstrumentationObject.js */,
                                E33F50881B844A1A00413856 /* InternalPromiseConstructor.js */,
                                7CF9BC5B1B65D9A3009DB1EF /* IteratorPrototype.js */,
                                7035587C1C418419004BD7BF /* MapPrototype.js */,
                                E30677971B8BC6F5003F87F0 /* ModuleLoaderObject.js */,
+                               A52704861D027C8800354C37 /* NumberConstructor.js */,
                                A15DE5C51C0FBF8D0089133D /* NumberPrototype.js */,
                                7CF9BC5C1B65D9B1009DB1EF /* ObjectConstructor.js */,
                                7CF9BC5D1B65D9B1009DB1EF /* PromiseOperations.js */,
index f085008..1fa79ff 100644 (file)
@@ -64,8 +64,6 @@ namespace JSC {
     macro(abs) \
     macro(floor) \
     macro(trunc) \
-    macro(isFinite) \
-    macro(isNaN) \
     macro(create) \
     macro(defineProperty) \
     macro(getPrototypeOf) \
index 5776a22..804930c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>.
+ * Copyright (C) 2015-2016 Yusuke Suzuki <utatane.tea@gmail.com>.
  * Copyright (C) 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-// @internal
-
 @globalPrivate
-function toInteger(target)
+function isFinite(value)
 {
     "use strict";
 
-    var numberValue = @Number(target);
-
-    // isNaN(numberValue)
+    var numberValue = @toNumber(value);
+    // Return false if numberValue is |NaN|.
     if (numberValue !== numberValue)
-        return 0;
-    return @trunc(numberValue);
-}
-
-@globalPrivate
-function toLength(target)
-{
-    "use strict";
-
-    var maxSafeInteger = 0x1FFFFFFFFFFFFF;
-    var length = @toInteger(target);
-    // originally Math.min(Math.max(length, 0), maxSafeInteger));
-    return length > 0 ? (length < maxSafeInteger ? length : maxSafeInteger) : 0;
+        return false;
+    return numberValue !== @Infinity && numberValue !== -@Infinity;
 }
 
 @globalPrivate
-function isDictionary(object)
+function isNaN(value)
 {
     "use strict";
 
-    return object === @undefined || object == null || typeof object === "object";
-}
-
-// FIXME: this needs to have it's name changed to "get [Symbol.species]".
-// see: https://bugs.webkit.org/show_bug.cgi?id=151363
-@globalPrivate
-function speciesGetter()
-{
-    return this;
-}
-
-@globalPrivate
-function speciesConstructor(obj, defaultConstructor)
-{
-    var constructor = obj.constructor;
-    if (constructor === @undefined)
-        return defaultConstructor;
-    if (!@isObject(constructor))
-        throw new @TypeError("|this|.constructor is not an Object or undefined");
-    constructor = constructor.@speciesSymbol;
-    if (constructor == null)
-        return defaultConstructor;
-    if (@isConstructor(constructor))
-        return constructor;
-    throw new @TypeError("|this|.constructor[Symbol.species] is not a constructor");
+    var numberValue = @toNumber(value);
+    return numberValue !== numberValue;
 }
diff --git a/Source/JavaScriptCore/builtins/GlobalOperations.js b/Source/JavaScriptCore/builtins/GlobalOperations.js
new file mode 100644 (file)
index 0000000..c85915c
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// @internal
+
+@globalPrivate
+function toInteger(target)
+{
+    "use strict";
+
+    var numberValue = @Number(target);
+
+    // isNaN(numberValue)
+    if (numberValue !== numberValue)
+        return 0;
+    return @trunc(numberValue);
+}
+
+@globalPrivate
+function toLength(target)
+{
+    "use strict";
+
+    var length = @toInteger(target);
+    // originally Math.min(Math.max(length, 0), maxSafeInteger));
+    return length > 0 ? (length < @MAX_SAFE_INTEGER? length : @MAX_SAFE_INTEGER) : 0;
+
+}
+
+@globalPrivate
+function isDictionary(object)
+{
+    "use strict";
+
+    return object === @undefined || object == null || typeof object === "object";
+}
+
+// FIXME: this needs to have it's name changed to "get [Symbol.species]".
+// see: https://bugs.webkit.org/show_bug.cgi?id=151363
+@globalPrivate
+function speciesGetter()
+{
+    return this;
+}
+
+@globalPrivate
+function speciesConstructor(obj, defaultConstructor)
+{
+    var constructor = obj.constructor;
+    if (constructor === @undefined)
+        return defaultConstructor;
+    if (!@isObject(constructor))
+        throw new @TypeError("|this|.constructor is not an Object or undefined");
+    constructor = constructor.@speciesSymbol;
+    if (constructor == null)
+        return defaultConstructor;
+    if (@isConstructor(constructor))
+        return constructor;
+    throw new @TypeError("|this|.constructor[Symbol.species] is not a constructor");
+}
diff --git a/Source/JavaScriptCore/builtins/NumberConstructor.js b/Source/JavaScriptCore/builtins/NumberConstructor.js
new file mode 100644 (file)
index 0000000..2c0e4c8
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+function isFinite(value)
+{
+    "use strict";
+
+    if (typeof value !== "number")
+        return false;
+
+    // Return false if value is |NaN|.
+    if (value !== value)
+        return false;
+
+    return value !== @Infinity && value !== -@Infinity;
+}
+
+function isNaN(value)
+{
+    "use strict";
+
+    return value !== value;
+}
index 8cf944c..2781593 100644 (file)
@@ -54,7 +54,7 @@ BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry(VM& vm)
     m_arrayIterationKindValue.set(m_vm, jsNumber(ArrayIterateValue));
     m_arrayIterationKindKeyValue.set(m_vm, jsNumber(ArrayIterateKeyValue));
     m_MAX_STRING_LENGTH.set(m_vm, jsNumber(JSString::MaxLength));
-    m_MAX_SAFE_INTEGER.set(m_vm, jsDoubleNumber(9007199254740991.0)); // 2 ^ 53 - 1
+    m_MAX_SAFE_INTEGER.set(m_vm, jsDoubleNumber(maxSafeInteger()));
     m_ModuleFetch.set(m_vm, jsNumber(static_cast<unsigned>(ModuleLoaderObject::Status::Fetch)));
     m_ModuleTranslate.set(m_vm, jsNumber(static_cast<unsigned>(ModuleLoaderObject::Status::Translate)));
     m_ModuleInstantiate.set(m_vm, jsNumber(static_cast<unsigned>(ModuleLoaderObject::Status::Instantiate)));
index 6c95a6b..2fc50db 100644 (file)
@@ -47,6 +47,7 @@ class Identifier;
     macro(tailCallForwardArguments) \
     macro(tryGetById) \
     macro(putByValDirect) \
+    macro(toNumber) \
     macro(toString)
 
 #define JSC_COMMON_BYTECODE_INTRINSIC_CONSTANTS_EACH_NAME(macro) \
index 345819b..88a7c52 100644 (file)
@@ -31,7 +31,7 @@
             { "name" : "op_greatereq", "length" : 4 },
             { "name" : "op_inc", "length" : 2 },
             { "name" : "op_dec", "length" : 2 },
-            { "name" : "op_to_number", "length" : 3 },
+            { "name" : "op_to_number", "length" : 4 },
             { "name" : "op_to_string", "length" : 3 },
             { "name" : "op_negate", "length" : 3 },
             { "name" : "op_add", "length" : 5 },
index d2fa017..eb93b83 100644 (file)
@@ -992,6 +992,7 @@ void CodeBlock::dumpBytecode(
         }
         case op_to_number: {
             printUnaryOp(out, exec, location, it, "to_number");
+            dumpValueProfiling(out, it, hasPrintedProfiling);
             break;
         }
         case op_to_string: {
@@ -2093,7 +2094,8 @@ void CodeBlock::finishCreation(VM& vm, ScriptExecutable* ownerExecutable, Unlink
         }
         case op_get_direct_pname:
         case op_get_by_id:
-        case op_get_from_arguments: {
+        case op_get_from_arguments:
+        case op_to_number: {
             ValueProfile* profile = &m_valueProfiles[pc[opLength - 1].u.operand];
             ASSERT(profile->m_bytecodeOffset == -1);
             profile->m_bytecodeOffset = i;
index 91f5c77..b4ac7f8 100644 (file)
@@ -1525,12 +1525,22 @@ RegisterID* BytecodeGenerator::emitMove(RegisterID* dst, RegisterID* src)
 
 RegisterID* BytecodeGenerator::emitUnaryOp(OpcodeID opcodeID, RegisterID* dst, RegisterID* src)
 {
+    ASSERT_WITH_MESSAGE(op_to_number != opcodeID, "op_to_number is profiled.");
     emitOpcode(opcodeID);
     instructions().append(dst->index());
     instructions().append(src->index());
     return dst;
 }
 
+RegisterID* BytecodeGenerator::emitUnaryOpProfiled(OpcodeID opcodeID, RegisterID* dst, RegisterID* src)
+{
+    UnlinkedValueProfile profile = emitProfiledOpcode(opcodeID);
+    instructions().append(dst->index());
+    instructions().append(src->index());
+    instructions().append(profile);
+    return dst;
+}
+
 RegisterID* BytecodeGenerator::emitInc(RegisterID* srcDst)
 {
     emitOpcode(op_inc);
index 8dce299..39b7973 100644 (file)
@@ -510,6 +510,7 @@ namespace JSC {
         RegisterID* emitLoadGlobalObject(RegisterID* dst);
 
         RegisterID* emitUnaryOp(OpcodeID, RegisterID* dst, RegisterID* src);
+        RegisterID* emitUnaryOpProfiled(OpcodeID, RegisterID* dst, RegisterID* src);
         RegisterID* emitBinaryOp(OpcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2, OperandTypes);
         RegisterID* emitEqualityOp(OpcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2);
         RegisterID* emitUnaryNoDstOp(OpcodeID, RegisterID* src);
@@ -536,7 +537,7 @@ namespace JSC {
         RegisterID* emitMoveEmptyValue(RegisterID* dst);
         RegisterID* emitMove(RegisterID* dst, RegisterID* src);
 
-        RegisterID* emitToNumber(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_to_number, dst, src); }
+        RegisterID* emitToNumber(RegisterID* dst, RegisterID* src) { return emitUnaryOpProfiled(op_to_number, dst, src); }
         RegisterID* emitToString(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_to_string, dst, src); }
         RegisterID* emitInc(RegisterID* srcDst);
         RegisterID* emitDec(RegisterID* srcDst);
index b6d0a37..61a54ec 100644 (file)
@@ -918,6 +918,15 @@ RegisterID* BytecodeIntrinsicNode::emit_intrinsic_tryGetById(BytecodeGenerator&
     return generator.emitTryGetById(finalDest.get(), base.get(), ident);
 }
 
+RegisterID* BytecodeIntrinsicNode::emit_intrinsic_toNumber(BytecodeGenerator& generator, RegisterID* dst)
+{
+    ArgumentListNode* node = m_args->m_listNode;
+    RefPtr<RegisterID> src = generator.emitNode(node);
+    ASSERT(!node->m_next);
+
+    return generator.moveToDestinationIfNeeded(dst, generator.emitToNumber(generator.tempDestination(dst), src.get()));
+}
+
 RegisterID* BytecodeIntrinsicNode::emit_intrinsic_toString(BytecodeGenerator& generator, RegisterID* dst)
 {
     ArgumentListNode* node = m_args->m_listNode;
@@ -1549,6 +1558,16 @@ RegisterID* UnaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID*
     return generator.emitUnaryOp(opcodeID(), generator.finalDestination(dst), src.get());
 }
 
+// ------------------------------ UnaryPlusNode -----------------------------------
+
+RegisterID* UnaryPlusNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
+{
+    ASSERT(opcodeID() == op_to_number);
+    RefPtr<RegisterID> src = generator.emitNode(expr());
+    generator.emitExpressionInfo(position(), position(), position());
+    return generator.emitToNumber(generator.finalDestination(dst), src.get());
+}
+
 // ------------------------------ BitwiseNotNode -----------------------------------
  
 RegisterID* BitwiseNotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
index ad827d5..ebdc79c 100644 (file)
@@ -1827,11 +1827,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         
         ASSERT(node->child1().useKind() == UntypedUse);
         
-        if (!forNode(node->child1()).m_type) {
-            m_state.setIsValid(false);
-            break;
-        }
-        
         if (!(forNode(node->child1()).m_type & ~(SpecFullNumber | SpecBoolean | SpecString | SpecSymbol))) {
             m_state.setFoundConstants(true);
             forNode(node) = forNode(node->child1());
@@ -1843,6 +1838,26 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         forNode(node).setType(m_graph, SpecHeapTop & ~SpecObject);
         break;
     }
+
+    case ToNumber: {
+        JSValue childConst = forNode(node->child1()).value();
+        if (childConst && childConst.isNumber()) {
+            setConstant(node, childConst);
+            break;
+        }
+
+        ASSERT(node->child1().useKind() == UntypedUse);
+
+        if (!(forNode(node->child1()).m_type & ~SpecBytecodeNumber)) {
+            m_state.setFoundConstants(true);
+            forNode(node) = forNode(node->child1());
+            break;
+        }
+
+        clobberWorld(node->origin.semantic, clobberLimit);
+        forNode(node).setType(m_graph, SpecBytecodeNumber);
+        break;
+    }
         
     case ToString:
     case CallStringConstructor: {
index da75443..82551fe 100644 (file)
 #include "DFGJITCode.h"
 #include "GetByIdStatus.h"
 #include "Heap.h"
-#include "JSLexicalEnvironment.h"
 #include "JSCInlines.h"
+#include "JSLexicalEnvironment.h"
 #include "JSModuleEnvironment.h"
+#include "NumberConstructor.h"
 #include "ObjectConstructor.h"
 #include "PreciseJumpTargets.h"
 #include "PutByIdFlags.h"
@@ -215,7 +216,7 @@ private:
     template<typename ChecksFunctor>
     bool handleTypedArrayConstructor(int resultOperand, InternalFunction*, int registerOffset, int argumentCountIncludingThis, TypedArrayType, const ChecksFunctor& insertChecks);
     template<typename ChecksFunctor>
-    bool handleConstantInternalFunction(Node* callTargetNode, int resultOperand, InternalFunction*, int registerOffset, int argumentCountIncludingThis, CodeSpecializationKind, const ChecksFunctor& insertChecks);
+    bool handleConstantInternalFunction(Node* callTargetNode, int resultOperand, InternalFunction*, int registerOffset, int argumentCountIncludingThis, CodeSpecializationKind, SpeculatedType, const ChecksFunctor& insertChecks);
     Node* handlePutByOffset(Node* base, unsigned identifier, PropertyOffset, const InferredType::Descriptor&, Node* value);
     Node* handleGetByOffset(SpeculatedType, Node* base, unsigned identifierNumber, PropertyOffset, const InferredType::Descriptor&, NodeType = GetByOffset);
 
@@ -1651,7 +1652,7 @@ bool ByteCodeParser::attemptToInlineCall(Node* callTargetNode, int resultOperand
         };
     
         if (InternalFunction* function = callee.internalFunction()) {
-            if (handleConstantInternalFunction(callTargetNode, resultOperand, function, registerOffset, argumentCountIncludingThis, specializationKind, insertChecksWithAccounting)) {
+            if (handleConstantInternalFunction(callTargetNode, resultOperand, function, registerOffset, argumentCountIncludingThis, specializationKind, prediction, insertChecksWithAccounting)) {
                 RELEASE_ASSERT(didInsertChecks);
                 addToGraph(Phantom, callTargetNode);
                 emitArgumentPhantoms(registerOffset, argumentCountIncludingThis);
@@ -2637,7 +2638,7 @@ bool ByteCodeParser::handleTypedArrayConstructor(
 template<typename ChecksFunctor>
 bool ByteCodeParser::handleConstantInternalFunction(
     Node* callTargetNode, int resultOperand, InternalFunction* function, int registerOffset,
-    int argumentCountIncludingThis, CodeSpecializationKind kind, const ChecksFunctor& insertChecks)
+    int argumentCountIncludingThis, CodeSpecializationKind kind, SpeculatedType prediction, const ChecksFunctor& insertChecks)
 {
     if (verbose)
         dataLog("    Handling constant internal function ", JSValue(function), "\n");
@@ -2668,6 +2669,19 @@ bool ByteCodeParser::handleConstantInternalFunction(
             addToGraph(Node::VarArg, NewArray, OpInfo(ArrayWithUndecided), OpInfo(0)));
         return true;
     }
+
+    if (function->classInfo() == NumberConstructor::info()) {
+        if (kind == CodeForConstruct)
+            return false;
+
+        insertChecks();
+        if (argumentCountIncludingThis <= 1)
+            set(VirtualRegister(resultOperand), jsConstant(jsNumber(0)));
+        else
+            set(VirtualRegister(resultOperand), addToGraph(ToNumber, OpInfo(0), OpInfo(prediction), get(virtualRegisterForArgument(1, registerOffset))));
+
+        return true;
+    }
     
     if (function->classInfo() == StringConstructor::info()) {
         insertChecks();
@@ -5046,9 +5060,9 @@ bool ByteCodeParser::parseBlock(unsigned limit)
         }
 
         case op_to_number: {
-            Node* node = get(VirtualRegister(currentInstruction[2].u.operand));
-            addToGraph(Phantom, Edge(node, NumberUse));
-            set(VirtualRegister(currentInstruction[1].u.operand), node);
+            SpeculatedType prediction = getPrediction();
+            Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
+            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(ToNumber, OpInfo(0), OpInfo(prediction), value));
             NEXT_OPCODE(op_to_number);
         }
 
index 97a404b..6ae2df2 100644 (file)
@@ -1173,6 +1173,12 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         read(World);
         write(Heap);
         return;
+
+    case ToNumber: {
+        read(World);
+        write(Heap);
+        return;
+    }
         
     case ToString:
     case CallStringConstructor:
index 9b46be5..f5face9 100644 (file)
@@ -566,6 +566,15 @@ private:
                 break;
             }
 
+            case ToNumber: {
+                if (m_state.forNode(node->child1()).m_type & ~SpecBytecodeNumber)
+                    break;
+
+                node->convertToIdentity();
+                changed = true;
+                break;
+            }
+
             case Check: {
                 alreadyHandled = true;
                 m_interpreter.execute(indexInBlock);
index 8d8f9e2..caf30e3 100644 (file)
@@ -169,6 +169,7 @@ bool doesGC(Graph& graph, Node* node)
     case TypeOf:
     case LogicalNot:
     case ToPrimitive:
+    case ToNumber:
     case ToString:
     case CallStringConstructor:
     case In:
index 48653b5..997acca 100644 (file)
@@ -989,6 +989,11 @@ private:
             fixupToPrimitive(node);
             break;
         }
+
+        case ToNumber: {
+            fixupToNumber(node);
+            break;
+        }
             
         case ToString:
         case CallStringConstructor: {
@@ -1798,6 +1803,32 @@ private:
             return;
         }
     }
+
+    void fixupToNumber(Node* node)
+    {
+        if (node->child1()->shouldSpeculateInt32()) {
+            fixEdge<Int32Use>(node->child1());
+            node->convertToIdentity();
+            return;
+        }
+
+        if (enableInt52() && node->child1()->shouldSpeculateAnyInt()) {
+            fixEdge<Int52RepUse>(node->child1());
+            node->convertToIdentity();
+            node->setResult(NodeResultInt52);
+            return;
+        }
+
+        if (node->child1()->shouldSpeculateNumber()) {
+            fixEdge<DoubleRepUse>(node->child1());
+            node->convertToIdentity();
+            node->setResult(NodeResultDouble);
+            return;
+        }
+
+        fixEdge<UntypedUse>(node->child1());
+        node->setResult(NodeResultJS);
+    }
     
     void fixupToStringOrCallStringConstructor(Node* node)
     {
index 946c0bf..3bf3b2c 100644 (file)
@@ -1439,6 +1439,7 @@ struct Node {
         case GetGlobalLexicalVariable:
         case StringReplace:
         case StringReplaceRegExp:
+        case ToNumber:
             return true;
         default:
             return false;
index 542b47f..f93b3bf 100644 (file)
@@ -322,6 +322,7 @@ namespace JSC { namespace DFG {
     macro(LogicalNot, NodeResultBoolean) \
     macro(ToPrimitive, NodeResultJS | NodeMustGenerate) \
     macro(ToString, NodeResultJS | NodeMustGenerate) \
+    macro(ToNumber, NodeResultJS | NodeMustGenerate) \
     macro(CallObjectConstructor, NodeResultJS) \
     macro(CallStringConstructor, NodeResultJS | NodeMustGenerate) \
     macro(NewStringObject, NodeResultJS) \
index 0a6aaa0..d1b388d 100644 (file)
@@ -731,6 +731,14 @@ EncodedJSValue JIT_OPERATION operationToPrimitive(ExecState* exec, EncodedJSValu
     return JSValue::encode(JSValue::decode(value).toPrimitive(exec));
 }
 
+EncodedJSValue JIT_OPERATION operationToNumber(ExecState* exec, EncodedJSValue value)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+
+    return JSValue::encode(jsNumber(JSValue::decode(value).toNumber(exec)));
+}
+
 EncodedJSValue JIT_OPERATION operationGetByIdWithThis(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedThis, UniquedStringImpl* impl)
 {
     VM& vm = exec->vm();
index fe84ee3..fbc862f 100644 (file)
@@ -58,6 +58,7 @@ EncodedJSValue JIT_OPERATION operationGetByValCell(ExecState*, JSCell*, EncodedJ
 EncodedJSValue JIT_OPERATION operationGetByValArrayInt(ExecState*, JSArray*, int32_t) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValStringInt(ExecState*, JSString*, int32_t) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationToPrimitive(ExecState*, EncodedJSValue) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationToNumber(ExecState*, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByIdWithThis(ExecState*, EncodedJSValue, EncodedJSValue, UniquedStringImpl*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValWithThis(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
 char* JIT_OPERATION operationNewArray(ExecState*, Structure*, void*, size_t) WTF_INTERNAL;
index 792aa85..a2dbe44 100644 (file)
@@ -712,7 +712,8 @@ private:
         case GetGlobalVar:
         case GetGlobalLexicalVariable:
         case GetClosureVar:
-        case GetFromArguments: {
+        case GetFromArguments:
+        case ToNumber: {
             setPrediction(m_currentNode->getHeapPrediction());
             break;
         }
index f8e6613..c1a3bbe 100644 (file)
@@ -272,6 +272,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case CallObjectConstructor:
     case ToPrimitive:
     case ToString:
+    case ToNumber:
     case SetFunctionName:
     case StrCat:
     case CallStringConstructor:
index 5b182b4..746c409 100644 (file)
@@ -3567,32 +3567,73 @@ void SpeculativeJIT::compile(Node* node)
         
     case ToPrimitive: {
         RELEASE_ASSERT(node->child1().useKind() == UntypedUse);
-        JSValueOperand op1(this, node->child1());
-        GPRTemporary resultTag(this, Reuse, op1, TagWord);
-        GPRTemporary resultPayload(this, Reuse, op1, PayloadWord);
+        JSValueOperand argument(this, node->child1());
+        GPRTemporary resultTag(this, Reuse, argument, TagWord);
+        GPRTemporary resultPayload(this, Reuse, argument, PayloadWord);
         
-        GPRReg op1TagGPR = op1.tagGPR();
-        GPRReg op1PayloadGPR = op1.payloadGPR();
+        GPRReg argumentTagGPR = argument.tagGPR();
+        GPRReg argumentPayloadGPR = argument.payloadGPR();
         GPRReg resultTagGPR = resultTag.gpr();
         GPRReg resultPayloadGPR = resultPayload.gpr();
         
-        op1.use();
+        argument.use();
         
-        MacroAssembler::Jump alreadyPrimitive = m_jit.branchIfNotCell(op1.jsValueRegs());
-        MacroAssembler::Jump notPrimitive = m_jit.branchIfObject(op1PayloadGPR);
+        MacroAssembler::Jump alreadyPrimitive = m_jit.branchIfNotCell(argument.jsValueRegs());
+        MacroAssembler::Jump notPrimitive = m_jit.branchIfObject(argumentPayloadGPR);
         
         alreadyPrimitive.link(&m_jit);
-        m_jit.move(op1TagGPR, resultTagGPR);
-        m_jit.move(op1PayloadGPR, resultPayloadGPR);
+        m_jit.move(argumentTagGPR, resultTagGPR);
+        m_jit.move(argumentPayloadGPR, resultPayloadGPR);
         
         addSlowPathGenerator(
             slowPathCall(
                 notPrimitive, this, operationToPrimitive,
-                JSValueRegs(resultTagGPR, resultPayloadGPR), op1TagGPR, op1PayloadGPR));
+                JSValueRegs(resultTagGPR, resultPayloadGPR), argumentTagGPR, argumentPayloadGPR));
         
         jsValueResult(resultTagGPR, resultPayloadGPR, node, UseChildrenCalledExplicitly);
         break;
     }
+
+    case ToNumber: {
+        JSValueOperand argument(this, node->child1());
+        GPRTemporary resultTag(this, Reuse, argument, TagWord);
+        GPRTemporary resultPayload(this, Reuse, argument, PayloadWord);
+
+        GPRReg argumentPayloadGPR = argument.payloadGPR();
+        GPRReg argumentTagGPR = argument.tagGPR();
+        JSValueRegs resultRegs(resultTag.gpr(), resultPayload.gpr());
+
+        argument.use();
+
+        // We have several attempts to remove ToNumber. But ToNumber still exists.
+        // It means that converting non-numbers to numbers by this ToNumber is not rare.
+        // Instead of the slow path generator, we emit callOperation here.
+        if (!(m_state.forNode(node->child1()).m_type & SpecBytecodeNumber)) {
+            flushRegisters();
+            callOperation(operationToNumber, resultRegs, argumentTagGPR, argumentPayloadGPR);
+            m_jit.exceptionCheck();
+        } else {
+            MacroAssembler::Jump notNumber;
+            {
+                GPRTemporary scratch(this);
+                notNumber = m_jit.branchIfNotNumber(argument.jsValueRegs(), scratch.gpr());
+            }
+            m_jit.move(argumentTagGPR, resultRegs.tagGPR());
+            m_jit.move(argumentPayloadGPR, resultRegs.payloadGPR());
+            MacroAssembler::Jump done = m_jit.jump();
+
+            notNumber.link(&m_jit);
+            silentSpillAllRegisters(resultRegs);
+            callOperation(operationToNumber, resultRegs, argumentTagGPR, argumentPayloadGPR);
+            silentFillAllRegisters(resultRegs);
+            m_jit.exceptionCheck();
+
+            done.link(&m_jit);
+        }
+
+        jsValueResult(resultRegs.tagGPR(), resultRegs.payloadGPR(), node, UseChildrenCalledExplicitly);
+        break;
+    }
         
     case ToString:
     case CallStringConstructor: {
index b63ba1a..24d6e32 100644 (file)
@@ -3520,26 +3520,60 @@ void SpeculativeJIT::compile(Node* node)
         
     case ToPrimitive: {
         DFG_ASSERT(m_jit.graph(), node, node->child1().useKind() == UntypedUse);
-        JSValueOperand op1(this, node->child1());
-        GPRTemporary result(this, Reuse, op1);
+        JSValueOperand argument(this, node->child1());
+        GPRTemporary result(this, Reuse, argument);
         
-        GPRReg op1GPR = op1.gpr();
+        GPRReg argumentGPR = argument.gpr();
         GPRReg resultGPR = result.gpr();
         
-        op1.use();
+        argument.use();
         
-        MacroAssembler::Jump alreadyPrimitive = m_jit.branchIfNotCell(JSValueRegs(op1GPR));
-        MacroAssembler::Jump notPrimitive = m_jit.branchIfObject(op1GPR);
+        MacroAssembler::Jump alreadyPrimitive = m_jit.branchIfNotCell(JSValueRegs(argumentGPR));
+        MacroAssembler::Jump notPrimitive = m_jit.branchIfObject(argumentGPR);
         
         alreadyPrimitive.link(&m_jit);
-        m_jit.move(op1GPR, resultGPR);
+        m_jit.move(argumentGPR, resultGPR);
         
         addSlowPathGenerator(
-            slowPathCall(notPrimitive, this, operationToPrimitive, resultGPR, op1GPR));
+            slowPathCall(notPrimitive, this, operationToPrimitive, resultGPR, argumentGPR));
         
         jsValueResult(resultGPR, node, UseChildrenCalledExplicitly);
         break;
     }
+
+    case ToNumber: {
+        JSValueOperand argument(this, node->child1());
+        GPRTemporary result(this, Reuse, argument);
+
+        GPRReg argumentGPR = argument.gpr();
+        GPRReg resultGPR = result.gpr();
+
+        argument.use();
+
+        // We have several attempts to remove ToNumber. But ToNumber still exists.
+        // It means that converting non-numbers to numbers by this ToNumber is not rare.
+        // Instead of the slow path generator, we emit callOperation here.
+        if (!(m_state.forNode(node->child1()).m_type & SpecBytecodeNumber)) {
+            flushRegisters();
+            callOperation(operationToNumber, resultGPR, argumentGPR);
+            m_jit.exceptionCheck();
+        } else {
+            MacroAssembler::Jump notNumber = m_jit.branchIfNotNumber(argumentGPR);
+            m_jit.move(argumentGPR, resultGPR);
+            MacroAssembler::Jump done = m_jit.jump();
+
+            notNumber.link(&m_jit);
+            silentSpillAllRegisters(resultGPR);
+            callOperation(operationToNumber, resultGPR, argumentGPR);
+            silentFillAllRegisters(resultGPR);
+            m_jit.exceptionCheck();
+
+            done.link(&m_jit);
+        }
+
+        jsValueResult(resultGPR, node, UseChildrenCalledExplicitly);
+        break;
+    }
         
     case ToString:
     case CallStringConstructor: {
index 93e1c26..ed784bf 100644 (file)
@@ -161,8 +161,9 @@ inline CapabilityLevel canCompile(Node* node)
     case GetScope:
     case GetCallee:
     case GetArgumentCountIncludingThis:
-    case CallObjectConstructor:
+    case ToNumber:
     case ToString:
+    case CallObjectConstructor:
     case CallStringConstructor:
     case MakeRope:
     case NewArrayWithSize:
@@ -404,6 +405,8 @@ inline CapabilityLevel canCompile(Node* node)
             break;
         if (node->isBinaryUseKind(BooleanUse))
             break;
+        if (node->isBinaryUseKind(UntypedUse))
+            break;
         if (node->isBinaryUseKind(SymbolUse))
             break;
         if (node->isBinaryUseKind(MiscUse, UntypedUse))
index a65da79..d0be77b 100644 (file)
@@ -728,6 +728,9 @@ private:
         case ReallocatePropertyStorage:
             compileReallocatePropertyStorage();
             break;
+        case ToNumber:
+            compileToNumber();
+            break;
         case ToString:
         case CallStringConstructor:
             compileToStringOrCallStringConstructor();
@@ -4099,6 +4102,33 @@ private:
             reallocatePropertyStorage(
                 object, oldStorage, transition->previous, transition->next));
     }
+
+    void compileToNumber()
+    {
+        LValue value = lowJSValue(m_node->child1());
+
+        if (!(abstractValue(m_node->child1()).m_type & SpecBytecodeNumber))
+            setJSValue(vmCall(m_out.int64, m_out.operation(operationToNumber), m_callFrame, value));
+        else {
+            LBasicBlock notNumber = m_out.newBlock();
+            LBasicBlock continuation = m_out.newBlock();
+
+            ValueFromBlock fastResult = m_out.anchor(value);
+            m_out.branch(isNumber(value, provenType(m_node->child1())), unsure(continuation), unsure(notNumber));
+
+            // notNumber case.
+            LBasicBlock lastNext = m_out.appendTo(notNumber, continuation);
+            // We have several attempts to remove ToNumber. But ToNumber still exists.
+            // It means that converting non-numbers to numbers by this ToNumber is not rare.
+            // Instead of the lazy slow path generator, we call the operation here.
+            ValueFromBlock slowResult = m_out.anchor(vmCall(m_out.int64, m_out.operation(operationToNumber), m_callFrame, value));
+            m_out.jump(continuation);
+
+            // continuation case.
+            m_out.appendTo(continuation, lastNext);
+            setJSValue(m_out.phi(m_out.int64, fastResult, slowResult));
+        }
+    }
     
     void compileToStringOrCallStringConstructor()
     {
@@ -4889,6 +4919,15 @@ private:
             return;
         }
 
+        if (m_node->isBinaryUseKind(UntypedUse)) {
+            nonSpeculativeCompare(
+                [&] (LValue left, LValue right) {
+                    return m_out.equal(left, right);
+                },
+                operationCompareStrictEq);
+            return;
+        }
+
         if (m_node->isBinaryUseKind(SymbolUse)) {
             LValue left = lowSymbol(m_node->child1());
             LValue right = lowSymbol(m_node->child2());
index e14306f..0555697 100644 (file)
@@ -755,24 +755,29 @@ public:
     {
 #if USE(JSVALUE64)
         UNUSED_PARAM(tempGPR);
-        if (mode == HaveTagRegisters)
-            return branchTest64(NonZero, regs.gpr(), GPRInfo::tagTypeNumberRegister);
-        return branchTest64(NonZero, regs.gpr(), TrustedImm64(TagTypeNumber));
+        return branchIfNumber(regs.gpr(), mode);
 #else
         UNUSED_PARAM(mode);
         add32(TrustedImm32(1), regs.tagGPR(), tempGPR);
         return branch32(Below, tempGPR, TrustedImm32(JSValue::LowestTag + 1));
 #endif
     }
+
+#if USE(JSVALUE64)
+    Jump branchIfNumber(GPRReg reg, TagRegistersMode mode = HaveTagRegisters)
+    {
+        if (mode == HaveTagRegisters)
+            return branchTest64(NonZero, reg, GPRInfo::tagTypeNumberRegister);
+        return branchTest64(NonZero, reg, TrustedImm64(TagTypeNumber));
+    }
+#endif
     
     // Note that the tempGPR is not used in 64-bit mode.
     Jump branchIfNotNumber(JSValueRegs regs, GPRReg tempGPR, TagRegistersMode mode = HaveTagRegisters)
     {
 #if USE(JSVALUE64)
         UNUSED_PARAM(tempGPR);
-        if (mode == HaveTagRegisters)
-            return branchTest64(Zero, regs.gpr(), GPRInfo::tagTypeNumberRegister);
-        return branchTest64(Zero, regs.gpr(), TrustedImm64(TagTypeNumber));
+        return branchIfNotNumber(regs.gpr(), mode);
 #else
         UNUSED_PARAM(mode);
         add32(TrustedImm32(1), regs.tagGPR(), tempGPR);
@@ -780,6 +785,15 @@ public:
 #endif
     }
 
+#if USE(JSVALUE64)
+    Jump branchIfNotNumber(GPRReg reg, TagRegistersMode mode = HaveTagRegisters)
+    {
+        if (mode == HaveTagRegisters)
+            return branchTest64(Zero, reg, GPRInfo::tagTypeNumberRegister);
+        return branchTest64(Zero, reg, TrustedImm64(TagTypeNumber));
+    }
+#endif
+
     Jump branchIfNotDoubleKnownNotInt32(JSValueRegs regs, TagRegistersMode mode = HaveTagRegisters)
     {
 #if USE(JSVALUE64)
index 81ae0df..c44391c 100644 (file)
@@ -516,12 +516,15 @@ void JIT::emit_op_nstricteq(Instruction* currentInstruction)
 
 void JIT::emit_op_to_number(Instruction* currentInstruction)
 {
+    int dstVReg = currentInstruction[1].u.operand;
     int srcVReg = currentInstruction[2].u.operand;
     emitGetVirtualRegister(srcVReg, regT0);
     
     addSlowCase(emitJumpIfNotNumber(regT0));
 
-    emitPutVirtualRegister(currentInstruction[1].u.operand);
+    emitValueProfilingSite();
+    if (srcVReg != dstVReg)
+        emitPutVirtualRegister(dstVReg);
 }
 
 void JIT::emit_op_to_string(Instruction* currentInstruction)
index 5790cba..55db416 100644 (file)
@@ -828,6 +828,7 @@ void JIT::emit_op_to_number(Instruction* currentInstruction)
     addSlowCase(branch32(AboveOrEqual, regT1, TrustedImm32(JSValue::LowestTag)));
     isInt32.link(this);
 
+    emitValueProfilingSite();
     if (src != dst)
         emitStore(dst, regT1, regT0);
 }
index 0622dc6..978128a 100644 (file)
@@ -955,7 +955,8 @@ _llint_op_to_number:
 .opToNumberIsInt:
     storei t2, TagOffset[cfr, t1, 8]
     storei t3, PayloadOffset[cfr, t1, 8]
-    dispatch(3)
+    valueProfile(t2, t3, 12, t1)
+    dispatch(4)
 
 .opToNumberSlow:
     callOpcodeSlowPath(_slow_path_to_number)
index 0495e27..b9448f1 100644 (file)
@@ -834,11 +834,12 @@ _llint_op_to_number:
     btqz t2, tagTypeNumber, .opToNumberSlow
 .opToNumberIsImmediate:
     storeq t2, [cfr, t1, 8]
-    dispatch(3)
+    valueProfile(t2, 3, t0)
+    dispatch(4)
 
 .opToNumberSlow:
     callOpcodeSlowPath(_slow_path_to_number)
-    dispatch(3)
+    dispatch(4)
 
 
 _llint_op_to_string:
index c811640..1dc74e2 100644 (file)
@@ -967,12 +967,11 @@ namespace JSC {
     protected:
         ExpressionNode* expr() { return m_expr; }
         const ExpressionNode* expr() const { return m_expr; }
+        OpcodeID opcodeID() const { return m_opcodeID; }
 
     private:
         RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
 
-        OpcodeID opcodeID() const { return m_opcodeID; }
-
         ExpressionNode* m_expr;
         OpcodeID m_opcodeID;
     };
@@ -982,6 +981,8 @@ namespace JSC {
         UnaryPlusNode(const JSTokenLocation&, ExpressionNode*);
 
     private:
+        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+
         ExpressionNode* stripUnaryPlus() override { return expr(); }
     };
 
index a6ecfd2..a157891 100644 (file)
@@ -346,12 +346,6 @@ SLOW_PATH_DECL(slow_path_dec)
     RETURN(jsNumber(OP(1).jsValue().toNumber(exec) - 1));
 }
 
-SLOW_PATH_DECL(slow_path_to_number)
-{
-    BEGIN();
-    RETURN(jsNumber(OP_C(2).jsValue().toNumber(exec)));
-}
-
 SLOW_PATH_DECL(slow_path_to_string)
 {
     BEGIN();
@@ -398,6 +392,14 @@ static void updateResultProfileForBinaryArithOp(ExecState* exec, Instruction* pc
 static void updateResultProfileForBinaryArithOp(ExecState*, Instruction*, JSValue, JSValue, JSValue) { }
 #endif
 
+SLOW_PATH_DECL(slow_path_to_number)
+{
+    BEGIN();
+    JSValue argument = OP_C(2).jsValue();
+    JSValue result = jsNumber(argument.toNumber(exec));
+    RETURN_PROFILED(op_to_number, result);
+}
+
 SLOW_PATH_DECL(slow_path_add)
 {
     BEGIN();
index 7c93274..704401f 100644 (file)
@@ -211,8 +211,8 @@ const GlobalObjectMethodTable JSGlobalObject::s_globalObjectMethodTable = { &all
 /* Source for JSGlobalObject.lut.h
 @begin globalObjectTable
   parseFloat            globalFuncParseFloat                         DontEnum|Function 1
-  isNaN                 globalFuncIsNaN                              DontEnum|Function 1
-  isFinite              globalFuncIsFinite                           DontEnum|Function 1
+  isNaN                 JSBuiltin                                    DontEnum|Function 1
+  isFinite              JSBuiltin                                    DontEnum|Function 1
   escape                globalFuncEscape                             DontEnum|Function 1
   unescape              globalFuncUnescape                           DontEnum|Function 1
   decodeURI             globalFuncDecodeURI                          DontEnum|Function 1
@@ -402,7 +402,7 @@ void JSGlobalObject::init(VM& vm)
     m_objectStructureForObjectConstructor.set(vm, this, vm.prototypeMap.emptyObjectStructureForPrototype(m_objectPrototype.get(), JSFinalObject::defaultInlineCapacity()));
 
     m_speciesGetterSetter.set(vm, this, GetterSetter::create(vm, this));
-    m_speciesGetterSetter->setGetter(vm, this, JSFunction::createBuiltinFunction(vm, globalObjectSpeciesGetterCodeGenerator(vm), this, "get [Symbol.species]"));
+    m_speciesGetterSetter->setGetter(vm, this, JSFunction::createBuiltinFunction(vm, globalOperationsSpeciesGetterCodeGenerator(vm), this, "get [Symbol.species]"));
 
     m_typedArrayProto.initLater(
         [] (const Initializer<JSTypedArrayViewPrototype>& init) {
@@ -647,8 +647,6 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
 
     JSFunction* privateFuncAbs = JSFunction::create(vm, this, 0, String(), mathProtoFuncAbs, AbsIntrinsic);
     JSFunction* privateFuncFloor = JSFunction::create(vm, this, 0, String(), mathProtoFuncFloor, FloorIntrinsic);
-    JSFunction* privateFuncIsFinite = JSFunction::create(vm, this, 0, String(), globalFuncIsFinite);
-    JSFunction* privateFuncIsNaN = JSFunction::create(vm, this, 0, String(), globalFuncIsNaN);
     JSFunction* privateFuncTrunc = JSFunction::create(vm, this, 0, String(), mathProtoFuncTrunc, TruncIntrinsic);
 
     JSFunction* privateFuncGetTemplateObject = JSFunction::create(vm, this, 0, String(), getTemplateObject);
@@ -715,8 +713,6 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         GlobalPropertyInfo(vm.propertyNames->builtinNames().absPrivateName(), privateFuncAbs, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().floorPrivateName(), privateFuncFloor, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().truncPrivateName(), privateFuncTrunc, DontEnum | DontDelete | ReadOnly),
-        GlobalPropertyInfo(vm.propertyNames->builtinNames().isFinitePrivateName(), privateFuncIsFinite, DontEnum | DontDelete | ReadOnly),
-        GlobalPropertyInfo(vm.propertyNames->builtinNames().isNaNPrivateName(), privateFuncIsNaN, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().PromisePrivateName(), promiseConstructor, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().ReflectPrivateName(), reflectObject, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().InternalPromisePrivateName(), internalPromiseConstructor, DontEnum | DontDelete | ReadOnly),
index 6b65395..cfd5f68 100644 (file)
@@ -706,17 +706,6 @@ EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec)
     return JSValue::encode(jsNumber(parseFloat(exec->argument(0).toString(exec)->view(exec).get())));
 }
 
-EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec)
-{
-    return JSValue::encode(jsBoolean(std::isnan(exec->argument(0).toNumber(exec))));
-}
-
-EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec)
-{
-    double n = exec->argument(0).toNumber(exec);
-    return JSValue::encode(jsBoolean(std::isfinite(n)));
-}
-
 EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState* exec)
 {
     static Bitmap<256> doNotUnescapeWhenDecodingURI = makeCharacterBitmap(
index adcfaff..4de4de6 100644 (file)
@@ -40,8 +40,6 @@ class JSObject;
 EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState*);
-EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState*);
-EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState*);
index 81b9936..bd300b6 100644 (file)
@@ -35,6 +35,18 @@ const int32_t maxExponentForIntegerMathPow = 1000;
 double JIT_OPERATION operationMathPow(double x, double y) WTF_INTERNAL;
 int32_t JIT_OPERATION operationToInt32(double) WTF_INTERNAL;
 
+inline constexpr double maxSafeInteger()
+{
+    // 2 ^ 53 - 1
+    return 9007199254740991.0;
+}
+
+inline constexpr double minSafeInteger()
+{
+    // -(2 ^ 53 - 1)
+    return -9007199254740991.0;
+}
+
 inline int clz32(uint32_t number)
 {
 #if COMPILER(GCC_OR_CLANG)
index 020ea69..5af356d 100644 (file)
 
 namespace JSC {
 
-static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsFinite(ExecState*);
 static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsInteger(ExecState*);
-static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsNaN(ExecState*);
 static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsSafeInteger(ExecState*);
 
 } // namespace JSC
 
+#include "NumberConstructor.lut.h"
+
 namespace JSC {
 
 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(NumberConstructor);
 
-const ClassInfo NumberConstructor::s_info = { "Function", &InternalFunction::s_info, 0, CREATE_METHOD_TABLE(NumberConstructor) };
+const ClassInfo NumberConstructor::s_info = { "Function", &InternalFunction::s_info, &numberConstructorTable, CREATE_METHOD_TABLE(NumberConstructor) };
+
+/* Source for NumberConstructor.lut.h
+@begin numberConstructorTable
+  isFinite       JSBuiltin                           DontEnum|Function 1
+  isInteger      numberConstructorFuncIsInteger      DontEnum|Function 1
+  isNaN          JSBuiltin                           DontEnum|Function 1
+  isSafeInteger  numberConstructorFuncIsSafeInteger  DontEnum|Function 1
+  parseFloat     globalFuncParseFloat                DontEnum|Function 1
+@end
+*/
 
 NumberConstructor::NumberConstructor(VM& vm, Structure* structure)
     : InternalFunction(vm, structure)
@@ -63,17 +73,12 @@ void NumberConstructor::finishCreation(VM& vm, NumberPrototype* numberPrototype)
     putDirectWithoutTransition(vm, Identifier::fromString(&vm, "EPSILON"), jsDoubleNumber(std::numeric_limits<double>::epsilon()), DontDelete | DontEnum | ReadOnly);
     putDirectWithoutTransition(vm, Identifier::fromString(&vm, "MAX_VALUE"), jsDoubleNumber(1.7976931348623157E+308), DontDelete | DontEnum | ReadOnly);
     putDirectWithoutTransition(vm, Identifier::fromString(&vm, "MIN_VALUE"), jsDoubleNumber(5E-324), DontDelete | DontEnum | ReadOnly);
-    putDirectWithoutTransition(vm, Identifier::fromString(&vm, "MAX_SAFE_INTEGER"), jsDoubleNumber(9007199254740991.0), DontDelete | DontEnum | ReadOnly);
-    putDirectWithoutTransition(vm, Identifier::fromString(&vm, "MIN_SAFE_INTEGER"), jsDoubleNumber(-9007199254740991.0), DontDelete | DontEnum | ReadOnly);
+    putDirectWithoutTransition(vm, Identifier::fromString(&vm, "MAX_SAFE_INTEGER"), jsDoubleNumber(maxSafeInteger()), DontDelete | DontEnum | ReadOnly);
+    putDirectWithoutTransition(vm, Identifier::fromString(&vm, "MIN_SAFE_INTEGER"), jsDoubleNumber(minSafeInteger()), DontDelete | DontEnum | ReadOnly);
     putDirectWithoutTransition(vm, Identifier::fromString(&vm, "NEGATIVE_INFINITY"), jsDoubleNumber(-std::numeric_limits<double>::infinity()), DontDelete | DontEnum | ReadOnly);
     putDirectWithoutTransition(vm, Identifier::fromString(&vm, "POSITIVE_INFINITY"), jsDoubleNumber(std::numeric_limits<double>::infinity()), DontDelete | DontEnum | ReadOnly);
     putDirectWithoutTransition(vm, Identifier::fromString(&vm, "NaN"), jsNaN(), DontDelete | DontEnum | ReadOnly);
 
-    putDirectNativeFunctionWithoutTransition(vm, numberPrototype->globalObject(), Identifier::fromString(&vm, "isFinite"), 1, numberConstructorFuncIsFinite, NoIntrinsic, DontEnum);
-    putDirectNativeFunctionWithoutTransition(vm, numberPrototype->globalObject(), Identifier::fromString(&vm, "isInteger"), 1, numberConstructorFuncIsInteger, NoIntrinsic, DontEnum);
-    putDirectNativeFunctionWithoutTransition(vm, numberPrototype->globalObject(), Identifier::fromString(&vm, "isNaN"), 1, numberConstructorFuncIsNaN, NoIntrinsic, DontEnum);
-    putDirectNativeFunctionWithoutTransition(vm, numberPrototype->globalObject(), Identifier::fromString(&vm, "isSafeInteger"), 1, numberConstructorFuncIsSafeInteger, NoIntrinsic, DontEnum);
-    putDirectNativeFunctionWithoutTransition(vm, numberPrototype->globalObject(), Identifier::fromString(&vm, "parseFloat"), 1, globalFuncParseFloat, NoIntrinsic, DontEnum);
     putDirectWithoutTransition(vm, Identifier::fromString(&vm, "parseInt"), numberPrototype->globalObject()->parseIntFunction(), DontEnum);
 }
 
@@ -108,13 +113,6 @@ CallType NumberConstructor::getCallData(JSCell*, CallData& callData)
     return CallType::Host;
 }
 
-// ECMA-262 20.1.2.2
-static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsFinite(ExecState* exec)
-{
-    JSValue argument = exec->argument(0);
-    return JSValue::encode(jsBoolean(argument.isNumber() && (argument.isInt32() || std::isfinite(argument.asDouble()))));
-}
-
 // ECMA-262 20.1.2.3
 static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsInteger(ExecState* exec)
 {
@@ -131,13 +129,6 @@ static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsInteger(ExecState* ex
     return JSValue::encode(jsBoolean(isInteger));
 }
 
-// ECMA-262 20.1.2.4
-static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsNaN(ExecState* exec)
-{
-    JSValue argument = exec->argument(0);
-    return JSValue::encode(jsBoolean(argument.isDouble() && std::isnan(argument.asDouble())));
-}
-
 // ECMA-262 20.1.2.5
 static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsSafeInteger(ExecState* exec)
 {
index 9f444eb..2cbe757 100644 (file)
@@ -31,7 +31,7 @@ class GetterSetter;
 class NumberConstructor : public InternalFunction {
 public:
     typedef InternalFunction Base;
-    static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | ImplementsHasInstance | ImplementsDefaultHasInstance;
+    static const unsigned StructureFlags = Base::StructureFlags | ImplementsHasInstance | HasStaticPropertyTable;
 
     static NumberConstructor* create(VM& vm, Structure* structure, NumberPrototype* numberPrototype, GetterSetter*)
     {
diff --git a/Source/JavaScriptCore/tests/stress/Number-isNaN-basics.js b/Source/JavaScriptCore/tests/stress/Number-isNaN-basics.js
new file mode 100644 (file)
index 0000000..77abe20
--- /dev/null
@@ -0,0 +1,221 @@
+function numberIsNaNOnInteger(value)
+{
+    return Number.isNaN(value);
+}
+noInline(numberIsNaNOnInteger);
+
+// *** Test simple cases on integers. ***
+function testNumberIsNaNOnIntegers()
+{
+    // Bounds.
+    var value = numberIsNaNOnInteger(0);
+    if (value)
+        throw "numberIsNaNOnInteger(0) = " + value;
+
+    var value = numberIsNaNOnInteger(-2147483648);
+    if (value)
+        throw "numberIsNaNOnInteger(-2147483648) = " + value;
+
+    var value = numberIsNaNOnInteger(2147483647);
+    if (value)
+        throw "numberIsNaNOnInteger(2147483647) = " + value;
+
+    // Simple values.
+    var value = numberIsNaNOnInteger(-1);
+    if (value)
+        throw "numberIsNaNOnInteger(-1) = " + value;
+
+    var value = numberIsNaNOnInteger(42);
+    if (value)
+        throw "numberIsNaNOnInteger(42) = " + value;
+
+    var value = numberIsNaNOnInteger(-42);
+    if (value)
+        throw "numberIsNaNOnInteger(-42) = " + value;
+}
+noInline(testNumberIsNaNOnIntegers);
+
+for (var i = 0; i < 1e4; ++i) {
+    testNumberIsNaNOnIntegers();
+}
+
+// Make sure we don't do anything stupid when the type is unexpected.
+function verifyNumberIsNaNOnIntegerWithOtherTypes()
+{
+    var value = numberIsNaNOnInteger(Math.PI);
+    if (value)
+        throw "numberIsNaNOnInteger(Math.PI) = " + value;
+
+    var value = numberIsNaNOnInteger("42");
+    if (value)
+        throw "numberIsNaNOnInteger(\"42\") = " + value;
+
+    var value = numberIsNaNOnInteger("WebKit");
+    if (value)
+        throw "numberIsNaNOnInteger(\"WebKit\") = " + value;
+
+    var value = numberIsNaNOnInteger(-0);
+    if (value)
+        throw "numberIsNaNOnInteger(-0) = " + value;
+}
+noInline(verifyNumberIsNaNOnIntegerWithOtherTypes);
+
+for (var i = 0; i < 1e4; ++i) {
+    verifyNumberIsNaNOnIntegerWithOtherTypes();
+}
+
+
+// *** Test simple cases on doubles. ***
+function numberIsNaNOnDouble(value)
+{
+    return Number.isNaN(value);
+}
+noInline(numberIsNaNOnDouble);
+
+// Test simple cases on doubles.
+function testNumberIsNaNOnDoubles()
+{
+    var value = numberIsNaNOnDouble(Math.PI);
+    if (value)
+        throw "numberIsNaNOnDouble(Math.PI) = " + value;
+
+    var value = numberIsNaNOnDouble(Math.E);
+    if (value)
+        throw "numberIsNaNOnDouble(Math.E) = " + value;
+
+    var value = numberIsNaNOnDouble(Math.LN2);
+    if (value)
+        throw "numberIsNaNOnDouble(Math.LN2) = " + value;
+
+    var value = numberIsNaNOnDouble(-0);
+    if (value)
+        throw "numberIsNaNOnDouble(-0) = " + value;
+
+    var value = numberIsNaNOnDouble(NaN);
+    if (!value)
+        throw "numberIsNaNOnDouble(NaN) = " + value;
+
+    var value = numberIsNaNOnDouble(Number.POSITIVE_INFINITY);
+    if (value)
+        throw "numberIsNaNOnDouble(Number.POSITIVE_INFINITY) = " + value;
+
+    var value = numberIsNaNOnDouble(Number.NEGATIVE_INFINITY);
+    if (value)
+        throw "numberIsNaNOnDouble(Number.NEGATIVE_INFINITY) = " + value;
+}
+noInline(testNumberIsNaNOnDoubles);
+
+for (var i = 0; i < 1e4; ++i) {
+    testNumberIsNaNOnDoubles();
+}
+
+// Make sure we don't do anything stupid when the type is unexpected.
+function verifyNumberIsNaNOnDoublesWithOtherTypes()
+{
+    var value = numberIsNaNOnDouble(1);
+    if (value)
+        throw "numberIsNaNOnDouble(1) = " + value;
+
+    var value = numberIsNaNOnDouble("42");
+    if (value)
+        throw "numberIsNaNOnDouble(\"42\") = " + value;
+
+    var value = numberIsNaNOnDouble("WebKit");
+    if (value)
+        throw "numberIsNaNOnDouble(\"WebKit\") = " + value;
+
+    var value = numberIsNaNOnDouble({});
+    if (value)
+        throw "numberIsNaNOnDouble({}) = " + value;
+}
+noInline(verifyNumberIsNaNOnDoublesWithOtherTypes);
+
+for (var i = 0; i < 1e4; ++i) {
+    verifyNumberIsNaNOnDoublesWithOtherTypes();
+}
+
+
+// *** Unusual arguments. ***
+function numberIsNaNNoArguments()
+{
+    return Number.isNaN();
+}
+noInline(numberIsNaNNoArguments);
+
+function numberIsNaNTooManyArguments(a, b, c)
+{
+    return Number.isNaN(a, b, c);
+}
+noInline(numberIsNaNTooManyArguments);
+
+
+for (var i = 0; i < 1e4; ++i) {
+    var value = numberIsNaNNoArguments();
+    if (value)
+        throw "numberIsNaNNoArguments() = " + value;
+
+    value = numberIsNaNTooManyArguments(2, 3, 5);
+    if (value)
+        throw "numberIsNaNTooManyArguments() = " + value;
+}
+
+
+// *** Constant as arguments. ***
+function testNumberIsNaNOnConstants()
+{
+    var value = Number.isNaN(0);
+    if (value)
+        throw "Number.isNaN(0) = " + value;
+    var value = Number.isNaN(-0);
+    if (value)
+        throw "Number.isNaN(-0) = " + value;
+    var value = Number.isNaN(1);
+    if (value)
+        throw "Number.isNaN(1) = " + value;
+    var value = Number.isNaN(-1);
+    if (value)
+        throw "Number.isNaN(-1) = " + value;
+    var value = Number.isNaN(42);
+    if (value)
+        throw "Number.isNaN(42) = " + value;
+    var value = Number.isNaN(-42);
+    if (value)
+        throw "Number.isNaN(-42) = " + value;
+    var value = Number.isNaN(Number.POSITIVE_INFINITY);
+    if (value)
+        throw "Number.isNaN(Number.POSITIVE_INFINITY) = " + value;
+    var value = Number.isNaN(Number.NEGATIVE_INFINITY);
+    if (value)
+        throw "Number.isNaN(Number.NEGATIVE_INFINITY) = " + value;
+    var value = Number.isNaN(Math.E);
+    if (value)
+        throw "Number.isNaN(Math.E) = " + value;
+    var value = Number.isNaN(NaN);
+    if (!value)
+        throw "Number.isNaN(NaN) = " + value;
+}
+noInline(testNumberIsNaNOnConstants);
+
+for (var i = 0; i < 1e4; ++i) {
+    testNumberIsNaNOnConstants();
+}
+
+
+// *** Struct transition. ***
+function numberIsNaNStructTransition(value)
+{
+    return Number.isNaN(value);
+}
+noInline(numberIsNaNStructTransition);
+
+for (var i = 0; i < 1e4; ++i) {
+    var value = numberIsNaNStructTransition(42);
+    if (value)
+        throw "numberIsNaNStructTransition(42) = " + value;
+}
+
+Number.isNaN = function() { return 123; }
+
+var value = numberIsNaNStructTransition(42);
+if (value !== 123)
+    throw "numberIsNaNStructTransition(42) after transition = " + value;
diff --git a/Source/JavaScriptCore/tests/stress/global-is-finite.js b/Source/JavaScriptCore/tests/stress/global-is-finite.js
new file mode 100644 (file)
index 0000000..bc7f2f7
--- /dev/null
@@ -0,0 +1,18 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${actual}`);
+}
+for (var i = -1e4; i < 1e4; ++i)
+    shouldBe(isFinite(i), true);
+
+// Emit DoubleRep.
+for (var i = 0; i < 1e4; ++i) {
+    shouldBe(isFinite(Infinity), false);
+    shouldBe(isFinite(-Infinity), false);
+    shouldBe(isFinite(NaN), false);
+}
+
+// Emit ToNumber.
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(isFinite("0"), true);
+shouldBe(isFinite("Hello"), false);
diff --git a/Source/JavaScriptCore/tests/stress/global-is-nan.js b/Source/JavaScriptCore/tests/stress/global-is-nan.js
new file mode 100644 (file)
index 0000000..c8408b1
--- /dev/null
@@ -0,0 +1,19 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${actual}`);
+}
+for (var i = -1e4; i < 1e4; ++i)
+    shouldBe(isNaN(i), false);
+
+// Emit DoubleRep.
+for (var i = 0; i < 1e4; ++i) {
+    shouldBe(isNaN(Infinity), false);
+    shouldBe(isNaN(-Infinity), false);
+    shouldBe(isNaN(NaN), true);
+}
+
+// Emit ToNumber.
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(isNaN("0"), false);
+shouldBe(isNaN("Hello"), true);
+shouldBe(isNaN("NaN"), true);
diff --git a/Source/JavaScriptCore/tests/stress/global-isNaN-basics.js b/Source/JavaScriptCore/tests/stress/global-isNaN-basics.js
new file mode 100644 (file)
index 0000000..87df1ce
--- /dev/null
@@ -0,0 +1,260 @@
+function isNaNOnInteger(value)
+{
+    return isNaN(value);
+}
+noInline(isNaNOnInteger);
+
+// *** Test simple cases on integers. ***
+function testIsNaNOnIntegers()
+{
+    // Bounds.
+    var value = isNaNOnInteger(0);
+    if (value)
+        throw "isNaNOnInteger(0) = " + value;
+
+    var value = isNaNOnInteger(-2147483648);
+    if (value)
+        throw "isNaNOnInteger(-2147483648) = " + value;
+
+    var value = isNaNOnInteger(2147483647);
+    if (value)
+        throw "isNaNOnInteger(2147483647) = " + value;
+
+    // Simple values.
+    var value = isNaNOnInteger(-1);
+    if (value)
+        throw "isNaNOnInteger(-1) = " + value;
+
+    var value = isNaNOnInteger(42);
+    if (value)
+        throw "isNaNOnInteger(42) = " + value;
+
+    var value = isNaNOnInteger(-42);
+    if (value)
+        throw "isNaNOnInteger(-42) = " + value;
+}
+noInline(testIsNaNOnIntegers);
+
+for (var i = 0; i < 1e4; ++i) {
+    testIsNaNOnIntegers();
+}
+
+// Make sure we don't do anything stupid when the type is unexpected.
+function verifyIsNaNOnIntegerWithOtherTypes()
+{
+    var value = isNaNOnInteger(Math.PI);
+    if (value)
+        throw "isNaNOnInteger(Math.PI) = " + value;
+
+    var value = isNaNOnInteger("42");
+    if (value)
+        throw "isNaNOnInteger(\"42\") = " + value;
+
+    var value = isNaNOnInteger("WebKit");
+    if (!value)
+        throw "isNaNOnInteger(\"WebKit\") = " + value;
+
+    var value = isNaNOnInteger(-0);
+    if (value)
+        throw "isNaNOnInteger(-0) = " + value;
+}
+noInline(verifyIsNaNOnIntegerWithOtherTypes);
+
+for (var i = 0; i < 1e4; ++i) {
+    verifyIsNaNOnIntegerWithOtherTypes();
+}
+
+// *** Test simple cases on doubles. ***
+function isNaNOnDouble(value)
+{
+    return isNaN(value);
+}
+noInline(isNaNOnDouble);
+
+// Test simple cases on doubles.
+function testIsNaNOnDoubles()
+{
+    var value = isNaNOnDouble(Math.PI);
+    if (value)
+        throw "isNaNOnDouble(Math.PI) = " + value;
+
+    var value = isNaNOnDouble(Math.E);
+    if (value)
+        throw "isNaNOnDouble(Math.E) = " + value;
+
+    var value = isNaNOnDouble(Math.LN2);
+    if (value)
+        throw "isNaNOnDouble(Math.LN2) = " + value;
+
+    var value = isNaNOnDouble(-0);
+    if (value)
+        throw "isNaNOnDouble(-0) = " + value;
+
+    var value = isNaNOnDouble(NaN);
+    if (!value)
+        throw "isNaNOnDouble(NaN) = " + value;
+
+    var value = isNaNOnDouble(Number.POSITIVE_INFINITY);
+    if (value)
+        throw "isNaNOnDouble(Number.POSITIVE_INFINITY) = " + value;
+
+    var value = isNaNOnDouble(Number.NEGATIVE_INFINITY);
+    if (value)
+        throw "isNaNOnDouble(Number.NEGATIVE_INFINITY) = " + value;
+}
+noInline(testIsNaNOnDoubles);
+
+for (var i = 0; i < 1e6; ++i) {
+    testIsNaNOnDoubles();
+}
+
+// Make sure we don't do anything stupid when the type is unexpected.
+function verifyIsNaNOnDoublesWithOtherTypes()
+{
+    var value = isNaNOnDouble(1);
+    if (value)
+        throw "isNaNOnDouble(1) = " + value;
+
+    var value = isNaNOnDouble("42");
+    if (value)
+        throw "isNaNOnDouble(\"42\") = " + value;
+
+    var value = isNaNOnDouble("WebKit");
+    if (!value)
+        throw "isNaNOnDouble(\"WebKit\") = " + value;
+
+    var value = isNaNOnDouble({});
+    if (!value)
+        throw "isNaNOnDouble({}) = " + value;
+}
+noInline(verifyIsNaNOnDoublesWithOtherTypes);
+
+for (var i = 0; i < 1e4; ++i) {
+    verifyIsNaNOnDoublesWithOtherTypes();
+}
+
+// Make sure we still return NaN for type coerced values for global isNaN.
+function verifyIsNaNOnCoercedTypes()
+{
+    var value = isNaNOnInteger("NaN");
+    if (!value)
+        throw "isNaNOnInteger(\"NaN\") = " + value;
+
+    var value = isNaNOnInteger({ valueOf() { return NaN; } });
+    if (!value)
+        throw "isNaNOnInteger({ valueOf() { return NaN; } }) = " + value;
+}
+noInline(verifyIsNaNOnCoercedTypes);
+
+for (var i = 0; i < 1e4; ++i) {
+    verifyIsNaNOnCoercedTypes();
+}
+
+
+// *** Unusual arguments. ***
+function isNaNNoArguments()
+{
+    return isNaN();
+}
+noInline(isNaNNoArguments);
+
+function isNaNTooManyArguments(a, b, c)
+{
+    return isNaN(a, b, c);
+}
+noInline(isNaNTooManyArguments);
+
+
+for (var i = 0; i < 1e4; ++i) {
+    var value = isNaNNoArguments();
+    if (!value)
+        throw "isNaNNoArguments() = " + value;
+
+    value = isNaNTooManyArguments(2, 3, 5);
+    if (value)
+        throw "isNaNTooManyArguments() = " + value;
+}
+
+
+// *** Constant as arguments. ***
+function testIsNaNOnConstants()
+{
+    var value = isNaN(0);
+    if (value)
+        throw "isNaN(0) = " + value;
+    var value = isNaN(-0);
+    if (value)
+        throw "isNaN(-0) = " + value;
+    var value = isNaN(1);
+    if (value)
+        throw "isNaN(1) = " + value;
+    var value = isNaN(-1);
+    if (value)
+        throw "isNaN(-1) = " + value;
+    var value = isNaN(42);
+    if (value)
+        throw "isNaN(42) = " + value;
+    var value = isNaN(-42);
+    if (value)
+        throw "isNaN(-42) = " + value;
+    var value = isNaN(Number.POSITIVE_INFINITY);
+    if (value)
+        throw "isNaN(Number.POSITIVE_INFINITY) = " + value;
+    var value = isNaN(Number.NEGATIVE_INFINITY);
+    if (value)
+        throw "isNaN(Number.NEGATIVE_INFINITY) = " + value;
+    var value = isNaN(Math.E);
+    if (value)
+        throw "isNaN(Math.E) = " + value;
+    var value = isNaN(NaN);
+    if (!value)
+        throw "isNaN(NaN) = " + value;
+}
+noInline(testIsNaNOnConstants);
+
+for (var i = 0; i < 1e4; ++i) {
+    testIsNaNOnConstants();
+}
+
+
+// *** Type Coercion Side effects. ***
+function isNaNTypeCoercionSideEffects(value)
+{
+    return isNaN(value);
+}
+noInline(isNaNTypeCoercionSideEffects);
+
+for (var i = 0; i < 1e4; ++i) {
+    var value = isNaNTypeCoercionSideEffects(42);
+    if (value)
+        throw "isNaNTypeCoercionSideEffects(42) = " + value;
+}
+
+var globalCounter = 0;
+for (var i = 0; i < 1e4; ++i) {
+    var value = isNaNTypeCoercionSideEffects({ valueOf() { return globalCounter++; } });
+    if (value)
+        throw "isNaNTypeCoercionSideEffects({ valueOf() { return globalCounter++; } }) = " + value;
+}
+if (globalCounter !== 1e4)
+    throw "globalCounter =" + globalCounter;
+
+
+// *** Struct transition. ***
+function isNaNStructTransition(value)
+{
+    return isNaN(value);
+}
+noInline(isNaNStructTransition);
+
+for (var i = 0; i < 1e4; ++i) {
+    var value = isNaNStructTransition(42);
+    if (value)
+        throw "isNaNStructTransition(42) = " + value;
+}
+
+isNaN = function() { return 123; }
+
+var value = isNaNStructTransition(42);
+if (value !== 123)
+    throw "isNaNStructTransition(42) after transition = " + value;
diff --git a/Source/JavaScriptCore/tests/stress/number-is-finite.js b/Source/JavaScriptCore/tests/stress/number-is-finite.js
new file mode 100644 (file)
index 0000000..887efb9
--- /dev/null
@@ -0,0 +1,37 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${actual}`);
+}
+
+function test1(i)
+{
+    shouldBe(Number.isFinite(i), true);
+}
+noInline(test1);
+
+for (var i = -1e4; i < 1e4; ++i)
+    test1(i);
+
+function test2(i)
+{
+    shouldBe(Number.isFinite(Infinity), false);
+    shouldBe(Number.isFinite(-Infinity), false);
+    shouldBe(Number.isFinite(NaN), false);
+}
+noInline(test2);
+
+// Emit DoubleRep.
+for (var i = 0; i < 100; ++i)
+    test2(i);
+
+
+function test3(i)
+{
+    shouldBe(Number.isFinite("0"), false);
+    shouldBe(Number.isFinite("Hello"), false);
+}
+noInline(test3);
+
+// Emit IsNumber.
+for (var i = 0; i < 100; ++i)
+    test3(i);
diff --git a/Source/JavaScriptCore/tests/stress/number-is-nan.js b/Source/JavaScriptCore/tests/stress/number-is-nan.js
new file mode 100644 (file)
index 0000000..de4812e
--- /dev/null
@@ -0,0 +1,38 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${actual}`);
+}
+
+function test1(i)
+{
+    shouldBe(Number.isNaN(i), false);
+}
+noInline(test1);
+
+for (var i = -1e4; i < 1e4; ++i)
+    test1(i);
+
+function test2(i)
+{
+    shouldBe(Number.isNaN(Infinity), false);
+    shouldBe(Number.isNaN(-Infinity), false);
+    shouldBe(Number.isNaN(NaN), true);
+}
+noInline(test2);
+
+// Emit DoubleRep.
+for (var i = 0; i < 100; ++i)
+    test2(i);
+
+
+function test3(i)
+{
+    shouldBe(Number.isNaN("0"), false);
+    shouldBe(Number.isNaN("Hello"), false);
+    shouldBe(Number.isNaN("NaN"), false);
+}
+noInline(test3);
+
+// Emit IsNumber.
+for (var i = 0; i < 100; ++i)
+    test3(i);
diff --git a/Source/JavaScriptCore/tests/stress/to-number-basics.js b/Source/JavaScriptCore/tests/stress/to-number-basics.js
new file mode 100644 (file)
index 0000000..438b216
--- /dev/null
@@ -0,0 +1,22 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function test1(value)
+{
+    return Number(value) <= 42;
+}
+noInline(test1);
+
+// Int32.
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test1(42), true);
+
+// Doubles.
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test1(42.195), false);
+
+// Non numbers.
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test1("Hello"), false);
diff --git a/Source/JavaScriptCore/tests/stress/to-number-convert-identity-without-execution.js b/Source/JavaScriptCore/tests/stress/to-number-convert-identity-without-execution.js
new file mode 100644 (file)
index 0000000..1378e47
--- /dev/null
@@ -0,0 +1,26 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function test(x, y)
+{
+    if (x)
+        return Number(y);
+    return y;
+}
+noInline(test);
+
+// Converted to Identity, but since Number is handled by inlining, it emits ForceOSRExit.
+// So converted Identity is never executed.
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(false, 41), 41);
+
+
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(true, 41), 41);
+var object = { valueOf() { return 41; } };
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(true, object), 41);
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(true, { valueOf() { return 42.195; } }), 42.195);
diff --git a/Source/JavaScriptCore/tests/stress/to-number-int52.js b/Source/JavaScriptCore/tests/stress/to-number-int52.js
new file mode 100644 (file)
index 0000000..b58ac92
--- /dev/null
@@ -0,0 +1,29 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function test(x) {
+    var y = x;
+    var z = y * 2;
+    if (z) {
+        z += y;
+        z += y;
+        z += y;
+    }
+    return Number(z) < 42;
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(1000000000), false);
+
+// Extend to Doubles.
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(42.195), false);
+
+// Leave ToNumber for objects.
+// And this should update the value profiling to accept doubles in ToNumber calls.
+var object = { valueOf() { return 42.195; } };
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(object), false);
diff --git a/Source/JavaScriptCore/tests/stress/to-number-intrinsic-convert-to-identity-without-execution.js b/Source/JavaScriptCore/tests/stress/to-number-intrinsic-convert-to-identity-without-execution.js
new file mode 100644 (file)
index 0000000..5d6007b
--- /dev/null
@@ -0,0 +1,22 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function test(x, y)
+{
+    if (x)
+        return +y;
+    return y;
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(false, 41), 41);
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(true, 41), 41);
+var object = { valueOf() { return 41; } };
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(true, object), 41);
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(true, { valueOf() { return 42.195; } }), 42.195);
diff --git a/Source/JavaScriptCore/tests/stress/to-number-intrinsic-int52.js b/Source/JavaScriptCore/tests/stress/to-number-intrinsic-int52.js
new file mode 100644 (file)
index 0000000..1dcbf83
--- /dev/null
@@ -0,0 +1,29 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function test(x) {
+    var y = x;
+    var z = y * 2;
+    if (z) {
+        z += y;
+        z += y;
+        z += y;
+    }
+    return isFinite(z);
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(1000000000), true);
+
+// Extend to Doubles.
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(42.195), true);
+
+// Leave ToNumber for objects.
+// And this should update the value profiling to accept doubles in ToNumber calls.
+var object = { valueOf() { return 42.195; } };
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(object), true);
diff --git a/Source/JavaScriptCore/tests/stress/to-number-intrinsic-object-without-execution.js b/Source/JavaScriptCore/tests/stress/to-number-intrinsic-object-without-execution.js
new file mode 100644 (file)
index 0000000..7482bbd
--- /dev/null
@@ -0,0 +1,18 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function test(x, y)
+{
+    if (x)
+        return +y;
+    return y;
+}
+noInline(test);
+
+var object = { valueOf() { return 41; } };
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(false, object), object);
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(true, object), 41);
diff --git a/Source/JavaScriptCore/tests/stress/to-number-intrinsic-value-profiling.js b/Source/JavaScriptCore/tests/stress/to-number-intrinsic-value-profiling.js
new file mode 100644 (file)
index 0000000..9f6cc04
--- /dev/null
@@ -0,0 +1,18 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function test(x) {
+    return isFinite(x);
+}
+noInline(test);
+
+var object = { valueOf() { return 42; } };
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(object), true);
+
+// This should update the value profiling to accept doubles in ToNumber calls.
+var object = { valueOf() { return 42.195; } };
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(object), true);
diff --git a/Source/JavaScriptCore/tests/stress/to-number-object-without-execution.js b/Source/JavaScriptCore/tests/stress/to-number-object-without-execution.js
new file mode 100644 (file)
index 0000000..856e0da
--- /dev/null
@@ -0,0 +1,20 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function test(x, y)
+{
+    if (x)
+        return Number(y);
+    return y;
+}
+noInline(test);
+
+var object = { valueOf() { return 41; } };
+// Since Number is handled by inlining, it emits ForceOSRExit.
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(false, object), object);
+
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(true, object), 41);
diff --git a/Source/JavaScriptCore/tests/stress/to-number-object.js b/Source/JavaScriptCore/tests/stress/to-number-object.js
new file mode 100644 (file)
index 0000000..689249a
--- /dev/null
@@ -0,0 +1,70 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function test1(object)
+{
+    // The prediction should be Int32.
+    return Number(object);
+}
+noInline(test1);
+
+function test12(object)
+{
+    // Should be Int32 comparison.
+    return Number(object) <= 42;
+}
+noInline(test12);
+
+var object1 = { valueOf() { return 42; } };
+for (var i = 0; i < 1e4; ++i) {
+    shouldBe(test1(object1), 42);
+    shouldBe(test12(object1), true);
+}
+
+function test2(object)
+{
+    // The prediction should be Doubles.
+    return Number(object);
+}
+noInline(test2);
+
+function test22(object)
+{
+    // Should be Double comparison.
+    return Number(object) <= 42;
+}
+noInline(test22);
+
+var object2 = { valueOf() { return 42.195; } };
+for (var i = 0; i < 1e4; ++i) {
+    shouldBe(test2(object2), 42.195);
+    shouldBe(test22(object2), false);
+}
+
+function test3(object)
+{
+    // The prediction should be Int32, and later it should be Doubles.
+    return Number(object);
+}
+noInline(test3);
+
+function test32(object)
+{
+    // Should be Int32 comparison. And later, OSR exit occurs with 42.195. And it should be recompiled as Double comparison.
+    return Number(object) <= 42;
+}
+noInline(test32);
+
+var value = 42;
+var object3 = { valueOf() { return value; } };
+for (var i = 0; i < 1e4; ++i) {
+    shouldBe(test3(object3), value);
+    shouldBe(test32(object3), true);
+}
+value = 42.195;
+for (var i = 0; i < 1e4; ++i) {
+    shouldBe(test3(object3), value);
+    shouldBe(test32(object3), false);
+}
diff --git a/Source/JavaScriptCore/tests/stress/to-number-value-profiling.js b/Source/JavaScriptCore/tests/stress/to-number-value-profiling.js
new file mode 100644 (file)
index 0000000..ea69fe5
--- /dev/null
@@ -0,0 +1,19 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function test(x) {
+    return Number(x) < 42;
+}
+noInline(test);
+
+var object = { valueOf() { return 42; } };
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(object), false);
+
+// Leave ToNumber for objects.
+// And this should update the value profiling to accept doubles in ToNumber calls.
+var object = { valueOf() { return 42.195; } };
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(object), false);