Polymorphic operands in operators coerces downstream values to double.
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 10 May 2016 02:01:28 +0000 (02:01 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 10 May 2016 02:01:28 +0000 (02:01 +0000)
https://bugs.webkit.org/show_bug.cgi?id=151793

Reviewed by Mark Lam.
Source/JavaScriptCore:

Previously if an object flowed into arithmetic, the prediction propagation phase would either
assume that the output of the arithmetic had to be double or sometimes it would assume that it
couldn't be double. We want it to only assume that the output is double if it actually had been.

The first part of this patch is to roll out http://trac.webkit.org/changeset/200502. That removed
some of the machinery that we had in place to detect whether the output of an operation is int or
double. That changeset claimed that the machinery was "fundamentally broken". It actually wasn't.
The reason why it didn't work was that ByteCodeParser was ignoring it if likelyToTakeSlowCase was
false. I think this was a complete goof-up: the code in ByteCodeParser::makeSafe was structured
in a way that made it non-obvious that the method is a no-op if !likelyToTakeSlowCase. So, this
change rolls out r200502 and makes ResultProfile do its job by reshaping how makeSafe processes
it.

This also makes two other changes to shore up ResultProfile:
- OSR exit can now refine a ResultProfile the same way that it refines ValueProfile.
- Baseline JIT slow paths now set bits in ResultProfile.

Based on this stuff, the DFG now predicts int/double/string in op_add/op_sub/op_mul based on
ResultProfiles. To be conservative, we still only use the ResultProfiles if the incoming
prediction is not number-or-boolean. This ensures that we exactly retain our old behavior in
those cases for which it was tuned. But I hope to remove this soon. I believe that ResultProfile
is already strictly better than what prediction propagation was doing before.

This can be an enormous win. This patch adds some simple microbenchmarks that demonstrate the
problem of assuming that arithmetic on objects returns double. The most extreme of these speeds
up 8x with this change (object-int-add-array).

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/CodeBlock.h:
(JSC::CodeBlock::addFrequentExitSite):
(JSC::CodeBlock::hasExitSite):
* bytecode/DFGExitProfile.cpp:
(JSC::DFG::FrequentExitSite::dump):
(JSC::DFG::ExitProfile::ExitProfile):
(JSC::DFG::ExitProfile::~ExitProfile):
(JSC::DFG::ExitProfile::add):
* bytecode/DFGExitProfile.h:
(JSC::DFG::FrequentExitSite::isHashTableDeletedValue):
* bytecode/MethodOfGettingAValueProfile.cpp:
(JSC::MethodOfGettingAValueProfile::fromLazyOperand):
(JSC::MethodOfGettingAValueProfile::emitReportValue):
(JSC::MethodOfGettingAValueProfile::getSpecFailBucket): Deleted.
* bytecode/MethodOfGettingAValueProfile.h:
(JSC::MethodOfGettingAValueProfile::MethodOfGettingAValueProfile):
(JSC::MethodOfGettingAValueProfile::operator bool):
(JSC::MethodOfGettingAValueProfile::operator!): Deleted.
* bytecode/PolymorphicAccess.cpp:
(JSC::AccessCase::generateImpl):
* bytecode/ValueProfile.cpp:
(JSC::ResultProfile::emitDetectBitsLight):
(JSC::ResultProfile::emitSetDouble):
(JSC::ResultProfile::emitSetNonNumber):
(WTF::printInternal):
* bytecode/ValueProfile.h:
(JSC::ResultProfile::ResultProfile):
(JSC::ResultProfile::bytecodeOffset):
(JSC::ResultProfile::specialFastPathCount):
(JSC::ResultProfile::didObserveNonInt32):
(JSC::ResultProfile::didObserveDouble):
(JSC::ResultProfile::didObserveNonNegZeroDouble):
(JSC::ResultProfile::didObserveNegZeroDouble):
(JSC::ResultProfile::didObserveNonNumber):
(JSC::ResultProfile::didObserveInt32Overflow):
(JSC::ResultProfile::didObserveInt52Overflow):
(JSC::ResultProfile::setObservedNonNegZeroDouble):
(JSC::ResultProfile::setObservedNegZeroDouble):
(JSC::ResultProfile::setObservedNonNumber):
(JSC::ResultProfile::setObservedInt32Overflow):
(JSC::ResultProfile::addressOfFlags):
(JSC::ResultProfile::addressOfSpecialFastPathCount):
(JSC::ResultProfile::detectBitsLight):
(JSC::ResultProfile::hasBits):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::makeSafe):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::ensureNaturalLoops):
(JSC::DFG::Graph::methodOfGettingAValueProfileFor):
(JSC::DFG::Graph::valueProfileFor): Deleted.
* dfg/DFGGraph.h:
(JSC::DFG::Graph::hasExitSite):
(JSC::DFG::Graph::numBlocks):
* dfg/DFGNode.h:
(JSC::DFG::Node::arithNodeFlags):
(JSC::DFG::Node::mayHaveNonIntResult):
(JSC::DFG::Node::mayHaveDoubleResult):
(JSC::DFG::Node::mayHaveNonNumberResult):
(JSC::DFG::Node::hasConstantBuffer):
* dfg/DFGNodeFlags.cpp:
(JSC::DFG::dumpNodeFlags):
* dfg/DFGNodeFlags.h:
* dfg/DFGOSRExitCompiler32_64.cpp:
(JSC::DFG::OSRExitCompiler::compileExit):
* dfg/DFGOSRExitCompiler64.cpp:
(JSC::DFG::OSRExitCompiler::compileExit):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* ftl/FTLOSRExitCompiler.cpp:
(JSC::FTL::compileStub):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::branchIfEqual):
(JSC::AssemblyHelpers::branchIfNotCell):
(JSC::AssemblyHelpers::branchIfNotNumber):
(JSC::AssemblyHelpers::branchIfNotDoubleKnownNotInt32):
(JSC::AssemblyHelpers::branchIfBoolean):
(JSC::AssemblyHelpers::branchIfEmpty):
(JSC::AssemblyHelpers::branchStructure):
* jit/CCallHelpers.h:
(JSC::CCallHelpers::CCallHelpers):
(JSC::CCallHelpers::setupArguments):
(JSC::CCallHelpers::setupArgumentsWithExecState):
* jit/IntrinsicEmitter.cpp:
(JSC::AccessCase::emitIntrinsicGetter):
* jit/JIT.h:
* jit/JITAddGenerator.cpp:
(JSC::JITAddGenerator::generateFastPath):
* jit/JITAddGenerator.h:
(JSC::JITAddGenerator::JITAddGenerator):
* jit/JITArithmetic.cpp:
(JSC::JIT::emit_op_add):
(JSC::JIT::emitSlow_op_add):
(JSC::JIT::emit_op_div):
(JSC::JIT::emit_op_mul):
(JSC::JIT::emitSlow_op_mul):
(JSC::JIT::emit_op_sub):
(JSC::JIT::emitSlow_op_sub):
* jit/JITInlines.h:
(JSC::JIT::callOperation):
(JSC::JIT::callOperationNoExceptionCheck):
* jit/JITMulGenerator.cpp:
(JSC::JITMulGenerator::generateFastPath):
* jit/JITOperations.cpp:
* jit/JITOperations.h:
* jit/JITSubGenerator.cpp:
(JSC::JITSubGenerator::generateFastPath):
* jit/JITSubGenerator.h:
(JSC::JITSubGenerator::JITSubGenerator):
* jit/TagRegistersMode.cpp: Added.
(WTF::printInternal):
* jit/TagRegistersMode.h: Added.
* runtime/CommonSlowPaths.cpp:
(JSC::updateResultProfileForBinaryArithOp):

LayoutTests:

* js/regress/object-int-add-array-expected.txt: Added.
* js/regress/object-int-add-array.html: Added.
* js/regress/object-int-add-expected.txt: Added.
* js/regress/object-int-add.html: Added.
* js/regress/object-int-mul-array-expected.txt: Added.
* js/regress/object-int-mul-array.html: Added.
* js/regress/object-int-sub-array-expected.txt: Added.
* js/regress/object-int-sub-array.html: Added.
* js/regress/object-int-sub-expected.txt: Added.
* js/regress/object-int-sub.html: Added.
* js/regress/script-tests/object-int-add-array.js: Added.
(i.o.valueOf):
* js/regress/script-tests/object-int-add.js: Added.
(i.o.valueOf):
* js/regress/script-tests/object-int-mul-array.js: Added.
(i.o.valueOf):
* js/regress/script-tests/object-int-sub-array.js: Added.
(i.o.valueOf):
* js/regress/script-tests/object-int-sub.js: Added.
(i.o.valueOf):

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

59 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/object-int-add-array-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/object-int-add-array.html [new file with mode: 0644]
LayoutTests/js/regress/object-int-add-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/object-int-add.html [new file with mode: 0644]
LayoutTests/js/regress/object-int-mul-array-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/object-int-mul-array.html [new file with mode: 0644]
LayoutTests/js/regress/object-int-sub-array-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/object-int-sub-array.html [new file with mode: 0644]
LayoutTests/js/regress/object-int-sub-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/object-int-sub.html [new file with mode: 0644]
LayoutTests/js/regress/script-tests/object-int-add-array.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/object-int-add.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/object-int-mul-array.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/object-int-sub-array.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/object-int-sub.js [new file with mode: 0644]
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/DFGExitProfile.cpp
Source/JavaScriptCore/bytecode/DFGExitProfile.h
Source/JavaScriptCore/bytecode/MethodOfGettingAValueProfile.cpp
Source/JavaScriptCore/bytecode/MethodOfGettingAValueProfile.h
Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp
Source/JavaScriptCore/bytecode/ValueProfile.cpp
Source/JavaScriptCore/bytecode/ValueProfile.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGGraph.h
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeFlags.cpp
Source/JavaScriptCore/dfg/DFGNodeFlags.h
Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jit/CCallHelpers.h
Source/JavaScriptCore/jit/GPRInfo.h
Source/JavaScriptCore/jit/IntrinsicEmitter.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITAddGenerator.cpp
Source/JavaScriptCore/jit/JITAddGenerator.h
Source/JavaScriptCore/jit/JITArithmetic.cpp
Source/JavaScriptCore/jit/JITInlines.h
Source/JavaScriptCore/jit/JITMulGenerator.cpp
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/jit/JITSubGenerator.cpp
Source/JavaScriptCore/jit/JITSubGenerator.h
Source/JavaScriptCore/jit/TagRegistersMode.cpp [new file with mode: 0644]
Source/JavaScriptCore/jit/TagRegistersMode.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/Options.h

index 4ffa145..7cfcb9f 100644 (file)
@@ -1,3 +1,31 @@
+2016-05-09  Filip Pizlo  <fpizlo@apple.com>
+
+        Polymorphic operands in operators coerces downstream values to double.
+        https://bugs.webkit.org/show_bug.cgi?id=151793
+
+        Reviewed by Mark Lam.
+
+        * js/regress/object-int-add-array-expected.txt: Added.
+        * js/regress/object-int-add-array.html: Added.
+        * js/regress/object-int-add-expected.txt: Added.
+        * js/regress/object-int-add.html: Added.
+        * js/regress/object-int-mul-array-expected.txt: Added.
+        * js/regress/object-int-mul-array.html: Added.
+        * js/regress/object-int-sub-array-expected.txt: Added.
+        * js/regress/object-int-sub-array.html: Added.
+        * js/regress/object-int-sub-expected.txt: Added.
+        * js/regress/object-int-sub.html: Added.
+        * js/regress/script-tests/object-int-add-array.js: Added.
+        (i.o.valueOf):
+        * js/regress/script-tests/object-int-add.js: Added.
+        (i.o.valueOf):
+        * js/regress/script-tests/object-int-mul-array.js: Added.
+        (i.o.valueOf):
+        * js/regress/script-tests/object-int-sub-array.js: Added.
+        (i.o.valueOf):
+        * js/regress/script-tests/object-int-sub.js: Added.
+        (i.o.valueOf):
+
 2016-05-09  Simon Fraser  <simon.fraser@apple.com>
 
         Land correct result for a recently added test.
