Skip to content

Commit

Permalink
Optimize Function.prototype.toString
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=226418
<rdar://77861846>

Reviewed by Saam Barati.

JSTests:

* microbenchmarks/function-to-string.js: Added.
(f):
(C):
(C.prototype.method1):
(C.prototype.method2):
(test):
(test2):

Source/JavaScriptCore:

Add caching to Function.prototype.toString. This is used heavily in Speedometer2, and repeatedly recomputing a
string which is a constant is costly. We cache the results of toString in all cases except for bound functions.
To make this work for bound functions, we'd need to add a new field they can use for this cache. For other
functions, we cache it on the executable (either NativeExecutable or FunctionExecutable). The reason we can't
do this on the executable for bound functions is that all bound functions share the same executable, but
individual bound functions can have different names. The reason it's valid to cache the results in general is that a
function's name field can't be changed from JS code -- it's non-writable.

This patch also makes Function.prototype.toString an intrinsic in the DFG/FTL. We emit code on the fast path
which reads the cached value if it's present. If not, we call into the slow path, which will compute
the cached value for non bound functions, or compute the result for bound functions.

I added a new microbenchmark that speeds up by >35x:

function-to-string     2197.5952+-30.7118    ^     59.9861+-2.5550        ^ definitely 36.6350x faster

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* 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):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
(JSC::DFG::JSC_DEFINE_JIT_OPERATION):
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::getExecutable):
(JSC::DFG::SpeculativeJIT::compileFunctionToString):
(JSC::DFG::SpeculativeJIT::compileGetExecutable):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::getExecutable):
(JSC::FTL::DFG::LowerDFGToB3::compileGetExecutable):
(JSC::FTL::DFG::LowerDFGToB3::compileFunctionToString):
* runtime/FunctionExecutable.cpp:
(JSC::FunctionExecutable::visitChildrenImpl):
(JSC::FunctionExecutable::toStringSlow):
* runtime/FunctionExecutable.h:
* runtime/FunctionExecutableInlines.h:
(JSC::FunctionExecutable::toString):
* runtime/FunctionPrototype.cpp:
(JSC::FunctionPrototype::addFunctionProperties):
(JSC::JSC_DEFINE_HOST_FUNCTION):
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/JSFunction.cpp:
(JSC::JSFunction::toString):
* runtime/JSFunction.h:
* runtime/JSFunctionInlines.h:
(JSC::JSFunction::asStringConcurrently const):
* runtime/JSStringInlines.h:
* runtime/NativeExecutable.cpp:
(JSC::NativeExecutable::toStringSlow):
(JSC::NativeExecutable::visitChildrenImpl):
* runtime/NativeExecutable.h:


Canonical link: https://commits.webkit.org/238482@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@278462 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
tadeuzagallo committed Jun 4, 2021
1 parent 3a671c1 commit 83d3986
Show file tree
Hide file tree
Showing 34 changed files with 483 additions and 68 deletions.
16 changes: 16 additions & 0 deletions JSTests/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
2021-06-04 Tadeu Zagallo <tzagallo@apple.com>

Optimize Function.prototype.toString
https://bugs.webkit.org/show_bug.cgi?id=226418
<rdar://77861846>

Reviewed by Saam Barati.

* microbenchmarks/function-to-string.js: Added.
(f):
(C):
(C.prototype.method1):
(C.prototype.method2):
(test):
(test2):

2021-06-03 Ross Kirsling <ross.kirsling@sony.com>

[JSC] Implement JIT ICs for InByVal
Expand Down
40 changes: 40 additions & 0 deletions JSTests/microbenchmarks/function-to-string.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
function f(x, y, z) {
// comment in the body
const w = 42;
return w + x + y + z;

}
noInline(f);

class C {
// comment in the class
constructor() {
}

method1() {
return "some string";
}

method2() {
return 42;
}
}

function test() {
f.toString();
C.toString();
print.toString();
}
noInline(test);

for (let i = 0; i < 1e7; ++i)
test();

