Changeset 239153 in webkit


Ignore:
Timestamp:
Dec 12, 2018 11:13:38 PM (5 years ago)
Author:
yusukesuzuki@slowstart.org
Message:

[JSC] Optimize Object.keys by caching own keys results in StructureRareData
https://bugs.webkit.org/show_bug.cgi?id=190047

Reviewed by Keith Miller.

JSTests:

  • stress/object-keys-cached-zero.js: Added.

(shouldBe):
(test):

  • stress/object-keys-changed-attribute.js: Added.

(shouldBe):
(test):

  • stress/object-keys-changed-index.js: Added.

(shouldBe):
(test):

  • stress/object-keys-changed.js: Added.

(shouldBe):
(test):

  • stress/object-keys-indexed-non-cache.js: Added.

(shouldBe):
(test):

  • stress/object-keys-overrides-get-property-names.js: Added.

(shouldBe):
(test):
(noInline):

Source/JavaScriptCore:

Object.keys is one of the most frequently used function in web-tooling-benchmarks (WTB).
Object.keys is dominant in lebab of WTB, and frequently called in babel and others.
Since our Structure knows the shape of JSObject, we can cache the result of Object.keys
in Structure (StructureRareData) as we cache JSPropertyNameEnumerator in StructureRareData.

This patch caches the result of Object.keys in StructureRareData. The cached array is created
as JSImmutableButterfly. And Object.keys creates CoW from this data. Currently, the lifetime
strategy of this JSImmutableButterfly is the same to cached JSPropertyNameEnumerator. It is
referenced from Structure, and collected when Structure is collected.

This improves several benchmarks in SixSpeed.

baseline patched

object-assign.es5 350.1710+-3.6303 226.0368+-4.7558 definitely 1.5492x faster
for-of-object.es6 269.1941+-3.3430 127.9317+-2.3875 definitely 2.1042x faster

And it improves WTB lebab by 11.8%.

Before: lebab: 6.10 runs/s
After: lebab: 6.82 runs/s

  • dfg/DFGAbstractInterpreterInlines.h:

(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::handleIntrinsicCall):

  • dfg/DFGClobberize.h:

(JSC::DFG::clobberize):

  • dfg/DFGConstantFoldingPhase.cpp:

(JSC::DFG::ConstantFoldingPhase::foldConstants):

  • dfg/DFGDoesGC.cpp:

(JSC::DFG::doesGC):

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::FixupPhase::fixupNode):

  • dfg/DFGNode.cpp:

(JSC::DFG::Node::convertToNewArrayBuffer):

  • dfg/DFGNode.h:
  • dfg/DFGNodeType.h:
  • dfg/DFGOperations.cpp:
  • dfg/DFGOperations.h:
  • dfg/DFGPredictionPropagationPhase.cpp:
  • dfg/DFGSafeToExecute.h:

(JSC::DFG::safeToExecute):

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileObjectKeys):

  • 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::compileObjectKeys):

  • runtime/Intrinsic.cpp:

(JSC::intrinsicName):

  • runtime/Intrinsic.h:
  • runtime/JSImmutableButterfly.h:

(JSC::JSImmutableButterfly::createSentinel):

  • runtime/ObjectConstructor.cpp:

(JSC::ownPropertyKeys):

  • runtime/Structure.cpp:

(JSC::Structure::canCachePropertyNameEnumerator const):

  • runtime/Structure.h:
  • runtime/StructureInlines.h:

(JSC::Structure::setCachedOwnKeys):
(JSC::Structure::cachedOwnKeys const):
(JSC::Structure::canCacheOwnKeys const):

  • runtime/StructureRareData.cpp:

(JSC::StructureRareData::visitChildren):
(JSC::StructureRareData::cachedPropertyNameEnumerator const): Deleted.
(JSC::StructureRareData::setCachedPropertyNameEnumerator): Deleted.

  • runtime/StructureRareData.h:
  • runtime/StructureRareDataInlines.h:

(JSC::StructureRareData::cachedPropertyNameEnumerator const):
(JSC::StructureRareData::setCachedPropertyNameEnumerator):
(JSC::StructureRareData::cachedOwnKeys const):
(JSC::StructureRareData::cachedOwnKeysConcurrently const):
(JSC::StructureRareData::setCachedOwnKeys):
(JSC::StructureRareData::previousID const): Deleted.

  • runtime/VM.cpp:

(JSC::VM::VM):

  • runtime/VM.h:
Location:
trunk
Files:
6 added
34 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r239142 r239153  
     12018-12-10  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
     2
     3        [JSC] Optimize Object.keys by caching own keys results in StructureRareData
     4        https://bugs.webkit.org/show_bug.cgi?id=190047
     5
     6        Reviewed by Keith Miller.
     7
     8        * stress/object-keys-cached-zero.js: Added.
     9        (shouldBe):
     10        (test):
     11        * stress/object-keys-changed-attribute.js: Added.
     12        (shouldBe):
     13        (test):
     14        * stress/object-keys-changed-index.js: Added.
     15        (shouldBe):
     16        (test):
     17        * stress/object-keys-changed.js: Added.
     18        (shouldBe):
     19        (test):
     20        * stress/object-keys-indexed-non-cache.js: Added.
     21        (shouldBe):
     22        (test):
     23        * stress/object-keys-overrides-get-property-names.js: Added.
     24        (shouldBe):
     25        (test):
     26        (noInline):
     27
    1282018-12-12  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
    229
  • trunk/Source/JavaScriptCore/ChangeLog

    r239142 r239153  
     12018-12-10  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
     2
     3        [JSC] Optimize Object.keys by caching own keys results in StructureRareData
     4        https://bugs.webkit.org/show_bug.cgi?id=190047
     5
     6        Reviewed by Keith Miller.
     7
     8        Object.keys is one of the most frequently used function in web-tooling-benchmarks (WTB).
     9        Object.keys is dominant in lebab of WTB, and frequently called in babel and others.
     10        Since our Structure knows the shape of JSObject, we can cache the result of Object.keys
     11        in Structure (StructureRareData) as we cache JSPropertyNameEnumerator in StructureRareData.
     12
     13        This patch caches the result of Object.keys in StructureRareData. The cached array is created
     14        as JSImmutableButterfly. And Object.keys creates CoW from this data. Currently, the lifetime
     15        strategy of this JSImmutableButterfly is the same to cached JSPropertyNameEnumerator. It is
     16        referenced from Structure, and collected when Structure is collected.
     17
     18        This improves several benchmarks in SixSpeed.
     19
     20                                        baseline                  patched
     21
     22            object-assign.es5      350.1710+-3.6303     ^    226.0368+-4.7558        ^ definitely 1.5492x faster
     23            for-of-object.es6      269.1941+-3.3430     ^    127.9317+-2.3875        ^ definitely 2.1042x faster
     24
     25        And it improves WTB lebab by 11.8%.
     26
     27            Before: lebab:  6.10 runs/s
     28            After:  lebab:  6.82 runs/s
     29
     30        * dfg/DFGAbstractInterpreterInlines.h:
     31        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
     32        * dfg/DFGByteCodeParser.cpp:
     33        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
     34        * dfg/DFGClobberize.h:
     35        (JSC::DFG::clobberize):
     36        * dfg/DFGConstantFoldingPhase.cpp:
     37        (JSC::DFG::ConstantFoldingPhase::foldConstants):
     38        * dfg/DFGDoesGC.cpp:
     39        (JSC::DFG::doesGC):
     40        * dfg/DFGFixupPhase.cpp:
     41        (JSC::DFG::FixupPhase::fixupNode):
     42        * dfg/DFGNode.cpp:
     43        (JSC::DFG::Node::convertToNewArrayBuffer):
     44        * dfg/DFGNode.h:
     45        * dfg/DFGNodeType.h:
     46        * dfg/DFGOperations.cpp:
     47        * dfg/DFGOperations.h:
     48        * dfg/DFGPredictionPropagationPhase.cpp:
     49        * dfg/DFGSafeToExecute.h:
     50        (JSC::DFG::safeToExecute):
     51        * dfg/DFGSpeculativeJIT.cpp:
     52        (JSC::DFG::SpeculativeJIT::compileObjectKeys):
     53        * dfg/DFGSpeculativeJIT.h:
     54        * dfg/DFGSpeculativeJIT32_64.cpp:
     55        (JSC::DFG::SpeculativeJIT::compile):
     56        * dfg/DFGSpeculativeJIT64.cpp:
     57        (JSC::DFG::SpeculativeJIT::compile):
     58        * ftl/FTLAbstractHeapRepository.h:
     59        * ftl/FTLCapabilities.cpp:
     60        (JSC::FTL::canCompile):
     61        * ftl/FTLLowerDFGToB3.cpp:
     62        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
     63        (JSC::FTL::DFG::LowerDFGToB3::compileObjectKeys):
     64        * runtime/Intrinsic.cpp:
     65        (JSC::intrinsicName):
     66        * runtime/Intrinsic.h:
     67        * runtime/JSImmutableButterfly.h:
     68        (JSC::JSImmutableButterfly::createSentinel):
     69        * runtime/ObjectConstructor.cpp:
     70        (JSC::ownPropertyKeys):
     71        * runtime/Structure.cpp:
     72        (JSC::Structure::canCachePropertyNameEnumerator const):
     73        * runtime/Structure.h:
     74        * runtime/StructureInlines.h:
     75        (JSC::Structure::setCachedOwnKeys):
     76        (JSC::Structure::cachedOwnKeys const):
     77        (JSC::Structure::canCacheOwnKeys const):
     78        * runtime/StructureRareData.cpp:
     79        (JSC::StructureRareData::visitChildren):
     80        (JSC::StructureRareData::cachedPropertyNameEnumerator const): Deleted.
     81        (JSC::StructureRareData::setCachedPropertyNameEnumerator): Deleted.
     82        * runtime/StructureRareData.h:
     83        * runtime/StructureRareDataInlines.h:
     84        (JSC::StructureRareData::cachedPropertyNameEnumerator const):
     85        (JSC::StructureRareData::setCachedPropertyNameEnumerator):
     86        (JSC::StructureRareData::cachedOwnKeys const):
     87        (JSC::StructureRareData::cachedOwnKeysConcurrently const):
     88        (JSC::StructureRareData::setCachedOwnKeys):
     89        (JSC::StructureRareData::previousID const): Deleted.
     90        * runtime/VM.cpp:
     91        (JSC::VM::VM):
     92        * runtime/VM.h:
     93
    1942018-12-12  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
    295
  • trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h

    r239142 r239153  
    4444#include "PutByIdStatus.h"
    4545#include "StringObject.h"
     46#include "StructureRareDataInlines.h"
    4647#include <wtf/BooleanLattice.h>
    4748#include <wtf/CheckedArithmetic.h>
     
    25732574            clobberWorld();
    25742575        setTypeForNode(node, SpecFinalObject);
     2576        break;
     2577    }
     2578
     2579    case ObjectKeys: {
     2580        if (node->child1().useKind() == ObjectUse) {
     2581            auto& structureSet = forNode(node->child1()).m_structure;
     2582            if (structureSet.isFinite() && structureSet.size() == 1) {
     2583                RegisteredStructure structure = structureSet.onlyStructure();
     2584                if (auto* rareData = structure->rareDataConcurrently()) {
     2585                    auto* immutableButterfly = rareData->cachedOwnKeysConcurrently();
     2586                    if (immutableButterfly && immutableButterfly != m_vm.sentinelImmutableButterfly.get()) {
     2587                        if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
     2588                            m_state.setFoundConstants(true);
     2589                            didFoldClobberWorld();
     2590                            setTypeForNode(node, SpecArray);
     2591                            break;
     2592                        }
     2593                    }
     2594                }
     2595            }
     2596        }
     2597
     2598        clobberWorld();
     2599        setTypeForNode(node, SpecArray);
    25752600        break;
    25762601    }
  • trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

    r239142 r239153  
    26862686        insertChecks();
    26872687        set(result, addToGraph(SameValue, get(virtualRegisterForArgument(1, registerOffset)), get(virtualRegisterForArgument(2, registerOffset))));
     2688        return true;
     2689    }
     2690
     2691    case ObjectKeysIntrinsic: {
     2692        if (argumentCountIncludingThis < 2)
     2693            return false;
     2694
     2695        insertChecks();
     2696        set(result, addToGraph(ObjectKeys, get(virtualRegisterForArgument(1, registerOffset))));
    26882697        return true;
    26892698    }
  • trunk/Source/JavaScriptCore/dfg/DFGClobberize.h

    r239142 r239153  
    668668    case InstanceOf:
    669669    case StringValueOf:
     670    case ObjectKeys:
    670671        read(World);
    671672        write(Heap);
     
    15281529        }
    15291530    }
    1530 
    15311531
    15321532    case NewObject:
  • trunk/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp

    r234178 r239153  
    767767            }
    768768
     769            case ObjectKeys: {
     770                if (node->child1().useKind() == ObjectUse) {
     771                    auto& structureSet = m_state.forNode(node->child1()).m_structure;
     772                    if (structureSet.isFinite() && structureSet.size() == 1) {
     773                        RegisteredStructure structure = structureSet.onlyStructure();
     774                        if (auto* rareData = structure->rareDataConcurrently()) {
     775                            auto* immutableButterfly = rareData->cachedOwnKeysConcurrently();
     776                            if (immutableButterfly && immutableButterfly != m_graph.m_vm.sentinelImmutableButterfly.get()) {
     777                                if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
     778                                    node->convertToNewArrayBuffer(m_graph.freeze(immutableButterfly));
     779                                    changed = true;
     780                                    break;
     781                                }
     782                            }
     783                        }
     784                    }
     785                }
     786                break;
     787            }
     788
    769789            case ToNumber: {
    770790                if (m_state.forNode(node->child1()).m_type & ~SpecBytecodeNumber)
  • trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp

    r239142 r239153  
    339339    case CreateThis:
    340340    case ObjectCreate:
     341    case ObjectKeys:
    341342    case AllocatePropertyStorage:
    342343    case ReallocatePropertyStorage:
  • trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp

    r239142 r239153  
    15721572                node->clearFlags(NodeMustGenerate);
    15731573                break;
     1574            }
     1575            break;
     1576        }
     1577
     1578        case ObjectKeys: {
     1579            if (node->child1()->shouldSpeculateObject()) {
     1580                watchHavingABadTime(node);
     1581                fixEdge<ObjectUse>(node->child1());
    15741582            }
    15751583            break;
  • trunk/Source/JavaScriptCore/dfg/DFGNode.cpp

    r235538 r239153  
    3232#include "DFGPromotedHeapLocation.h"
    3333#include "JSCInlines.h"
     34#include "JSImmutableButterfly.h"
    3435
    3536namespace JSC { namespace DFG {
     
    224225}
    225226
     227void Node::convertToNewArrayBuffer(FrozenValue* immutableButterfly)
     228{
     229    setOpAndDefaultFlags(NewArrayBuffer);
     230    NewArrayBufferData data { };
     231    data.indexingMode = immutableButterfly->cast<JSImmutableButterfly*>()->indexingMode();
     232    data.vectorLengthHint = immutableButterfly->cast<JSImmutableButterfly*>()->toButterfly()->vectorLength();
     233    children.reset();
     234    m_opInfo = immutableButterfly;
     235    m_opInfo2 = data.asQuadWord;
     236}
     237
    226238void Node::convertToDirectCall(FrozenValue* executable)
    227239{
  • trunk/Source/JavaScriptCore/dfg/DFGNode.h

    r239045 r239153  
    762762        m_opInfo2 = OpInfoWrapper();
    763763    }
     764
     765    void convertToNewArrayBuffer(FrozenValue* immutableButterfly);
    764766   
    765767    void convertToDirectCall(FrozenValue*);
  • trunk/Source/JavaScriptCore/dfg/DFGNodeType.h

    r239142 r239153  
    265265    macro(GetPrototypeOf, NodeMustGenerate | NodeResultJS) \
    266266    macro(ObjectCreate, NodeMustGenerate | NodeResultJS) \
     267    macro(ObjectKeys, NodeMustGenerate | NodeResultJS) \
    267268    \
    268269    /* Atomics object functions. */\
  • trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp

    r239142 r239153  
    249249}
    250250
     251JSArray* JIT_OPERATION operationObjectKeys(ExecState* exec, EncodedJSValue encodedObject)
     252{
     253    VM& vm = exec->vm();
     254    NativeCallFrameTracer tracer(&vm, exec);
     255    auto scope = DECLARE_THROW_SCOPE(vm);
     256
     257    JSObject* object = JSValue::decode(encodedObject).toObject(exec);
     258    RETURN_IF_EXCEPTION(scope, nullptr);
     259    scope.release();
     260    return ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude);
     261}
     262
     263JSArray* JIT_OPERATION operationObjectKeysObject(ExecState* exec, JSObject* object)
     264{
     265    VM& vm = exec->vm();
     266    NativeCallFrameTracer tracer(&vm, exec);
     267    return ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude);
     268}
     269
    251270JSCell* JIT_OPERATION operationObjectCreate(ExecState* exec, EncodedJSValue encodedPrototype)
    252271{
  • trunk/Source/JavaScriptCore/dfg/DFGOperations.h

    r239142 r239153  
    4444JSCell* JIT_OPERATION operationCallObjectConstructor(ExecState*, JSGlobalObject*, EncodedJSValue encodedTarget) WTF_INTERNAL;
    4545JSCell* JIT_OPERATION operationToObject(ExecState*, JSGlobalObject*, EncodedJSValue encodedTarget, UniquedStringImpl*) WTF_INTERNAL;
     46JSArray* JIT_OPERATION operationObjectKeys(ExecState*, EncodedJSValue) WTF_INTERNAL;
     47JSArray* JIT_OPERATION operationObjectKeysObject(ExecState*, JSObject*) WTF_INTERNAL;
    4648JSCell* JIT_OPERATION operationObjectCreate(ExecState*, EncodedJSValue) WTF_INTERNAL;
    4749JSCell* JIT_OPERATION operationObjectCreateObject(ExecState*, JSObject*) WTF_INTERNAL;
  • trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp

    r239142 r239153  
    976976        case NewArrayWithSize:
    977977        case CreateRest:
    978         case NewArrayBuffer: {
     978        case NewArrayBuffer:
     979        case ObjectKeys: {
    979980            setPrediction(SpecArray);
    980981            break;
  • trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h

    r239142 r239153  
    179179    case CreateThis:
    180180    case ObjectCreate:
     181    case ObjectKeys:
    181182    case GetCallee:
    182183    case SetCallee:
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp

    r239142 r239153  
    1234612346}
    1234712347
     12348void SpeculativeJIT::compileObjectKeys(Node* node)
     12349{
     12350    switch (node->child1().useKind()) {
     12351    case ObjectUse: {
     12352        if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
     12353            SpeculateCellOperand object(this, node->child1());
     12354            GPRTemporary structure(this);
     12355            GPRTemporary scratch(this);
     12356            GPRTemporary scratch2(this);
     12357            GPRTemporary scratch3(this);
     12358            GPRTemporary result(this);
     12359
     12360            GPRReg objectGPR = object.gpr();
     12361            GPRReg structureGPR = structure.gpr();
     12362            GPRReg scratchGPR = scratch.gpr();
     12363            GPRReg scratch2GPR = scratch2.gpr();
     12364            GPRReg scratch3GPR = scratch3.gpr();
     12365            GPRReg resultGPR = result.gpr();
     12366
     12367            speculateObject(node->child1(), objectGPR);
     12368
     12369            CCallHelpers::JumpList slowCases;
     12370            m_jit.emitLoadStructure(*m_jit.vm(), objectGPR, structureGPR, scratchGPR);
     12371            m_jit.loadPtr(CCallHelpers::Address(structureGPR, Structure::previousOrRareDataOffset()), scratchGPR);
     12372
     12373            slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratchGPR));
     12374            slowCases.append(m_jit.branch32(CCallHelpers::Equal, CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()), TrustedImm32(bitwise_cast<int32_t>(m_jit.vm()->structureStructure->structureID()))));
     12375
     12376            m_jit.loadPtr(CCallHelpers::Address(scratchGPR, StructureRareData::offsetOfCachedOwnKeys()), scratchGPR);
     12377
     12378            slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratchGPR));
     12379            slowCases.append(m_jit.branchPtr(CCallHelpers::Equal, scratchGPR, TrustedImmPtr::weakPointer(m_jit.graph(), m_jit.vm()->sentinelImmutableButterfly.get())));
     12380
     12381            MacroAssembler::JumpList slowButArrayBufferCases;
     12382
     12383            JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic);
     12384            RegisteredStructure arrayStructure = m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(CopyOnWriteArrayWithContiguous));
     12385
     12386            m_jit.move(scratchGPR, scratch3GPR);
     12387            m_jit.addPtr(TrustedImmPtr(JSImmutableButterfly::offsetOfData()), scratchGPR);
     12388
     12389            emitAllocateJSObject<JSArray>(resultGPR, TrustedImmPtr(arrayStructure), scratchGPR, structureGPR, scratch2GPR, slowButArrayBufferCases);
     12390
     12391            addSlowPathGenerator(slowPathCall(slowButArrayBufferCases, this, operationNewArrayBuffer, resultGPR, arrayStructure, scratch3GPR));
     12392
     12393            addSlowPathGenerator(slowPathCall(slowCases, this, operationObjectKeysObject, resultGPR, objectGPR));
     12394
     12395            cellResult(resultGPR, node);
     12396            break;
     12397        }
     12398
     12399        SpeculateCellOperand object(this, node->child1());
     12400
     12401        GPRReg objectGPR = object.gpr();
     12402
     12403        speculateObject(node->child1(), objectGPR);
     12404
     12405        flushRegisters();
     12406        GPRFlushedCallResult result(this);
     12407        GPRReg resultGPR = result.gpr();
     12408        callOperation(operationObjectKeysObject, resultGPR, objectGPR);
     12409        m_jit.exceptionCheck();
     12410
     12411        cellResult(resultGPR, node);
     12412        break;
     12413    }
     12414
     12415    case UntypedUse: {
     12416        JSValueOperand object(this, node->child1());
     12417
     12418        JSValueRegs objectRegs = object.jsValueRegs();
     12419
     12420        flushRegisters();
     12421        GPRFlushedCallResult result(this);
     12422        GPRReg resultGPR = result.gpr();
     12423        callOperation(operationObjectKeys, resultGPR, objectRegs);
     12424        m_jit.exceptionCheck();
     12425
     12426        cellResult(resultGPR, node);
     12427        break;
     12428    }
     12429
     12430    default:
     12431        RELEASE_ASSERT_NOT_REACHED();
     12432        break;
     12433    }
     12434}
     12435
    1234812436void SpeculativeJIT::compileObjectCreate(Node* node)
    1234912437{
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h

    r239142 r239153  
    14791479    void compileNewTypedArray(Node*);
    14801480    void compileToThis(Node*);
     1481    void compileObjectKeys(Node*);
    14811482    void compileObjectCreate(Node*);
    14821483    void compileCreateThis(Node*);
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp

    r239142 r239153  
    31603160    case ObjectCreate: {
    31613161        compileObjectCreate(node);
     3162        break;
     3163    }
     3164
     3165    case ObjectKeys: {
     3166        compileObjectKeys(node);
    31623167        break;
    31633168    }
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp

    r239142 r239153  
    34023402    case ObjectCreate: {
    34033403        compileObjectCreate(node);
     3404        break;
     3405    }
     3406
     3407    case ObjectKeys: {
     3408        compileObjectKeys(node);
    34043409        break;
    34053410    }
  • trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h

    r239099 r239153  
    117117    macro(Structure_indexingModeIncludingHistory, Structure::indexingModeIncludingHistoryOffset()) \
    118118    macro(Structure_inlineCapacity, Structure::inlineCapacityOffset()) \
     119    macro(Structure_previousOrRareData, Structure::previousOrRareDataOffset()) \
    119120    macro(Structure_prototype, Structure::prototypeOffset()) \
    120121    macro(Structure_structureID, Structure::structureIDOffset()) \
     122    macro(StructureRareData_cachedOwnKeys, StructureRareData::offsetOfCachedOwnKeys()) \
    121123    macro(HashMapImpl_capacity, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfCapacity()) \
    122124    macro(HashMapImpl_buffer,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfBuffer()) \
  • trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp

    r239142 r239153  
    201201    case CallStringConstructor:
    202202    case ObjectCreate:
     203    case ObjectKeys:
    203204    case MakeRope:
    204205    case NewArrayWithSize:
  • trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp

    r239142 r239153  
    864864            compileObjectCreate();
    865865            break;
     866        case ObjectKeys:
     867            compileObjectKeys();
     868            break;
    866869        case NewObject:
    867870            compileNewObject();
     
    54845487        m_out.appendTo(continuation, lastNext);
    54855488        setInt32(m_out.phi(Int32, zeroLengthResult, nonZeroLengthResult));
     5489    }
     5490
     5491    void compileObjectKeys()
     5492    {
     5493        switch (m_node->child1().useKind()) {
     5494        case ObjectUse: {
     5495            if (m_graph.isWatchingHavingABadTimeWatchpoint(m_node)) {
     5496                LBasicBlock notNullCase = m_out.newBlock();
     5497                LBasicBlock rareDataCase = m_out.newBlock();
     5498                LBasicBlock notNullCacheCase = m_out.newBlock();
     5499                LBasicBlock useCacheCase = m_out.newBlock();
     5500                LBasicBlock slowButArrayBufferCase = m_out.newBlock();
     5501                LBasicBlock slowCase = m_out.newBlock();
     5502                LBasicBlock continuation = m_out.newBlock();
     5503
     5504                LValue object = lowObject(m_node->child1());
     5505                LValue structure = loadStructure(object);
     5506                LValue previousOrRareData = m_out.loadPtr(structure, m_heaps.Structure_previousOrRareData);
     5507                m_out.branch(m_out.notNull(previousOrRareData), unsure(notNullCase), unsure(slowCase));
     5508
     5509                LBasicBlock lastNext = m_out.appendTo(notNullCase, rareDataCase);
     5510                m_out.branch(
     5511                    m_out.notEqual(m_out.load32(previousOrRareData, m_heaps.JSCell_structureID), m_out.constInt32(m_graph.m_vm.structureStructure->structureID())),
     5512                    unsure(rareDataCase), unsure(slowCase));
     5513
     5514                m_out.appendTo(rareDataCase, notNullCacheCase);
     5515                LValue cachedOwnKeys = m_out.loadPtr(previousOrRareData, m_heaps.StructureRareData_cachedOwnKeys);
     5516                m_out.branch(m_out.notNull(cachedOwnKeys), unsure(notNullCacheCase), unsure(slowCase));
     5517
     5518                m_out.appendTo(notNullCacheCase, useCacheCase);
     5519                m_out.branch(m_out.notEqual(cachedOwnKeys, weakPointer(m_graph.m_vm.sentinelImmutableButterfly.get())), unsure(useCacheCase), unsure(slowCase));
     5520
     5521                m_out.appendTo(useCacheCase, slowButArrayBufferCase);
     5522                JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
     5523                RegisteredStructure arrayStructure = m_graph.registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(CopyOnWriteArrayWithContiguous));
     5524                LValue fastArray = allocateObject<JSArray>(arrayStructure, m_out.addPtr(cachedOwnKeys, JSImmutableButterfly::offsetOfData()), slowButArrayBufferCase);
     5525                ValueFromBlock fastResult = m_out.anchor(fastArray);
     5526                m_out.jump(continuation);
     5527
     5528                m_out.appendTo(slowButArrayBufferCase, slowCase);
     5529                LValue slowArray = vmCall(Int64, m_out.operation(operationNewArrayBuffer), m_callFrame, weakStructure(arrayStructure), cachedOwnKeys);
     5530                ValueFromBlock slowButArrayBufferResult = m_out.anchor(slowArray);
     5531                m_out.jump(continuation);
     5532
     5533                m_out.appendTo(slowCase, continuation);
     5534                VM& vm = this->vm();
     5535                LValue slowResultValue = lazySlowPath(
     5536                    [=, &vm] (const Vector<Location>& locations) -> RefPtr<LazySlowPath::Generator> {
     5537                        return createLazyCallGenerator(vm,
     5538                            operationObjectKeysObject, locations[0].directGPR(), locations[1].directGPR());
     5539                    },
     5540                    object);
     5541                ValueFromBlock slowResult = m_out.anchor(slowResultValue);
     5542                m_out.jump(continuation);
     5543
     5544                m_out.appendTo(continuation, lastNext);
     5545                setJSValue(m_out.phi(pointerType(), fastResult, slowButArrayBufferResult, slowResult));
     5546                break;
     5547            }
     5548            setJSValue(vmCall(Int64, m_out.operation(operationObjectKeysObject), m_callFrame, lowObject(m_node->child1())));
     5549            break;
     5550        }
     5551        case UntypedUse:
     5552            setJSValue(vmCall(Int64, m_out.operation(operationObjectKeys), m_callFrame, lowJSValue(m_node->child1())));
     5553            break;
     5554        default:
     5555            RELEASE_ASSERT_NOT_REACHED();
     5556            break;
     5557        }
    54865558    }
    54875559
  • trunk/Source/JavaScriptCore/runtime/Intrinsic.cpp

    r235106 r239153  
    120120    case ObjectIsIntrinsic:
    121121        return "ObjectIsIntrinsic";
     122    case ObjectKeysIntrinsic:
     123        return "ObjectKeysIntrinsic";
    122124    case ReflectGetPrototypeOfIntrinsic:
    123125        return "ReflectGetPrototypeOfIntrinsic";
  • trunk/Source/JavaScriptCore/runtime/Intrinsic.h

    r235106 r239153  
    7373    ObjectGetPrototypeOfIntrinsic,
    7474    ObjectIsIntrinsic,
     75    ObjectKeysIntrinsic,
    7576    ReflectGetPrototypeOfIntrinsic,
    7677    StringPrototypeValueOfIntrinsic,
  • trunk/Source/JavaScriptCore/runtime/JSImmutableButterfly.h

    r235972 r239153  
    6868    }
    6969
     70    static JSImmutableButterfly* createSentinel(VM& vm)
     71    {
     72        return create(vm, CopyOnWriteArrayWithContiguous, 0);
     73    }
     74
    7075    unsigned publicLength() const { return m_header.publicLength(); }
    7176    unsigned vectorLength() const { return m_header.vectorLength(); }
  • trunk/Source/JavaScriptCore/runtime/ObjectConstructor.cpp

    r236791 r239153  
    3131#include "JSGlobalObject.h"
    3232#include "JSGlobalObjectFunctions.h"
     33#include "JSImmutableButterfly.h"
    3334#include "Lookup.h"
    3435#include "ObjectPrototype.h"
     
    7475  getOwnPropertyNames       objectConstructorGetOwnPropertyNames        DontEnum|Function 1
    7576  getOwnPropertySymbols     objectConstructorGetOwnPropertySymbols      DontEnum|Function 1
    76   keys                      objectConstructorKeys                       DontEnum|Function 1
     77  keys                      objectConstructorKeys                       DontEnum|Function 1 ObjectKeysIntrinsic
    7778  defineProperty            objectConstructorDefineProperty             DontEnum|Function 3
    7879  defineProperties          objectConstructorDefineProperties           DontEnum|Function 2
     
    272273}
    273274
    274 // FIXME: Use the enumeration cache.
    275275EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec)
    276276{
     
    893893}
    894894
    895 // FIXME: Use the enumeration cache.
    896895JSArray* ownPropertyKeys(ExecState* exec, JSObject* object, PropertyNameMode propertyNameMode, DontEnumPropertiesMode dontEnumPropertiesMode)
    897896{
    898897    VM& vm = exec->vm();
    899898    auto scope = DECLARE_THROW_SCOPE(vm);
     899
     900    auto* globalObject = exec->lexicalGlobalObject();
     901    bool isObjectKeys = propertyNameMode == PropertyNameMode::Strings && dontEnumPropertiesMode == DontEnumPropertiesMode::Exclude;
     902    // We attempt to look up own property keys cache in Object.keys case.
     903    if (isObjectKeys) {
     904        if (LIKELY(!globalObject->isHavingABadTime())) {
     905            if (auto* immutableButterfly = object->structure(vm)->cachedOwnKeys()) {
     906                if (immutableButterfly != vm.sentinelImmutableButterfly.get()) {
     907                    Structure* arrayStructure = globalObject->originalArrayStructureForIndexingType(immutableButterfly->indexingMode());
     908                    return JSArray::createWithButterfly(vm, nullptr, arrayStructure, immutableButterfly->toButterfly());
     909                }
     910            }
     911        }
     912    }
     913
    900914    PropertyNameArray properties(&vm, propertyNameMode, PrivateSymbolMode::Exclude);
    901915    object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(dontEnumPropertiesMode));
     
    919933        ASSERT(propertyNameMode == PropertyNameMode::Strings || propertyNameMode == PropertyNameMode::Symbols);
    920934        if (!mustFilterProperty && properties.size() < MIN_SPARSE_ARRAY_INDEX) {
    921             auto* globalObject = exec->lexicalGlobalObject();
    922935            if (LIKELY(!globalObject->isHavingABadTime())) {
     936                if (isObjectKeys) {
     937                    Structure* structure = object->structure(vm);
     938                    if (structure->canCacheOwnKeys()) {
     939                        auto* cachedButterfly = structure->cachedOwnKeys();
     940                        if (cachedButterfly == vm.sentinelImmutableButterfly.get()) {
     941                            // Cache the immutable butterfly!
     942                            size_t numProperties = properties.size();
     943                            auto* newButterfly = JSImmutableButterfly::create(vm, CopyOnWriteArrayWithContiguous, numProperties);
     944                            for (size_t i = 0; i < numProperties; i++) {
     945                                const auto& identifier = properties[i];
     946                                ASSERT(!identifier.isSymbol());
     947                                newButterfly->setIndex(vm, i, jsOwnedString(&vm, identifier.string()));
     948                            }
     949
     950                            structure->setCachedOwnKeys(vm, newButterfly);
     951                            Structure* arrayStructure = globalObject->originalArrayStructureForIndexingType(newButterfly->indexingMode());
     952                            return JSArray::createWithButterfly(vm, nullptr, arrayStructure, newButterfly->toButterfly());
     953                        }
     954
     955                        if (cachedButterfly == nullptr)
     956                            structure->setCachedOwnKeys(vm, jsCast<JSImmutableButterfly*>(vm.sentinelImmutableButterfly.get()));
     957                    }
     958                }
     959
    923960                size_t numProperties = properties.size();
    924961                JSArray* keys = JSArray::create(vm, globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous), numProperties);
  • trunk/Source/JavaScriptCore/runtime/Structure.cpp

    r234284 r239153  
    12531253bool Structure::canCachePropertyNameEnumerator() const
    12541254{
    1255     auto canCache = [] (const Structure* structure) {
    1256         if (structure->isDictionary())
    1257             return false;
    1258         if (hasIndexedProperties(structure->indexingType()))
    1259             return false;
    1260         if (structure->typeInfo().overridesGetPropertyNames())
    1261             return false;
    1262         return true;
    1263     };
    1264 
    1265     if (!canCache(this))
     1255    if (!this->canCacheOwnKeys())
    12661256        return false;
    12671257
     
    12721262        if (!structure->get())
    12731263            return true;
    1274         if (!canCache(structure->get()))
     1264        if (!structure->get()->canCacheOwnKeys())
    12751265            return false;
    12761266        structure++;
  • trunk/Source/JavaScriptCore/runtime/Structure.h

    r238392 r239153  
    3939#include "StructureIDBlob.h"
    4040#include "StructureRareData.h"
    41 #include "StructureRareDataInlines.h"
    4241#include "StructureTransitionTable.h"
    4342#include "JSTypeInfo.h"
     
    327326    }
    328327
     328    const StructureRareData* rareDataConcurrently() const
     329    {
     330        JSCell* cell = m_previousOrRareData.get();
     331        WTF::loadLoadFence();
     332        if (isRareData(cell))
     333            return static_cast<StructureRareData*>(cell);
     334        return nullptr;
     335    }
     336
    329337    StructureRareData* ensureRareData(VM& vm)
    330338    {
     
    473481    bool canAccessPropertiesQuicklyForEnumeration() const;
    474482
     483    void setCachedOwnKeys(VM&, JSImmutableButterfly*);
     484    JSImmutableButterfly* cachedOwnKeys() const;
     485    bool canCacheOwnKeys() const;
     486
    475487    void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode);
    476488
     
    519531    {
    520532        return OBJECT_OFFSETOF(Structure, m_inlineCapacity);
     533    }
     534
     535    static ptrdiff_t previousOrRareDataOffset()
     536    {
     537        return OBJECT_OFFSETOF(Structure, m_previousOrRareData);
    521538    }
    522539
  • trunk/Source/JavaScriptCore/runtime/StructureInlines.h

    r238366 r239153  
    220220}
    221221
     222inline void Structure::setCachedOwnKeys(VM& vm, JSImmutableButterfly* ownKeys)
     223{
     224    ensureRareData(vm)->setCachedOwnKeys(vm, ownKeys);
     225}
     226
     227inline JSImmutableButterfly* Structure::cachedOwnKeys() const
     228{
     229    if (!hasRareData())
     230        return nullptr;
     231    return rareData()->cachedOwnKeys();
     232}
     233
     234inline bool Structure::canCacheOwnKeys() const
     235{
     236    if (isDictionary())
     237        return false;
     238    if (hasIndexedProperties(indexingType()))
     239        return false;
     240    if (typeInfo().overridesGetPropertyNames())
     241        return false;
     242    return true;
     243}
     244
    222245ALWAYS_INLINE JSValue prototypeForLookupPrimitiveImpl(JSGlobalObject* globalObject, const Structure* structure)
    223246{
  • trunk/Source/JavaScriptCore/runtime/StructureRareData.cpp

    r233245 r239153  
    2828
    2929#include "AdaptiveInferredPropertyValueWatchpointBase.h"
     30#include "JSImmutableButterfly.h"
    3031#include "JSPropertyNameEnumerator.h"
    3132#include "JSString.h"
     
    7172    visitor.append(thisObject->m_objectToStringValue);
    7273    visitor.append(thisObject->m_cachedPropertyNameEnumerator);
    73 }
    74 
    75 JSPropertyNameEnumerator* StructureRareData::cachedPropertyNameEnumerator() const
    76 {
    77     return m_cachedPropertyNameEnumerator.get();
    78 }
    79 
    80 void StructureRareData::setCachedPropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator)
    81 {
    82     m_cachedPropertyNameEnumerator.set(vm, this, enumerator);
     74    visitor.append(thisObject->m_cachedOwnKeys);
    8375}
    8476
  • trunk/Source/JavaScriptCore/runtime/StructureRareData.h

    r228500 r239153  
    5959    static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
    6060
    61     Structure* previousID() const;
     61    Structure* previousID() const
     62    {
     63        return m_previous.get();
     64    }
    6265    void setPreviousID(VM&, Structure*);
    6366    void clearPreviousID();
     
    6972    void setCachedPropertyNameEnumerator(VM&, JSPropertyNameEnumerator*);
    7073
     74    JSImmutableButterfly* cachedOwnKeys() const;
     75    JSImmutableButterfly* cachedOwnKeysConcurrently() const;
     76    void setCachedOwnKeys(VM&, JSImmutableButterfly*);
     77
    7178    Box<InlineWatchpointSet> copySharedPolyProtoWatchpoint() const { return m_polyProtoWatchpoint; }
    7279    const Box<InlineWatchpointSet>& sharedPolyProtoWatchpoint() const { return m_polyProtoWatchpoint; }
    7380    void setSharedPolyProtoWatchpoint(Box<InlineWatchpointSet>&& sharedPolyProtoWatchpoint) { m_polyProtoWatchpoint = WTFMove(sharedPolyProtoWatchpoint); }
    7481    bool hasSharedPolyProtoWatchpoint() const { return static_cast<bool>(m_polyProtoWatchpoint); }
     82
     83    static ptrdiff_t offsetOfCachedOwnKeys()
     84    {
     85        return OBJECT_OFFSETOF(StructureRareData, m_cachedOwnKeys);
     86    }
    7587
    7688    DECLARE_EXPORT_INFO;
     
    8799    WriteBarrier<Structure> m_previous;
    88100    WriteBarrier<JSString> m_objectToStringValue;
     101    // FIXME: We should have some story for clearing these property names caches in GC.
     102    // https://bugs.webkit.org/show_bug.cgi?id=192659
    89103    WriteBarrier<JSPropertyNameEnumerator> m_cachedPropertyNameEnumerator;
     104    WriteBarrier<JSImmutableButterfly> m_cachedOwnKeys;
    90105
    91106    typedef HashMap<PropertyOffset, RefPtr<WatchpointSet>, WTF::IntHash<PropertyOffset>, WTF::UnsignedWithZeroKeyHashTraits<PropertyOffset>> PropertyWatchpointMap;
  • trunk/Source/JavaScriptCore/runtime/StructureRareDataInlines.h

    r206525 r239153  
    2626#pragma once
    2727
     28#include "JSImmutableButterfly.h"
     29#include "JSPropertyNameEnumerator.h"
    2830#include "JSString.h"
    2931#include "StructureRareData.h"
    3032
    3133namespace JSC {
    32 
    33 inline Structure* StructureRareData::previousID() const
    34 {
    35     return m_previous.get();
    36 }
    3734
    3835inline void StructureRareData::setPreviousID(VM& vm, Structure* structure)
     
    5148}
    5249
     50inline JSPropertyNameEnumerator* StructureRareData::cachedPropertyNameEnumerator() const
     51{
     52    return m_cachedPropertyNameEnumerator.get();
     53}
     54
     55inline void StructureRareData::setCachedPropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator)
     56{
     57    m_cachedPropertyNameEnumerator.set(vm, this, enumerator);
     58}
     59
     60inline JSImmutableButterfly* StructureRareData::cachedOwnKeys() const
     61{
     62    ASSERT(!compilationOrGCThread())
     63    return m_cachedOwnKeys.get();
     64}
     65
     66inline JSImmutableButterfly* StructureRareData::cachedOwnKeysConcurrently() const
     67{
     68    auto* result = m_cachedOwnKeys.get();
     69    WTF::loadLoadFence();
     70    return result;
     71}
     72
     73inline void StructureRareData::setCachedOwnKeys(VM& vm, JSImmutableButterfly* butterfly)
     74{
     75    WTF::storeStoreFence();
     76    m_cachedOwnKeys.set(vm, this, butterfly);
     77}
     78
    5379} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/VM.cpp

    r238162 r239153  
    435435    promiseDeferredStructure.set(*this, JSPromiseDeferred::createStructure(*this, 0, jsNull()));
    436436    internalPromiseDeferredStructure.set(*this, JSInternalPromiseDeferred::createStructure(*this, 0, jsNull()));
     437    nativeStdFunctionCellStructure.set(*this, NativeStdFunctionCell::createStructure(*this, 0, jsNull()));
    437438    programCodeBlockStructure.set(*this, ProgramCodeBlock::createStructure(*this, 0, jsNull()));
    438439    moduleProgramCodeBlockStructure.set(*this, ModuleProgramCodeBlock::createStructure(*this, 0, jsNull()));
     
    448449    sentinelSetBucket.set(*this, JSSet::BucketType::createSentinel(*this));
    449450    sentinelMapBucket.set(*this, JSMap::BucketType::createSentinel(*this));
    450 
    451     nativeStdFunctionCellStructure.set(*this, NativeStdFunctionCell::createStructure(*this, 0, jsNull()));
     451    sentinelImmutableButterfly.set(*this, JSImmutableButterfly::createSentinel(*this));
     452
    452453    smallStrings.initializeCommonStrings(*this);
    453454
  • trunk/Source/JavaScriptCore/runtime/VM.h

    r238162 r239153  
    572572    Strong<JSCell> sentinelSetBucket;
    573573    Strong<JSCell> sentinelMapBucket;
     574    Strong<JSCell> sentinelImmutableButterfly;
    574575
    575576    std::unique_ptr<PromiseDeferredTimer> promiseDeferredTimer;
Note: See TracChangeset for help on using the changeset viewer.