Diff
Modified: trunk/JSTests/ChangeLog (221600 => 221601)
--- trunk/JSTests/ChangeLog 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/JSTests/ChangeLog 2017-09-05 03:03:03 UTC (rev 221601)
@@ -1,3 +1,37 @@
+2017-09-03 Yusuke Suzuki <[email protected]>
+
+ [DFG][FTL] Efficiently execute number#toString()
+ https://bugs.webkit.org/show_bug.cgi?id=170007
+
+ Reviewed by Keith Miller.
+
+ * microbenchmarks/number-to-string-strength-reduction.js: Added.
+ (test):
+ * microbenchmarks/number-to-string-with-radix-10.js: Added.
+ (test):
+ * microbenchmarks/number-to-string-with-radix-cse.js: Added.
+ (test):
+ * microbenchmarks/number-to-string-with-radix.js: Added.
+ (test):
+ * stress/number-to-string-strength-reduction.js: Added.
+ (shouldBe):
+ (test):
+ * stress/number-to-string-with-radix-10.js: Added.
+ (shouldBe):
+ (test):
+ * stress/number-to-string-with-radix-cse.js: Added.
+ (shouldBe):
+ (test):
+ * stress/number-to-string-with-radix-invalid.js: Added.
+ (shouldThrow):
+ * stress/number-to-string-with-radix-watchpoint.js: Added.
+ (shouldBe):
+ (test):
+ (i.i.1e3.Number.prototype.toString):
+ * stress/number-to-string-with-radix.js: Added.
+ (shouldBe):
+ (test):
+
2017-09-02 Yusuke Suzuki <[email protected]>
[DFG] Relax arity requirement
Added: trunk/JSTests/microbenchmarks/number-to-string-strength-reduction.js (0 => 221601)
--- trunk/JSTests/microbenchmarks/number-to-string-strength-reduction.js (rev 0)
+++ trunk/JSTests/microbenchmarks/number-to-string-strength-reduction.js 2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,9 @@
+function test()
+{
+ var target = 42;
+ return target.toString(16);
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i)
+ test();
Added: trunk/JSTests/microbenchmarks/number-to-string-with-radix-10.js (0 => 221601)
--- trunk/JSTests/microbenchmarks/number-to-string-with-radix-10.js (rev 0)
+++ trunk/JSTests/microbenchmarks/number-to-string-with-radix-10.js 2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,10 @@
+function test()
+{
+ for (var i = 0; i < 10; ++i)
+ var result = i.toString(10);
+ return result;
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i)
+ test();
Added: trunk/JSTests/microbenchmarks/number-to-string-with-radix-cse.js (0 => 221601)
--- trunk/JSTests/microbenchmarks/number-to-string-with-radix-cse.js (rev 0)
+++ trunk/JSTests/microbenchmarks/number-to-string-with-radix-cse.js 2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,14 @@
+function test()
+{
+ for (var i = 0; i < 1e2; ++i) {
+ i.toString(16);
+ i.toString(16);
+ i.toString(16);
+ i.toString(16);
+ i.toString(16);
+ }
+}
+noInline(test);
+
+for (var i = 0; i < 1e3; ++i)
+ test();
Added: trunk/JSTests/microbenchmarks/number-to-string-with-radix.js (0 => 221601)
--- trunk/JSTests/microbenchmarks/number-to-string-with-radix.js (rev 0)
+++ trunk/JSTests/microbenchmarks/number-to-string-with-radix.js 2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,16 @@
+function test()
+{
+ for (var i = 0; i < 10; ++i) {
+ var result = '';
+ result += i.toString(2);
+ result += i.toString(4);
+ result += i.toString(8);
+ result += i.toString(16);
+ result += i.toString(32);
+ }
+ return result;
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i)
+ test();
Added: trunk/JSTests/stress/number-to-string-strength-reduction.js (0 => 221601)
--- trunk/JSTests/stress/number-to-string-strength-reduction.js (rev 0)
+++ trunk/JSTests/stress/number-to-string-strength-reduction.js 2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,14 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function test()
+{
+ var target = 42;
+ return target.toString(16);
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i)
+ shouldBe(test(), `2a`);
Added: trunk/JSTests/stress/number-to-string-with-radix-10.js (0 => 221601)
--- trunk/JSTests/stress/number-to-string-with-radix-10.js (rev 0)
+++ trunk/JSTests/stress/number-to-string-with-radix-10.js 2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,15 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+noInline(shouldBe);
+
+function test()
+{
+ for (var i = 0; i < 10; ++i)
+ shouldBe(i.toString(10), "" + i);
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i)
+ test();
Added: trunk/JSTests/stress/number-to-string-with-radix-cse.js (0 => 221601)
--- trunk/JSTests/stress/number-to-string-with-radix-cse.js (rev 0)
+++ trunk/JSTests/stress/number-to-string-with-radix-cse.js 2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,21 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function test()
+{
+ var result;
+ for (var i = 0; i < 1e2; ++i) {
+ i.toString(16);
+ i.toString(16);
+ i.toString(16);
+ i.toString(16);
+ result = i.toString(16);
+ }
+ return result;
+}
+noInline(test);
+
+for (var i = 0; i < 1e3; ++i)
+ shouldBe(test(), `63`);
Added: trunk/JSTests/stress/number-to-string-with-radix-invalid.js (0 => 221601)
--- trunk/JSTests/stress/number-to-string-with-radix-invalid.js (rev 0)
+++ trunk/JSTests/stress/number-to-string-with-radix-invalid.js 2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,24 @@
+function shouldThrow(func, errorMessage) {
+ var errorThrown = false;
+ var error = null;
+ try {
+ func();
+ } catch (e) {
+ errorThrown = true;
+ error = e;
+ }
+ if (!errorThrown)
+ throw new Error('not thrown');
+ if (String(error) !== errorMessage)
+ throw new Error(`bad error: ${String(error)}`);
+}
+
+function test(i, radix)
+{
+ return i.toString(radix);
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i) {
+ shouldThrow(() => test(i, 42), `RangeError: toString() radix argument must be between 2 and 36`);
+}
Added: trunk/JSTests/stress/number-to-string-with-radix-watchpoint.js (0 => 221601)
--- trunk/JSTests/stress/number-to-string-with-radix-watchpoint.js (rev 0)
+++ trunk/JSTests/stress/number-to-string-with-radix-watchpoint.js 2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,27 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function test()
+{
+ for (var i = 0; i < 10; ++i) {
+ var result = '';
+ result += i.toString(2);
+ result += i.toString(4);
+ result += i.toString(8);
+ result += i.toString(16);
+ result += i.toString(32);
+ }
+ return result;
+}
+noInline(test);
+
+var result = `1001211199`;
+for (var i = 0; i < 1e4; ++i) {
+ if (i === 1e3) {
+ Number.prototype.toString = function (radix) { return "Hello"; }
+ result = `HelloHelloHelloHelloHello`;
+ }
+ shouldBe(test(), result);
+}
Added: trunk/JSTests/stress/number-to-string-with-radix.js (0 => 221601)
--- trunk/JSTests/stress/number-to-string-with-radix.js (rev 0)
+++ trunk/JSTests/stress/number-to-string-with-radix.js 2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,21 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function test()
+{
+ for (var i = 0; i < 10; ++i) {
+ var result = '';
+ result += i.toString(2);
+ result += i.toString(4);
+ result += i.toString(8);
+ result += i.toString(16);
+ result += i.toString(32);
+ }
+ return result;
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i)
+ shouldBe(test(), `1001211199`);
Modified: trunk/Source/_javascript_Core/ChangeLog (221600 => 221601)
--- trunk/Source/_javascript_Core/ChangeLog 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/ChangeLog 2017-09-05 03:03:03 UTC (rev 221601)
@@ -1,3 +1,88 @@
+2017-09-03 Yusuke Suzuki <[email protected]>
+
+ [DFG][FTL] Efficiently execute number#toString()
+ https://bugs.webkit.org/show_bug.cgi?id=170007
+
+ Reviewed by Keith Miller.
+
+ In JS, the natural way to convert number to string with radix is `number.toString(radix)`.
+ However, our IC only cares about cells. If the base value is a number, it always goes to the slow path.
+
+ While extending our IC for number and boolean, the most meaningful use of this IC is calling `number.toString(radix)`.
+ So, in this patch, we first add a fast path for this in DFG by using watchpoint. We set up a watchpoint for
+ Number.prototype.toString. And if this watchpoint is kept alive and GetById(base, "toString")'s base should be
+ speculated as Number, we emit Number related Checks and convert GetById to Number.prototype.toString constant.
+ It removes costly GetById slow path, and makes it non-clobbering node (JSConstant).
+
+ In addition, we add NumberToStringWithValidRadixConstant node. We have NumberToStringWithRadix node, but it may
+ throw an error if the valid value is incorrect (for example, number.toString(2000)). So its clobbering rule is
+ conservatively use read(World)/write(Heap). But in reality, `number.toString` is mostly called with the constant
+ radix, and we can easily figure out this radix is valid (2 <= radix && radix < 32).
+ We add a rule to the constant folding phase to convert NumberToStringWithRadix to NumberToStringWithValidRadixConstant.
+ It ensures that it has valid constant radix. And we relax our clobbering rule for NumberToStringWithValidRadixConstant.
+
+ Added microbenchmarks show performance improvement.
+
+ baseline patched
+
+ number-to-string-with-radix-cse 43.8312+-1.3017 ^ 7.4930+-0.5105 ^ definitely 5.8496x faster
+ number-to-string-with-radix-10 7.2775+-0.5225 ^ 2.1906+-0.1864 ^ definitely 3.3222x faster
+ number-to-string-with-radix 39.7378+-1.4921 ^ 16.6137+-0.7776 ^ definitely 2.3919x faster
+ number-to-string-strength-reduction 94.9667+-2.7157 ^ 9.3060+-0.7202 ^ definitely 10.2049x faster
+
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGConstantFoldingPhase.cpp:
+ (JSC::DFG::ConstantFoldingPhase::foldConstants):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGGraph.h:
+ (JSC::DFG::Graph::isWatchingGlobalObjectWatchpoint):
+ (JSC::DFG::Graph::isWatchingArrayIteratorProtocolWatchpoint):
+ (JSC::DFG::Graph::isWatchingNumberToStringWatchpoint):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::convertToNumberToStringWithValidRadixConstant):
+ (JSC::DFG::Node::hasValidRadixConstant):
+ (JSC::DFG::Node::validRadixConstant):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructor):
+ (JSC::DFG::SpeculativeJIT::compileNumberToStringWithValidRadixConstant):
+ (JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructorOnNumber): Deleted.
+ * dfg/DFGSpeculativeJIT.h:
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGStrengthReductionPhase.cpp:
+ (JSC::DFG::StrengthReductionPhase::handleNode):
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+ (JSC::FTL::DFG::LowerDFGToB3::compileNumberToStringWithValidRadixConstant):
+ * runtime/JSGlobalObject.cpp:
+ (JSC::JSGlobalObject::JSGlobalObject):
+ (JSC::JSGlobalObject::init):
+ (JSC::JSGlobalObject::visitChildren):
+ * runtime/JSGlobalObject.h:
+ (JSC::JSGlobalObject::numberToStringWatchpoint):
+ (JSC::JSGlobalObject::numberProtoToStringFunction const):
+ * runtime/NumberPrototype.cpp:
+ (JSC::NumberPrototype::finishCreation):
+ (JSC::toStringWithRadixInternal):
+ (JSC::toStringWithRadix):
+ (JSC::int32ToStringInternal):
+ (JSC::numberToStringInternal):
+ * runtime/NumberPrototype.h:
+
2017-09-04 Yusuke Suzuki <[email protected]>
[DFG] Consider increasing the number of DFG worklist threads
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2017-09-05 03:03:03 UTC (rev 221601)
@@ -1949,10 +1949,25 @@
break;
}
- case NumberToStringWithRadix:
+ case NumberToStringWithRadix: {
+ JSValue radixValue = forNode(node->child2()).m_value;
+ if (radixValue && radixValue.isInt32()) {
+ int32_t radix = radixValue.asInt32();
+ if (2 <= radix && radix <= 36) {
+ m_state.setFoundConstants(true);
+ forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
+ break;
+ }
+ }
clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
break;
+ }
+
+ case NumberToStringWithValidRadixConstant: {
+ forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
+ break;
+ }
case NewStringObject: {
ASSERT(node->structure()->classInfo() == StringObject::info());
Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2017-09-05 03:03:03 UTC (rev 221601)
@@ -1584,9 +1584,14 @@
return;
case NumberToStringWithRadix:
+ // If the radix is invalid, NumberToStringWithRadix can throw an error.
read(World);
write(Heap);
return;
+
+ case NumberToStringWithValidRadixConstant:
+ def(PureValue(node, node->validRadixConstant()));
+ return;
case LastNodeType:
RELEASE_ASSERT_NOT_REACHED();
Modified: trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2017-09-05 03:03:03 UTC (rev 221601)
@@ -645,6 +645,23 @@
break;
}
+ case NumberToStringWithRadix: {
+ JSValue radixValue = m_state.forNode(node->child2()).m_value;
+ if (radixValue && radixValue.isInt32()) {
+ int32_t radix = radixValue.asInt32();
+ if (2 <= radix && radix <= 36) {
+ if (radix == 10) {
+ node->setOpAndDefaultFlags(ToString);
+ node->child2() = Edge();
+ } else
+ node->convertToNumberToStringWithValidRadixConstant(radix);
+ changed = true;
+ break;
+ }
+ }
+ break;
+ }
+
case Check: {
alreadyHandled = true;
m_interpreter.execute(indexInBlock);
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2017-09-05 03:03:03 UTC (rev 221601)
@@ -178,6 +178,7 @@
case ToString:
case CallStringConstructor:
case NumberToStringWithRadix:
+ case NumberToStringWithValidRadixConstant:
case In:
case HasOwnProperty:
case Jump:
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2017-09-05 03:03:03 UTC (rev 221601)
@@ -1311,6 +1311,7 @@
// FIXME: This should be done in the ByteCodeParser based on reading the
// PolymorphicAccess, which will surely tell us that this is a AccessCase::ArrayLength.
// https://bugs.webkit.org/show_bug.cgi?id=154990
+ auto uid = m_graph.identifiers()[node->identifierNumber()];
if (node->child1()->shouldSpeculateCellOrOther()
&& !m_graph.hasExitSite(node->origin.semantic, BadType)
&& !m_graph.hasExitSite(node->origin.semantic, BadCache)
@@ -1317,8 +1318,6 @@
&& !m_graph.hasExitSite(node->origin.semantic, BadIndexingType)
&& !m_graph.hasExitSite(node->origin.semantic, ExoticObjectMode)) {
- auto uid = m_graph.identifiers()[node->identifierNumber()];
-
if (uid == vm().propertyNames->length.impl()) {
attemptToMakeGetArrayLength(node);
break;
@@ -1333,6 +1332,30 @@
}
}
+ if (node->child1()->shouldSpeculateNumber()) {
+ if (uid == vm().propertyNames->toString.impl()) {
+ if (m_graph.isWatchingNumberToStringWatchpoint(node)) {
+ JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic);
+ if (node->child1()->shouldSpeculateInt32()) {
+ insertCheck<Int32Use>(node->child1().node());
+ m_graph.convertToConstant(node, m_graph.freeze(globalObject->numberProtoToStringFunction()));
+ break;
+ }
+
+ if (enableInt52() && node->child1()->shouldSpeculateAnyInt()) {
+ insertCheck<Int52RepUse>(node->child1().node());
+ m_graph.convertToConstant(node, m_graph.freeze(globalObject->numberProtoToStringFunction()));
+ break;
+ }
+
+ ASSERT(node->child1()->shouldSpeculateNumber());
+ insertCheck<DoubleRepUse>(node->child1().node());
+ m_graph.convertToConstant(node, m_graph.freeze(globalObject->numberProtoToStringFunction()));
+ break;
+ }
+ }
+ }
+
if (node->child1()->shouldSpeculateCell())
fixEdge<CellUse>(node->child1());
break;
@@ -2022,6 +2045,7 @@
case PutByValWithThis:
case GetByValWithThis:
case CompareEqPtr:
+ case NumberToStringWithValidRadixConstant:
break;
#else
default:
Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.h (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGGraph.h 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.h 2017-09-05 03:03:03 UTC (rev 221601)
@@ -730,10 +730,8 @@
return watchpoints().isWatched(globalObject->havingABadTimeWatchpoint());
}
- bool isWatchingArrayIteratorProtocolWatchpoint(Node* node)
+ bool isWatchingGlobalObjectWatchpoint(JSGlobalObject* globalObject, InlineWatchpointSet& set)
{
- JSGlobalObject* globalObject = globalObjectFor(node->origin.semantic);
- InlineWatchpointSet& set = globalObject->arrayIteratorProtocolWatchpoint();
if (watchpoints().isWatched(set))
return true;
@@ -749,6 +747,20 @@
return false;
}
+
+ bool isWatchingArrayIteratorProtocolWatchpoint(Node* node)
+ {
+ JSGlobalObject* globalObject = globalObjectFor(node->origin.semantic);
+ InlineWatchpointSet& set = globalObject->arrayIteratorProtocolWatchpoint();
+ return isWatchingGlobalObjectWatchpoint(globalObject, set);
+ }
+
+ bool isWatchingNumberToStringWatchpoint(Node* node)
+ {
+ JSGlobalObject* globalObject = globalObjectFor(node->origin.semantic);
+ InlineWatchpointSet& set = globalObject->numberToStringWatchpoint();
+ return isWatchingGlobalObjectWatchpoint(globalObject, set);
+ }
Profiler::Compilation* compilation() { return m_plan.compilation.get(); }
Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGNode.h 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h 2017-09-05 03:03:03 UTC (rev 221601)
@@ -691,6 +691,15 @@
children.setChild2(Edge());
m_opInfo = cell;
}
+
+ void convertToNumberToStringWithValidRadixConstant(int32_t radix)
+ {
+ ASSERT(m_op == NumberToStringWithRadix);
+ ASSERT(2 <= radix && radix <= 36);
+ setOpAndDefaultFlags(NumberToStringWithValidRadixConstant);
+ children.setChild2(Edge());
+ m_opInfo = radix;
+ }
void convertToDirectCall(FrozenValue*);
@@ -2583,6 +2592,17 @@
return m_opInfo.as<BucketOwnerType>();
}
+ bool hasValidRadixConstant()
+ {
+ return op() == NumberToStringWithValidRadixConstant;
+ }
+
+ int32_t validRadixConstant()
+ {
+ ASSERT(hasValidRadixConstant());
+ return m_opInfo.as<int32_t>();
+ }
+
uint32_t errorType()
{
ASSERT(op() == ThrowStaticError);
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2017-09-05 03:03:03 UTC (rev 221601)
@@ -354,6 +354,7 @@
macro(CallObjectConstructor, NodeResultJS) \
macro(CallStringConstructor, NodeResultJS | NodeMustGenerate) \
macro(NumberToStringWithRadix, NodeResultJS | NodeMustGenerate) \
+ macro(NumberToStringWithValidRadixConstant, NodeResultJS) \
macro(NewStringObject, NodeResultJS) \
macro(MakeRope, NodeResultJS) \
macro(In, NodeResultBoolean | NodeMustGenerate) \
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2017-09-05 03:03:03 UTC (rev 221601)
@@ -912,6 +912,7 @@
case CallStringConstructor:
case ToString:
case NumberToStringWithRadix:
+ case NumberToStringWithValidRadixConstant:
case MakeRope:
case StrCat: {
setPrediction(SpecString);
Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2017-09-05 03:03:03 UTC (rev 221601)
@@ -288,6 +288,7 @@
case ToString:
case ToNumber:
case NumberToStringWithRadix:
+ case NumberToStringWithValidRadixConstant:
case SetFunctionName:
case StrCat:
case CallStringConstructor:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2017-09-05 03:03:03 UTC (rev 221601)
@@ -8353,7 +8353,7 @@
case Int32Use:
case Int52RepUse:
case DoubleRepUse:
- compileToStringOrCallStringConstructorOnNumber(node);
+ compileNumberToStringWithValidRadixConstant(node, 10);
return;
default:
@@ -8434,11 +8434,16 @@
}
}
-void SpeculativeJIT::compileToStringOrCallStringConstructorOnNumber(Node* node)
+void SpeculativeJIT::compileNumberToStringWithValidRadixConstant(Node* node)
{
+ compileNumberToStringWithValidRadixConstant(node, node->validRadixConstant());
+}
+
+void SpeculativeJIT::compileNumberToStringWithValidRadixConstant(Node* node, int32_t radix)
+{
auto callToString = [&] (auto operation, GPRReg resultGPR, auto valueReg) {
flushRegisters();
- callOperation(operation, resultGPR, valueReg, CCallHelpers::TrustedImm32(10));
+ callOperation(operation, resultGPR, valueReg, TrustedImm32(radix));
m_jit.exceptionCheck();
cellResult(resultGPR, node);
};
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2017-09-05 03:03:03 UTC (rev 221601)
@@ -2766,8 +2766,9 @@
void emitSwitch(Node*);
void compileToStringOrCallStringConstructor(Node*);
- void compileToStringOrCallStringConstructorOnNumber(Node*);
void compileNumberToStringWithRadix(Node*);
+ void compileNumberToStringWithValidRadixConstant(Node*);
+ void compileNumberToStringWithValidRadixConstant(Node*, int32_t radix);
void compileNewStringObject(Node*);
void compileNewTypedArray(Node*);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2017-09-05 03:03:03 UTC (rev 221601)
@@ -2843,6 +2843,11 @@
break;
}
+ case NumberToStringWithValidRadixConstant: {
+ compileNumberToStringWithValidRadixConstant(node);
+ break;
+ }
+
case GetByValWithThis: {
JSValueOperand base(this, node->child1());
JSValueRegs baseRegs = base.jsValueRegs();
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2017-09-05 03:03:03 UTC (rev 221601)
@@ -5303,6 +5303,11 @@
break;
}
+ case NumberToStringWithValidRadixConstant: {
+ compileNumberToStringWithValidRadixConstant(node);
+ break;
+ }
+
case IsObject: {
JSValueOperand value(this, node->child1());
GPRTemporary result(this, Reuse, value);
Modified: trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp (221600 => 221601)
--- trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp 2017-09-05 03:03:03 UTC (rev 221601)
@@ -426,6 +426,19 @@
break;
}
+ case NumberToStringWithValidRadixConstant: {
+ Edge& child1 = m_node->child1();
+ if (child1->hasConstant()) {
+ JSValue value = child1->constant()->value();
+ if (value && value.isNumber()) {
+ String result = toStringWithRadix(value.asNumber(), m_node->validRadixConstant());
+ m_node->convertToLazyJSConstant(m_graph, LazyJSValue::newString(m_graph, result));
+ m_changed = true;
+ }
+ }
+ break;
+ }
+
case GetArrayLength: {
if (m_node->arrayMode().type() == Array::Generic
|| m_node->arrayMode().type() == Array::String) {
Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (221600 => 221601)
--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2017-09-05 03:03:03 UTC (rev 221601)
@@ -284,6 +284,7 @@
case DefineAccessorProperty:
case ToLowerCase:
case NumberToStringWithRadix:
+ case NumberToStringWithValidRadixConstant:
case CheckSubClass:
case CallDOM:
case CallDOMGetter:
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (221600 => 221601)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2017-09-05 03:03:03 UTC (rev 221601)
@@ -1123,6 +1123,9 @@
case NumberToStringWithRadix:
compileNumberToStringWithRadix();
break;
+ case NumberToStringWithValidRadixConstant:
+ compileNumberToStringWithValidRadixConstant();
+ break;
case CheckSubClass:
compileCheckSubClass();
break;
@@ -10419,6 +10422,23 @@
}
}
+ void compileNumberToStringWithValidRadixConstant()
+ {
+ switch (m_node->child1().useKind()) {
+ case Int32Use:
+ setJSValue(vmCall(pointerType(), m_out.operation(operationInt32ToStringWithValidRadix), m_callFrame, lowInt32(m_node->child1()), m_out.constInt32(m_node->validRadixConstant())));
+ break;
+ case Int52RepUse:
+ setJSValue(vmCall(pointerType(), m_out.operation(operationInt52ToStringWithValidRadix), m_callFrame, lowStrictInt52(m_node->child1()), m_out.constInt32(m_node->validRadixConstant())));
+ break;
+ case DoubleRepUse:
+ setJSValue(vmCall(pointerType(), m_out.operation(operationDoubleToStringWithValidRadix), m_callFrame, lowDouble(m_node->child1()), m_out.constInt32(m_node->validRadixConstant())));
+ break;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ }
+
void compileResolveScopeForHoistingFuncDeclInEval()
{
UniquedStringImpl* uid = m_graph.identifiers()[m_node->identifierNumber()];
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (221600 => 221601)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2017-09-05 03:03:03 UTC (rev 221601)
@@ -325,6 +325,7 @@
, m_mapSetWatchpoint(IsWatched)
, m_setAddWatchpoint(IsWatched)
, m_arraySpeciesWatchpoint(ClearWatchpoint)
+ , m_numberToStringWatchpoint(IsWatched)
, m_templateRegistry(vm)
, m_runtimeFlags()
, m_globalObjectMethodTable(globalObjectMethodTable ? globalObjectMethodTable : &s_globalObjectMethodTable)
@@ -1015,6 +1016,13 @@
m_setPrototypeAddWatchpoint = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_setAddWatchpoint);
m_setPrototypeAddWatchpoint->install();
}
+
+ {
+ ObjectPropertyCondition condition = setupAdaptiveWatchpoint(numberPrototype(), m_vm.propertyNames->toString);
+ m_numberPrototypeToStringWatchpoint = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_numberToStringWatchpoint);
+ m_numberPrototypeToStringWatchpoint->install();
+ m_numberProtoToStringFunction.set(vm, this, jsCast<JSFunction*>(numberPrototype()->getDirect(vm, vm.propertyNames->toString)));
+ }
}
resetPrototype(vm, getPrototypeDirect());
@@ -1264,6 +1272,7 @@
thisObject->m_iteratorProtocolFunction.visit(visitor);
thisObject->m_promiseResolveFunction.visit(visitor);
visitor.append(thisObject->m_objectProtoValueOfFunction);
+ visitor.append(thisObject->m_numberProtoToStringFunction);
visitor.append(thisObject->m_newPromiseCapabilityFunction);
visitor.append(thisObject->m_functionProtoHasInstanceSymbolFunction);
thisObject->m_throwTypeErrorGetterSetter.visit(visitor);
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.h (221600 => 221601)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.h 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.h 2017-09-05 03:03:03 UTC (rev 221601)
@@ -269,6 +269,7 @@
LazyProperty<JSGlobalObject, JSFunction> m_iteratorProtocolFunction;
LazyProperty<JSGlobalObject, JSFunction> m_promiseResolveFunction;
WriteBarrier<JSFunction> m_objectProtoValueOfFunction;
+ WriteBarrier<JSFunction> m_numberProtoToStringFunction;
WriteBarrier<JSFunction> m_newPromiseCapabilityFunction;
WriteBarrier<JSFunction> m_functionProtoHasInstanceSymbolFunction;
LazyProperty<JSGlobalObject, GetterSetter> m_throwTypeErrorGetterSetter;
@@ -413,6 +414,7 @@
InlineWatchpointSet& mapSetWatchpoint() { return m_mapSetWatchpoint; }
InlineWatchpointSet& setAddWatchpoint() { return m_setAddWatchpoint; }
InlineWatchpointSet& arraySpeciesWatchpoint() { return m_arraySpeciesWatchpoint; }
+ InlineWatchpointSet& numberToStringWatchpoint() { return m_numberToStringWatchpoint; }
// If this hasn't been invalidated, it means the array iterator protocol
// is not observable to user code yet.
InlineWatchpointSet m_arrayIteratorProtocolWatchpoint;
@@ -422,6 +424,7 @@
InlineWatchpointSet m_mapSetWatchpoint;
InlineWatchpointSet m_setAddWatchpoint;
InlineWatchpointSet m_arraySpeciesWatchpoint;
+ InlineWatchpointSet m_numberToStringWatchpoint;
std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_arrayPrototypeSymbolIteratorWatchpoint;
std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_arrayIteratorPrototypeNext;
std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_mapPrototypeSymbolIteratorWatchpoint;
@@ -432,6 +435,7 @@
std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_stringIteratorPrototypeNextWatchpoint;
std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_mapPrototypeSetWatchpoint;
std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_setPrototypeAddWatchpoint;
+ std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_numberPrototypeToStringWatchpoint;
bool isArrayPrototypeIteratorProtocolFastAndNonObservable();
bool isMapPrototypeIteratorProtocolFastAndNonObservable();
@@ -542,6 +546,7 @@
JSFunction* iteratorProtocolFunction() const { return m_iteratorProtocolFunction.get(this); }
JSFunction* promiseResolveFunction() const { return m_promiseResolveFunction.get(this); }
JSFunction* objectProtoValueOfFunction() const { return m_objectProtoValueOfFunction.get(); }
+ JSFunction* numberProtoToStringFunction() const { return m_numberProtoToStringFunction.get(); }
JSFunction* newPromiseCapabilityFunction() const { return m_newPromiseCapabilityFunction.get(); }
JSFunction* functionProtoHasInstanceSymbolFunction() const { return m_functionProtoHasInstanceSymbolFunction.get(); }
JSObject* regExpProtoExecFunction() const { return m_regExpProtoExec.get(); }
Modified: trunk/Source/_javascript_Core/runtime/NumberPrototype.cpp (221600 => 221601)
--- trunk/Source/_javascript_Core/runtime/NumberPrototype.cpp 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/runtime/NumberPrototype.cpp 2017-09-05 03:03:03 UTC (rev 221601)
@@ -58,7 +58,6 @@
/* Source for NumberPrototype.lut.h
@begin numberPrototypeTable
- toString numberProtoFuncToString DontEnum|Function 1 NumberPrototypeToStringIntrinsic
toLocaleString numberProtoFuncToLocaleString DontEnum|Function 0
valueOf numberProtoFuncValueOf DontEnum|Function 0
toFixed numberProtoFuncToFixed DontEnum|Function 1
@@ -79,10 +78,9 @@
Base::finishCreation(vm);
setInternalValue(vm, jsNumber(0));
+ JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toString, numberProtoFuncToString, DontEnum, 1, NumberPrototypeToStringIntrinsic);
#if ENABLE(INTL)
JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("toLocaleString", numberPrototypeToLocaleStringCodeGenerator, DontEnum);
-#else
- UNUSED_PARAM(globalObject);
#endif // ENABLE(INTL)
ASSERT(inherits(vm, info()));
@@ -165,7 +163,7 @@
return startOfResultString;
}
-static char* toStringWithRadix(RadixBuffer& buffer, double originalNumber, unsigned radix)
+static char* toStringWithRadixInternal(RadixBuffer& buffer, double originalNumber, unsigned radix)
{
ASSERT(std::isfinite(originalNumber));
ASSERT(radix >= 2 && radix <= 36);
@@ -359,7 +357,7 @@
return startOfResultString;
}
-static String toStringWithRadix(int32_t number, unsigned radix)
+static String toStringWithRadixInternal(int32_t number, unsigned radix)
{
LChar buf[1 + 32]; // Worst case is radix == 2, which gives us 32 digits + sign.
LChar* end = std::end(buf);
@@ -384,6 +382,21 @@
return String(p, static_cast<unsigned>(end - p));
}
+String toStringWithRadix(double doubleValue, int32_t radix)
+{
+ ASSERT(2 <= radix && radix <= 36);
+
+ int32_t integerValue = static_cast<int32_t>(doubleValue);
+ if (integerValue == doubleValue)
+ return toStringWithRadixInternal(integerValue, radix);
+
+ if (radix == 10 || !std::isfinite(doubleValue))
+ return String::numberToStringECMAScript(doubleValue);
+
+ RadixBuffer buffer;
+ return toStringWithRadixInternal(buffer, doubleValue, radix);
+}
+
// toExponential converts a number to a string, always formatting as an exponential.
// This method takes an optional argument specifying a number of *decimal places*
// to round the significand to (or, put another way, this method optionally rounds
@@ -520,7 +533,7 @@
if (radix == 10)
return jsNontrivialString(&vm, vm.numericStrings.add(value));
- return jsNontrivialString(&vm, toStringWithRadix(value, radix));
+ return jsNontrivialString(&vm, toStringWithRadixInternal(value, radix));
}
@@ -539,7 +552,7 @@
return jsNontrivialString(&vm, String::numberToStringECMAScript(doubleValue));
RadixBuffer buffer;
- return jsString(&vm, toStringWithRadix(buffer, doubleValue, radix));
+ return jsString(&vm, toStringWithRadixInternal(buffer, doubleValue, radix));
}
JSString* int32ToString(VM& vm, int32_t value, int32_t radix)
Modified: trunk/Source/_javascript_Core/runtime/NumberPrototype.h (221600 => 221601)
--- trunk/Source/_javascript_Core/runtime/NumberPrototype.h 2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/runtime/NumberPrototype.h 2017-09-05 03:03:03 UTC (rev 221601)
@@ -54,5 +54,6 @@
JSString* int32ToString(VM&, int32_t value, int32_t radix);
JSString* int52ToString(VM&, int64_t value, int32_t radix);
JSString* numberToString(VM&, double value, int32_t radix);
+String toStringWithRadix(double doubleValue, int32_t radix);
} // namespace JSC