diff --git a/LayoutTests/js/regress/object-int-add-array-expected.txt b/LayoutTests/js/regress/object-int-add-array-expected.txt
new file mode 100644 (file)
index 0000000..e5135b7
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/object-int-add-array
+
+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/object-int-add-array.html b/LayoutTests/js/regress/object-int-add-array.html
new file mode 100644 (file)
index 0000000..09906f7
--- /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/object-int-add-array.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/object-int-add-expected.txt b/LayoutTests/js/regress/object-int-add-expected.txt
new file mode 100644 (file)
index 0000000..7ebd321
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/object-int-add
+
+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/object-int-add.html b/LayoutTests/js/regress/object-int-add.html
new file mode 100644 (file)
index 0000000..903898e
--- /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/object-int-add.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/object-int-mul-array-expected.txt b/LayoutTests/js/regress/object-int-mul-array-expected.txt
new file mode 100644 (file)
index 0000000..58b2ab7
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/object-int-mul-array
+
+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/object-int-mul-array.html b/LayoutTests/js/regress/object-int-mul-array.html
new file mode 100644 (file)
index 0000000..c942303
--- /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/object-int-mul-array.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/object-int-sub-array-expected.txt b/LayoutTests/js/regress/object-int-sub-array-expected.txt
new file mode 100644 (file)
index 0000000..c902115
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/object-int-sub-array
+
+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/object-int-sub-array.html b/LayoutTests/js/regress/object-int-sub-array.html
new file mode 100644 (file)
index 0000000..70c1388
--- /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/object-int-sub-array.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/object-int-sub-expected.txt b/LayoutTests/js/regress/object-int-sub-expected.txt
new file mode 100644 (file)
index 0000000..5ef759b
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/object-int-sub
+
+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/object-int-sub.html b/LayoutTests/js/regress/object-int-sub.html
new file mode 100644 (file)
index 0000000..7cec7b5
--- /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/object-int-sub.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/object-int-add-array.js b/LayoutTests/js/regress/script-tests/object-int-add-array.js
new file mode 100644 (file)
index 0000000..3d3ab3e
--- /dev/null
@@ -0,0 +1,18 @@
+(function() {
+    var result = 0;
+    var m = 100;
+    var n = 100000;
+    var array = [];
+    for (var i = 0; i < 100; ++i)
+        array.push(12);
+    for (var i = 0; i < m; ++i) {
+        var value = 0;
+        var o = {valueOf: function() { return 42; }};
+        value += o;
+        var result = 0;
+        for (var j = 0; j < n; ++j)
+            result += array[value];
+    }
+    if (result != n * 12)
+        throw "Error: bad result: " + result;
+})();
diff --git a/LayoutTests/js/regress/script-tests/object-int-add.js b/LayoutTests/js/regress/script-tests/object-int-add.js
new file mode 100644 (file)
index 0000000..7f11f5b
--- /dev/null
@@ -0,0 +1,13 @@
+(function() {
+    var result = 0;
+    var m = 1000;
+    var n = 10000;
+    for (var i = 0; i < m; ++i) {
+        var o = {valueOf: function() { return 42; }};
+        result += o;
+        for (var j = 0; j < n; ++j)
+            result += j;
+    }
+    if (result != m * (42 + n * (n - 1) / 2))
+        throw "Error: bad result: " + result;
+})();
diff --git a/LayoutTests/js/regress/script-tests/object-int-mul-array.js b/LayoutTests/js/regress/script-tests/object-int-mul-array.js
new file mode 100644 (file)
index 0000000..4a3e7d5
--- /dev/null
@@ -0,0 +1,18 @@
+(function() {
+    var result = 0;
+    var m = 100;
+    var n = 100000;
+    var array = [];
+    for (var i = 0; i < 100; ++i)
+        array.push(12);
+    for (var i = 0; i < m; ++i) {
+        var value = 1;
+        var o = {valueOf: function() { return 42; }};
+        value *= o;
+        var result = 0;
+        for (var j = 0; j < n; ++j)
+            result += array[value];
+    }
+    if (result != n * 12)
+        throw "Error: bad result: " + result;
+})();
diff --git a/LayoutTests/js/regress/script-tests/object-int-sub-array.js b/LayoutTests/js/regress/script-tests/object-int-sub-array.js
new file mode 100644 (file)
index 0000000..f4359a6
--- /dev/null
@@ -0,0 +1,18 @@
+(function() {
+    var result = 0;
+    var m = 100;
+    var n = 100000;
+    var array = [];
+    for (var i = 0; i < 100; ++i)
+        array.push(12);
+    for (var i = 0; i < m; ++i) {
+        var value = 0;
+        var o = {valueOf: function() { return 42; }};
+        value -= o;
+        var result = 0;
+        for (var j = 0; j < n; ++j)
+            result += array[-value];
+    }
+    if (result != n * 12)
+        throw "Error: bad result: " + result;
+})();
diff --git a/LayoutTests/js/regress/script-tests/object-int-sub.js b/LayoutTests/js/regress/script-tests/object-int-sub.js
new file mode 100644 (file)
index 0000000..2fa6afd
--- /dev/null
@@ -0,0 +1,13 @@
+(function() {
+    var result = 0;
+    var m = 1000;
+    var n = 10000;
+    for (var i = 0; i < m; ++i) {
+        var o = {valueOf: function() { return 42; }};
+        result -= o;
+        for (var j = 0; j < n; ++j)
+            result -= j;
+    }
+    if (result != -m * (42 + n * (n - 1) / 2))
+        throw "Error: bad result: " + result;
+})();
index ed7c3a7..8ea5a7a 100644 (file)
@@ -563,6 +563,7 @@ set(JavaScriptCore_SOURCES
     jit/Repatch.cpp
     jit/ScratchRegisterAllocator.cpp
     jit/SetupVarargsFrame.cpp
+    jit/TagRegistersMode.cpp
     jit/TempRegisterSet.cpp
     jit/ThunkGenerators.cpp
 
index 1d6993b..fe9d247 100644 (file)
@@ -1,3 +1,158 @@
+2016-05-09  Filip Pizlo  <fpizlo@apple.com>
+
+        Polymorphic operands in operators coerces downstream values to double.
+        https://bugs.webkit.org/show_bug.cgi?id=151793
+
+        Reviewed by Mark Lam.
+        
+        Previously if an object flowed into arithmetic, the prediction propagation phase would either
+        assume that the output of the arithmetic had to be double or sometimes it would assume that it
+        couldn't be double. We want it to only assume that the output is double if it actually had been.
+        
+        The first part of this patch is to roll out http://trac.webkit.org/changeset/200502. That removed
+        some of the machinery that we had in place to detect whether the output of an operation is int or
+        double. That changeset claimed that the machinery was "fundamentally broken". It actually wasn't.
+        The reason why it didn't work was that ByteCodeParser was ignoring it if likelyToTakeSlowCase was
+        false. I think this was a complete goof-up: the code in ByteCodeParser::makeSafe was structured
+        in a way that made it non-obvious that the method is a no-op if !likelyToTakeSlowCase. So, this
+        change rolls out r200502 and makes ResultProfile do its job by reshaping how makeSafe processes
+        it.
+        
+        This also makes two other changes to shore up ResultProfile:
+        - OSR exit can now refine a ResultProfile the same way that it refines ValueProfile.
+        - Baseline JIT slow paths now set bits in ResultProfile.
+        
+        Based on this stuff, the DFG now predicts int/double/string in op_add/op_sub/op_mul based on
+        ResultProfiles. To be conservative, we still only use the ResultProfiles if the incoming
+        prediction is not number-or-boolean. This ensures that we exactly retain our old behavior in
+        those cases for which it was tuned. But I hope to remove this soon. I believe that ResultProfile
+        is already strictly better than what prediction propagation was doing before.
+        
+        This can be an enormous win. This patch adds some simple microbenchmarks that demonstrate the
+        problem of assuming that arithmetic on objects returns double. The most extreme of these speeds
+        up 8x with this change (object-int-add-array).
+        
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/CodeBlock.h:
+        (JSC::CodeBlock::addFrequentExitSite):
+        (JSC::CodeBlock::hasExitSite):
+        * bytecode/DFGExitProfile.cpp:
+        (JSC::DFG::FrequentExitSite::dump):
+        (JSC::DFG::ExitProfile::ExitProfile):
+        (JSC::DFG::ExitProfile::~ExitProfile):
+        (JSC::DFG::ExitProfile::add):
+        * bytecode/DFGExitProfile.h:
+        (JSC::DFG::FrequentExitSite::isHashTableDeletedValue):
+        * bytecode/MethodOfGettingAValueProfile.cpp:
+        (JSC::MethodOfGettingAValueProfile::fromLazyOperand):
+        (JSC::MethodOfGettingAValueProfile::emitReportValue):
+        (JSC::MethodOfGettingAValueProfile::getSpecFailBucket): Deleted.
+        * bytecode/MethodOfGettingAValueProfile.h:
+        (JSC::MethodOfGettingAValueProfile::MethodOfGettingAValueProfile):
+        (JSC::MethodOfGettingAValueProfile::operator bool):
+        (JSC::MethodOfGettingAValueProfile::operator!): Deleted.
+        * bytecode/PolymorphicAccess.cpp:
+        (JSC::AccessCase::generateImpl):
+        * bytecode/ValueProfile.cpp:
+        (JSC::ResultProfile::emitDetectBitsLight):
+        (JSC::ResultProfile::emitSetDouble):
+        (JSC::ResultProfile::emitSetNonNumber):
+        (WTF::printInternal):
+        * bytecode/ValueProfile.h:
+        (JSC::ResultProfile::ResultProfile):
+        (JSC::ResultProfile::bytecodeOffset):
+        (JSC::ResultProfile::specialFastPathCount):
+        (JSC::ResultProfile::didObserveNonInt32):
+        (JSC::ResultProfile::didObserveDouble):
+        (JSC::ResultProfile::didObserveNonNegZeroDouble):
+        (JSC::ResultProfile::didObserveNegZeroDouble):
+        (JSC::ResultProfile::didObserveNonNumber):
+        (JSC::ResultProfile::didObserveInt32Overflow):
+        (JSC::ResultProfile::didObserveInt52Overflow):
+        (JSC::ResultProfile::setObservedNonNegZeroDouble):
+        (JSC::ResultProfile::setObservedNegZeroDouble):
+        (JSC::ResultProfile::setObservedNonNumber):
+        (JSC::ResultProfile::setObservedInt32Overflow):
+        (JSC::ResultProfile::addressOfFlags):
+        (JSC::ResultProfile::addressOfSpecialFastPathCount):
+        (JSC::ResultProfile::detectBitsLight):
+        (JSC::ResultProfile::hasBits):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::makeSafe):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGGraph.cpp:
+        (JSC::DFG::Graph::ensureNaturalLoops):
+        (JSC::DFG::Graph::methodOfGettingAValueProfileFor):
+        (JSC::DFG::Graph::valueProfileFor): Deleted.
+        * dfg/DFGGraph.h:
+        (JSC::DFG::Graph::hasExitSite):
+        (JSC::DFG::Graph::numBlocks):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::arithNodeFlags):
+        (JSC::DFG::Node::mayHaveNonIntResult):
+        (JSC::DFG::Node::mayHaveDoubleResult):
+        (JSC::DFG::Node::mayHaveNonNumberResult):
+        (JSC::DFG::Node::hasConstantBuffer):
+        * dfg/DFGNodeFlags.cpp:
+        (JSC::DFG::dumpNodeFlags):
+        * dfg/DFGNodeFlags.h:
+        * dfg/DFGOSRExitCompiler32_64.cpp:
+        (JSC::DFG::OSRExitCompiler::compileExit):
+        * dfg/DFGOSRExitCompiler64.cpp:
+        (JSC::DFG::OSRExitCompiler::compileExit):
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        * ftl/FTLOSRExitCompiler.cpp:
+        (JSC::FTL::compileStub):
+        * jit/AssemblyHelpers.h:
+        (JSC::AssemblyHelpers::branchIfEqual):
+        (JSC::AssemblyHelpers::branchIfNotCell):
+        (JSC::AssemblyHelpers::branchIfNotNumber):
+        (JSC::AssemblyHelpers::branchIfNotDoubleKnownNotInt32):
+        (JSC::AssemblyHelpers::branchIfBoolean):
+        (JSC::AssemblyHelpers::branchIfEmpty):
+        (JSC::AssemblyHelpers::branchStructure):
+        * jit/CCallHelpers.h:
+        (JSC::CCallHelpers::CCallHelpers):
+        (JSC::CCallHelpers::setupArguments):
+        (JSC::CCallHelpers::setupArgumentsWithExecState):
+        * jit/IntrinsicEmitter.cpp:
+        (JSC::AccessCase::emitIntrinsicGetter):
+        * jit/JIT.h:
+        * jit/JITAddGenerator.cpp:
+        (JSC::JITAddGenerator::generateFastPath):
+        * jit/JITAddGenerator.h:
+        (JSC::JITAddGenerator::JITAddGenerator):
+        * jit/JITArithmetic.cpp:
+        (JSC::JIT::emit_op_add):
+        (JSC::JIT::emitSlow_op_add):
+        (JSC::JIT::emit_op_div):
+        (JSC::JIT::emit_op_mul):
+        (JSC::JIT::emitSlow_op_mul):
+        (JSC::JIT::emit_op_sub):
+        (JSC::JIT::emitSlow_op_sub):
+        * jit/JITInlines.h:
+        (JSC::JIT::callOperation):
+        (JSC::JIT::callOperationNoExceptionCheck):
+        * jit/JITMulGenerator.cpp:
+        (JSC::JITMulGenerator::generateFastPath):
+        * jit/JITOperations.cpp:
+        * jit/JITOperations.h:
+        * jit/JITSubGenerator.cpp:
+        (JSC::JITSubGenerator::generateFastPath):
+        * jit/JITSubGenerator.h:
+        (JSC::JITSubGenerator::JITSubGenerator):
+        * jit/TagRegistersMode.cpp: Added.
+        (WTF::printInternal):
+        * jit/TagRegistersMode.h: Added.
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::updateResultProfileForBinaryArithOp):
+
 2016-05-09  Keith Miller  <keith_miller@apple.com>
 
         CallObjectConstructor should not call operationToThis in the FTL
