Diff
Modified: trunk/JSTests/ChangeLog (214218 => 214219)
--- trunk/JSTests/ChangeLog 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/JSTests/ChangeLog 2017-03-21 11:31:43 UTC (rev 214219)
@@ -1,3 +1,29 @@
+2017-03-21 Yusuke Suzuki <utatane....@gmail.com>
+
+ [JSC] Optimize Number.prototype.toString on Int32 / Int52 / Double
+ https://bugs.webkit.org/show_bug.cgi?id=167454
+
+ Reviewed by Saam Barati.
+
+ * stress/number-to-string-abstract-operation.js: Added.
+ (shouldBe):
+ (int32ToString):
+ (shouldBe.int32ToString.new.Number.int52ToString):
+ (shouldBe.int32ToString.new.Number):
+ (shouldBe.doubleToString):
+ * stress/number-to-string-radix.js: Added.
+ (shouldBe):
+ (int32ToString):
+ (shouldBe.int32ToString.new.Number.int52ToString):
+ (shouldBe.int32ToString.new.Number):
+ (shouldBe.doubleToString):
+ * stress/number-to-string.js: Added.
+ (shouldBe):
+ (int32ToString):
+ (shouldBe.int32ToString.new.Number.int52ToString):
+ (shouldBe.int32ToString.new.Number):
+ (shouldBe.doubleToString):
+
2017-03-19 Chris Dumez <cdu...@apple.com>
`const location = "foo"` throws in a worker
Added: trunk/JSTests/stress/number-to-string-abstract-operation.js (0 => 214219)
--- trunk/JSTests/stress/number-to-string-abstract-operation.js (rev 0)
+++ trunk/JSTests/stress/number-to-string-abstract-operation.js 2017-03-21 11:31:43 UTC (rev 214219)
@@ -0,0 +1,80 @@
+function shouldBe(actual, expected)
+{
+ if (actual !== expected)
+ throw new Error(`bad value: expected:(${expected}),actual:(${actual})`);
+}
+
+function expected(num, radix)
+{
+ let characters = "0123456789abcdefghijklmnopqrstuvwxyz";
+ let result = "";
+ let negative = false;
+ if (num < 0) {
+ negative = true;
+ num = -num;
+ }
+
+ do {
+ const index = num % radix;
+ result = characters[index] + result;
+ num = (num - index) / radix;
+ } while (num);
+
+ if (negative)
+ return '-' + result;
+ return result;
+}
+
+{
+ function int32ToString(num)
+ {
+ return `${num}`;
+ }
+ noInline(int32ToString);
+
+ for (var i = 0; i < 1e3; ++i) {
+ shouldBe(int32ToString(i), expected(i, 10));
+ shouldBe(int32ToString(-i), expected(-i, 10));
+ }
+
+ shouldBe(int32ToString(0xffffffffff), expected(0xffffffffff, 10));
+ shouldBe(int32ToString(0.1), `0.1`);
+ shouldBe(int32ToString(-0.1), `-0.1`);
+ shouldBe(int32ToString(new Number(0xff)), `255`);
+}
+
+{
+ function int52ToString(num)
+ {
+ return `${fiatInt52(num)}`;
+ }
+ noInline(int52ToString);
+
+ for (var i = 0; i < 1e3; ++i) {
+ shouldBe(int52ToString(0xffffffff + i), expected(0xffffffff + i, 10));
+ shouldBe(int52ToString(-(0xffffffff + i)), expected(-(0xffffffff + i), 10));
+ }
+
+ shouldBe(int52ToString(0xff), `255`);
+ shouldBe(int52ToString(0.1), `0.1`);
+ shouldBe(int52ToString(-0.1), `-0.1`);
+ shouldBe(int52ToString(new Number(0xff)), `255`);
+}
+
+{
+ function doubleToString(num)
+ {
+ return `${num}`;
+ }
+ noInline(doubleToString);
+
+ for (var i = 0; i < 1e3; ++i) {
+ shouldBe(doubleToString(1.01), `1.01`);
+ shouldBe(doubleToString(-1.01), `-1.01`);
+ }
+
+ shouldBe(doubleToString(0xff), `255`);
+ shouldBe(doubleToString(0.1), `0.1`);
+ shouldBe(doubleToString(-0.1), `-0.1`);
+ shouldBe(doubleToString(new Number(0xff)), `255`);
+}
Added: trunk/JSTests/stress/number-to-string-radix.js (0 => 214219)
--- trunk/JSTests/stress/number-to-string-radix.js (rev 0)
+++ trunk/JSTests/stress/number-to-string-radix.js 2017-03-21 11:31:43 UTC (rev 214219)
@@ -0,0 +1,80 @@
+function shouldBe(actual, expected)
+{
+ if (actual !== expected)
+ throw new Error(`bad value: expected:(${expected}),actual:(${actual})`);
+}
+
+function expected(num, radix)
+{
+ let characters = "0123456789abcdefghijklmnopqrstuvwxyz";
+ let result = "";
+ let negative = false;
+ if (num < 0) {
+ negative = true;
+ num = -num;
+ }
+
+ do {
+ const index = num % radix;
+ result = characters[index] + result;
+ num = (num - index) / radix;
+ } while (num);
+
+ if (negative)
+ return '-' + result;
+ return result;
+}
+
+{
+ function int32ToString(num, radix)
+ {
+ return num.toString(radix);
+ }
+ noInline(int32ToString);
+
+ for (var i = 0; i < 1e3; ++i) {
+ shouldBe(int32ToString(i, 16), expected(i, 16));
+ shouldBe(int32ToString(-i, 16), expected(-i, 16));
+ }
+
+ shouldBe(int32ToString(0xffffffffff, 16), expected(0xffffffffff, 16));
+ shouldBe(int32ToString(0.1, 16), `0.1999999999999a`);
+ shouldBe(int32ToString(-0.1, 16), `-0.1999999999999a`);
+ shouldBe(int32ToString(new Number(0xff), 16), `ff`);
+}
+
+{
+ function int52ToString(num, radix)
+ {
+ return fiatInt52(num).toString(radix);
+ }
+ noInline(int52ToString);
+
+ for (var i = 0; i < 1e3; ++i) {
+ shouldBe(int52ToString(0xffffffff + i, 16), expected(0xffffffff + i, 16));
+ shouldBe(int52ToString(-(0xffffffff + i), 16), expected(-(0xffffffff + i), 16));
+ }
+
+ shouldBe(int52ToString(0xff, 16), `ff`);
+ shouldBe(int52ToString(0.1, 16), `0.1999999999999a`);
+ shouldBe(int52ToString(-0.1, 16), `-0.1999999999999a`);
+ shouldBe(int52ToString(new Number(0xff), 16), `ff`);
+}
+
+{
+ function doubleToString(num, radix)
+ {
+ return num.toString(radix);
+ }
+ noInline(doubleToString);
+
+ for (var i = 0; i < 1e3; ++i) {
+ shouldBe(doubleToString(1.01, 16), `1.028f5c28f5c29`);
+ shouldBe(doubleToString(-1.01, 16), `-1.028f5c28f5c29`);
+ }
+
+ shouldBe(doubleToString(0xff, 16), `ff`);
+ shouldBe(doubleToString(0.1, 16), `0.1999999999999a`);
+ shouldBe(doubleToString(-0.1, 16), `-0.1999999999999a`);
+ shouldBe(doubleToString(new Number(0xff), 16), `ff`);
+}
Added: trunk/JSTests/stress/number-to-string.js (0 => 214219)
--- trunk/JSTests/stress/number-to-string.js (rev 0)
+++ trunk/JSTests/stress/number-to-string.js 2017-03-21 11:31:43 UTC (rev 214219)
@@ -0,0 +1,80 @@
+function shouldBe(actual, expected)
+{
+ if (actual !== expected)
+ throw new Error(`bad value: expected:(${expected}),actual:(${actual})`);
+}
+
+function expected(num, radix)
+{
+ let characters = "0123456789abcdefghijklmnopqrstuvwxyz";
+ let result = "";
+ let negative = false;
+ if (num < 0) {
+ negative = true;
+ num = -num;
+ }
+
+ do {
+ const index = num % radix;
+ result = characters[index] + result;
+ num = (num - index) / radix;
+ } while (num);
+
+ if (negative)
+ return '-' + result;
+ return result;
+}
+
+{
+ function int32ToString(num)
+ {
+ return num.toString(16);
+ }
+ noInline(int32ToString);
+
+ for (var i = 0; i < 1e3; ++i) {
+ shouldBe(int32ToString(i), expected(i, 16));
+ shouldBe(int32ToString(-i), expected(-i, 16));
+ }
+
+ shouldBe(int32ToString(0xffffffffff), expected(0xffffffffff, 16));
+ shouldBe(int32ToString(0.1), `0.1999999999999a`);
+ shouldBe(int32ToString(-0.1), `-0.1999999999999a`);
+ shouldBe(int32ToString(new Number(0xff)), `ff`);
+}
+
+{
+ function int52ToString(num)
+ {
+ return fiatInt52(num).toString(16);
+ }
+ noInline(int52ToString);
+
+ for (var i = 0; i < 1e3; ++i) {
+ shouldBe(int52ToString(0xffffffff + i), expected(0xffffffff + i, 16));
+ shouldBe(int52ToString(-(0xffffffff + i)), expected(-(0xffffffff + i), 16));
+ }
+
+ shouldBe(int52ToString(0xff), `ff`);
+ shouldBe(int52ToString(0.1), `0.1999999999999a`);
+ shouldBe(int52ToString(-0.1), `-0.1999999999999a`);
+ shouldBe(int52ToString(new Number(0xff)), `ff`);
+}
+
+{
+ function doubleToString(num)
+ {
+ return num.toString(16);
+ }
+ noInline(doubleToString);
+
+ for (var i = 0; i < 1e3; ++i) {
+ shouldBe(doubleToString(1.01), `1.028f5c28f5c29`);
+ shouldBe(doubleToString(-1.01), `-1.028f5c28f5c29`);
+ }
+
+ shouldBe(doubleToString(0xff), `ff`);
+ shouldBe(doubleToString(0.1), `0.1999999999999a`);
+ shouldBe(doubleToString(-0.1), `-0.1999999999999a`);
+ shouldBe(doubleToString(new Number(0xff)), `ff`);
+}
Modified: trunk/Source/_javascript_Core/ChangeLog (214218 => 214219)
--- trunk/Source/_javascript_Core/ChangeLog 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/ChangeLog 2017-03-21 11:31:43 UTC (rev 214219)
@@ -1,5 +1,20 @@
2017-03-21 Yusuke Suzuki <utatane....@gmail.com>
+ [JSC] Optimize Number.prototype.toString on Int32 / Int52 / Double
+ https://bugs.webkit.org/show_bug.cgi?id=167454
+
+ Reviewed by Saam Barati.
+
+ This patch improves Number.toString(radix) performance
+ by introducing NumberToStringWithRadix DFG node. It directly
+ calls the operation and it always returns String.
+
+ baseline patched
+
+ stanford-crypto-sha256-iterative 45.130+-0.928 44.032+-1.184 might be 1.0250x faster
+
+2017-03-21 Yusuke Suzuki <utatane....@gmail.com>
+
[JSC] Add JSPromiseDeferred::reject(ExecState*, Exception*) interface
https://bugs.webkit.org/show_bug.cgi?id=169908
@@ -27,6 +42,59 @@
* assembler/MacroAssemblerMIPS.h:
(JSC::MacroAssemblerMIPS::branchPtr): Added.
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ (JSC::DFG::FixupPhase::fixupToStringOrCallStringConstructor):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGOperations.cpp:
+ * dfg/DFGOperations.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructor):
+ (JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructorOnNumber):
+ (JSC::DFG::SpeculativeJIT::compileNumberToStringWithRadix):
+ (JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructorOnCell): Deleted.
+ * dfg/DFGSpeculativeJIT.h:
+ (JSC::DFG::SpeculativeJIT::callOperation):
+ * 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::compileToStringOrCallStringConstructor):
+ (JSC::FTL::DFG::LowerDFGToB3::compileNumberToStringWithRadix):
+ * jit/CCallHelpers.h:
+ (JSC::CCallHelpers::setupArgumentsWithExecState):
+ * jit/JITOperations.h:
+ * runtime/Intrinsic.h:
+ * runtime/NumberPrototype.cpp:
+ (JSC::int52ToStringWithRadix):
+ (JSC::int32ToStringInternal):
+ (JSC::numberToStringInternal):
+ (JSC::int32ToString):
+ (JSC::int52ToString):
+ (JSC::numberToString):
+ (JSC::numberProtoFuncToString):
+ (JSC::integerValueToString): Deleted.
+ * runtime/NumberPrototype.h:
+ * runtime/StringPrototype.cpp:
+ (JSC::StringPrototype::finishCreation):
+
2017-03-20 Filip Pizlo <fpi...@apple.com>
Graph coloring should use coalescable moves when spilling
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2017-03-21 11:31:43 UTC (rev 214219)
@@ -1858,6 +1858,9 @@
m_graph.registerStructure(m_graph.globalObjectFor(node->origin.semantic)->stringObjectStructure()));
break;
case StringOrStringObjectUse:
+ case Int32Use:
+ case Int52RepUse:
+ case DoubleRepUse:
case NotCellUse:
break;
case CellUse:
@@ -1871,6 +1874,11 @@
forNode(node).set(m_graph, m_vm.stringStructure.get());
break;
}
+
+ case NumberToStringWithRadix:
+ clobberWorld(node->origin.semantic, clobberLimit);
+ 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/DFGByteCodeParser.cpp (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2017-03-21 11:31:43 UTC (rev 214219)
@@ -2711,7 +2711,7 @@
return true;
}
- case ToLowerCaseIntrinsic: {
+ case StringPrototypeToLowerCaseIntrinsic: {
if (argumentCountIncludingThis != 1)
return false;
@@ -2725,6 +2725,26 @@
return true;
}
+ case NumberPrototypeToStringIntrinsic: {
+ if (argumentCountIncludingThis != 1 && argumentCountIncludingThis != 2)
+ return false;
+
+ if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType))
+ return false;
+
+ insertChecks();
+ Node* thisNumber = get(virtualRegisterForArgument(0, registerOffset));
+ if (argumentCountIncludingThis == 1) {
+ Node* result = addToGraph(ToString, thisNumber);
+ set(VirtualRegister(resultOperand), result);
+ } else {
+ Node* radix = get(virtualRegisterForArgument(1, registerOffset));
+ Node* result = addToGraph(NumberToStringWithRadix, thisNumber, radix);
+ set(VirtualRegister(resultOperand), result);
+ }
+ return true;
+ }
+
default:
return false;
}
Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2017-03-21 11:31:43 UTC (rev 214219)
@@ -1409,6 +1409,9 @@
write(Heap);
return;
+ case Int32Use:
+ case Int52RepUse:
+ case DoubleRepUse:
case NotCellUse:
def(PureValue(node));
return;
@@ -1456,6 +1459,11 @@
case ToLowerCase:
def(PureValue(node));
return;
+
+ case NumberToStringWithRadix:
+ read(World);
+ write(Heap);
+ return;
case LastNodeType:
RELEASE_ASSERT_NOT_REACHED();
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2017-03-21 11:31:43 UTC (rev 214219)
@@ -178,6 +178,7 @@
case ToNumber:
case ToString:
case CallStringConstructor:
+ case NumberToStringWithRadix:
case In:
case HasOwnProperty:
case Jump:
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2017-03-21 11:31:43 UTC (rev 214219)
@@ -1761,6 +1761,17 @@
break;
}
+ case NumberToStringWithRadix: {
+ if (node->child1()->shouldSpeculateInt32())
+ fixEdge<Int32Use>(node->child1());
+ else if (enableInt52() && node->child1()->shouldSpeculateAnyInt())
+ fixEdge<Int52RepUse>(node->child1());
+ else
+ fixEdge<DoubleRepUse>(node->child1());
+ fixEdge<Int32Use>(node->child2());
+ break;
+ }
+
case DefineAccessorProperty: {
fixEdge<CellUse>(m_graph.varArgChild(node, 0));
Edge& propertyEdge = m_graph.varArgChild(node, 1);
@@ -2236,6 +2247,24 @@
return;
}
+ if (node->child1()->shouldSpeculateInt32()) {
+ fixEdge<Int32Use>(node->child1());
+ node->clearFlags(NodeMustGenerate);
+ return;
+ }
+
+ if (enableInt52() && node->child1()->shouldSpeculateAnyInt()) {
+ fixEdge<Int52RepUse>(node->child1());
+ node->clearFlags(NodeMustGenerate);
+ return;
+ }
+
+ if (node->child1()->shouldSpeculateNumber()) {
+ fixEdge<DoubleRepUse>(node->child1());
+ node->clearFlags(NodeMustGenerate);
+ return;
+ }
+
// ToString(Symbol) throws an error. So if the child1 can include Symbols,
// we need to care about it in the clobberize. In the following case,
// since NotCellUse edge filter is used and this edge filters Symbols,
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2017-03-21 11:31:43 UTC (rev 214219)
@@ -335,6 +335,7 @@
macro(ToNumber, NodeResultJS | NodeMustGenerate) \
macro(CallObjectConstructor, NodeResultJS) \
macro(CallStringConstructor, NodeResultJS | NodeMustGenerate) \
+ macro(NumberToStringWithRadix, NodeResultJS | NodeMustGenerate) \
macro(NewStringObject, NodeResultJS) \
macro(MakeRope, NodeResultJS) \
macro(In, NodeResultBoolean | NodeMustGenerate) \
Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2017-03-21 11:31:43 UTC (rev 214219)
@@ -1644,6 +1644,75 @@
return jsString(exec, lowercasedString);
}
+char* JIT_OPERATION operationInt32ToString(ExecState* exec, int32_t value, int32_t radix)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ if (radix < 2 || radix > 36) {
+ throwVMError(exec, scope, createRangeError(exec, ASCIILiteral("toString() radix argument must be between 2 and 36")));
+ return nullptr;
+ }
+
+ return reinterpret_cast<char*>(int32ToString(vm, value, radix));
+}
+
+char* JIT_OPERATION operationInt52ToString(ExecState* exec, int64_t value, int32_t radix)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ if (radix < 2 || radix > 36) {
+ throwVMError(exec, scope, createRangeError(exec, ASCIILiteral("toString() radix argument must be between 2 and 36")));
+ return nullptr;
+ }
+
+ return reinterpret_cast<char*>(int52ToString(vm, value, radix));
+}
+
+char* JIT_OPERATION operationDoubleToString(ExecState* exec, double value, int32_t radix)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ if (radix < 2 || radix > 36) {
+ throwVMError(exec, scope, createRangeError(exec, ASCIILiteral("toString() radix argument must be between 2 and 36")));
+ return nullptr;
+ }
+
+ return reinterpret_cast<char*>(numberToString(vm, value, radix));
+}
+
+char* JIT_OPERATION operationInt32ToStringWithValidRadix(ExecState* exec, int32_t value, int32_t radix)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ return reinterpret_cast<char*>(int32ToString(vm, value, radix));
+}
+
+char* JIT_OPERATION operationInt52ToStringWithValidRadix(ExecState* exec, int64_t value, int32_t radix)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ return reinterpret_cast<char*>(int52ToString(vm, value, radix));
+}
+
+char* JIT_OPERATION operationDoubleToStringWithValidRadix(ExecState* exec, double value, int32_t radix)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ return reinterpret_cast<char*>(numberToString(vm, value, radix));
+}
+
JSString* JIT_OPERATION operationSingleCharacterString(ExecState* exec, int32_t character)
{
VM& vm = exec->vm();
Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGOperations.h 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h 2017-03-21 11:31:43 UTC (rev 214219)
@@ -159,6 +159,13 @@
JSString* JIT_OPERATION operationToLowerCase(ExecState*, JSString*, uint32_t);
+char* JIT_OPERATION operationInt32ToString(ExecState*, int32_t, int32_t);
+char* JIT_OPERATION operationInt52ToString(ExecState*, int64_t, int32_t);
+char* JIT_OPERATION operationDoubleToString(ExecState*, double, int32_t);
+char* JIT_OPERATION operationInt32ToStringWithValidRadix(ExecState*, int32_t, int32_t);
+char* JIT_OPERATION operationInt52ToStringWithValidRadix(ExecState*, int64_t, int32_t);
+char* JIT_OPERATION operationDoubleToStringWithValidRadix(ExecState*, double, int32_t);
+
int32_t JIT_OPERATION operationMapHash(ExecState*, EncodedJSValue input);
JSCell* JIT_OPERATION operationJSMapFindBucket(ExecState*, JSCell*, EncodedJSValue, int32_t);
JSCell* JIT_OPERATION operationJSSetFindBucket(ExecState*, JSCell*, EncodedJSValue, int32_t);
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2017-03-21 11:31:43 UTC (rev 214219)
@@ -897,6 +897,7 @@
case StringCharAt:
case CallStringConstructor:
case ToString:
+ case NumberToStringWithRadix:
case MakeRope:
case StrCat: {
setPrediction(SpecString);
Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2017-03-21 11:31:43 UTC (rev 214219)
@@ -288,6 +288,7 @@
case ToPrimitive:
case ToString:
case ToNumber:
+ case NumberToStringWithRadix:
case SetFunctionName:
case StrCat:
case CallStringConstructor:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2017-03-21 11:31:43 UTC (rev 214219)
@@ -7923,9 +7923,10 @@
return temporary.gpr();
}
-void SpeculativeJIT::compileToStringOrCallStringConstructorOnCell(Node* node)
+void SpeculativeJIT::compileToStringOrCallStringConstructor(Node* node)
{
- if (node->child1().useKind() == NotCellUse) {
+ switch (node->child1().useKind()) {
+ case NotCellUse: {
JSValueOperand op1(this, node->child1(), ManualOperandSpeculation);
JSValueRegs op1Regs = op1.jsValueRegs();
@@ -7947,7 +7948,7 @@
return;
}
- if (node->child1().useKind() == UntypedUse) {
+ case UntypedUse: {
JSValueOperand op1(this, node->child1());
JSValueRegs op1Regs = op1.jsValueRegs();
GPRReg op1PayloadGPR = op1Regs.payloadGPR();
@@ -7979,7 +7980,16 @@
return;
}
+ case Int32Use:
+ case Int52RepUse:
+ case DoubleRepUse:
+ compileToStringOrCallStringConstructorOnNumber(node);
+ return;
+ default:
+ break;
+ }
+
SpeculateCellOperand op1(this, node->child1());
GPRReg op1GPR = op1.gpr();
@@ -8054,6 +8064,92 @@
}
}
+void SpeculativeJIT::compileToStringOrCallStringConstructorOnNumber(Node* node)
+{
+ auto callToString = [&] (auto operation, GPRReg resultGPR, auto valueReg) {
+ flushRegisters();
+ callOperation(operation, resultGPR, valueReg, CCallHelpers::TrustedImm32(10));
+ m_jit.exceptionCheck();
+ cellResult(resultGPR, node);
+ };
+
+ switch (node->child1().useKind()) {
+ case Int32Use: {
+ SpeculateStrictInt32Operand value(this, node->child1());
+ GPRFlushedCallResult result(this);
+ callToString(operationInt32ToStringWithValidRadix, result.gpr(), value.gpr());
+ break;
+ }
+
+#if USE(JSVALUE64)
+ case Int52RepUse: {
+ SpeculateStrictInt52Operand value(this, node->child1());
+ GPRFlushedCallResult result(this);
+ callToString(operationInt52ToStringWithValidRadix, result.gpr(), value.gpr());
+ break;
+ }
+#endif
+
+ case DoubleRepUse: {
+ SpeculateDoubleOperand value(this, node->child1());
+ GPRFlushedCallResult result(this);
+ callToString(operationDoubleToStringWithValidRadix, result.gpr(), value.fpr());
+ break;
+ }
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+}
+
+void SpeculativeJIT::compileNumberToStringWithRadix(Node* node)
+{
+ bool validRadixIsGuaranteed = false;
+ if (node->child2()->isInt32Constant()) {
+ int32_t radix = node->child2()->asInt32();
+ if (radix >= 2 && radix <= 36)
+ validRadixIsGuaranteed = true;
+ }
+
+ auto callToString = [&] (auto operation, GPRReg resultGPR, auto valueReg, GPRReg radixGPR) {
+ flushRegisters();
+ callOperation(operation, resultGPR, valueReg, radixGPR);
+ m_jit.exceptionCheck();
+ cellResult(resultGPR, node);
+ };
+
+ switch (node->child1().useKind()) {
+ case Int32Use: {
+ SpeculateStrictInt32Operand value(this, node->child1());
+ SpeculateStrictInt32Operand radix(this, node->child2());
+ GPRFlushedCallResult result(this);
+ callToString(validRadixIsGuaranteed ? operationInt32ToStringWithValidRadix : operationInt32ToString, result.gpr(), value.gpr(), radix.gpr());
+ break;
+ }
+
+#if USE(JSVALUE64)
+ case Int52RepUse: {
+ SpeculateStrictInt52Operand value(this, node->child1());
+ SpeculateStrictInt32Operand radix(this, node->child2());
+ GPRFlushedCallResult result(this);
+ callToString(validRadixIsGuaranteed ? operationInt52ToStringWithValidRadix : operationInt52ToString, result.gpr(), value.gpr(), radix.gpr());
+ break;
+ }
+#endif
+
+ case DoubleRepUse: {
+ SpeculateDoubleOperand value(this, node->child1());
+ SpeculateStrictInt32Operand radix(this, node->child2());
+ GPRFlushedCallResult result(this);
+ callToString(validRadixIsGuaranteed ? operationDoubleToStringWithValidRadix : operationDoubleToString, result.gpr(), value.fpr(), radix.gpr());
+ break;
+ }
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+}
+
void SpeculativeJIT::compileNewStringObject(Node* node)
{
SpeculateCellOperand operand(this, node->child1());
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2017-03-21 11:31:43 UTC (rev 214219)
@@ -1381,7 +1381,30 @@
return appendCallSetResult(operation, result);
}
+ JITCompiler::Call callOperation(P_JITOperation_EZZ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2);
+ return appendCallSetResult(operation, result);
+ }
+ JITCompiler::Call callOperation(P_JITOperation_EZZ operation, GPRReg result, GPRReg arg1, TrustedImm32 arg2)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2);
+ return appendCallSetResult(operation, result);
+ }
+
+ JITCompiler::Call callOperation(P_JITOperation_EDZ operation, GPRReg result, FPRReg arg1, GPRReg arg2)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2);
+ return appendCallSetResult(operation, result);
+ }
+
+ JITCompiler::Call callOperation(P_JITOperation_EDZ operation, GPRReg result, FPRReg arg1, TrustedImm32 arg2)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2);
+ return appendCallSetResult(operation, result);
+ }
+
#if USE(JSVALUE64)
JITCompiler::Call callOperation(Z_JITOperation_EOJ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
{
@@ -1610,6 +1633,17 @@
m_jit.setupArgumentsWithExecState(TrustedImm32(arg1), arg2);
return appendCallSetResult(operation, result);
}
+ JITCompiler::Call callOperation(P_JITOperation_EQZ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2);
+ return appendCallSetResult(operation, result);
+ }
+ JITCompiler::Call callOperation(P_JITOperation_EQZ operation, GPRReg result, GPRReg arg1, TrustedImm32 arg2)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2);
+ return appendCallSetResult(operation, result);
+ }
+
JITCompiler::Call callOperation(J_JITOperation_EZIcfZ operation, GPRReg result, int32_t arg1, InlineCallFrame* inlineCallFrame, GPRReg arg2)
{
m_jit.setupArgumentsWithExecState(TrustedImm32(arg1), TrustedImmPtr(inlineCallFrame), arg2);
@@ -2681,7 +2715,9 @@
void emitSwitchString(Node*, SwitchData*);
void emitSwitch(Node*);
- void compileToStringOrCallStringConstructorOnCell(Node*);
+ void compileToStringOrCallStringConstructor(Node*);
+ void compileToStringOrCallStringConstructorOnNumber(Node*);
+ void compileNumberToStringWithRadix(Node*);
void compileNewStringObject(Node*);
void compileNewTypedArray(Node*);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2017-03-21 11:31:43 UTC (rev 214219)
@@ -2835,6 +2835,11 @@
break;
}
+ case NumberToStringWithRadix: {
+ compileNumberToStringWithRadix(node);
+ break;
+ }
+
case GetByValWithThis: {
JSValueOperand base(this, node->child1());
JSValueRegs baseRegs = base.jsValueRegs();
@@ -3769,7 +3774,7 @@
case ToString:
case CallStringConstructor: {
- compileToStringOrCallStringConstructorOnCell(node);
+ compileToStringOrCallStringConstructor(node);
break;
}
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2017-03-21 11:31:43 UTC (rev 214219)
@@ -3739,7 +3739,7 @@
case ToString:
case CallStringConstructor: {
- compileToStringOrCallStringConstructorOnCell(node);
+ compileToStringOrCallStringConstructor(node);
break;
}
@@ -5057,6 +5057,11 @@
break;
}
+ case NumberToStringWithRadix: {
+ compileNumberToStringWithRadix(node);
+ break;
+ }
+
case IsObject: {
JSValueOperand value(this, node->child1());
GPRTemporary result(this, Reuse, value);
Modified: trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp (214218 => 214219)
--- trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp 2017-03-21 11:31:43 UTC (rev 214219)
@@ -390,6 +390,37 @@
break;
}
+ case ToString:
+ case CallStringConstructor: {
+ Edge& child1 = m_node->child1();
+ switch (child1.useKind()) {
+ case Int32Use:
+ case Int52RepUse:
+ case DoubleRepUse: {
+ if (child1->hasConstant()) {
+ JSValue value = child1->constant()->value();
+ if (value) {
+ String result;
+ if (value.isInt32())
+ result = String::number(value.asInt32());
+ else if (value.isNumber())
+ result = String::numberToStringECMAScript(value.asNumber());
+
+ if (!result.isNull()) {
+ m_node->convertToLazyJSConstant(m_graph, LazyJSValue::newString(m_graph, result));
+ m_changed = true;
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ break;
+ }
+
case GetArrayLength: {
if (m_node->arrayMode().type() == Array::Generic
|| m_node->arrayMode().type() == Array::String) {
Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (214218 => 214219)
--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2017-03-21 11:31:43 UTC (rev 214219)
@@ -279,6 +279,7 @@
case DefineDataProperty:
case DefineAccessorProperty:
case ToLowerCase:
+ case NumberToStringWithRadix:
case CheckDOM:
case CallDOM:
case CallDOMGetter:
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (214218 => 214219)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2017-03-21 11:31:43 UTC (rev 214219)
@@ -1078,6 +1078,9 @@
case ToLowerCase:
compileToLowerCase();
break;
+ case NumberToStringWithRadix:
+ compileNumberToStringWithRadix();
+ break;
case CheckDOM:
compileCheckDOM();
break;
@@ -4981,6 +4984,18 @@
setJSValue(m_out.phi(Int64, simpleResult, convertedResult));
return;
}
+
+ case Int32Use:
+ setJSValue(vmCall(Int64, m_out.operation(operationInt32ToStringWithValidRadix), m_callFrame, lowInt32(m_node->child1()), m_out.constInt32(10)));
+ return;
+
+ case Int52RepUse:
+ setJSValue(vmCall(Int64, m_out.operation(operationInt52ToStringWithValidRadix), m_callFrame, lowStrictInt52(m_node->child1()), m_out.constInt32(10)));
+ return;
+
+ case DoubleRepUse:
+ setJSValue(vmCall(Int64, m_out.operation(operationDoubleToStringWithValidRadix), m_callFrame, lowDouble(m_node->child1()), m_out.constInt32(10)));
+ return;
default:
DFG_CRASH(m_graph, m_node, "Bad use kind");
@@ -9984,6 +9999,30 @@
setJSValue(m_out.phi(pointerType(), fastResult, slowResult));
}
+ void compileNumberToStringWithRadix()
+ {
+ bool validRadixIsGuaranteed = false;
+ if (m_node->child2()->isInt32Constant()) {
+ int32_t radix = m_node->child2()->asInt32();
+ if (radix >= 2 && radix <= 36)
+ validRadixIsGuaranteed = true;
+ }
+
+ switch (m_node->child1().useKind()) {
+ case Int32Use:
+ setJSValue(vmCall(pointerType(), m_out.operation(validRadixIsGuaranteed ? operationInt32ToStringWithValidRadix : operationInt32ToString), m_callFrame, lowInt32(m_node->child1()), lowInt32(m_node->child2())));
+ break;
+ case Int52RepUse:
+ setJSValue(vmCall(pointerType(), m_out.operation(validRadixIsGuaranteed ? operationInt52ToStringWithValidRadix : operationInt52ToString), m_callFrame, lowStrictInt52(m_node->child1()), lowInt32(m_node->child2())));
+ break;
+ case DoubleRepUse:
+ setJSValue(vmCall(pointerType(), m_out.operation(validRadixIsGuaranteed ? operationDoubleToStringWithValidRadix : operationDoubleToString), m_callFrame, lowDouble(m_node->child1()), lowInt32(m_node->child2())));
+ break;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ }
+
void compileResolveScope()
{
UniquedStringImpl* uid = m_graph.identifiers()[m_node->identifierNumber()];
Modified: trunk/Source/_javascript_Core/jit/CCallHelpers.h (214218 => 214219)
--- trunk/Source/_javascript_Core/jit/CCallHelpers.h 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/jit/CCallHelpers.h 2017-03-21 11:31:43 UTC (rev 214219)
@@ -892,6 +892,14 @@
addCallArgument(arg2);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, TrustedImm32 arg2)
+ {
+ resetCallArguments();
+ addCallArgument(GPRInfo::callFrameRegister);
+ addCallArgument(arg1);
+ addCallArgument(arg2);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3)
{
resetCallArguments();
@@ -1093,6 +1101,20 @@
move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, TrustedImm32 arg2)
+ {
+#if OS(WINDOWS) && CPU(X86_64)
+ // On Windows, arguments map to designated registers based on the argument positions, even when there are interlaced scalar and floating point arguments.
+ // See http://msdn.microsoft.com/en-us/library/zthk2dkh.aspx
+ moveDouble(arg1, FPRInfo::argumentFPR1);
+ move(arg2, GPRInfo::argumentGPR2);
+#else
+ moveDouble(arg1, FPRInfo::argumentFPR0);
+ move(arg2, GPRInfo::argumentGPR1);
+#endif
+ move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3)
{
#if OS(WINDOWS) && CPU(X86_64)
@@ -1135,6 +1157,13 @@
move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, TrustedImm32 arg2)
+ {
+ moveDouble(arg1, FPRInfo::argumentFPR0);
+ move(arg2, GPRInfo::argumentGPR1);
+ move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3)
{
moveDouble(arg3, FPRInfo::argumentFPR0);
@@ -1175,6 +1204,13 @@
move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, TrustedImm32 arg2)
+ {
+ move(arg2, GPRInfo::argumentGPR3);
+ assembler().vmov(GPRInfo::argumentGPR1, GPRInfo::argumentGPR2, arg1);
+ move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3)
{
setupStubArguments(arg1, arg2);
@@ -1228,6 +1264,13 @@
poke(arg2, 4);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, TrustedImm32 arg2)
+ {
+ assembler().vmov(GPRInfo::argumentGPR2, GPRInfo::argumentGPR3, arg1);
+ move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+ poke(arg2, 4);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3)
{
setupStubArguments(arg1, arg2);
Modified: trunk/Source/_javascript_Core/jit/JITOperations.h (214218 => 214219)
--- trunk/Source/_javascript_Core/jit/JITOperations.h 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/jit/JITOperations.h 2017-03-21 11:31:43 UTC (rev 214219)
@@ -312,6 +312,8 @@
typedef char* (JIT_OPERATION *P_JITOperation_EStZB)(ExecState*, Structure*, int32_t, Butterfly*);
typedef char* (JIT_OPERATION *P_JITOperation_EStZP)(ExecState*, Structure*, int32_t, char*);
typedef char* (JIT_OPERATION *P_JITOperation_EZZ)(ExecState*, int32_t, int32_t);
+typedef char* (JIT_OPERATION *P_JITOperation_EQZ)(ExecState*, int64_t, int32_t);
+typedef char* (JIT_OPERATION *P_JITOperation_EDZ)(ExecState*, double, int32_t);
typedef SlowPathReturnType (JIT_OPERATION *Sprt_JITOperation_ECli)(ExecState*, CallLinkInfo*);
typedef StringImpl* (JIT_OPERATION *T_JITOperation_EJss)(ExecState*, JSString*);
typedef JSString* (JIT_OPERATION *Jss_JITOperation_EZ)(ExecState*, int32_t);
Modified: trunk/Source/_javascript_Core/runtime/Intrinsic.h (214218 => 214219)
--- trunk/Source/_javascript_Core/runtime/Intrinsic.h 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/runtime/Intrinsic.h 2017-03-21 11:31:43 UTC (rev 214219)
@@ -56,6 +56,8 @@
StringPrototypeValueOfIntrinsic,
StringPrototypeReplaceIntrinsic,
StringPrototypeReplaceRegExpIntrinsic,
+ StringPrototypeToLowerCaseIntrinsic,
+ NumberPrototypeToStringIntrinsic,
IMulIntrinsic,
RandomIntrinsic,
FRoundIntrinsic,
@@ -78,7 +80,6 @@
AtomicsWaitIntrinsic,
AtomicsWakeIntrinsic,
AtomicsXorIntrinsic,
- ToLowerCaseIntrinsic,
ParseIntIntrinsic,
// Getter intrinsics.
Modified: trunk/Source/_javascript_Core/runtime/NumberPrototype.cpp (214218 => 214219)
--- trunk/Source/_javascript_Core/runtime/NumberPrototype.cpp 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/runtime/NumberPrototype.cpp 2017-03-21 11:31:43 UTC (rev 214219)
@@ -58,7 +58,7 @@
/* Source for NumberPrototype.lut.h
@begin numberPrototypeTable
- toString numberProtoFuncToString DontEnum|Function 1
+ toString numberProtoFuncToString DontEnum|Function 1 NumberPrototypeToStringIntrinsic
toLocaleString numberProtoFuncToLocaleString DontEnum|Function 0
valueOf numberProtoFuncValueOf DontEnum|Function 0
toFixed numberProtoFuncToFixed DontEnum|Function 1
@@ -144,7 +144,7 @@
// Mapping from integers 0..35 to digit identifying this value, for radix 2..36.
static const char radixDigits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
-static char* int52ToStringWithRadix(char* startOfResultString, int64_t int52Value, unsigned radix)
+static inline char* int52ToStringWithRadix(char* startOfResultString, int64_t int52Value, unsigned radix)
{
bool negative = false;
uint64_t positiveNumber = int52Value;
@@ -499,25 +499,68 @@
return radix;
}
-static inline EncodedJSValue integerValueToString(ExecState* exec, int32_t radix, int32_t value)
+static inline JSString* int32ToStringInternal(VM& vm, int32_t value, int32_t radix)
{
+ ASSERT(!(radix < 2 || radix > 36));
// A negative value casted to unsigned would be bigger than 36 (the max radix).
if (static_cast<unsigned>(value) < static_cast<unsigned>(radix)) {
ASSERT(value <= 36);
ASSERT(value >= 0);
- VM* vm = &exec->vm();
- return JSValue::encode(vm->smallStrings.singleCharacterString(radixDigits[value]));
+ return vm.smallStrings.singleCharacterString(radixDigits[value]);
}
if (radix == 10) {
- VM* vm = &exec->vm();
- return JSValue::encode(jsString(vm, vm->numericStrings.add(value)));
+ return jsString(&vm, vm.numericStrings.add(value));
}
- return JSValue::encode(jsString(exec, toStringWithRadix(value, radix)));
+ return jsString(&vm, toStringWithRadix(value, radix));
}
+static ALWAYS_INLINE JSString* numberToStringInternal(VM& vm, double doubleValue, int32_t radix)
+{
+ ASSERT(!(radix < 2 || radix > 36));
+
+ int32_t integerValue = static_cast<int32_t>(doubleValue);
+ if (integerValue == doubleValue)
+ return int32ToStringInternal(vm, integerValue, radix);
+
+ if (radix == 10)
+ return jsString(&vm, vm.numericStrings.add(doubleValue));
+
+ if (!std::isfinite(doubleValue))
+ return jsNontrivialString(&vm, String::numberToStringECMAScript(doubleValue));
+
+ RadixBuffer buffer;
+ return jsString(&vm, toStringWithRadix(buffer, doubleValue, radix));
+}
+
+JSString* int32ToString(VM& vm, int32_t value, int32_t radix)
+{
+ return int32ToStringInternal(vm, value, radix);
+}
+
+JSString* int52ToString(VM& vm, int64_t value, int32_t radix)
+{
+ ASSERT(!(radix < 2 || radix > 36));
+ if (radix == 10)
+ return jsString(&vm, vm.numericStrings.add(static_cast<double>(value)));
+
+ // Position the decimal point at the center of the string, set
+ // the startOfResultString pointer to point at the decimal point.
+ RadixBuffer buffer;
+ char* decimalPoint = buffer + sizeof(buffer) / 2;
+ char* startOfResultString = decimalPoint;
+ *decimalPoint = '\0';
+
+ return jsString(&vm, int52ToStringWithRadix(startOfResultString, value, radix));
+}
+
+JSString* numberToString(VM& vm, double doubleValue, int32_t radix)
+{
+ return numberToStringInternal(vm, doubleValue, radix);
+}
+
EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec)
{
VM& vm = exec->vm();
@@ -531,18 +574,7 @@
if (radix < 2 || radix > 36)
return throwVMError(exec, scope, createRangeError(exec, ASCIILiteral("toString() radix argument must be between 2 and 36")));
- int32_t integerValue = static_cast<int32_t>(doubleValue);
- if (integerValue == doubleValue)
- return integerValueToString(exec, radix, integerValue);
-
- if (radix == 10)
- return JSValue::encode(jsString(&vm, vm.numericStrings.add(doubleValue)));
-
- if (!std::isfinite(doubleValue))
- return JSValue::encode(jsNontrivialString(exec, String::numberToStringECMAScript(doubleValue)));
-
- RadixBuffer s;
- return JSValue::encode(jsString(exec, toStringWithRadix(s, doubleValue, radix)));
+ return JSValue::encode(numberToStringInternal(vm, doubleValue, radix));
}
EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState* exec)
Modified: trunk/Source/_javascript_Core/runtime/NumberPrototype.h (214218 => 214219)
--- trunk/Source/_javascript_Core/runtime/NumberPrototype.h 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/runtime/NumberPrototype.h 2017-03-21 11:31:43 UTC (rev 214219)
@@ -51,5 +51,8 @@
};
EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState*);
+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);
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/StringPrototype.cpp (214218 => 214219)
--- trunk/Source/_javascript_Core/runtime/StringPrototype.cpp 2017-03-21 11:16:47 UTC (rev 214218)
+++ trunk/Source/_javascript_Core/runtime/StringPrototype.cpp 2017-03-21 11:31:43 UTC (rev 214219)
@@ -142,7 +142,7 @@
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("slice", stringProtoFuncSlice, DontEnum, 2);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substr", stringProtoFuncSubstr, DontEnum, 2);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substring", stringProtoFuncSubstring, DontEnum, 2);
- JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0, ToLowerCaseIntrinsic);
+ JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0, StringPrototypeToLowerCaseIntrinsic);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
#if ENABLE(INTL)
JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("localeCompare", stringPrototypeLocaleCompareCodeGenerator, DontEnum);