function test2(x, y, z) {
for (let i = 0; i < 1e6; ++i) {
f(x.toString(), y.toString(), z.toString(), x.toString(), y.toString());
}
}
noInline(test2);

test2(f, C, print);
1 change: 1 addition & 0 deletions Source/JavaScriptCore/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,7 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS
runtime/JSArrayBufferViewInlines.h
runtime/JSArrayIterator.h
runtime/JSBigInt.h
runtime/JSBoundFunction.h
runtime/JSCConfig.h
runtime/JSCInlines.h
runtime/JSCJSValue.h
Expand Down
83 changes: 83 additions & 0 deletions Source/JavaScriptCore/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,86 @@
2021-06-04 Tadeu Zagallo <tzagallo@apple.com>

Optimize Function.prototype.toString
https://bugs.webkit.org/show_bug.cgi?id=226418
<rdar://77861846>

Reviewed by Saam Barati.

Add caching to Function.prototype.toString. This is used heavily in Speedometer2, and repeatedly recomputing a
string which is a constant is costly. We cache the results of toString in all cases except for bound functions.
To make this work for bound functions, we'd need to add a new field they can use for this cache. For other
functions, we cache it on the executable (either NativeExecutable or FunctionExecutable). The reason we can't
do this on the executable for bound functions is that all bound functions share the same executable, but
individual bound functions can have different names. The reason it's valid to cache the results in general is that a
function's name field can't be changed from JS code -- it's non-writable.

This patch also makes Function.prototype.toString an intrinsic in the DFG/FTL. We emit code on the fast path
which reads the cached value if it's present. If not, we call into the slow path, which will compute
the cached value for non bound functions, or compute the result for bound functions.

I added a new microbenchmark that speeds up by >35x:

function-to-string 2197.5952+-30.7118 ^ 59.9861+-2.5550 ^ definitely 36.6350x faster

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* 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):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
(JSC::DFG::JSC_DEFINE_JIT_OPERATION):
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::getExecutable):
(JSC::DFG::SpeculativeJIT::compileFunctionToString):
(JSC::DFG::SpeculativeJIT::compileGetExecutable):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::getExecutable):
(JSC::FTL::DFG::LowerDFGToB3::compileGetExecutable):
(JSC::FTL::DFG::LowerDFGToB3::compileFunctionToString):
* runtime/FunctionExecutable.cpp:
(JSC::FunctionExecutable::visitChildrenImpl):
(JSC::FunctionExecutable::toStringSlow):
* runtime/FunctionExecutable.h:
* runtime/FunctionExecutableInlines.h:
(JSC::FunctionExecutable::toString):
* runtime/FunctionPrototype.cpp:
(JSC::FunctionPrototype::addFunctionProperties):
(JSC::JSC_DEFINE_HOST_FUNCTION):
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/JSFunction.cpp:
(JSC::JSFunction::toString):
* runtime/JSFunction.h:
* runtime/JSFunctionInlines.h:
(JSC::JSFunction::asStringConcurrently const):
* runtime/JSStringInlines.h:
* runtime/NativeExecutable.cpp:
(JSC::NativeExecutable::toStringSlow):
(JSC::NativeExecutable::visitChildrenImpl):
* runtime/NativeExecutable.h:

2021-06-04 Michael Catanzaro <mcatanzaro@gnome.org>