index 7a3bbf5..f3a980b 100644 (file)
                DC17E81A1C9C91E9008A6AB3 /* CCallHelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC17E8161C9C802B008A6AB3 /* CCallHelpers.cpp */; };
                DC2143071CA32E55000A8869 /* ICStats.h in Headers */ = {isa = PBXBuildFile; fileRef = DC2143061CA32E52000A8869 /* ICStats.h */; };
                DC2143081CA32E58000A8869 /* ICStats.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC2143051CA32E52000A8869 /* ICStats.cpp */; };
+               DC7997831CDE9FA0004D4A09 /* TagRegistersMode.h in Headers */ = {isa = PBXBuildFile; fileRef = DC7997821CDE9F9E004D4A09 /* TagRegistersMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               DC7997841CDE9FA2004D4A09 /* TagRegistersMode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC7997811CDE9F9E004D4A09 /* TagRegistersMode.cpp */; };
                DCF3D5691CD2946D003D5C65 /* LazyClassStructure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DCF3D5641CD29468003D5C65 /* LazyClassStructure.cpp */; };
                DCF3D56A1CD29470003D5C65 /* LazyClassStructure.h in Headers */ = {isa = PBXBuildFile; fileRef = DCF3D5651CD29468003D5C65 /* LazyClassStructure.h */; settings = {ATTRIBUTES = (Private, ); }; };
                DCF3D56B1CD29472003D5C65 /* LazyClassStructureInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = DCF3D5661CD29468003D5C65 /* LazyClassStructureInlines.h */; };
                DC17E8161C9C802B008A6AB3 /* CCallHelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CCallHelpers.cpp; sourceTree = "<group>"; };
                DC2143051CA32E52000A8869 /* ICStats.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ICStats.cpp; sourceTree = "<group>"; };
                DC2143061CA32E52000A8869 /* ICStats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ICStats.h; sourceTree = "<group>"; };
+               DC7997811CDE9F9E004D4A09 /* TagRegistersMode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TagRegistersMode.cpp; sourceTree = "<group>"; };
+               DC7997821CDE9F9E004D4A09 /* TagRegistersMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TagRegistersMode.h; sourceTree = "<group>"; };
                DCF3D5641CD29468003D5C65 /* LazyClassStructure.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LazyClassStructure.cpp; sourceTree = "<group>"; };
                DCF3D5651CD29468003D5C65 /* LazyClassStructure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LazyClassStructure.h; sourceTree = "<group>"; };
                DCF3D5661CD29468003D5C65 /* LazyClassStructureInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LazyClassStructureInlines.h; sourceTree = "<group>"; };
                                FE187A0C1C02EBA70038BBCA /* SnippetOperand.h */,
                                A7386551118697B400540279 /* SpecializedThunkJIT.h */,
                                A7FF647A18C52E8500B55307 /* SpillRegistersMode.h */,
+                               DC7997811CDE9F9E004D4A09 /* TagRegistersMode.cpp */,
+                               DC7997821CDE9F9E004D4A09 /* TagRegistersMode.h */,
                                0FC314111814559100033232 /* TempRegisterSet.cpp */,
                                0F24E54817EE274900ABB217 /* TempRegisterSet.h */,
                                0F572D4D16879FDB00E57FBD /* ThunkGenerator.h */,
                                86F3EEBF168CDE930077B92A /* ObjcRuntimeExtras.h in Headers */,
                                14CA958D16AB50FA00938A06 /* ObjectAllocationProfile.h in Headers */,
                                DCF3D56C1CD29475003D5C65 /* LazyProperty.h in Headers */,
+                               DC7997831CDE9FA0004D4A09 /* TagRegistersMode.h in Headers */,
                                BC18C4450E16F5CD00B34460 /* ObjectConstructor.h in Headers */,
                                996B73221BDA08EF00331B84 /* ObjectConstructor.lut.h in Headers */,
                                0FD3E40A1B618B6600C80E1E /* ObjectPropertyCondition.h in Headers */,
                                FE3A06B31C10CB8E00390FDD /* JITBitXorGenerator.cpp in Sources */,
                                A57D23E51890CEBF0031C7FA /* InspectorDebuggerAgent.cpp in Sources */,
                                A532438918568335002ED692 /* InspectorFrontendDispatchers.cpp in Sources */,
+                               DC7997841CDE9FA2004D4A09 /* TagRegistersMode.cpp in Sources */,
                                99F1A6FE1B8E6D9400463B26 /* InspectorFrontendRouter.cpp in Sources */,
                                A5339EC71BB399A90054F005 /* InspectorHeapAgent.cpp in Sources */,
                                E35E035F1B7AB43E0073AD2A /* InspectorInstrumentationObject.cpp in Sources */,
index c385660..edce4a0 100644 (file)
@@ -500,7 +500,7 @@ public:
     {
         ASSERT(JITCode::isBaselineCode(jitType()));
         ConcurrentJITLocker locker(m_lock);
-        return m_exitProfile.add(locker, site);
+        return m_exitProfile.add(locker, this, site);
     }
 
     bool hasExitSite(const ConcurrentJITLocker& locker, const DFG::FrequentExitSite& site) const
index 40a25ce..06b7d9b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #if ENABLE(DFG_JIT)
 
