Changeset 278462 in webkit


Ignore:
Timestamp:
Jun 4, 2021 8:58:13 AM (14 months ago)
Author:
Tadeu Zagallo
Message:

Optimize Function.prototype.toString
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:
Location:
trunk
Files:
1 added
33 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r278445 r278462  
     12021-06-04  Tadeu Zagallo  <tzagallo@apple.com>
     2
     3        Optimize Function.prototype.toString
     4        https://bugs.webkit.org/show_bug.cgi?id=226418
     5        <rdar://77861846>
     6
     7        Reviewed by Saam Barati.
     8
     9        * microbenchmarks/function-to-string.js: Added.
     10        (f):
     11        (C):
     12        (C.prototype.method1):
     13        (C.prototype.method2):
     14        (test):
     15        (test2):
     16
    1172021-06-03  Ross Kirsling  <ross.kirsling@sony.com>
    218
  • trunk/Source/JavaScriptCore/CMakeLists.txt

    r278229 r278462  
    944944    runtime/JSArrayIterator.h
    945945    runtime/JSBigInt.h
     946    runtime/JSBoundFunction.h
    946947    runtime/JSCConfig.h
    947948    runtime/JSCInlines.h
  • trunk/Source/JavaScriptCore/ChangeLog

    r278458 r278462  
     12021-06-04  Tadeu Zagallo  <tzagallo@apple.com>
     2
     3        Optimize Function.prototype.toString
     4        https://bugs.webkit.org/show_bug.cgi?id=226418
     5        <rdar://77861846>
     6
     7        Reviewed by Saam Barati.
     8
     9        Add caching to Function.prototype.toString. This is used heavily in Speedometer2, and repeatedly recomputing a
     10        string which is a constant is costly. We cache the results of toString in all cases except for bound functions.
     11        To make this work for bound functions, we'd need to add a new field they can use for this cache. For other
     12        functions, we cache it on the executable (either NativeExecutable or FunctionExecutable). The reason we can't
     13        do this on the executable for bound functions is that all bound functions share the same executable, but
     14        individual bound functions can have different names. The reason it's valid to cache the results in general is that a
     15        function's name field can't be changed from JS code -- it's non-writable.
     16
     17        This patch also makes Function.prototype.toString an intrinsic in the DFG/FTL. We emit code on the fast path
     18        which reads the cached value if it's present. If not, we call into the slow path, which will compute
     19        the cached value for non bound functions, or compute the result for bound functions.
     20
     21        I added a new microbenchmark that speeds up by >35x:
     22
     23        function-to-string     2197.5952+-30.7118    ^     59.9861+-2.5550        ^ definitely 36.6350x faster
     24
     25        * CMakeLists.txt:
     26        * JavaScriptCore.xcodeproj/project.pbxproj:
     27        * dfg/DFGAbstractInterpreterInlines.h:
     28        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
     29        * dfg/DFGByteCodeParser.cpp:
     30        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
     31        * dfg/DFGClobberize.h:
     32        (JSC::DFG::clobberize):
     33        * dfg/DFGDoesGC.cpp:
     34        (JSC::DFG::doesGC):
     35        * dfg/DFGFixupPhase.cpp:
     36        (JSC::DFG::FixupPhase::fixupNode):
     37        * dfg/DFGNodeType.h:
     38        * dfg/DFGOperations.cpp:
     39        (JSC::DFG::JSC_DEFINE_JIT_OPERATION):
     40        * dfg/DFGOperations.h:
     41        * dfg/DFGPredictionPropagationPhase.cpp:
     42        * dfg/DFGSafeToExecute.h:
     43        (JSC::DFG::safeToExecute):
     44        * dfg/DFGSpeculativeJIT.cpp:
     45        (JSC::DFG::getExecutable):
     46        (JSC::DFG::SpeculativeJIT::compileFunctionToString):
     47        (JSC::DFG::SpeculativeJIT::compileGetExecutable):
     48        * dfg/DFGSpeculativeJIT.h:
     49        * dfg/DFGSpeculativeJIT32_64.cpp:
     50        (JSC::DFG::SpeculativeJIT::compile):
     51        * dfg/DFGSpeculativeJIT64.cpp:
     52        (JSC::DFG::SpeculativeJIT::compile):
     53        * ftl/FTLAbstractHeapRepository.h:
     54        * ftl/FTLCapabilities.cpp:
     55        (JSC::FTL::canCompile):
     56        * ftl/FTLLowerDFGToB3.cpp:
     57        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
     58        (JSC::FTL::DFG::LowerDFGToB3::getExecutable):
     59        (JSC::FTL::DFG::LowerDFGToB3::compileGetExecutable):
     60        (JSC::FTL::DFG::LowerDFGToB3::compileFunctionToString):
     61        * runtime/FunctionExecutable.cpp:
     62        (JSC::FunctionExecutable::visitChildrenImpl):
     63        (JSC::FunctionExecutable::toStringSlow):
     64        * runtime/FunctionExecutable.h:
     65        * runtime/FunctionExecutableInlines.h:
     66        (JSC::FunctionExecutable::toString):
     67        * runtime/FunctionPrototype.cpp:
     68        (JSC::FunctionPrototype::addFunctionProperties):
     69        (JSC::JSC_DEFINE_HOST_FUNCTION):
     70        * runtime/Intrinsic.cpp:
     71        (JSC::intrinsicName):
     72        * runtime/Intrinsic.h:
     73        * runtime/JSFunction.cpp:
     74        (JSC::JSFunction::toString):
     75        * runtime/JSFunction.h:
     76        * runtime/JSFunctionInlines.h:
     77        (JSC::JSFunction::asStringConcurrently const):
     78        * runtime/JSStringInlines.h:
     79        * runtime/NativeExecutable.cpp:
     80        (JSC::NativeExecutable::toStringSlow):
     81        (JSC::NativeExecutable::visitChildrenImpl):
     82        * runtime/NativeExecutable.h:
     83
    1842021-06-04  Michael Catanzaro  <mcatanzaro@gnome.org>
    285
  • trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj

    r278445 r278462  
    13031303                86F3EEBD168CDE930077B92A /* ObjCCallbackFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F3EEB9168CCF750077B92A /* ObjCCallbackFunction.h */; };
    13041304                86F3EEBF168CDE930077B92A /* ObjcRuntimeExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F3EEB616855A5B0077B92A /* ObjcRuntimeExtras.h */; };
    1305                 86FA9E92142BBB2E001773B7 /* JSBoundFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 86FA9E90142BBB2E001773B7 /* JSBoundFunction.h */; };
     1305                86FA9E92142BBB2E001773B7 /* JSBoundFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 86FA9E90142BBB2E001773B7 /* JSBoundFunction.h */; settings = {ATTRIBUTES = (Private, ); }; };
    13061306                8B3BF5E41E3D368B0076A87A /* AsyncGeneratorPrototype.lut.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B3BF5E31E3D365A0076A87A /* AsyncGeneratorPrototype.lut.h */; };
    13071307                8B6016F61F3E3CC000F9DE6A /* AsyncFromSyncIteratorPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B6016F41F3E3CC000F9DE6A /* AsyncFromSyncIteratorPrototype.h */; };
  • trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h

    r278445 r278462  
    28562856    }
    28572857
     2858    case FunctionToString: {
     2859        JSValue value = m_state.forNode(node->child1()).value();
     2860        if (value) {
     2861            JSFunction* function = jsDynamicCast<JSFunction*>(m_vm, value);
     2862            if (JSString* asString = function->asStringConcurrently(m_vm)) {
     2863                setConstant(node, *m_graph.freeze(asString));
     2864                break;
     2865            }
     2866        }
     2867        setForNode(node, m_vm.stringStructure.get());
     2868        break;
     2869    }
     2870
    28582871    case NumberToStringWithRadix: {
    28592872        JSValue radixValue = forNode(node->child2()).m_value;
  • trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

    r278445 r278462  
    37413741            return false;
    37423742#endif
     3743        }
     3744
     3745        case FunctionToStringIntrinsic: {
     3746            if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType))
     3747                return false;
     3748
     3749            insertChecks();
     3750            Node* function = get(virtualRegisterForArgumentIncludingThis(0, registerOffset));
     3751            Node* resultNode = addToGraph(FunctionToString, function);
     3752            setResult(resultNode);
     3753            return true;
    37433754        }
    37443755
  • trunk/Source/JavaScriptCore/dfg/DFGClobberize.h

    r278445 r278462  
    18191819            return;
    18201820        }
     1821
     1822    case FunctionToString:
     1823        def(PureValue(node));
     1824        return;
    18211825       
    18221826    case CountExecution:
  • trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp

    r278445 r278462  
    291291    case DirectTailCallInlinedCaller:
    292292    case ForceOSRExit:
     293    case FunctionToString:
    293294    case GetById:
    294295    case GetByIdDirect:
  • trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp

    r278445 r278462  
    19741974        }
    19751975
     1976        case FunctionToString: {
     1977            fixEdge<FunctionUse>(node->child1());
     1978            break;
     1979        }
    19761980
    19771981        case SetPrivateBrand: {
  • trunk/Source/JavaScriptCore/dfg/DFGNodeType.h

    r278445 r278462  
    429429    macro(NumberToStringWithRadix, NodeResultJS | NodeMustGenerate) \
    430430    macro(NumberToStringWithValidRadixConstant, NodeResultJS) \
     431    macro(FunctionToString, NodeResultJS) \
    431432    macro(MakeRope, NodeResultJS) \
    432433    macro(InByVal, NodeResultBoolean | NodeMustGenerate) \
  • trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp

    r278338 r278462  
    25612561}
    25622562
     2563JSC_DEFINE_JIT_OPERATION(operationFunctionToString, JSString*, (JSGlobalObject* globalObject, JSFunction* function))
     2564{
     2565    VM& vm = globalObject->vm();
     2566    CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
     2567    JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
     2568
     2569    return function->toString(globalObject);
     2570}
     2571
    25632572JSC_DEFINE_JIT_OPERATION(operationSingleCharacterString, JSString*, (VM* vmPointer, int32_t character))
    25642573{
  • trunk/Source/JavaScriptCore/dfg/DFGOperations.h

    r276427 r278462  
    247247JSC_DECLARE_JIT_OPERATION(operationInt52ToStringWithValidRadix, char*, (JSGlobalObject*, int64_t, int32_t));
    248248JSC_DECLARE_JIT_OPERATION(operationDoubleToStringWithValidRadix, char*, (JSGlobalObject*, double, int32_t));
     249JSC_DECLARE_JIT_OPERATION(operationFunctionToString, JSString*, (JSGlobalObject*, JSFunction*));
    249250
    250251JSC_DECLARE_JIT_OPERATION(operationNormalizeMapKeyHeapBigInt, EncodedJSValue, (VM*, JSBigInt*));
  • trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp

    r278445 r278462  
    11591159        case CallStringConstructor:
    11601160        case ToString:
     1161        case FunctionToString:
    11611162        case NumberToStringWithRadix:
    11621163        case NumberToStringWithValidRadixConstant:
  • trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h

    r278445 r278462  
    274274    case LogicalNot:
    275275    case ToString:
     276    case FunctionToString:
    276277    case NumberToStringWithValidRadixConstant:
    277278    case StrCat:
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp

    r278445 r278462  
    5353#include "JSAsyncFunction.h"
    5454#include "JSAsyncGeneratorFunction.h"
     55#include "JSBoundFunction.h"
    5556#include "JSCInlines.h"
    5657#include "JSGeneratorFunction.h"
     
    1055610557}
    1055710558
     10559static void getExecutable(JITCompiler& jit, GPRReg functionGPR, GPRReg resultGPR)
     10560{
     10561    jit.loadPtr(JITCompiler::Address(functionGPR, JSFunction::offsetOfExecutableOrRareData()), resultGPR);
     10562    auto hasExecutable = jit.branchTestPtr(CCallHelpers::Zero, resultGPR, CCallHelpers::TrustedImm32(JSFunction::rareDataTag));
     10563    jit.loadPtr(CCallHelpers::Address(resultGPR, FunctionRareData::offsetOfExecutable() - JSFunction::rareDataTag), resultGPR);
     10564    hasExecutable.link(&jit);
     10565}
     10566
     10567void SpeculativeJIT::compileFunctionToString(Node* node)
     10568{
     10569    SpeculateCellOperand function(this, node->child1());
     10570    GPRTemporary executable(this);
     10571    GPRTemporary result(this);
     10572    JITCompiler::JumpList slowCases;
     10573
     10574    speculateFunction(node->child1(), function.gpr());
     10575
     10576    m_jit.emitLoadStructure(vm(), function.gpr(), result.gpr(), executable.gpr());
     10577    m_jit.loadPtr(JITCompiler::Address(result.gpr(), Structure::classInfoOffset()), result.gpr());
     10578    static_assert(std::is_final_v<JSBoundFunction>, "We don't handle subclasses when comparing classInfo below");
     10579    slowCases.append(m_jit.branchPtr(CCallHelpers::Equal, result.gpr(), TrustedImmPtr(JSBoundFunction::info())));
     10580
     10581    getExecutable(m_jit, function.gpr(), executable.gpr());
     10582    JITCompiler::Jump isNativeExecutable = m_jit.branch8(JITCompiler::Equal, JITCompiler::Address(executable.gpr(), JSCell::typeInfoTypeOffset()), TrustedImm32(NativeExecutableType));
     10583
     10584    m_jit.loadPtr(MacroAssembler::Address(executable.gpr(), FunctionExecutable::offsetOfRareData()), result.gpr());
     10585    slowCases.append(m_jit.branchTestPtr(MacroAssembler::Zero, result.gpr()));
     10586    m_jit.loadPtr(MacroAssembler::Address(result.gpr(), FunctionExecutable::offsetOfAsStringInRareData()), result.gpr());
     10587    JITCompiler::Jump continuation = m_jit.jump();
     10588
     10589    isNativeExecutable.link(&m_jit);
     10590    m_jit.loadPtr(MacroAssembler::Address(executable.gpr(), NativeExecutable::offsetOfAsString()), result.gpr());
     10591
     10592    continuation.link(&m_jit);
     10593    slowCases.append(m_jit.branchTestPtr(MacroAssembler::Zero, result.gpr()));
     10594
     10595    addSlowPathGenerator(slowPathCall(slowCases, this, operationFunctionToString, result.gpr(), TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), function.gpr()));
     10596
     10597    cellResult(result.gpr(), node);
     10598}
     10599
    1055810600void SpeculativeJIT::compileNumberToStringWithValidRadixConstant(Node* node)
    1055910601{
     
    1330513347    SpeculateCellOperand function(this, node->child1());
    1330613348    GPRTemporary result(this, Reuse, function);
    13307     GPRReg functionGPR = function.gpr();
    13308     GPRReg resultGPR = result.gpr();
    13309     speculateCellType(node->child1(), functionGPR, SpecFunction, JSFunctionType);
    13310     m_jit.loadPtr(JITCompiler::Address(functionGPR, JSFunction::offsetOfExecutableOrRareData()), resultGPR);
    13311     auto hasExecutable = m_jit.branchTestPtr(CCallHelpers::Zero, resultGPR, CCallHelpers::TrustedImm32(JSFunction::rareDataTag));
    13312     m_jit.loadPtr(CCallHelpers::Address(resultGPR, FunctionRareData::offsetOfExecutable() - JSFunction::rareDataTag), resultGPR);
    13313     hasExecutable.link(&m_jit);
    13314     cellResult(resultGPR, node);
     13349    speculateFunction(node->child1(), function.gpr());
     13350    getExecutable(m_jit, function.gpr(), result.gpr());
     13351    cellResult(result.gpr(), node);
    1331513352}
    1331613353
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h

    r278253 r278462  
    12281228   
    12291229    void compileToStringOrCallStringConstructorOrStringValueOf(Node*);
     1230    void compileFunctionToString(Node*);
    12301231    void compileNumberToStringWithRadix(Node*);
    12311232    void compileNumberToStringWithValidRadixConstant(Node*);
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp

    r278445 r278462  
    32063206        break;
    32073207    }
     3208
     3209    case FunctionToString:
     3210        compileFunctionToString(node);
     3211        break;
    32083212       
    32093213    case NewStringObject: {
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp

    r278445 r278462  
    37913791        break;
    37923792    }
     3793
     3794    case FunctionToString:
     3795        compileFunctionToString(node);
     3796        break;
    37933797       
    37943798    case NewStringObject: {
  • trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h

    r265934 r278462  
    7878    macro(DirectArguments_mappedArguments, DirectArguments::offsetOfMappedArguments()) \
    7979    macro(DirectArguments_modifiedArgumentsDescriptor, DirectArguments::offsetOfModifiedArgumentsDescriptor()) \
     80    macro(FunctionExecutable_rareData, FunctionExecutable::offsetOfRareData()) \
     81    macro(FunctionExecutableRareData_asString, FunctionExecutable::offsetOfAsStringInRareData()) \
    8082    macro(FunctionRareData_allocator, FunctionRareData::offsetOfObjectAllocationProfile() + ObjectAllocationProfileWithPrototype::offsetOfAllocator()) \
    8183    macro(FunctionRareData_structure, FunctionRareData::offsetOfObjectAllocationProfile() + ObjectAllocationProfileWithPrototype::offsetOfStructure()) \
     
    120122    macro(JSScope_next, JSScope::offsetOfNext()) \
    121123    macro(JSSymbolTableObject_symbolTable, JSSymbolTableObject::offsetOfSymbolTable()) \
     124    macro(NativeExecutable_asString, NativeExecutable::offsetOfAsString()) \
    122125    macro(RegExpObject_regExpAndLastIndexIsNotWritableFlag, RegExpObject::offsetOfRegExpAndLastIndexIsNotWritableFlag()) \
    123126    macro(RegExpObject_lastIndex, RegExpObject::offsetOfLastIndex()) \
  • trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp

    r278445 r278462  
    218218    case ToNumeric:
    219219    case ToString:
     220    case FunctionToString:
    220221    case ToObject:
    221222    case CallObjectConstructor:
  • trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp

    r278445 r278462  
    7878#include "JSAsyncGenerator.h"
    7979#include "JSAsyncGeneratorFunction.h"
     80#include "JSBoundFunction.h"
    8081#include "JSCInlines.h"
    8182#include "JSGenerator.h"
     
    11971198            compileToStringOrCallStringConstructorOrStringValueOf();
    11981199            break;
     1200        case FunctionToString:
     1201            compileFunctionToString();
     1202            break;
    11991203        case ToPrimitive:
    12001204            compileToPrimitive();
     
    37753779    }
    37763780
    3777     void compileGetExecutable()
     3781    LValue getExecutable(LValue function)
    37783782    {
    37793783        LBasicBlock continuation = m_out.newBlock();
    37803784        LBasicBlock hasRareData = m_out.newBlock();
    3781         LValue cell = lowCell(m_node->child1());
    3782         speculateFunction(m_node->child1(), cell);
    3783 
    3784         LValue rareDataTags = m_out.loadPtr(cell, m_heaps.JSFunction_executableOrRareData);
     3785
     3786        LValue rareDataTags = m_out.loadPtr(function, m_heaps.JSFunction_executableOrRareData);
    37853787        ValueFromBlock fastExecutable = m_out.anchor(rareDataTags);
    37863788        m_out.branch(m_out.testIsZeroPtr(rareDataTags, m_out.constIntPtr(JSFunction::rareDataTag)), unsure(continuation), unsure(hasRareData));
     
    37923794
    37933795        m_out.appendTo(continuation, lastNext);
    3794         setJSValue(m_out.phi(pointerType(), fastExecutable, slowExecutable));
     3796        return m_out.phi(pointerType(), fastExecutable, slowExecutable);
     3797    }
     3798
     3799    void compileGetExecutable()
     3800    {
     3801        LValue cell = lowCell(m_node->child1());
     3802        speculateFunction(m_node->child1(), cell);
     3803        LValue executable = getExecutable(cell);
     3804        setJSValue(executable);
    37953805    }
    37963806   
     
    82208230            break;
    82218231        }
     8232    }
     8233
     8234    void compileFunctionToString()
     8235    {
     8236        JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic);
     8237
     8238        LBasicBlock notBoundFunctionCase = m_out.newBlock();
     8239        LBasicBlock functionExecutableCase = m_out.newBlock();
     8240        LBasicBlock nativeExecutableCase = m_out.newBlock();
     8241        LBasicBlock testPtr = m_out.newBlock();
     8242        LBasicBlock hasRareData = m_out.newBlock();
     8243        LBasicBlock slowCase = m_out.newBlock();
     8244        LBasicBlock continuation = m_out.newBlock();
     8245
     8246        LValue function = lowCell(m_node->child1());
     8247        speculateFunction(m_node->child1(), function);
     8248
     8249        LValue structure = loadStructure(function);
     8250        LValue classInfo = m_out.loadPtr(structure, m_heaps.Structure_classInfo);
     8251        static_assert(std::is_final_v<JSBoundFunction>, "We don't handle subclasses when comparing classInfo below");
     8252        m_out.branch(m_out.equal(classInfo, m_out.constIntPtr(JSBoundFunction::info())), unsure(slowCase), unsure(notBoundFunctionCase));
     8253
     8254        LBasicBlock lastNext = m_out.appendTo(notBoundFunctionCase, nativeExecutableCase);
     8255        LValue executable = getExecutable(function);
     8256        m_out.branch(isType(executable, NativeExecutableType), unsure(nativeExecutableCase), unsure(functionExecutableCase));
     8257
     8258        m_out.appendTo(nativeExecutableCase, functionExecutableCase);
     8259        ValueFromBlock nativeResult = m_out.anchor(m_out.loadPtr(executable, m_heaps.NativeExecutable_asString));
     8260        m_out.jump(testPtr);
     8261
     8262        m_out.appendTo(functionExecutableCase, testPtr);
     8263        LValue rareData = m_out.loadPtr(executable, m_heaps.FunctionExecutable_rareData);
     8264        m_out.branch(m_out.notNull(rareData), usually(hasRareData), rarely(slowCase));
     8265
     8266        m_out.appendTo(hasRareData, slowCase);
     8267        ValueFromBlock functionResult = m_out.anchor(m_out.loadPtr(rareData, m_heaps.FunctionExecutableRareData_asString));
     8268        m_out.jump(testPtr);
     8269
     8270        m_out.appendTo(testPtr, continuation);
     8271        LValue asString = m_out.phi(pointerType(), nativeResult, functionResult);
     8272        ValueFromBlock fastResult = m_out.anchor(asString);
     8273        m_out.branch(m_out.notNull(asString), usually(continuation), rarely(slowCase));
     8274
     8275        m_out.appendTo(slowCase, continuation);
     8276        ValueFromBlock slowResult = m_out.anchor(vmCall(pointerType(), operationFunctionToString, weakPointer(globalObject), function));
     8277        m_out.jump(continuation);
     8278
     8279        m_out.appendTo(continuation, lastNext);
     8280        setJSValue(m_out.phi(pointerType(), fastResult, slowResult));
    82228281    }
    82238282   
  • trunk/Source/JavaScriptCore/runtime/FunctionExecutable.cpp

    r278253 r278462  
    8080    if (RareData* rareData = thisObject->m_rareData.get()) {
    8181        visitor.append(rareData->m_cachedPolyProtoStructure);
     82        visitor.append(rareData->m_asString);
    8283        if (TemplateObjectMap* map = rareData->m_templateObjectMap.get()) {
    8384            Locker locker { thisObject->cellLock() };
     
    117118}
    118119
     120JSString* FunctionExecutable::toStringSlow(JSGlobalObject* globalObject)
     121{
     122    VM& vm = getVM(globalObject);
     123    ASSERT(m_rareData && !m_rareData->m_asString);
     124
     125    auto throwScope = DECLARE_THROW_SCOPE(vm);
     126
     127    const auto& cache = [&](JSString* asString) {
     128        WTF::storeStoreFence();
     129        m_rareData->m_asString.set(vm, this, asString);
     130        return asString;
     131    };
     132
     133    const auto& cacheIfNoException = [&](JSValue value) -> JSString* {
     134        RETURN_IF_EXCEPTION(throwScope, nullptr);
     135        return cache(::JSC::asString(value));
     136    };
     137
     138    if (isBuiltinFunction())
     139        return cacheIfNoException(jsMakeNontrivialString(globalObject, "function ", name().string(), "() {\n    [native code]\n}"));
     140
     141    if (isClass())
     142        return cache(jsString(vm, classSource().view().toString()));
     143
     144    String functionHeader;
     145    switch (parseMode()) {
     146    case SourceParseMode::GeneratorWrapperFunctionMode:
     147    case SourceParseMode::GeneratorWrapperMethodMode:
     148        functionHeader = "function* ";
     149        break;
     150
     151    case SourceParseMode::NormalFunctionMode:
     152    case SourceParseMode::GetterMode:
     153    case SourceParseMode::SetterMode:
     154    case SourceParseMode::MethodMode:
     155    case SourceParseMode::ProgramMode:
     156    case SourceParseMode::ModuleAnalyzeMode:
     157    case SourceParseMode::ModuleEvaluateMode:
     158    case SourceParseMode::GeneratorBodyMode:
     159    case SourceParseMode::AsyncGeneratorBodyMode:
     160    case SourceParseMode::AsyncFunctionBodyMode:
     161    case SourceParseMode::AsyncArrowFunctionBodyMode:
     162        functionHeader = "function ";
     163        break;
     164
     165    case SourceParseMode::ArrowFunctionMode:
     166    case SourceParseMode::ClassFieldInitializerMode:
     167        functionHeader = "";
     168        break;
     169
     170    case SourceParseMode::AsyncFunctionMode:
     171    case SourceParseMode::AsyncMethodMode:
     172        functionHeader = "async function ";
     173        break;
     174
     175    case SourceParseMode::AsyncArrowFunctionMode:
     176        functionHeader = "async ";
     177        break;
     178
     179    case SourceParseMode::AsyncGeneratorWrapperFunctionMode:
     180    case SourceParseMode::AsyncGeneratorWrapperMethodMode:
     181        functionHeader = "async function* ";
     182        break;
     183    }
     184
     185    StringView src = source().provider()->getRange(
     186        parametersStartOffset(),
     187        parametersStartOffset() + source().length());
     188
     189    String name = this->name().string();
     190    if (name == vm.propertyNames->starDefaultPrivateName.string())
     191        name = emptyString();
     192    return cacheIfNoException(jsMakeNontrivialString(globalObject, functionHeader, name, src));
     193}
     194
    119195void FunctionExecutable::overrideInfo(const FunctionOverrideInfo& overrideInfo)
    120196{
  • trunk/Source/JavaScriptCore/runtime/FunctionExecutable.h

    r278253 r278462  
    286286    void finalizeUnconditionally(VM&);
    287287
     288    JSString* toString(JSGlobalObject*);
     289    JSString* asStringConcurrently() const
     290    {
     291        if (!m_rareData)
     292            return nullptr;
     293        return m_rareData->m_asString.get();
     294    }
     295
     296    static inline ptrdiff_t offsetOfRareData() { return OBJECT_OFFSETOF(FunctionExecutable, m_rareData); }
     297    static inline ptrdiff_t offsetOfAsStringInRareData() { return OBJECT_OFFSETOF(RareData, m_asString); }
     298
    288299private:
    289300    friend class ExecutableBase;
     
    305316        std::unique_ptr<TemplateObjectMap> m_templateObjectMap;
    306317        WriteBarrier<Structure> m_cachedPolyProtoStructure;
     318        WriteBarrier<JSString> m_asString;
    307319    };
    308320
     
    314326    }
    315327    RareData& ensureRareDataSlow();
     328
     329    JSString* toStringSlow(JSGlobalObject*);
    316330
    317331    // FIXME: We can merge rareData pointer and top-level executable pointer. First time, setting parent.
  • trunk/Source/JavaScriptCore/runtime/FunctionExecutableInlines.h

    r246073 r278462  
    3636}
    3737
     38JSString* FunctionExecutable::toString(JSGlobalObject* globalObject)
     39{
     40    RareData& rareData = ensureRareData();
     41    if (!rareData.m_asString)
     42        return toStringSlow(globalObject);
     43    return rareData.m_asString.get();
     44}
     45
    3846} // namespace JSC
    3947
  • trunk/Source/JavaScriptCore/runtime/FunctionPrototype.cpp

    r269922 r278462  
    5555void FunctionPrototype::addFunctionProperties(VM& vm, JSGlobalObject* globalObject, JSFunction** callFunction, JSFunction** applyFunction, JSFunction** hasInstanceSymbolFunction)
    5656{
    57     JSFunction* toStringFunction = JSFunction::create(vm, globalObject, 0, vm.propertyNames->toString.string(), functionProtoFuncToString);
    58     putDirectWithoutTransition(vm, vm.propertyNames->toString, toStringFunction, static_cast<unsigned>(PropertyAttribute::DontEnum));
     57    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().toStringPublicName(), functionProtoFuncToString, static_cast<unsigned>(PropertyAttribute::DontEnum), 0, FunctionToStringIntrinsic);
    5958
    6059    *applyFunction = putDirectBuiltinFunctionWithoutTransition(vm, globalObject, vm.propertyNames->builtinNames().applyPublicName(), functionPrototypeApplyCodeGenerator(vm), static_cast<unsigned>(PropertyAttribute::DontEnum));
     
    8281        JSFunction* function = jsCast<JSFunction*>(thisValue);
    8382        Integrity::auditStructureID(vm, function->structureID());
    84         if (function->isHostOrBuiltinFunction())
    85             RELEASE_AND_RETURN(scope, JSValue::encode(jsMakeNontrivialString(globalObject, "function ", function->name(vm), "() {\n    [native code]\n}")));
    86 
    87         FunctionExecutable* executable = function->jsExecutable();
    88         if (executable->isClass())
    89             return JSValue::encode(jsString(vm, executable->classSource().view().toString()));
    90 
    91         String functionHeader;
    92         switch (executable->parseMode()) {
    93         case SourceParseMode::GeneratorWrapperFunctionMode:
    94         case SourceParseMode::GeneratorWrapperMethodMode:
    95             functionHeader = "function* ";
    96             break;
    97 
    98         case SourceParseMode::NormalFunctionMode:
    99         case SourceParseMode::GetterMode:
    100         case SourceParseMode::SetterMode:
    101         case SourceParseMode::MethodMode:
    102         case SourceParseMode::ProgramMode:
    103         case SourceParseMode::ModuleAnalyzeMode:
    104         case SourceParseMode::ModuleEvaluateMode:
    105         case SourceParseMode::GeneratorBodyMode:
    106         case SourceParseMode::AsyncGeneratorBodyMode:
    107         case SourceParseMode::AsyncFunctionBodyMode:
    108         case SourceParseMode::AsyncArrowFunctionBodyMode:
    109             functionHeader = "function ";
    110             break;
    111 
    112         case SourceParseMode::ArrowFunctionMode:
    113         case SourceParseMode::ClassFieldInitializerMode:
    114             functionHeader = "";
    115             break;
    116 
    117         case SourceParseMode::AsyncFunctionMode:
    118         case SourceParseMode::AsyncMethodMode:
    119             functionHeader = "async function ";
    120             break;
    121 
    122         case SourceParseMode::AsyncArrowFunctionMode:
    123             functionHeader = "async ";
    124             break;
    125 
    126         case SourceParseMode::AsyncGeneratorWrapperFunctionMode:
    127         case SourceParseMode::AsyncGeneratorWrapperMethodMode:
    128             functionHeader = "async function* ";
    129             break;
    130         }
    131 
    132         StringView source = executable->source().provider()->getRange(
    133             executable->parametersStartOffset(),
    134             executable->parametersStartOffset() + executable->source().length());
    135         RELEASE_AND_RETURN(scope, JSValue::encode(jsMakeNontrivialString(globalObject, functionHeader, function->name(vm), source)));
     83        RELEASE_AND_RETURN(scope, JSValue::encode(function->toString(globalObject)));
    13684    }
    13785
  • trunk/Source/JavaScriptCore/runtime/Intrinsic.cpp

    r278253 r278462  
    276276    case ParseIntIntrinsic:
    277277        return "ParseIntIntrinsic";
     278    case FunctionToStringIntrinsic:
     279        return "FunctionToStringIntrinsic";
    278280    case TypedArrayLengthIntrinsic:
    279281        return "TypedArrayLengthIntrinsic";
  • trunk/Source/JavaScriptCore/runtime/Intrinsic.h

    r278340 r278462  
    153153    AtomicsXorIntrinsic,
    154154    ParseIntIntrinsic,
     155    FunctionToStringIntrinsic,
    155156
    156157    // Getter intrinsics.
  • trunk/Source/JavaScriptCore/runtime/JSFunction.cpp

    r277665 r278462  
    248248}
    249249
     250JSString* JSFunction::toString(JSGlobalObject* globalObject)
     251{
     252    VM& vm = getVM(globalObject);
     253    if (inherits<JSBoundFunction>(vm)) {
     254        JSBoundFunction* function = jsCast<JSBoundFunction*>(this);
     255        auto scope = DECLARE_THROW_SCOPE(vm);
     256        JSValue string = jsMakeNontrivialString(globalObject, "function ", function->nameString(), "() {\n    [native code]\n}");
     257        RETURN_IF_EXCEPTION(scope, nullptr);
     258        return asString(string);
     259    }
     260
     261    if (isHostFunction())
     262        return static_cast<NativeExecutable*>(executable())->toString(globalObject);
     263    return jsExecutable()->toString(globalObject);
     264}
     265
    250266const SourceCode* JSFunction::sourceCode() const
    251267{
  • trunk/Source/JavaScriptCore/runtime/JSFunction.h

    r277665 r278462  
    9191    JS_EXPORT_PRIVATE String displayName(VM&);
    9292    JS_EXPORT_PRIVATE const String calculatedDisplayName(VM&);
     93    JS_EXPORT_PRIVATE JSString* toString(JSGlobalObject*);
     94
     95    JSString* asStringConcurrently(VM&) const;
    9396
    9497    ExecutableBase* executable() const
  • trunk/Source/JavaScriptCore/runtime/JSFunctionInlines.h

    r257784 r278462  
    2727
    2828#include "FunctionExecutable.h"
     29#include "JSBoundFunction.h"
    2930#include "JSFunction.h"
    3031#include "NativeExecutable.h"
     
    157158}
    158159
     160inline JSString* JSFunction::asStringConcurrently(VM& vm) const
     161{
     162    if (inherits<JSBoundFunction>(vm))
     163        return nullptr;
     164    if (isHostFunction())
     165        return static_cast<NativeExecutable*>(executable())->asStringConcurrently();
     166    return jsExecutable()->asStringConcurrently();
     167}
     168
    159169} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/JSStringInlines.h

    r251425 r278462  
    2626#pragma once
    2727
     28#include "JSGlobalObjectInlines.h"
    2829#include "JSString.h"
    2930
  • trunk/Source/JavaScriptCore/runtime/NativeExecutable.cpp

    r268247 r278462  
    9494}
    9595
     96JSString* NativeExecutable::toStringSlow(JSGlobalObject *globalObject)
     97{
     98    VM& vm = getVM(globalObject);
     99
     100    auto throwScope = DECLARE_THROW_SCOPE(vm);
     101
     102    JSValue value = jsMakeNontrivialString(globalObject, "function ", name(), "() {\n    [native code]\n}");
     103
     104    RETURN_IF_EXCEPTION(throwScope, nullptr);
     105
     106    JSString* asString = ::JSC::asString(value);
     107    WTF::storeStoreFence();
     108    m_asString.set(vm, this, asString);
     109    return asString;
     110}
     111
     112template<typename Visitor>
     113void NativeExecutable::visitChildrenImpl(JSCell* cell, Visitor& visitor)
     114{
     115    NativeExecutable* thisObject = jsCast<NativeExecutable*>(cell);
     116    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
     117    Base::visitChildren(thisObject, visitor);
     118    visitor.append(thisObject->m_asString);
     119}
     120
     121DEFINE_VISIT_CHILDREN(NativeExecutable);
     122
    96123} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/NativeExecutable.h

    r260415 r278462  
    6868    }
    6969
     70    DECLARE_VISIT_CHILDREN;
    7071    static Structure* createStructure(VM&, JSGlobalObject*, JSValue proto);
    7172       
     
    7778    Intrinsic intrinsic() const;
    7879
     80    JSString* toString(JSGlobalObject* globalObject)
     81    {
     82        if (!m_asString)
     83            return toStringSlow(globalObject);
     84        return m_asString.get();
     85    }
     86
     87    JSString* asStringConcurrently() const { return m_asString.get(); }
     88    static inline ptrdiff_t offsetOfAsString() { return OBJECT_OFFSETOF(NativeExecutable, m_asString); }
     89
    7990private:
    8091    NativeExecutable(VM&, TaggedNativeFunction, TaggedNativeFunction constructor);
    8192    void finishCreation(VM&, Ref<JITCode>&& callThunk, Ref<JITCode>&& constructThunk, const String& name);
     93
     94    JSString* toStringSlow(JSGlobalObject*);
    8295
    8396    TaggedNativeFunction m_function;
     
    8598
    8699    String m_name;
     100    WriteBarrier<JSString> m_asString;
    87101};
    88102
Note: See TracChangeset for help on using the changeset viewer.