Fix more GCC warnings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1302,7 +1302,7 @@
86ECA3FA132DF25A002B2AD7 /* DFGScoreBoard.h in Headers */ = {isa = PBXBuildFile; fileRef = 86ECA3F9132DF25A002B2AD7 /* DFGScoreBoard.h */; };
86F3EEBD168CDE930077B92A /* ObjCCallbackFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F3EEB9168CCF750077B92A /* ObjCCallbackFunction.h */; };
86F3EEBF168CDE930077B92A /* ObjcRuntimeExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F3EEB616855A5B0077B92A /* ObjcRuntimeExtras.h */; };
86FA9E92142BBB2E001773B7 /* JSBoundFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 86FA9E90142BBB2E001773B7 /* JSBoundFunction.h */; };
86FA9E92142BBB2E001773B7 /* JSBoundFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 86FA9E90142BBB2E001773B7 /* JSBoundFunction.h */; settings = {ATTRIBUTES = (Private, ); }; };
8B3BF5E41E3D368B0076A87A /* AsyncGeneratorPrototype.lut.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B3BF5E31E3D365A0076A87A /* AsyncGeneratorPrototype.lut.h */; };
8B6016F61F3E3CC000F9DE6A /* AsyncFromSyncIteratorPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6016F41F3E3CC000F9DE6A /* AsyncFromSyncIteratorPrototype.h */; };
8B9F6D561D5912FA001C739F /* IterationKind.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B9F6D551D5912FA001C739F /* IterationKind.h */; settings = {ATTRIBUTES = (Private, ); }; };
Expand Down
13 changes: 13 additions & 0 deletions Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Original file line number Diff line number Diff line change
Expand Up @@ -2855,6 +2855,19 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
}

case FunctionToString: {
JSValue value = m_state.forNode(node->child1()).value();
if (value) {
JSFunction* function = jsDynamicCast<JSFunction*>(m_vm, value);
if (JSString* asString = function->asStringConcurrently(m_vm)) {
setConstant(node, *m_graph.freeze(asString));
break;
}
}
setForNode(node, m_vm.stringStructure.get());
break;
}

case NumberToStringWithRadix: {
JSValue radixValue = forNode(node->child2()).m_value;
if (radixValue && radixValue.isInt32()) {
Expand Down
11 changes: 11 additions & 0 deletions Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3742,6 +3742,17 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, Operand result, Intrinsic
#endif
}

case FunctionToStringIntrinsic: {
if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType))
return false;

insertChecks();
Node* function = get(virtualRegisterForArgumentIncludingThis(0, registerOffset));
Node* resultNode = addToGraph(FunctionToString, function);
setResult(resultNode);
return true;
}

default:
return false;
}
Expand Down
4 changes: 4 additions & 0 deletions Source/JavaScriptCore/dfg/DFGClobberize.h
Original file line number Diff line number Diff line change
Expand Up @@ -1818,6 +1818,10 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
RELEASE_ASSERT_NOT_REACHED();
return;
}

case FunctionToString:
def(PureValue(node));
return;

case CountExecution:
case SuperSamplerBegin:
Expand Down
1 change: 1 addition & 0 deletions Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ bool doesGC(Graph& graph, Node* node)
case DirectTailCall:
case DirectTailCallInlinedCaller:
case ForceOSRExit:
case FunctionToString:
case GetById:
case GetByIdDirect:
case GetByIdDirectFlush:
Expand Down
4 changes: 4 additions & 0 deletions Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1973,6 +1973,10 @@ class FixupPhase : public Phase {
break;
}

case FunctionToString: {
fixEdge<FunctionUse>(node->child1());
break;
}