+#include "CodeBlock.h"
+
 namespace JSC { namespace DFG {
 
+void FrequentExitSite::dump(PrintStream& out) const
+{
+    out.print("bc#", m_bytecodeOffset, ": ", m_kind, "/", m_jitType);
+}
+
 ExitProfile::ExitProfile() { }
 ExitProfile::~ExitProfile() { }
 
-bool ExitProfile::add(const ConcurrentJITLocker&, const FrequentExitSite& site)
+bool ExitProfile::add(const ConcurrentJITLocker&, CodeBlock* owner, const FrequentExitSite& site)
 {
     ASSERT(site.jitType() != ExitFromAnything);
+
+    if (Options::verboseExitProfile())
+        dataLog(pointerDump(owner), ": Adding exit site: ", site, "\n");
     
     // If we've never seen any frequent exits then create the list and put this site
     // into it.
index cdecbaf..15cfccb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2012, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2014, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -116,6 +116,8 @@ public:
     {
         return m_kind == ExitKindUnset && m_bytecodeOffset;
     }
+    
+    void dump(PrintStream& out) const;
 
 private:
     unsigned m_bytecodeOffset;
@@ -159,7 +161,7 @@ public:
     // be called a fixed number of times per recompilation. Recompilation is
     // rare to begin with, and implies doing O(n) operations on the CodeBlock
     // anyway.
-    bool add(const ConcurrentJITLocker&, const FrequentExitSite&);
+    bool add(const ConcurrentJITLocker&, CodeBlock* owner, const FrequentExitSite&);
     
     // Get the frequent exit sites for a bytecode index. This is O(n), and is
     // meant to only be used from debugging/profiling code.
index bec692e..4e059e2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -28,6 +28,7 @@
 
 #if ENABLE(DFG_JIT)
 
+#include "CCallHelpers.h"
 #include "CodeBlock.h"
 #include "JSCInlines.h"
 
@@ -44,14 +45,15 @@ MethodOfGettingAValueProfile MethodOfGettingAValueProfile::fromLazyOperand(
     return result;
 }
 
-EncodedJSValue* MethodOfGettingAValueProfile::getSpecFailBucket(unsigned index) const
+void MethodOfGettingAValueProfile::emitReportValue(CCallHelpers& jit, JSValueRegs regs) const
 {
     switch (m_kind) {
     case None:
-        return 0;
+        return;
         
     case Ready:
-        return u.profile->specFailBucket(index);
+        jit.storeValue(regs, u.profile->specFailBucket(0));
+        return;
         
     case LazyOperand: {
         LazyOperandValueProfileKey key(u.lazyOperand.bytecodeOffset, VirtualRegister(u.lazyOperand.operand));
@@ -59,13 +61,16 @@ EncodedJSValue* MethodOfGettingAValueProfile::getSpecFailBucket(unsigned index)
         ConcurrentJITLocker locker(u.lazyOperand.codeBlock->m_lock);
         LazyOperandValueProfile* profile =
             u.lazyOperand.codeBlock->lazyOperandValueProfiles().add(locker, key);
-        return profile->specFailBucket(index);
+        jit.storeValue(regs, profile->specFailBucket(0));
+        return;
     }
         
-    default:
-        RELEASE_ASSERT_NOT_REACHED();
-        return 0;
-    }
+    case ResultProfileReady: {
+        u.resultProfile->emitDetectNumericness(jit, regs, DoNotHaveTagRegisters);
+        return;
+    } }
+    
+    RELEASE_ASSERT_NOT_REACHED();
 }
 
 } // namespace JSC
index 846f8cf..127eb9a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 // these #if's will disappear...
 #if ENABLE(DFG_JIT)
 
+#include "GPRInfo.h"
 #include "JSCJSValue.h"
 
 namespace JSC {
 
+class CCallHelpers;
 class CodeBlock;
 class LazyOperandValueProfileKey;
+struct ResultProfile;
 struct ValueProfile;
 
 class MethodOfGettingAValueProfile {
@@ -47,7 +50,7 @@ public:
     {
     }
     
-    explicit MethodOfGettingAValueProfile(ValueProfile* profile)
+    MethodOfGettingAValueProfile(ValueProfile* profile)
     {
         if (profile) {
             m_kind = Ready;
@@ -56,31 +59,34 @@ public:
             m_kind = None;
     }
     
+    MethodOfGettingAValueProfile(ResultProfile* profile)
+    {
+        if (profile) {
+            m_kind = ResultProfileReady;
+            u.resultProfile = profile;
+        } else
+            m_kind = None;
+    }
+    
     static MethodOfGettingAValueProfile fromLazyOperand(
         CodeBlock*, const LazyOperandValueProfileKey&);
     
-    bool operator!() const { return m_kind == None; }
-    
-    // This logically has a pointer to a "There exists X such that
-    // ValueProfileBase<X>". But since C++ does not have existential
-    // templates, I cannot return it. So instead, for any methods that
-    // users of this class would like to call, we'll just have to provide
-    // a method here that does it through an indirection. Or we could
-    // possibly just make ValueProfile less template-based. But last I
-    // tried that, it felt more yucky than this class.
+    explicit operator bool() const { return m_kind != None; }
     
-    EncodedJSValue* getSpecFailBucket(unsigned index) const;
+    void emitReportValue(CCallHelpers&, JSValueRegs) const;
     
 private:
     enum Kind {
         None,
         Ready,
+        ResultProfileReady,
         LazyOperand
     };
     
     Kind m_kind;
     union {
         ValueProfile* profile;
+        ResultProfile* resultProfile;
         struct {
             CodeBlock* codeBlock;
             unsigned bytecodeOffset;
index dacefb2..b643ace 100644 (file)
@@ -1146,8 +1146,7 @@ void AccessCase::generateImpl(AccessGenerationState& state)
             if (verbose)
                 dataLog("Have type: ", type->descriptor(), "\n");
             state.failAndRepatch.append(
-                jit.branchIfNotType(
-                    valueRegs, scratchGPR, type->descriptor(), CCallHelpers::HaveTagRegisters));
+                jit.branchIfNotType(valueRegs, scratchGPR, type->descriptor()));
         } else if (verbose)
             dataLog("Don't have type.\n");
         
@@ -1177,8 +1176,7 @@ void AccessCase::generateImpl(AccessGenerationState& state)
             if (verbose)
                 dataLog("Have type: ", type->descriptor(), "\n");
             state.failAndRepatch.append(
-                jit.branchIfNotType(
-                    valueRegs, scratchGPR, type->descriptor(), CCallHelpers::HaveTagRegisters));
+                jit.branchIfNotType(valueRegs, scratchGPR, type->descriptor()));
         } else if (verbose)
             dataLog("Don't have type.\n");
         
index f046a9d..6ea5172 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "config.h"
 #include "ValueProfile.h"
 
+#include "CCallHelpers.h"
+#include "JSCInlines.h"
+
+namespace JSC {
+
+void ResultProfile::emitDetectNumericness(CCallHelpers& jit, JSValueRegs regs, TagRegistersMode mode)
+{
+    CCallHelpers::Jump isInt32 = jit.branchIfInt32(regs, mode);
+    CCallHelpers::Jump notDouble = jit.branchIfNotDoubleKnownNotInt32(regs, mode);
+    // FIXME: We could be more precise here.
+    emitSetDouble(jit);
+    CCallHelpers::Jump done = jit.jump();
+    notDouble.link(&jit);
+    emitSetNonNumber(jit);
+    done.link(&jit);
+    isInt32.link(&jit);
+}
+
+void ResultProfile::emitSetDouble(CCallHelpers& jit)
+{
+    jit.or32(CCallHelpers::TrustedImm32(ResultProfile::Int32Overflow | ResultProfile::Int52Overflow | ResultProfile::NegZeroDouble | ResultProfile::NonNegZeroDouble), CCallHelpers::AbsoluteAddress(addressOfFlags()));
+}
+
+void ResultProfile::emitSetNonNumber(CCallHelpers& jit)
+{
+    jit.or32(CCallHelpers::TrustedImm32(ResultProfile::NonNumber), CCallHelpers::AbsoluteAddress(addressOfFlags()));
+}
+
+} // namespace JSC
+
 namespace WTF {
     
 using namespace JSC;
@@ -34,23 +64,31 @@ void printInternal(PrintStream& out, const ResultProfile& profile)
 {
     const char* separator = "";
 
-    if (profile.didObserveNegZeroDouble()) {
-        out.print(separator, "NegZeroDouble");
+    if (!profile.didObserveNonInt32()) {
+        out.print("Int32");
         separator = "|";
+    } else {
+        if (profile.didObserveNegZeroDouble()) {
+            out.print(separator, "NegZeroDouble");
+            separator = "|";
+        }
+        if (profile.didObserveNonNegZeroDouble()) {
+            out.print("NonNegZeroDouble");
+            separator = "|";
+        }
+        if (profile.didObserveNonNumber()) {
+            out.print("NonNumber");
+            separator = "|";
+        }
+        if (profile.didObserveInt32Overflow()) {
+            out.print("Int32Overflow");
+            separator = "|";
+        }
+        if (profile.didObserveInt52Overflow()) {
+            out.print("Int52Overflow");
+            separator = "|";
+        }
     }
-    if (profile.didObserveNonNumber()) {
-        out.print("NonNumber");
-        separator = "|";
-    }
-    if (profile.didObserveInt32Overflow()) {
-        out.print("Int32Overflow");
-        separator = "|";
-    }
-    if (profile.didObserveInt52Overflow()) {
-        out.print("Int52Overflow");
-        separator = "|";
-    }
-
     if (profile.specialFastPathCount()) {
         out.print(" special fast path: ");
         out.print(profile.specialFastPathCount());
index 1fac6b4..8cc2bac 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2013, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "JSArray.h"
 #include "SpeculatedType.h"
 #include "Structure.h"
+#include "TagRegistersMode.h"
 #include "WriteBarrier.h"
 #include <wtf/PrintStream.h>
 #include <wtf/StringPrintStream.h>
 
 namespace JSC {
 
+class CCallHelpers;
+
 template<unsigned numberOfBucketsArgument>
 struct ValueProfileBase {
     static const unsigned numberOfBuckets = numberOfBucketsArgument;
@@ -218,20 +221,25 @@ public:
     }
 
     enum ObservedResults {
-        NegZeroDouble    = 1 << 0,
-        NonNumber        = 1 << 1,
-        Int32Overflow    = 1 << 2,
-        Int52Overflow    = 1 << 3,
+        NonNegZeroDouble = 1 << 0,
+        NegZeroDouble    = 1 << 1,
+        NonNumber        = 1 << 2,
+        Int32Overflow    = 1 << 3,
+        Int52Overflow    = 1 << 4,
     };
 
     int bytecodeOffset() const { return m_bytecodeOffsetAndFlags >> numberOfFlagBits; }
     unsigned specialFastPathCount() const { return m_specialFastPathCount; }
 
+    bool didObserveNonInt32() const { return hasBits(NonNegZeroDouble | NegZeroDouble | NonNumber); }
+    bool didObserveDouble() const { return hasBits(NonNegZeroDouble | NegZeroDouble); }
+    bool didObserveNonNegZeroDouble() const { return hasBits(NonNegZeroDouble); }
     bool didObserveNegZeroDouble() const { return hasBits(NegZeroDouble); }
     bool didObserveNonNumber() const { return hasBits(NonNumber); }
     bool didObserveInt32Overflow() const { return hasBits(Int32Overflow); }
     bool didObserveInt52Overflow() const { return hasBits(Int52Overflow); }
 
+    void setObservedNonNegZeroDouble() { setBit(NonNegZeroDouble); }
     void setObservedNegZeroDouble() { setBit(NegZeroDouble); }
     void setObservedNonNumber() { setBit(NonNumber); }
     void setObservedInt32Overflow() { setBit(Int32Overflow); }
@@ -239,6 +247,27 @@ public:
 
     void* addressOfFlags() { return &m_bytecodeOffsetAndFlags; }
     void* addressOfSpecialFastPathCount() { return &m_specialFastPathCount; }
+    
+    // Sets (Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble) if it sees a
+    // double. Sets NonNumber if it sees a non-number.
+    void emitDetectNumericness(CCallHelpers&, JSValueRegs, TagRegistersMode = HaveTagRegisters);
+    
+    void detectNumericness(JSValue value)
+    {
+        if (value.isInt32())
+            return;
+        if (value.isNumber()) {
+            m_bytecodeOffsetAndFlags |= Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble;
+            return;
+        }
+        m_bytecodeOffsetAndFlags |= NonNumber;
+    }
+    
+    // Sets (Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble).
+    void emitSetDouble(CCallHelpers&);
+    
+    // Sets NonNumber.
+    void emitSetNonNumber(CCallHelpers&);
 
 private:
     bool hasBits(int mask) const { return m_bytecodeOffsetAndFlags & mask; }
index 0b41c06..6d079f6 100644 (file)
@@ -906,40 +906,58 @@ private:
         if (!isX86() && node->op() == ArithMod)
             return node;
 
-        if (!m_inlineStackTop->m_profiledBlock->likelyToTakeSlowCase(m_currentIndex))
-            return node;
+        ResultProfile* resultProfile = m_inlineStackTop->m_profiledBlock->resultProfileForBytecodeOffset(m_currentIndex);
+        if (resultProfile) {
+            switch (node->op()) {
+            case ArithAdd:
+            case ArithSub:
+            case ValueAdd:
+                if (resultProfile->didObserveDouble())
+                    node->mergeFlags(NodeMayHaveDoubleResult);
+                if (resultProfile->didObserveNonNumber())
+                    node->mergeFlags(NodeMayHaveNonNumberResult);
+                break;
+                
+            case ArithMul: {
+                if (resultProfile->didObserveInt52Overflow())
+                    node->mergeFlags(NodeMayOverflowInt52);
+                if (resultProfile->didObserveInt32Overflow() || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow))
+                    node->mergeFlags(NodeMayOverflowInt32InBaseline);
+                if (resultProfile->didObserveNegZeroDouble() || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero))
+                    node->mergeFlags(NodeMayNegZeroInBaseline);
+                if (resultProfile->didObserveDouble())
+                    node->mergeFlags(NodeMayHaveDoubleResult);
+                if (resultProfile->didObserveNonNumber())
+                    node->mergeFlags(NodeMayHaveNonNumberResult);
+                break;
+            }
+                
+            default:
+                break;
+            }
+        }
         
-        switch (node->op()) {
-        case UInt32ToNumber:
-        case ArithAdd:
-        case ArithSub:
-        case ValueAdd:
-        case ArithMod: // for ArithMod "MayOverflow" means we tried to divide by zero, or we saw double.
-            node->mergeFlags(NodeMayOverflowInt32InBaseline);
-            break;
-            
-        case ArithNegate:
-            // Currently we can't tell the difference between a negation overflowing
-            // (i.e. -(1 << 31)) or generating negative zero (i.e. -0). If it took slow
-            // path then we assume that it did both of those things.
-            node->mergeFlags(NodeMayOverflowInt32InBaseline);
-            node->mergeFlags(NodeMayNegZeroInBaseline);
-            break;
-
-        case ArithMul: {
-            ResultProfile& resultProfile = *m_inlineStackTop->m_profiledBlock->resultProfileForBytecodeOffset(m_currentIndex);
-            if (resultProfile.didObserveInt52Overflow())
-                node->mergeFlags(NodeMayOverflowInt52);
-            if (resultProfile.didObserveInt32Overflow() || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow))
+        if (m_inlineStackTop->m_profiledBlock->likelyToTakeSlowCase(m_currentIndex)) {
+            switch (node->op()) {
+            case UInt32ToNumber:
+            case ArithAdd:
+            case ArithSub:
+            case ValueAdd:
+            case ArithMod: // for ArithMod "MayOverflow" means we tried to divide by zero, or we saw double.
+                node->mergeFlags(NodeMayOverflowInt32InBaseline);
+                break;
+                
+            case ArithNegate:
+                // Currently we can't tell the difference between a negation overflowing
+                // (i.e. -(1 << 31)) or generating negative zero (i.e. -0). If it took slow
+                // path then we assume that it did both of those things.
                 node->mergeFlags(NodeMayOverflowInt32InBaseline);
-            if (resultProfile.didObserveNegZeroDouble() || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero))
                 node->mergeFlags(NodeMayNegZeroInBaseline);
-            break;
-        }
-
-        default:
-            RELEASE_ASSERT_NOT_REACHED();
-            break;
+                break;
+                
+            default:
+                break;
+            }
         }
         
         return node;
index 75ea3b9..0395a3f 100644 (file)
@@ -192,9 +192,7 @@ private:
         case ArithAdd:
         case ArithSub: {
             if (op == ArithSub
-                && Node::shouldSpeculateUntypedForArithmetic(node->child1().node(), node->child2().node())
-                && m_graph.hasExitSite(node->origin.semantic, BadType)) {
-
+                && Node::shouldSpeculateUntypedForArithmetic(node->child1().node(), node->child2().node())) {
                 fixEdge<UntypedUse>(node->child1());
                 fixEdge<UntypedUse>(node->child2());
                 node->setResult(NodeResultJS);
@@ -236,8 +234,7 @@ private:
         case ArithMul: {
             Edge& leftChild = node->child1();
             Edge& rightChild = node->child2();
-            if (Node::shouldSpeculateUntypedForArithmetic(leftChild.node(), rightChild.node())
-                && m_graph.hasExitSite(node->origin.semantic, BadType)) {
+            if (Node::shouldSpeculateUntypedForArithmetic(leftChild.node(), rightChild.node())) {
                 fixEdge<UntypedUse>(leftChild);
                 fixEdge<UntypedUse>(rightChild);
                 node->setResult(NodeResultJS);
index 5219741..10c96f9 100644 (file)
@@ -1446,46 +1446,50 @@ void Graph::ensureNaturalLoops()
         m_naturalLoops = std::make_unique<NaturalLoops>(*this);
 }
 
-ValueProfile* Graph::valueProfileFor(Node* node)
+MethodOfGettingAValueProfile Graph::methodOfGettingAValueProfileFor(Node* node)
 {
-    if (!node)
-        return nullptr;
+    while (node) {
+        CodeBlock* profiledBlock = baselineCodeBlockFor(node->origin.semantic);
         
-    CodeBlock* profiledBlock = baselineCodeBlockFor(node->origin.semantic);
+        if (node->hasLocal(*this)) {
+            ValueProfile* result = [&] () -> ValueProfile* {
+                if (!node->local().isArgument())
+                    return nullptr;
+                int argument = node->local().toArgument();
+                Node* argumentNode = m_arguments[argument];
+                if (!argumentNode)
+                    return nullptr;
+                if (node->variableAccessData() != argumentNode->variableAccessData())
+                    return nullptr;
+                return profiledBlock->valueProfileForArgument(argument);
+            }();
+            if (result)
+                return result;
+            
+            if (node->op() == GetLocal) {
+                return MethodOfGettingAValueProfile::fromLazyOperand(
+                    profiledBlock,
+                    LazyOperandValueProfileKey(
+                        node->origin.semantic.bytecodeIndex, node->local()));
+            }
+        }
         
-    if (node->hasLocal(*this)) {
-        if (!node->local().isArgument())
-            return nullptr;
-        int argument = node->local().toArgument();
-        Node* argumentNode = m_arguments[argument];
-        if (!argumentNode)
-            return nullptr;
-        if (node->variableAccessData() != argumentNode->variableAccessData())
-            return nullptr;
-        return profiledBlock->valueProfileForArgument(argument);
-    }
+        if (node->hasHeapPrediction())
+            return profiledBlock->valueProfileForBytecodeOffset(node->origin.semantic.bytecodeIndex);
         
-    if (node->hasHeapPrediction())
-        return profiledBlock->valueProfileForBytecodeOffset(node->origin.semantic.bytecodeIndex);
+        if (ResultProfile* result = profiledBlock->resultProfileForBytecodeOffset(node->origin.semantic.bytecodeIndex))
+            return result;
         
-    return nullptr;
-}
-
-MethodOfGettingAValueProfile Graph::methodOfGettingAValueProfileFor(Node* node)
-{
-    if (!node)
-        return MethodOfGettingAValueProfile();
-    
-    if (ValueProfile* valueProfile = valueProfileFor(node))
-        return MethodOfGettingAValueProfile(valueProfile);
-    
-    if (node->op() == GetLocal) {
-        CodeBlock* profiledBlock = baselineCodeBlockFor(node->origin.semantic);
-        
-        return MethodOfGettingAValueProfile::fromLazyOperand(
-            profiledBlock,
-            LazyOperandValueProfileKey(
-                node->origin.semantic.bytecodeIndex, node->local()));
+        switch (node->op()) {
+        case Identity:
+        case ValueRep:
+        case DoubleRep:
+        case Int52Rep:
+            node = node->child1().node();
+            break;
+        default:
+            node = nullptr;
+        }
     }
     
     return MethodOfGettingAValueProfile();
index ad7e580..86b3eef 100644 (file)
@@ -400,7 +400,6 @@ public:
         return hasExitSite(node->origin.semantic, exitKind);
     }
     
-    ValueProfile* valueProfileFor(Node*);
     MethodOfGettingAValueProfile methodOfGettingAValueProfileFor(Node*);
     
     BlockIndex numBlocks() const { return m_blocks.size(); }
index 98a7b18..e54f1da 100644 (file)
@@ -952,6 +952,21 @@ struct Node {
         return result & ~NodeBytecodeNeedsNegZero;
     }
 
+    bool mayHaveNonIntResult()
+    {
+        return m_flags & NodeMayHaveNonIntResult;
+    }
+    
+    bool mayHaveDoubleResult()
+    {
+        return m_flags & NodeMayHaveDoubleResult;
+    }
+    
+    bool mayHaveNonNumberResult()
+    {
+        return m_flags & NodeMayHaveNonNumberResult;
+    }
+
     bool hasConstantBuffer()
     {
         return op() == NewArrayBuffer;
index 4894242..3408421 100644 (file)
@@ -85,6 +85,12 @@ void dumpNodeFlags(PrintStream& actualOut, NodeFlags flags)
             out.print(comma, "UseAsOther");
     }
 
+    if (flags & NodeMayHaveDoubleResult)
+        out.print(comma, "MayHaveDoubleResult");
+
+    if (flags & NodeMayHaveNonNumberResult)
+        out.print(comma, "MayHaveNonNumberResult");
+
     if (flags & NodeMayOverflowInt52)
         out.print(comma, "MayOverflowInt52");
 
index 308641e..01aca2b 100644 (file)
@@ -48,28 +48,30 @@ namespace JSC { namespace DFG {
 #define NodeHasVarArgs                   0x0010
     
 #define NodeBehaviorMask                 0x07e0
+#define NodeMayHaveDoubleResult          0x0020
 #define NodeMayOverflowInt52             0x0040
 #define NodeMayOverflowInt32InBaseline   0x0080
 #define NodeMayOverflowInt32InDFG        0x0100
 #define NodeMayNegZeroInBaseline         0x0200
 #define NodeMayNegZeroInDFG              0x0400
+#define NodeMayHaveNonNumberResult       0x0800
+#define NodeMayHaveNonIntResult          (NodeMayHaveDoubleResult | NodeMayHaveNonNumberResult)
                                 
-#define NodeBytecodeBackPropMask         0xf800
-#define NodeBytecodeUseBottom            0x0000
-#define NodeBytecodeUsesAsNumber         0x0800 // The result of this computation may be used in a context that observes fractional, or bigger-than-int32, results.
-#define NodeBytecodeNeedsNegZero         0x1000 // The result of this computation may be used in a context that observes -0.
-#define NodeBytecodeUsesAsOther          0x2000 // The result of this computation may be used in a context that distinguishes between NaN and other things (like undefined).
-#define NodeBytecodeUsesAsValue          (NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero | NodeBytecodeUsesAsOther)
-#define NodeBytecodeUsesAsInt            0x4000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values.
-#define NodeBytecodeUsesAsArrayIndex     0x8000 // The result of this computation is known to be used in a context that strongly prefers integer values, to the point that we should avoid using doubles if at all possible.
+#define NodeBytecodeBackPropMask        0x1f000
+#define NodeBytecodeUseBottom           0x00000
+#define NodeBytecodeUsesAsNumber        0x01000 // The result of this computation may be used in a context that observes fractional, or bigger-than-int32, results.
+#define NodeBytecodeNeedsNegZero        0x02000 // The result of this computation may be used in a context that observes -0.
+#define NodeBytecodeUsesAsOther         0x04000 // The result of this computation may be used in a context that distinguishes between NaN and other things (like undefined).
+#define NodeBytecodeUsesAsValue         (NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero | NodeBytecodeUsesAsOther)
+#define NodeBytecodeUsesAsInt           0x08000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values.
+#define NodeBytecodeUsesAsArrayIndex    0x10000 // The result of this computation is known to be used in a context that strongly prefers integer values, to the point that we should avoid using doubles if at all possible.
 
 #define NodeArithFlagsMask               (NodeBehaviorMask | NodeBytecodeBackPropMask)
 
-#define NodeIsFlushed                   0x10000 // Computed by CPSRethreadingPhase, will tell you which local nodes are backwards-reachable from a Flush.
+#define NodeIsFlushed                   0x20000 // Computed by CPSRethreadingPhase, will tell you which local nodes are backwards-reachable from a Flush.
 
-#define NodeMiscFlag1                   0x20000
-#define NodeMiscFlag2                   0x40000
-#define NodeMiscFlag3                   0x80000
+#define NodeMiscFlag1                   0x40000
+#define NodeMiscFlag2                   0x80000
 
 typedef uint32_t NodeFlags;
 
index c3746bd..2721f4f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2013-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -128,28 +128,30 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
             }
         }
         
-        if (!!exit.m_valueProfile) {
-            EncodedJSValue* bucket = exit.m_valueProfile.getSpecFailBucket(0);
-        
+        if (MethodOfGettingAValueProfile profile = exit.m_valueProfile) {
             if (exit.m_jsValueSource.isAddress()) {
                 // Save a register so we can use it.
-                GPRReg scratch = AssemblyHelpers::selectScratchGPR(exit.m_jsValueSource.base());
-                
-                m_jit.push(scratch);
+                GPRReg scratchPayload = AssemblyHelpers::selectScratchGPR(exit.m_jsValueSource.base());
+                GPRReg scratchTag = AssemblyHelpers::selectScratchGPR(exit.m_jsValueSource.base(), scratchPayload);
+                m_jit.pushToSave(scratchPayload);
+                m_jit.pushToSave(scratchTag);
 
-                m_jit.load32(exit.m_jsValueSource.asAddress(OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), scratch);
-                m_jit.store32(scratch, &bitwise_cast<EncodedValueDescriptor*>(bucket)->asBits.tag);
-                m_jit.load32(exit.m_jsValueSource.asAddress(OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), scratch);
-                m_jit.store32(scratch, &bitwise_cast<EncodedValueDescriptor*>(bucket)->asBits.payload);
+                JSValueRegs scratch(scratchTag, scratchPayload);
                 
-                m_jit.pop(scratch);
+                m_jit.loadValue(exit.m_jsValueSource.asAddress(), scratch);
+                profile.emitReportValue(m_jit, scratch);
+                
+                m_jit.popToRestore(scratchTag);
+                m_jit.popToRestore(scratchPayload);
             } else if (exit.m_jsValueSource.hasKnownTag()) {
-                m_jit.store32(AssemblyHelpers::TrustedImm32(exit.m_jsValueSource.tag()), &bitwise_cast<EncodedValueDescriptor*>(bucket)->asBits.tag);
-                m_jit.store32(exit.m_jsValueSource.payloadGPR(), &bitwise_cast<EncodedValueDescriptor*>(bucket)->asBits.payload);
-            } else {
-                m_jit.store32(exit.m_jsValueSource.tagGPR(), &bitwise_cast<EncodedValueDescriptor*>(bucket)->asBits.tag);
-                m_jit.store32(exit.m_jsValueSource.payloadGPR(), &bitwise_cast<EncodedValueDescriptor*>(bucket)->asBits.payload);
-            }
+                GPRReg scratchTag = AssemblyHelpers::selectScratchGPR(exit.m_jsValueSource.payloadGPR());
+                m_jit.pushToSave(scratchTag);
+                m_jit.move(AssemblyHelpers::TrustedImm32(exit.m_jsValueSource.tag()), scratchTag);
+                JSValueRegs value(scratchTag, exit.m_jsValueSource.payloadGPR());
+                profile.emitReportValue(m_jit, value);
+                m_jit.popToRestore(scratchTag);
+            } else
+                profile.emitReportValue(m_jit, exit.m_jsValueSource.regs());
         }
     }
     
index f45c833..eb20ac6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2013-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -134,17 +134,15 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
             }
         }
         
-        if (!!exit.m_valueProfile) {
-            EncodedJSValue* bucket = exit.m_valueProfile.getSpecFailBucket(0);
-            
+        if (MethodOfGettingAValueProfile profile = exit.m_valueProfile) {
             if (exit.m_jsValueSource.isAddress()) {
                 // We can't be sure that we have a spare register. So use the tagTypeNumberRegister,
                 // since we know how to restore it.
                 m_jit.load64(AssemblyHelpers::Address(exit.m_jsValueSource.asAddress()), GPRInfo::tagTypeNumberRegister);
-                m_jit.store64(GPRInfo::tagTypeNumberRegister, bucket);
+                profile.emitReportValue(m_jit, JSValueRegs(GPRInfo::tagTypeNumberRegister));
                 m_jit.move(AssemblyHelpers::TrustedImm64(TagTypeNumber), GPRInfo::tagTypeNumberRegister);
             } else
-                m_jit.store64(exit.m_jsValueSource.gpr(), bucket);
+                profile.emitReportValue(m_jit, JSValueRegs(exit.m_jsValueSource.gpr()));
         }
     }
     
index ce4c283..0f2e505 100644 (file)
@@ -278,17 +278,6 @@ EncodedJSValue JIT_OPERATION operationValueBitURShift(ExecState* exec, EncodedJS
     return JSValue::encode(jsNumber(static_cast<int32_t>(a >> (b & 0x1f))));
 }
 
-EncodedJSValue JIT_OPERATION operationValueAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
-{
-    VM* vm = &exec->vm();
-    NativeCallFrameTracer tracer(vm, exec);
-    
-    JSValue op1 = JSValue::decode(encodedOp1);
-    JSValue op2 = JSValue::decode(encodedOp2);
-    
-    return JSValue::encode(jsAdd(exec, op1, op2));
-}
-
 EncodedJSValue JIT_OPERATION operationValueAddNotNumber(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
 {
     VM* vm = &exec->vm();
@@ -318,32 +307,6 @@ EncodedJSValue JIT_OPERATION operationValueDiv(ExecState* exec, EncodedJSValue e
     return JSValue::encode(jsNumber(a / b));
 }
 
-EncodedJSValue JIT_OPERATION operationValueMul(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
-{
-    VM* vm = &exec->vm();
-    NativeCallFrameTracer tracer(vm, exec);
-
-    JSValue op1 = JSValue::decode(encodedOp1);
-    JSValue op2 = JSValue::decode(encodedOp2);
-
-    double a = op1.toNumber(exec);
-    double b = op2.toNumber(exec);
-    return JSValue::encode(jsNumber(a * b));
-}
-
-EncodedJSValue JIT_OPERATION operationValueSub(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
-{
-    VM* vm = &exec->vm();
-    NativeCallFrameTracer tracer(vm, exec);
-    
-    JSValue op1 = JSValue::decode(encodedOp1);
-    JSValue op2 = JSValue::decode(encodedOp2);
-
-    double a = op1.toNumber(exec);
-    double b = op2.toNumber(exec);
-    return JSValue::encode(jsNumber(a - b));
-}
-
 static ALWAYS_INLINE EncodedJSValue getByVal(ExecState* exec, JSCell* base, uint32_t index)
 {
     VM& vm = exec->vm();
index b199dd7..fcae75e 100644 (file)
@@ -51,11 +51,8 @@ EncodedJSValue JIT_OPERATION operationValueBitXor(ExecState*, EncodedJSValue enc
 EncodedJSValue JIT_OPERATION operationValueBitLShift(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValueBitRShift(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValueBitURShift(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
-EncodedJSValue JIT_OPERATION operationValueAdd(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValueAddNotNumber(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValueDiv(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
-EncodedJSValue JIT_OPERATION operationValueMul(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
-EncodedJSValue JIT_OPERATION operationValueSub(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByVal(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValCell(ExecState*, JSCell*, EncodedJSValue encodedProperty) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValArrayInt(ExecState*, JSArray*, int32_t) WTF_INTERNAL;
index 212dc79..1661e94 100644 (file)
@@ -191,13 +191,16 @@ private:
                         changed |= mergePrediction(SpecInt52Only);
                     else
                         changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
-                } else if (
-                    !(left & (SpecFullNumber | SpecBoolean))
-                    || !(right & (SpecFullNumber | SpecBoolean))) {
+                } else if (isStringOrStringObjectSpeculation(left) && isStringOrStringObjectSpeculation(right)) {
                     // left or right is definitely something other than a number.
                     changed |= mergePrediction(SpecString);
-                } else
-                    changed |= mergePrediction(SpecString | SpecInt32Only | SpecBytecodeDouble);
+                } else {
+                    changed |= mergePrediction(SpecInt32Only);
+                    if (node->mayHaveDoubleResult())
+                        changed |= mergePrediction(SpecBytecodeDouble);
+                    if (node->mayHaveNonNumberResult())
+                        changed |= mergePrediction(SpecString);
+                }
             }
             break;
         }
@@ -211,8 +214,12 @@ private:
                     changed |= mergePrediction(SpecInt32Only);
                 else if (m_graph.addShouldSpeculateAnyInt(node))
                     changed |= mergePrediction(SpecInt52Only);
-                else
+                else if (isFullNumberOrBooleanSpeculation(left) && isFullNumberOrBooleanSpeculation(right))
                     changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
+                else if (node->mayHaveNonIntResult() || (left & SpecBytecodeDouble) || (right & SpecBytecodeDouble))
+                    changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
+                else
+                    changed |= mergePrediction(SpecInt32Only);
             }
             break;
         }
@@ -230,8 +237,10 @@ private:
                         changed |= mergePrediction(SpecInt52Only);
                     else
                         changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
-                } else
+                } else if (node->mayHaveNonIntResult() || (left & SpecBytecodeDouble) || (right & SpecBytecodeDouble))
                     changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
+                else
+                    changed |= mergePrediction(SpecInt32Only);
             }
             break;
         }
@@ -267,6 +276,10 @@ private:
             SpeculatedType right = node->child2()->prediction();
             
             if (left && right) {
+                // FIXME: We're currently relying on prediction propagation and backwards propagation
+                // whenever we can, and only falling back on result flags if that fails. And the result
+                // flags logic doesn't know how to use backwards propagation. We should get rid of the
+                // prediction propagation logic and rely solely on the result type.
                 if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
                     && isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
                     if (m_graph.binaryArithShouldSpeculateInt32(node, m_pass))
@@ -275,8 +288,14 @@ private:
                         changed |= mergePrediction(SpecInt52Only);
                     else
                         changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
-                } else
-                    changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
+                } else {
+                    if (node->mayHaveNonIntResult()
+                        || (left & SpecBytecodeDouble)
+                        || (right & SpecBytecodeDouble))
+                        changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
+                    else
+                        changed |= mergePrediction(SpecInt32Only);
+                }
             }
             break;
         }
index da797d7..1157ccc 100644 (file)
@@ -1687,23 +1687,6 @@ public:
     }
 #else // USE(JSVALUE32_64)
 
-// EncodedJSValue in JSVALUE32_64 is a 64-bit integer. When being compiled in ARM EABI, it must be aligned on an even-numbered register (r0, r2 or [sp]).
-// To prevent the assembler from using wrong registers, let's occupy r1 or r3 with a dummy argument when necessary.
-#if (COMPILER_SUPPORTS(EABI) && CPU(ARM)) || CPU(MIPS)
-#define EABI_32BIT_DUMMY_ARG      TrustedImm32(0),
-#else
-#define EABI_32BIT_DUMMY_ARG
-#endif
-
-// JSVALUE32_64 is a 64-bit integer that cannot be put half in an argument register and half on stack when using SH4 architecture.
-// To avoid this, let's occupy the 4th argument register (r7) with a dummy argument when necessary. This must only be done when there
-// is no other 32-bit value argument behind this 64-bit JSValue.
-#if CPU(SH4)
-#define SH4_32BIT_DUMMY_ARG      TrustedImm32(0),
-#else
-#define SH4_32BIT_DUMMY_ARG
-#endif
-
     JITCompiler::Call callOperation(J_JITOperation_EJJI operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2Tag, GPRReg arg2Payload, UniquedStringImpl* uid)
     {
         m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, arg2Payload, arg2Tag, TrustedImmPtr(uid));
@@ -2144,8 +2127,6 @@ public:
         m_jit.setupArgumentsWithExecState(arg1, TrustedImmPtr(identOp2), TrustedImm32(op3), arg4, arg5);
         return appendCall(operation);
     }
-#undef EABI_32BIT_DUMMY_ARG
-#undef SH4_32BIT_DUMMY_ARG
     
     template<typename FunctionType>
     JITCompiler::Call callOperation(
index fcf836d..ef0598d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -284,8 +284,8 @@ static void compileStub(
             }
         }
 
-        if (!!exit.m_descriptor->m_valueProfile)
-            jit.store64(GPRInfo::regT0, exit.m_descriptor->m_valueProfile.getSpecFailBucket(0));
+        if (exit.m_descriptor->m_valueProfile)
+            exit.m_descriptor->m_valueProfile.emitReportValue(jit, JSValueRegs(GPRInfo::regT0));
     }
 
     // Materialize all objects. Don't materialize an object until all
index ea85c38..db2f42f 100644 (file)
@@ -653,11 +653,6 @@ public:
 #endif
     }
 
-    enum TagRegistersMode {
-        DoNotHaveTagRegisters,
-        HaveTagRegisters
-    };
-
     Jump branchIfNotCell(GPRReg reg, TagRegistersMode mode = HaveTagRegisters)
     {
 #if USE(JSVALUE64)
@@ -783,6 +778,18 @@ public:
 #endif
     }
 
+    Jump branchIfNotDoubleKnownNotInt32(JSValueRegs regs, TagRegistersMode mode = HaveTagRegisters)
+    {
+#if USE(JSVALUE64)
+        if (mode == HaveTagRegisters)
+            return branchTest64(Zero, regs.gpr(), GPRInfo::tagTypeNumberRegister);
+        return branchTest64(Zero, regs.gpr(), TrustedImm64(TagTypeNumber));
+#else
+        UNUSED_PARAM(mode);
+        return branch32(AboveOrEqual, regs.tagGPR(), TrustedImm32(JSValue::LowestTag));
+#endif
+    }
+
     // Note that the tempGPR is not used in 32-bit mode.
     Jump branchIfBoolean(JSValueRegs regs, GPRReg tempGPR)
     {
@@ -848,7 +855,8 @@ public:
     }
 
     JumpList branchIfNotType(
-        JSValueRegs, GPRReg tempGPR, const InferredType::Descriptor&, TagRegistersMode);
+        JSValueRegs, GPRReg tempGPR, const InferredType::Descriptor&,
+        TagRegistersMode = HaveTagRegisters);
 
     template<typename T>
     Jump branchStructure(RelationalCondition condition, T leftHandSide, Structure* structure)
index 4ddfb7e..2426abd 100644 (file)
@@ -41,6 +41,23 @@ namespace JSC {
 #define POKE_ARGUMENT_OFFSET 0
 #endif
 
+// EncodedJSValue in JSVALUE32_64 is a 64-bit integer. When being compiled in ARM EABI, it must be aligned even-numbered register (r0, r2 or [sp]).
+// To avoid assemblies from using wrong registers, let's occupy r1 or r3 with a dummy argument when necessary.
+#if (COMPILER_SUPPORTS(EABI) && CPU(ARM)) || CPU(MIPS)
+#define EABI_32BIT_DUMMY_ARG      TrustedImm32(0),
+#else
+#define EABI_32BIT_DUMMY_ARG
+#endif
+
+// JSVALUE32_64 is a 64-bit integer that cannot be put half in an argument register and half on stack when using SH4 architecture.
+// To avoid this, let's occupy the 4th argument register (r7) with a dummy argument when necessary. This must only be done when there
+// is no other 32-bit value argument behind this 64-bit JSValue.
+#if CPU(SH4)
+#define SH4_32BIT_DUMMY_ARG      TrustedImm32(0),
+#else
+#define SH4_32BIT_DUMMY_ARG
+#endif
+
 class CCallHelpers : public AssemblyHelpers {
 public:
     CCallHelpers(VM* vm, CodeBlock* codeBlock = 0)
@@ -2167,6 +2184,15 @@ public:
     }
 #endif
     
+    void setupArgumentsWithExecState(JSValueRegs arg1, JSValueRegs arg2, TrustedImmPtr arg3)
+    {
+#if USE(JSVALUE64)
+        setupArgumentsWithExecState(arg1.gpr(), arg2.gpr(), arg3);
+#else
+        setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1.payloadGPR(), arg1.tagGPR(), arg2.payloadGPR(), arg2.tagGPR(), arg3);
+#endif
+    }
+    
     void setupArguments(JSValueRegs arg1)
     {
 #if USE(JSVALUE64)
index 437be62..7cca00e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2013-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -139,6 +139,11 @@ public:
         return m_base;
     }
     
+    JSValueRegs regs() const
+    {
+        return JSValueRegs(gpr());
+    }
+    
     MacroAssembler::Address asAddress() const { return MacroAssembler::Address(base(), offset()); }
     
 private:
@@ -306,6 +311,11 @@ public:
         return static_cast<int32_t>(m_tagType);
     }
     
+    JSValueRegs regs() const
+    {
+        return JSValueRegs(tagGPR(), payloadGPR());
+    }
+    
     MacroAssembler::Address asAddress(unsigned additionalOffset = 0) const { return MacroAssembler::Address(base(), offset() + additionalOffset); }
     
 private:
index 094b006..c82b8a3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -78,7 +78,7 @@ void AccessCase::emitIntrinsicGetter(AccessGenerationState& state)
     switch (intrinsic()) {
     case TypedArrayLengthIntrinsic: {
         jit.load32(MacroAssembler::Address(state.baseGPR, JSArrayBufferView::offsetOfLength()), valueGPR);
-        jit.boxInt32(valueGPR, valueRegs, CCallHelpers::DoNotHaveTagRegisters);
+        jit.boxInt32(valueGPR, valueRegs);
         state.succeed();
         return;
     }
@@ -93,7 +93,7 @@ void AccessCase::emitIntrinsicGetter(AccessGenerationState& state)
             jit.lshift32(valueGPR, Imm32(logElementSize(type)), valueGPR);
         }
 
-        jit.boxInt32(valueGPR, valueRegs, CCallHelpers::DoNotHaveTagRegisters);
+        jit.boxInt32(valueGPR, valueRegs);
         state.succeed();
         return;
     }
@@ -119,7 +119,7 @@ void AccessCase::emitIntrinsicGetter(AccessGenerationState& state)
         
         done.link(&jit);
         
-        jit.boxInt32(valueGPR, valueRegs, CCallHelpers::DoNotHaveTagRegisters);
+        jit.boxInt32(valueGPR, valueRegs);
         state.succeed();
         return;
     }
