Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
[DFG][FTL] Efficiently execute number#toString()
https://bugs.webkit.org/show_bug.cgi?id=170007 Reviewed by Keith Miller. JSTests: * 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): Source/JavaScriptCore: 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: Canonical link: https://commits.webkit.org/192972@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@221601 268f45cc-cd09-0410-ab3c-d52691b4dbfc
- Loading branch information
1 parent
297e758
commit fb8d73d
Showing
33 changed files
with
482 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
JSTests/microbenchmarks/number-to-string-strength-reduction.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
function test() | ||
{ | ||
var target = 42; | ||
return target.toString(16); | ||
} | ||
noInline(test); | ||
|
||
for (var i = 0; i < 1e6; ++i) | ||
test(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); |
14 changes: 14 additions & 0 deletions
14
JSTests/microbenchmarks/number-to-string-with-radix-cse.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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`); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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`); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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`); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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`); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.