case SetPrivateBrand: {
fixEdge<CellUse>(node->child1());
Expand Down
1 change: 1 addition & 0 deletions Source/JavaScriptCore/dfg/DFGNodeType.h
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ namespace JSC { namespace DFG {
macro(CallNumberConstructor, NodeResultJS | NodeMustGenerate) \
macro(NumberToStringWithRadix, NodeResultJS | NodeMustGenerate) \
macro(NumberToStringWithValidRadixConstant, NodeResultJS) \
macro(FunctionToString, NodeResultJS) \
macro(MakeRope, NodeResultJS) \
macro(InByVal, NodeResultBoolean | NodeMustGenerate) \
macro(InById, NodeResultBoolean | NodeMustGenerate) \
Expand Down
9 changes: 9 additions & 0 deletions Source/JavaScriptCore/dfg/DFGOperations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2560,6 +2560,15 @@ JSC_DEFINE_JIT_OPERATION(operationDoubleToStringWithValidRadix, char*, (JSGlobal
return reinterpret_cast<char*>(numberToString(vm, value, radix));
}

JSC_DEFINE_JIT_OPERATION(operationFunctionToString, JSString*, (JSGlobalObject* globalObject, JSFunction* function))
{
VM& vm = globalObject->vm();
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);

return function->toString(globalObject);
}

JSC_DEFINE_JIT_OPERATION(operationSingleCharacterString, JSString*, (VM* vmPointer, int32_t character))
{
VM& vm = *vmPointer;
Expand Down
1 change: 1 addition & 0 deletions Source/JavaScriptCore/dfg/DFGOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ JSC_DECLARE_JIT_OPERATION(operationDoubleToString, char*, (JSGlobalObject*, doub
JSC_DECLARE_JIT_OPERATION(operationInt32ToStringWithValidRadix, char*, (JSGlobalObject*, int32_t, int32_t));
JSC_DECLARE_JIT_OPERATION(operationInt52ToStringWithValidRadix, char*, (JSGlobalObject*, int64_t, int32_t));
JSC_DECLARE_JIT_OPERATION(operationDoubleToStringWithValidRadix, char*, (JSGlobalObject*, double, int32_t));
JSC_DECLARE_JIT_OPERATION(operationFunctionToString, JSString*, (JSGlobalObject*, JSFunction*));

JSC_DECLARE_JIT_OPERATION(operationNormalizeMapKeyHeapBigInt, EncodedJSValue, (VM*, JSBigInt*));
JSC_DECLARE_JIT_OPERATION(operationMapHash, UCPUStrictInt32, (JSGlobalObject*, EncodedJSValue input));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,7 @@ class PredictionPropagationPhase : public Phase {
case StringCharAt:
case CallStringConstructor:
case ToString:
case FunctionToString:
case NumberToStringWithRadix:
case NumberToStringWithValidRadixConstant:
case MakeRope:
Expand Down
1 change: 1 addition & 0 deletions Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno
case ToBoolean:
case LogicalNot:
case ToString:
case FunctionToString:
case NumberToStringWithValidRadixConstant:
case StrCat:
case CallStringConstructor:
Expand Down
53 changes: 45 additions & 8 deletions Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include "JSArrayIterator.h"
#include "JSAsyncFunction.h"
#include "JSAsyncGeneratorFunction.h"
#include "JSBoundFunction.h"
#include "JSCInlines.h"
#include "JSGeneratorFunction.h"
#include "JSImmutableButterfly.h"
Expand Down Expand Up @@ -10555,6 +10556,47 @@ void SpeculativeJIT::compileToStringOrCallStringConstructorOrStringValueOf(Node*
}
}

static void getExecutable(JITCompiler& jit, GPRReg functionGPR, GPRReg resultGPR)
{
jit.loadPtr(JITCompiler::Address(functionGPR, JSFunction::offsetOfExecutableOrRareData()), resultGPR);
auto hasExecutable = jit.branchTestPtr(CCallHelpers::Zero, resultGPR, CCallHelpers::TrustedImm32(JSFunction::rareDataTag));
jit.loadPtr(CCallHelpers::Address(resultGPR, FunctionRareData::offsetOfExecutable() - JSFunction::rareDataTag), resultGPR);
hasExecutable.link(&jit);
}

void SpeculativeJIT::compileFunctionToString(Node* node)
{
SpeculateCellOperand function(this, node->child1());
GPRTemporary executable(this);
GPRTemporary result(this);
JITCompiler::JumpList slowCases;

speculateFunction(node->child1(), function.gpr());

m_jit.emitLoadStructure(vm(), function.gpr(), result.gpr(), executable.gpr());
m_jit.loadPtr(JITCompiler::Address(result.gpr(), Structure::classInfoOffset()), result.gpr());
static_assert(std::is_final_v<JSBoundFunction>, "We don't handle subclasses when comparing classInfo below");
slowCases.append(m_jit.branchPtr(CCallHelpers::Equal, result.gpr(), TrustedImmPtr(JSBoundFunction::info())));

getExecutable(m_jit, function.gpr(), executable.gpr());
JITCompiler::Jump isNativeExecutable = m_jit.branch8(JITCompiler::Equal, JITCompiler::Address(executable.gpr(), JSCell::typeInfoTypeOffset()), TrustedImm32(NativeExecutableType));

m_jit.loadPtr(MacroAssembler::Address(executable.gpr(), FunctionExecutable::offsetOfRareData()), result.gpr());
slowCases.append(m_jit.branchTestPtr(MacroAssembler::Zero, result.gpr()));
m_jit.loadPtr(MacroAssembler::Address(result.gpr(), FunctionExecutable::offsetOfAsStringInRareData()), result.gpr());
JITCompiler::Jump continuation = m_jit.jump();

isNativeExecutable.link(&m_jit);
m_jit.loadPtr(MacroAssembler::Address(executable.gpr(), NativeExecutable::offsetOfAsString()), result.gpr());

continuation.link(&m_jit);
slowCases.append(m_jit.branchTestPtr(MacroAssembler::Zero, result.gpr()));

addSlowPathGenerator(slowPathCall(slowCases, this, operationFunctionToString, result.gpr(), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), function.gpr()));

cellResult(result.gpr(), node);
}

void SpeculativeJIT::compileNumberToStringWithValidRadixConstant(Node* node)
{
compileNumberToStringWithValidRadixConstant(node, node->validRadixConstant());
Expand Down Expand Up @@ -13304,14 +13346,9 @@ void SpeculativeJIT::compileGetExecutable(Node* node)
{
SpeculateCellOperand function(this, node->child1());
GPRTemporary result(this, Reuse, function);
GPRReg functionGPR = function.gpr();
GPRReg resultGPR = result.gpr();
speculateCellType(node->child1(), functionGPR, SpecFunction, JSFunctionType);
m_jit.loadPtr(JITCompiler::Address(functionGPR, JSFunction::offsetOfExecutableOrRareData()), resultGPR);
auto hasExecutable = m_jit.branchTestPtr(CCallHelpers::Zero, resultGPR, CCallHelpers::TrustedImm32(JSFunction::rareDataTag));
m_jit.loadPtr(CCallHelpers::Address(resultGPR, FunctionRareData::offsetOfExecutable() - JSFunction::rareDataTag), resultGPR);
hasExecutable.link(&m_jit);
cellResult(resultGPR, node);
speculateFunction(node->child1(), function.gpr());
getExecutable(m_jit, function.gpr(), result.gpr());
cellResult(result.gpr(), node);
}

void SpeculativeJIT::compileGetGetter(Node* node)
Expand Down
1 change: 1 addition & 0 deletions Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,7 @@ class SpeculativeJIT {
void emitSwitch(Node*);

void compileToStringOrCallStringConstructorOrStringValueOf(Node*);
void compileFunctionToString(Node*);
void compileNumberToStringWithRadix(Node*);
void compileNumberToStringWithValidRadixConstant(Node*);
void compileNumberToStringWithValidRadixConstant(Node*, int32_t radix);
Expand Down
4 changes: 4 additions & 0 deletions Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3205,6 +3205,10 @@ void SpeculativeJIT::compile(Node* node)
compileToStringOrCallStringConstructorOrStringValueOf(node);
break;
}

case FunctionToString:
compileFunctionToString(node);
break;

case NewStringObject: {
compileNewStringObject(node);
Expand Down
4 changes: 4 additions & 0 deletions Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3790,6 +3790,10 @@ void SpeculativeJIT::compile(Node* node)
compileToStringOrCallStringConstructorOrStringValueOf(node);
break;
}

case FunctionToString:
compileFunctionToString(node);
break;

case NewStringObject: {
compileNewStringObject(node);
Expand Down

0 comments on commit 83d3986

Please sign in to comment.