index 075cfe1..5fe3c6e 100644 (file)
@@ -756,6 +756,7 @@ namespace JSC {
 #endif
         MacroAssembler::Call callOperation(J_JITOperation_EJI, int, GPRReg, UniquedStringImpl*);
         MacroAssembler::Call callOperation(J_JITOperation_EJJ, int, GPRReg, GPRReg);
+        MacroAssembler::Call callOperation(J_JITOperation_EJJRp, JSValueRegs, JSValueRegs, JSValueRegs, ResultProfile*);
         MacroAssembler::Call callOperation(J_JITOperation_EJJAp, int, GPRReg, GPRReg, ArrayProfile*);
         MacroAssembler::Call callOperation(J_JITOperation_EJJBy, int, GPRReg, GPRReg, ByValInfo*);
         MacroAssembler::Call callOperation(Z_JITOperation_EJOJ, GPRReg, GPRReg, GPRReg);
index 5d91a51..f291243 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -128,6 +128,9 @@ void JITAddGenerator::generateFastPath(CCallHelpers& jit)
 
     // Do doubleVar + doubleVar.
     jit.addDouble(m_rightFPR, m_leftFPR);
+    if (m_resultProfile)
+        m_resultProfile->emitSetDouble(jit);
+        
     jit.boxDouble(m_leftFPR, m_result);
 }
 
index c28db72..8bf97ce 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -37,7 +37,8 @@ class JITAddGenerator {
 public:
     JITAddGenerator(SnippetOperand leftOperand, SnippetOperand rightOperand,
         JSValueRegs result, JSValueRegs left, JSValueRegs right,
-        FPRReg leftFPR, FPRReg rightFPR, GPRReg scratchGPR, FPRReg scratchFPR)
+        FPRReg leftFPR, FPRReg rightFPR, GPRReg scratchGPR, FPRReg scratchFPR,
+        ResultProfile* resultProfile = nullptr)
         : m_leftOperand(leftOperand)
         , m_rightOperand(rightOperand)
         , m_result(result)
@@ -47,6 +48,7 @@ public:
         , m_rightFPR(rightFPR)
         , m_scratchGPR(scratchGPR)
         , m_scratchFPR(scratchFPR)
+        , m_resultProfile(resultProfile)
     {
         ASSERT(!m_leftOperand.isConstInt32() || !m_rightOperand.isConstInt32());
     }
@@ -67,6 +69,7 @@ private:
     FPRReg m_rightFPR;
     GPRReg m_scratchGPR;
     FPRReg m_scratchFPR;
+    ResultProfile* m_resultProfile;
     bool m_didEmitFastPath { false };
 
     CCallHelpers::JumpList m_endJumpList;
index 7110fb4..2ca367e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -696,6 +696,10 @@ void JIT::emit_op_add(Instruction* currentInstruction)
     FPRReg scratchFPR = fpRegT2;
 #endif
 
+    ResultProfile* resultProfile = nullptr;
+    if (shouldEmitProfiling())
+        resultProfile = m_codeBlock->ensureResultProfile(m_bytecodeOffset);
+
     SnippetOperand leftOperand(types.first());
     SnippetOperand rightOperand(types.second());
 
@@ -712,7 +716,7 @@ void JIT::emit_op_add(Instruction* currentInstruction)
         emitGetVirtualRegister(op2, rightRegs);
 
     JITAddGenerator gen(leftOperand, rightOperand, resultRegs, leftRegs, rightRegs,
-        fpRegT0, fpRegT1, scratchGPR, scratchFPR);
+        fpRegT0, fpRegT1, scratchGPR, scratchFPR, resultProfile);
 
     gen.generateFastPath(*this);
 
@@ -724,8 +728,17 @@ void JIT::emit_op_add(Instruction* currentInstruction)
     } else {
         ASSERT(gen.endJumpList().empty());
         ASSERT(gen.slowPathJumpList().empty());
-        JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_add);
-        slowPathCall.call();
+        if (resultProfile) {
+            if (leftOperand.isConst())
+                emitGetVirtualRegister(op1, leftRegs);
+            if (rightOperand.isConst())
+                emitGetVirtualRegister(op2, rightRegs);
+            callOperation(operationValueAddProfiled, resultRegs, leftRegs, rightRegs, resultProfile);
+            emitPutVirtualRegister(result, resultRegs);
+        } else {
+            JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_add);
+            slowPathCall.call();
+        }
     }
 }
 
@@ -733,8 +746,41 @@ void JIT::emitSlow_op_add(Instruction* currentInstruction, Vector<SlowCaseEntry>
 {
     linkAllSlowCasesForBytecodeOffset(m_slowCases, iter, m_bytecodeOffset);
 
-    JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_add);
-    slowPathCall.call();
+    int result = currentInstruction[1].u.operand;
+    int op1 = currentInstruction[2].u.operand;
+    int op2 = currentInstruction[3].u.operand;
+    OperandTypes types = OperandTypes::fromInt(currentInstruction[4].u.operand);
+
+#if USE(JSVALUE64)
+    JSValueRegs leftRegs = JSValueRegs(regT0);
+    JSValueRegs rightRegs = JSValueRegs(regT1);
+    JSValueRegs resultRegs = leftRegs;
+#else
+    JSValueRegs leftRegs = JSValueRegs(regT1, regT0);
+    JSValueRegs rightRegs = JSValueRegs(regT3, regT2);
+    JSValueRegs resultRegs = leftRegs;
+#endif
+    
+    SnippetOperand leftOperand(types.first());
+    SnippetOperand rightOperand(types.second());
+
+    if (isOperandConstantInt(op1))
+        leftOperand.setConstInt32(getOperandConstantInt(op1));
+    else if (isOperandConstantInt(op2))
+        rightOperand.setConstInt32(getOperandConstantInt(op2));
+
+    if (shouldEmitProfiling()) {
+        if (leftOperand.isConst())
+            emitGetVirtualRegister(op1, leftRegs);
+        if (rightOperand.isConst())
+            emitGetVirtualRegister(op2, rightRegs);
+        ResultProfile* resultProfile = m_codeBlock->ensureResultProfile(m_bytecodeOffset);
+        callOperation(operationValueAddProfiled, resultRegs, leftRegs, rightRegs, resultProfile);
+        emitPutVirtualRegister(result, resultRegs);
+    } else {
+        JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_add);
+        slowPathCall.call();
+    }
 }
 
 void JIT::emit_op_div(Instruction* currentInstruction)
@@ -864,8 +910,17 @@ void JIT::emit_op_mul(Instruction* currentInstruction)
     } else {
         ASSERT(gen.endJumpList().empty());
         ASSERT(gen.slowPathJumpList().empty());
-        JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_mul);
-        slowPathCall.call();
+        if (resultProfile) {
+            if (leftOperand.isPositiveConstInt32())
+                emitGetVirtualRegister(op1, leftRegs);
+            if (rightOperand.isPositiveConstInt32())
+                emitGetVirtualRegister(op2, rightRegs);
+            callOperation(operationValueMulProfiled, resultRegs, leftRegs, rightRegs, resultProfile);
+            emitPutVirtualRegister(result, resultRegs);
+        } else {
+            JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_mul);
+            slowPathCall.call();
+        }
     }
 }
 
@@ -873,8 +928,41 @@ void JIT::emitSlow_op_mul(Instruction* currentInstruction, Vector<SlowCaseEntry>
 {
     linkAllSlowCasesForBytecodeOffset(m_slowCases, iter, m_bytecodeOffset);
     
-    JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_mul);
-    slowPathCall.call();
+    int result = currentInstruction[1].u.operand;
+    int op1 = currentInstruction[2].u.operand;
+    int op2 = currentInstruction[3].u.operand;
+    OperandTypes types = OperandTypes::fromInt(currentInstruction[4].u.operand);
+
+#if USE(JSVALUE64)
+    JSValueRegs leftRegs = JSValueRegs(regT0);
+    JSValueRegs rightRegs = JSValueRegs(regT1);
+    JSValueRegs resultRegs = leftRegs;
+#else
+    JSValueRegs leftRegs = JSValueRegs(regT1, regT0);
+    JSValueRegs rightRegs = JSValueRegs(regT3, regT2);
+    JSValueRegs resultRegs = leftRegs;
+#endif
+
+    SnippetOperand leftOperand(types.first());
+    SnippetOperand rightOperand(types.second());
+
+    if (isOperandConstantInt(op1))
+        leftOperand.setConstInt32(getOperandConstantInt(op1));
+    else if (isOperandConstantInt(op2))
+        rightOperand.setConstInt32(getOperandConstantInt(op2));
+
+    if (shouldEmitProfiling()) {
+        if (leftOperand.isPositiveConstInt32())
+            emitGetVirtualRegister(op1, leftRegs);
+        if (rightOperand.isPositiveConstInt32())
+            emitGetVirtualRegister(op2, rightRegs);
+        ResultProfile* resultProfile = m_codeBlock->ensureResultProfile(m_bytecodeOffset);
+        callOperation(operationValueMulProfiled, resultRegs, leftRegs, rightRegs, resultProfile);
+        emitPutVirtualRegister(result, resultRegs);
+    } else {
+        JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_mul);
+        slowPathCall.call();
+    }
 }
 
 void JIT::emit_op_sub(Instruction* currentInstruction)
@@ -898,6 +986,10 @@ void JIT::emit_op_sub(Instruction* currentInstruction)
     FPRReg scratchFPR = fpRegT2;
 #endif
 
+    ResultProfile* resultProfile = nullptr;
+    if (shouldEmitProfiling())
+        resultProfile = m_codeBlock->ensureResultProfile(m_bytecodeOffset);
+
     SnippetOperand leftOperand(types.first());
     SnippetOperand rightOperand(types.second());
     
@@ -905,7 +997,7 @@ void JIT::emit_op_sub(Instruction* currentInstruction)
     emitGetVirtualRegister(op2, rightRegs);
 
     JITSubGenerator gen(leftOperand, rightOperand, resultRegs, leftRegs, rightRegs,
-        fpRegT0, fpRegT1, scratchGPR, scratchFPR);
+        fpRegT0, fpRegT1, scratchGPR, scratchFPR, resultProfile);
 
     gen.generateFastPath(*this);
 
@@ -920,8 +1012,25 @@ void JIT::emitSlow_op_sub(Instruction* currentInstruction, Vector<SlowCaseEntry>
 {
     linkAllSlowCasesForBytecodeOffset(m_slowCases, iter, m_bytecodeOffset);
 
-    JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_sub);
-    slowPathCall.call();
+    int result = currentInstruction[1].u.operand;
+#if USE(JSVALUE64)
+    JSValueRegs leftRegs = JSValueRegs(regT0);
+    JSValueRegs rightRegs = JSValueRegs(regT1);
+    JSValueRegs resultRegs = leftRegs;
+#else
+    JSValueRegs leftRegs = JSValueRegs(regT1, regT0);
+    JSValueRegs rightRegs = JSValueRegs(regT3, regT2);
+    JSValueRegs resultRegs = leftRegs;
+#endif
+
+    if (shouldEmitProfiling()) {
+        ResultProfile* resultProfile = m_codeBlock->ensureResultProfile(m_bytecodeOffset);
+        callOperation(operationValueSubProfiled, resultRegs, leftRegs, rightRegs, resultProfile);
+        emitPutVirtualRegister(result, resultRegs);
+    } else {
+        JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_sub);
+        slowPathCall.call();
+    }
 }
 
 /* ------------------------------ END: OP_ADD, OP_SUB, OP_MUL ------------------------------ */
index aec5fa3..32b41c6 100644 (file)
@@ -416,6 +416,14 @@ ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_ECIZC opera
     return appendCallWithExceptionCheck(operation);
 }
 
+inline MacroAssembler::Call JIT::callOperation(J_JITOperation_EJJRp operation, JSValueRegs result, JSValueRegs arg1, JSValueRegs arg2, ResultProfile* resultProfile)
+{
+    setupArgumentsWithExecState(arg1, arg2, TrustedImmPtr(resultProfile));
+    Call call = appendCallWithExceptionCheck(operation);
+    setupResults(result);
+    return call;
+}
+
 #if USE(JSVALUE64)
 ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(Z_JITOperation_EJZZ operation, GPRReg arg1, int32_t arg2, int32_t arg3)
 {
@@ -602,23 +610,6 @@ ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EJZJ operat
 
 #else // USE(JSVALUE32_64)
 
-// EncodedJSValue in JSVALUE32_64 is a 64-bit integer. When being compiled in ARM EABI, it must be aligned even-numbered register (r0, r2 or [sp]).
-// To avoid assemblies from using wrong registers, let's occupy r1 or r3 with a dummy argument when necessary.
-#if (COMPILER_SUPPORTS(EABI) && CPU(ARM)) || CPU(MIPS)
-#define EABI_32BIT_DUMMY_ARG      TrustedImm32(0),
-#else
-#define EABI_32BIT_DUMMY_ARG
-#endif
-
-// JSVALUE32_64 is a 64-bit integer that cannot be put half in an argument register and half on stack when using SH4 architecture.
-// To avoid this, let's occupy the 4th argument register (r7) with a dummy argument when necessary. This must only be done when there
-// is no other 32-bit value argument behind this 64-bit JSValue.
-#if CPU(SH4)
-#define SH4_32BIT_DUMMY_ARG      TrustedImm32(0),
-#else
-#define SH4_32BIT_DUMMY_ARG
-#endif
-
 ALWAYS_INLINE MacroAssembler::Call JIT::callOperationNoExceptionCheck(V_JITOperation_EJ operation, GPRReg arg1Tag, GPRReg arg1Payload)
 {
     setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag);
@@ -800,9 +791,6 @@ ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(J_JITOperation_EJscCJ oper
     return appendCallWithExceptionCheckSetJSValueResult(operation, dst);
 }
 
-#undef EABI_32BIT_DUMMY_ARG
-#undef SH4_32BIT_DUMMY_ARG
-
 #endif // USE(JSVALUE32_64)
 
 ALWAYS_INLINE JIT::Jump JIT::checkStructure(RegisterID reg, Structure* structure)
index 1552707..e0732be 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -153,6 +153,7 @@ void JITMulGenerator::generateFastPath(CCallHelpers& jit)
         CCallHelpers::Jump done = jit.jump();
 
         notNegativeZero.link(&jit);
+        jit.or32(CCallHelpers::TrustedImm32(ResultProfile::NonNegZeroDouble), CCallHelpers::AbsoluteAddress(m_resultProfile->addressOfFlags()));
 
         jit.move(m_result.payloadGPR(), m_scratchGPR);
         jit.urshiftPtr(CCallHelpers::Imm32(52), m_scratchGPR);
@@ -174,6 +175,7 @@ void JITMulGenerator::generateFastPath(CCallHelpers& jit)
         CCallHelpers::Jump done = jit.jump();
 
         notNegativeZero.link(&jit);
+        jit.or32(CCallHelpers::TrustedImm32(ResultProfile::NonNegZeroDouble), CCallHelpers::AbsoluteAddress(m_resultProfile->addressOfFlags()));
 
         jit.move(m_result.tagGPR(), m_scratchGPR);
         jit.urshiftPtr(CCallHelpers::Imm32(52 - 32), m_scratchGPR);
index 903b694..efae064 100644 (file)
@@ -2243,6 +2243,88 @@ JSCell* JIT_OPERATION operationToIndexString(ExecState* exec, int32_t index)
     return jsString(exec, Identifier::from(exec, index).string());
 }
 
+EncodedJSValue JIT_OPERATION operationValueAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    
+    JSValue op1 = JSValue::decode(encodedOp1);
+    JSValue op2 = JSValue::decode(encodedOp2);
+    
+    return JSValue::encode(jsAdd(exec, op1, op2));
+}
+
+EncodedJSValue JIT_OPERATION operationValueAddProfiled(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ResultProfile* resultProfile)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    
+    JSValue op1 = JSValue::decode(encodedOp1);
+    JSValue op2 = JSValue::decode(encodedOp2);
+    
+    JSValue result = jsAdd(exec, op1, op2);
+    resultProfile->detectNumericness(result);
+    return JSValue::encode(result);
+}
+
+EncodedJSValue JIT_OPERATION operationValueMul(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+
+    JSValue op1 = JSValue::decode(encodedOp1);
+    JSValue op2 = JSValue::decode(encodedOp2);
+
+    double a = op1.toNumber(exec);
+    double b = op2.toNumber(exec);
+    return JSValue::encode(jsNumber(a * b));
+}
+
+EncodedJSValue JIT_OPERATION operationValueMulProfiled(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ResultProfile* resultProfile)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+
+    JSValue op1 = JSValue::decode(encodedOp1);
+    JSValue op2 = JSValue::decode(encodedOp2);
+
+    double a = op1.toNumber(exec);
+    double b = op2.toNumber(exec);
+    
+    JSValue result = jsNumber(a * b);
+    resultProfile->detectNumericness(result);
+    return JSValue::encode(result);
+}
+
+EncodedJSValue JIT_OPERATION operationValueSub(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    
+    JSValue op1 = JSValue::decode(encodedOp1);
+    JSValue op2 = JSValue::decode(encodedOp2);
+
+    double a = op1.toNumber(exec);
+    double b = op2.toNumber(exec);
+    return JSValue::encode(jsNumber(a - b));
+}
+
+EncodedJSValue JIT_OPERATION operationValueSubProfiled(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ResultProfile* resultProfile)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    
+    JSValue op1 = JSValue::decode(encodedOp1);
+    JSValue op2 = JSValue::decode(encodedOp2);
+
+    double a = op1.toNumber(exec);
+    double b = op2.toNumber(exec);
+    
+    JSValue result = jsNumber(a - b);
+    resultProfile->detectNumericness(result);
+    return JSValue::encode(result);
+}
+
 void JIT_OPERATION operationProcessTypeProfilerLog(ExecState* exec)
 {
     VM& vm = exec->vm();
index db7d978..2129703 100644 (file)
@@ -53,6 +53,7 @@ class WatchpointSet;
 
 struct ByValInfo;
 struct InlineCallFrame;
+struct ResultProfile;
 
 typedef ExecState CallFrame;
 
@@ -96,6 +97,7 @@ typedef char* UnusedPtr;
     Q: int64_t
     R: Register
     Reo: RegExpObject*
+    Rp: ResultProfile*
     S: size_t
     Sprt: SlowPathReturnType
     Ssi: StructureStubInfo*
@@ -136,6 +138,7 @@ typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EJJJ)(ExecState*, EncodedJ
 typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EJJAp)(ExecState*, EncodedJSValue, EncodedJSValue, ArrayProfile*);
 typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EJJBy)(ExecState*, EncodedJSValue, EncodedJSValue, ByValInfo*);
 typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EJJJ)(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue);
+typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EJJRp)(ExecState*, EncodedJSValue, EncodedJSValue, ResultProfile*);
 typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EJssZ)(ExecState*, JSString*, int32_t);
 typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EJssReo)(ExecState*, JSString*, RegExpObject*);
 typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EJssReoJss)(ExecState*, JSString*, RegExpObject*, JSString*);
@@ -409,6 +412,13 @@ JSCell* JIT_OPERATION operationGetPropertyEnumerator(ExecState*, JSCell*);
 EncodedJSValue JIT_OPERATION operationNextEnumeratorPname(ExecState*, JSCell*, int32_t);
 JSCell* JIT_OPERATION operationToIndexString(ExecState*, int32_t);
 
+EncodedJSValue JIT_OPERATION operationValueAdd(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationValueAddProfiled(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ResultProfile*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationValueMul(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationValueMulProfiled(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ResultProfile*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationValueSub(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationValueSubProfiled(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ResultProfile*) WTF_INTERNAL;
+
 void JIT_OPERATION operationProcessTypeProfilerLog(ExecState*) WTF_INTERNAL;
 void JIT_OPERATION operationProcessShadowChickenLog(ExecState*) WTF_INTERNAL;
 
index 3ebaaa3..4cfed8f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -83,6 +83,9 @@ void JITSubGenerator::generateFastPath(CCallHelpers& jit)
     rightWasInteger.link(&jit);
 
     jit.subDouble(m_rightFPR, m_leftFPR);
+    if (m_resultProfile)
+        m_resultProfile->emitSetDouble(jit);
+
     jit.boxDouble(m_leftFPR, m_result);
 }
 
index f677e08..9dd78ef 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -37,7 +37,8 @@ class JITSubGenerator {
 public:
     JITSubGenerator(SnippetOperand leftOperand, SnippetOperand rightOperand,
         JSValueRegs result, JSValueRegs left, JSValueRegs right,
-        FPRReg leftFPR, FPRReg rightFPR, GPRReg scratchGPR, FPRReg scratchFPR)
+        FPRReg leftFPR, FPRReg rightFPR, GPRReg scratchGPR, FPRReg scratchFPR,
+        ResultProfile* resultProfile = nullptr)
         : m_leftOperand(leftOperand)
         , m_rightOperand(rightOperand)
         , m_result(result)
@@ -47,6 +48,7 @@ public:
         , m_rightFPR(rightFPR)
         , m_scratchGPR(scratchGPR)
         , m_scratchFPR(scratchFPR)
+        , m_resultProfile(resultProfile)
     { }
 
     void generateFastPath(CCallHelpers&);
@@ -65,6 +67,7 @@ private:
     FPRReg m_rightFPR;
     GPRReg m_scratchGPR;
     FPRReg m_scratchFPR;
+    ResultProfile* m_resultProfile;
     bool m_didEmitFastPath { false };
 
     CCallHelpers::JumpList m_endJumpList;
diff --git a/Source/JavaScriptCore/jit/TagRegistersMode.cpp b/Source/JavaScriptCore/jit/TagRegistersMode.cpp
new file mode 100644 (file)
index 0000000..deb6913
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * 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. 
+ */
+
+#include "config.h"
+#include "TagRegistersMode.h"
+
+#include <wtf/PrintStream.h>
+
+namespace WTF {
+
+using namespace JSC;
+
+void printInternal(PrintStream& out, TagRegistersMode mode)
+{
+    switch (mode) {
+    case DoNotHaveTagRegisters:
+        out.print("DoNotHaveTagRegisters");
+        return;
+    case HaveTagRegisters:
+        out.print("HaveTagRegisters");
+        return;
+    }
+    
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace WTF
+
diff --git a/Source/JavaScriptCore/jit/TagRegistersMode.h b/Source/JavaScriptCore/jit/TagRegistersMode.h
new file mode 100644 (file)
index 0000000..d886891
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * 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. 
+ */
+
+#ifndef TagRegistersMode_h
+#define TagRegistersMode_h
+
+namespace JSC {
+
+enum TagRegistersMode {
+    DoNotHaveTagRegisters,
+    HaveTagRegisters
+};
+
+} // namespace JSC
+
+namespace WTF {
+
+class PrintStream;
+void printInternal(PrintStream&, JSC::TagRegistersMode);
+
+} // namespace WTF
+
+#endif // TagRegistersMode_h
+
index 92d252c..09a1704 100644 (file)
@@ -378,6 +378,8 @@ static void updateResultProfileForBinaryArithOp(ExecState* exec, Instruction* pc
             if (!doubleVal && std::signbit(doubleVal))
                 profile->setObservedNegZeroDouble();
             else {
+                profile->setObservedNonNegZeroDouble();
+
                 // The Int52 overflow check here intentionally omits 1ll << 51 as a valid negative Int52 value.
                 // Therefore, we will get a false positive if the result is that value. This is intentionally
                 // done to simplify the checking algorithm.
index 4c2c522..66add3c 100644 (file)
@@ -175,6 +175,7 @@ typedef const char* optionString;
     v(bool, reportDFGCompileTimes, false, Normal, "dumps JS function signature and the time it took to DFG and FTL compile") \
     v(bool, reportFTLCompileTimes, false, Normal, "dumps JS function signature and the time it took to FTL compile") \
     v(bool, reportTotalCompileTimes, false, Normal, nullptr) \
+    v(bool, verboseExitProfile, false, Normal, nullptr) \
     v(bool, verboseCFA, false, Normal, nullptr) \
     v(bool, verboseFTLToJSThunk, false, Normal, nullptr) \
     v(bool, verboseFTLFailure, false, Normal, nullptr) \