Changeset 90950 in webkit


Ignore:
Timestamp:
Jul 13, 2011 2:44:42 PM (13 years ago)
Author:
commit-queue@webkit.org
Message:

DFG JIT does not implement prototype chain or list caching for get_by_id.
https://bugs.webkit.org/show_bug.cgi?id=64147

Patch by Filip Pizlo <fpizlo@apple.com> on 2011-07-13
Reviewed by Gavin Barraclough.

This implements unified support for prototype caching, prototype chain
caching, and polymorphic (i.e. list) prototype and prototype chain
caching. This is done by creating common code for emitting prototype
or chain access stubs, and having it factored out into
generateProtoChainAccessStub(). This function is called by
tryCacheGetByID once the latter determines that some form of prototype
access caching is necessary (i.e. the slot being accessed is not on the
base value but on some other object).

Direct prototype list, and prototype chain list, caching is implemented by
linking the slow path to operationGetByIdProtoBuildList(), which uses the
same helper function (generateProtoChainAccessStub()) as tryCacheGetByID.

This change required ensuring that the value in the scratchGPR field in
StructureStubInfo is preserved even after the stub info is in the
chain, or proto_list, states. Hence scratchGPR was moved out of the union
and into the top-level of StructureStubInfo.

  • bytecode/StructureStubInfo.h:
  • dfg/DFGJITCompiler.cpp:

(JSC::DFG::JITCompiler::compileFunction):

  • dfg/DFGOperations.cpp:
  • dfg/DFGOperations.h:
  • dfg/DFGRepatch.cpp:

(JSC::DFG::emitRestoreScratch):
(JSC::DFG::linkRestoreScratch):
(JSC::DFG::generateProtoChainAccessStub):
(JSC::DFG::tryCacheGetByID):
(JSC::DFG::tryBuildGetByIDProtoList):
(JSC::DFG::dfgBuildGetByIDProtoList):
(JSC::DFG::tryCachePutByID):

  • dfg/DFGRepatch.h:
Location:
trunk/Source/JavaScriptCore
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r90940 r90950  
     12011-07-13  Filip Pizlo  <fpizlo@apple.com>
     2
     3        DFG JIT does not implement prototype chain or list caching for get_by_id.
     4        https://bugs.webkit.org/show_bug.cgi?id=64147
     5
     6        Reviewed by Gavin Barraclough.
     7       
     8        This implements unified support for prototype caching, prototype chain
     9        caching, and polymorphic (i.e. list) prototype and prototype chain
     10        caching.  This is done by creating common code for emitting prototype
     11        or chain access stubs, and having it factored out into
     12        generateProtoChainAccessStub().  This function is called by
     13        tryCacheGetByID once the latter determines that some form of prototype
     14        access caching is necessary (i.e. the slot being accessed is not on the
     15        base value but on some other object).
     16       
     17        Direct prototype list, and prototype chain list, caching is implemented by
     18        linking the slow path to operationGetByIdProtoBuildList(), which uses the
     19        same helper function (generateProtoChainAccessStub()) as tryCacheGetByID.
     20       
     21        This change required ensuring that the value in the scratchGPR field in
     22        StructureStubInfo is preserved even after the stub info is in the
     23        chain, or proto_list, states.  Hence scratchGPR was moved out of the union
     24        and into the top-level of StructureStubInfo.
     25       
     26        * bytecode/StructureStubInfo.h:
     27        * dfg/DFGJITCompiler.cpp:
     28        (JSC::DFG::JITCompiler::compileFunction):
     29        * dfg/DFGOperations.cpp:
     30        * dfg/DFGOperations.h:
     31        * dfg/DFGRepatch.cpp:
     32        (JSC::DFG::emitRestoreScratch):
     33        (JSC::DFG::linkRestoreScratch):
     34        (JSC::DFG::generateProtoChainAccessStub):
     35        (JSC::DFG::tryCacheGetByID):
     36        (JSC::DFG::tryBuildGetByIDProtoList):
     37        (JSC::DFG::dfgBuildGetByIDProtoList):
     38        (JSC::DFG::tryCachePutByID):
     39        * dfg/DFGRepatch.h:
     40
    1412011-07-12  Brent Fulgham  <bfulgham@webkit.org>
    242
  • trunk/Source/JavaScriptCore/bytecode/StructureStubInfo.h

    r90035 r90950  
    134134        int8_t baseGPR;
    135135        int8_t valueGPR;
     136        int8_t scratchGPR;
    136137        int8_t deltaCallToDone;
    137138        int8_t deltaCallToStructCheck;
     
    143144                int8_t deltaCheckImmToCall;
    144145                int8_t deltaCallToLoadOrStore;
    145                 int8_t scratchGPR;
    146146            } unset;
    147147            struct {
  • trunk/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp

    r90529 r90950  
    399399        info.baseGPR = m_propertyAccesses[i].m_baseGPR;
    400400        info.valueGPR = m_propertyAccesses[i].m_valueGPR;
    401         info.u.unset.scratchGPR = m_propertyAccesses[i].m_scratchGPR;
     401        info.scratchGPR = m_propertyAccesses[i].m_scratchGPR;
    402402    }
    403403   
  • trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp

    r90877 r90950  
    260260}
    261261
     262EncodedJSValue operationGetByIdProtoBuildListWithReturnAddress(ExecState*, EncodedJSValue, Identifier*, ReturnAddressPtr);
     263FUNCTION_WRAPPER_WITH_ARG4_RETURN_ADDRESS(operationGetByIdProtoBuildList);
     264EncodedJSValue operationGetByIdProtoBuildListWithReturnAddress(ExecState* exec, EncodedJSValue encodedBase, Identifier* propertyName, ReturnAddressPtr returnAddress)
     265{
     266    JSValue baseValue = JSValue::decode(encodedBase);
     267    PropertySlot slot(baseValue);
     268    JSValue result = baseValue.get(exec, *propertyName, slot);
     269
     270    StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
     271    dfgBuildGetByIDProtoList(exec, baseValue, *propertyName, slot, stubInfo);
     272
     273    return JSValue::encode(result);
     274}
     275
    262276EncodedJSValue operationGetByIdOptimizeWithReturnAddress(ExecState*, EncodedJSValue, Identifier*, ReturnAddressPtr);
    263277FUNCTION_WRAPPER_WITH_ARG4_RETURN_ADDRESS(operationGetByIdOptimize);
  • trunk/Source/JavaScriptCore/dfg/DFGOperations.h

    r90877 r90950  
    6262EncodedJSValue operationGetById(ExecState*, EncodedJSValue encodedBase, Identifier*);
    6363EncodedJSValue operationGetByIdBuildList(ExecState*, EncodedJSValue encodedBase, Identifier*);
     64EncodedJSValue operationGetByIdProtoBuildList(ExecState*, EncodedJSValue encodedBase, Identifier*);
    6465EncodedJSValue operationGetByIdOptimize(ExecState*, EncodedJSValue encodedBase, Identifier*);
    6566EncodedJSValue operationGetMethodOptimize(ExecState*, EncodedJSValue encodedBase, Identifier*);
  • trunk/Source/JavaScriptCore/dfg/DFGRepatch.cpp

    r90877 r90950  
    5757}
    5858
     59static void emitRestoreScratch(MacroAssembler& stubJit, bool needToRestoreScratch, GPRReg scratchGPR, MacroAssembler::Jump& success, MacroAssembler::Jump& fail, MacroAssembler::JumpList failureCases)
     60{
     61    if (needToRestoreScratch) {
     62        stubJit.pop(scratchGPR);
     63       
     64        success = stubJit.jump();
     65       
     66        // link failure cases here, so we can pop scratchGPR, and then jump back.
     67        failureCases.link(&stubJit);
     68       
     69        stubJit.pop(scratchGPR);
     70       
     71        fail = stubJit.jump();
     72        return;
     73    }
     74   
     75    success = stubJit.jump();
     76}
     77
     78static void linkRestoreScratch(LinkBuffer& patchBuffer, bool needToRestoreScratch, MacroAssembler::Jump success, MacroAssembler::Jump fail, MacroAssembler::JumpList failureCases, CodeLocationLabel successLabel, CodeLocationLabel slowCaseBegin)
     79{
     80    patchBuffer.link(success, successLabel);
     81       
     82    if (needToRestoreScratch) {
     83        patchBuffer.link(fail, slowCaseBegin);
     84        return;
     85    }
     86   
     87    // link failure cases directly back to normal path
     88    patchBuffer.link(failureCases, slowCaseBegin);
     89}
     90
     91static void linkRestoreScratch(LinkBuffer& patchBuffer, bool needToRestoreScratch, StructureStubInfo& stubInfo, MacroAssembler::Jump success, MacroAssembler::Jump fail, MacroAssembler::JumpList failureCases)
     92{
     93    linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToDone), stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToSlowCase));
     94}
     95
     96static void generateProtoChainAccessStub(ExecState* exec, StructureStubInfo& stubInfo, StructureChain* chain, size_t count, size_t offset, Structure* structure, CodeLocationLabel successLabel, CodeLocationLabel slowCaseLabel, CodeLocationLabel& stubRoutine)
     97{
     98    JSGlobalData* globalData = &exec->globalData();
     99
     100    MacroAssembler stubJit;
     101       
     102    GPRReg baseGPR = static_cast<GPRReg>(stubInfo.baseGPR);
     103    GPRReg resultGPR = static_cast<GPRReg>(stubInfo.valueGPR);
     104    GPRReg scratchGPR = static_cast<GPRReg>(stubInfo.scratchGPR);
     105    bool needToRestoreScratch = false;
     106   
     107    if (scratchGPR == InvalidGPRReg) {
     108        scratchGPR = JITCodeGenerator::selectScratchGPR(baseGPR, resultGPR);
     109        stubJit.push(scratchGPR);
     110        needToRestoreScratch = true;
     111    }
     112   
     113    MacroAssembler::JumpList failureCases;
     114   
     115    failureCases.append(stubJit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(structure)));
     116   
     117    Structure* currStructure = structure;
     118    WriteBarrier<Structure>* it = chain->head();
     119    JSObject* protoObject = 0;
     120    for (unsigned i = 0; i < count; ++i, ++it) {
     121        protoObject = asObject(currStructure->prototypeForLookup(exec));
     122        stubJit.move(MacroAssembler::TrustedImmPtr(protoObject), scratchGPR);
     123        failureCases.append(stubJit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(scratchGPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(protoObject->structure())));
     124        currStructure = it->get();
     125    }
     126   
     127    if (protoObject->structure()->isUsingInlineStorage())
     128        stubJit.loadPtr(MacroAssembler::Address(scratchGPR, JSObject::offsetOfInlineStorage() + offset * sizeof(JSValue)), resultGPR);
     129    else
     130        stubJit.loadPtr(protoObject->addressOfPropertyAtOffset(offset), resultGPR);
     131       
     132    MacroAssembler::Jump success, fail;
     133   
     134    emitRestoreScratch(stubJit, needToRestoreScratch, scratchGPR, success, fail, failureCases);
     135   
     136    LinkBuffer patchBuffer(*globalData, &stubJit, exec->codeBlock()->executablePool());
     137   
     138    linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, successLabel, slowCaseLabel);
     139   
     140    stubRoutine = patchBuffer.finalizeCodeAddendum();
     141}
     142
    59143static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
    60144{
     
    68152        GPRReg baseGPR = static_cast<GPRReg>(stubInfo.baseGPR);
    69153        GPRReg resultGPR = static_cast<GPRReg>(stubInfo.valueGPR);
    70         GPRReg scratchGPR = static_cast<GPRReg>(stubInfo.u.unset.scratchGPR);
     154        GPRReg scratchGPR = static_cast<GPRReg>(stubInfo.scratchGPR);
    71155        bool needToRestoreScratch = false;
    72156       
     
    79163        }
    80164       
    81         MacroAssembler::Jump failureCase1 = stubJit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR), MacroAssembler::TrustedImmPtr(globalData->jsArrayVPtr));
     165        MacroAssembler::JumpList failureCases;
     166       
     167        failureCases.append(stubJit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR), MacroAssembler::TrustedImmPtr(globalData->jsArrayVPtr)));
    82168       
    83169        stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), scratchGPR);
    84170        stubJit.load32(MacroAssembler::Address(scratchGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), scratchGPR);
    85         MacroAssembler::Jump failureCase2 = stubJit.branch32(MacroAssembler::LessThan, scratchGPR, MacroAssembler::TrustedImm32(0));
     171        failureCases.append(stubJit.branch32(MacroAssembler::LessThan, scratchGPR, MacroAssembler::TrustedImm32(0)));
    86172       
    87173        stubJit.orPtr(GPRInfo::tagTypeNumberRegister, scratchGPR, resultGPR);
     
    89175        MacroAssembler::Jump success, fail;
    90176       
    91         if (needToRestoreScratch) {
    92             stubJit.pop(scratchGPR);
    93            
    94             success = stubJit.jump();
    95            
    96             // link failure cases here, so we can pop scratchGPR, and then jump back.
    97             failureCase1.link(&stubJit);
    98             failureCase2.link(&stubJit);
    99            
    100             stubJit.pop(scratchGPR);
    101            
    102             fail = stubJit.jump();
    103         } else
    104             success = stubJit.jump();
     177        emitRestoreScratch(stubJit, needToRestoreScratch, scratchGPR, success, fail, failureCases);
    105178       
    106179        LinkBuffer patchBuffer(*globalData, &stubJit, codeBlock->executablePool());
    107180       
    108         CodeLocationLabel slowCaseBegin = stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToSlowCase);
    109        
    110         patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToDone));
    111        
    112         if (needToRestoreScratch)
    113             patchBuffer.link(fail, slowCaseBegin);
    114         else {
    115             // link failure cases directly back to normal path
    116             patchBuffer.link(failureCase1, slowCaseBegin);
    117             patchBuffer.link(failureCase2, slowCaseBegin);
    118         }
     181        linkRestoreScratch(patchBuffer, needToRestoreScratch, stubInfo, success, fail, failureCases);
    119182       
    120183        CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
    121184        stubInfo.stubRoutine = entryLabel;
    122185       
    123         CodeLocationLabel hotPathBegin = stubInfo.hotPathBegin;
    124186        RepatchBuffer repatchBuffer(codeBlock);
    125187        repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.deltaCallToStructCheck), entryLabel);
     
    138200    if (!slot.isCacheable())
    139201        return false;
    140     if (structure->isUncacheableDictionary())
     202    if (structure->isUncacheableDictionary() || structure->typeInfo().prohibitsPropertyCaching())
    141203        return false;
    142204
     
    151213    }
    152214   
    153     // FIXME: should support prototype & chain accesses!
    154     return false;
     215    if (structure->isDictionary())
     216        return false;
     217   
     218    // FIXME: optimize getters and setters
     219    if (slot.cachedPropertyType() != PropertySlot::Value)
     220        return false;
     221   
     222    size_t offset = slot.cachedOffset();
     223    size_t count = normalizePrototypeChain(exec, baseValue, slot.slotBase(), propertyName, offset);
     224    if (!count)
     225        return false;
     226
     227    StructureChain* prototypeChain = structure->prototypeChain(exec);
     228   
     229    ASSERT(slot.slotBase().isObject());
     230   
     231    generateProtoChainAccessStub(exec, stubInfo, prototypeChain, count, offset, structure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToDone), stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToSlowCase), stubInfo.stubRoutine);
     232   
     233    RepatchBuffer repatchBuffer(codeBlock);
     234    repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.deltaCallToStructCheck), stubInfo.stubRoutine);
     235    repatchBuffer.relink(stubInfo.callReturnLocation, operationGetByIdProtoBuildList);
     236   
     237    stubInfo.initGetByIdChain(*globalData, codeBlock->ownerExecutable(), structure, prototypeChain);
     238    return true;
    155239}
    156240
     
    297381}
    298382
     383static bool tryBuildGetByIDProtoList(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
     384{
     385    if (!baseValue.isCell()
     386        || !slot.isCacheable()
     387        || baseValue.asCell()->structure()->isDictionary()
     388        || baseValue.asCell()->structure()->typeInfo().prohibitsPropertyCaching()
     389        || slot.slotBase() == baseValue
     390        || slot.cachedPropertyType() != PropertySlot::Value)
     391        return false;
     392   
     393    ASSERT(slot.slotBase().isObject());
     394   
     395    size_t offset = slot.cachedOffset();
     396    size_t count = normalizePrototypeChain(exec, baseValue, slot.slotBase(), propertyName, offset);
     397    if (!count)
     398        return false;
     399
     400    Structure* structure = baseValue.asCell()->structure();
     401    StructureChain* prototypeChain = structure->prototypeChain(exec);
     402    CodeBlock* codeBlock = exec->codeBlock();
     403    JSGlobalData* globalData = &exec->globalData();
     404   
     405    PolymorphicAccessStructureList* polymorphicStructureList;
     406    int listIndex = 1;
     407   
     408    if (stubInfo.accessType == access_get_by_id_chain) {
     409        ASSERT(!!stubInfo.stubRoutine);
     410        polymorphicStructureList = new PolymorphicAccessStructureList(*globalData, codeBlock->ownerExecutable(), stubInfo.stubRoutine, stubInfo.u.getByIdChain.baseObjectStructure.get(), stubInfo.u.getByIdChain.chain.get());
     411        stubInfo.stubRoutine = CodeLocationLabel();
     412        stubInfo.initGetByIdProtoList(polymorphicStructureList, 1);
     413    } else {
     414        ASSERT(stubInfo.accessType = access_get_by_id_proto_list);
     415        polymorphicStructureList = stubInfo.u.getByIdProtoList.structureList;
     416        listIndex = stubInfo.u.getByIdProtoList.listSize;
     417    }
     418   
     419    if (listIndex < POLYMORPHIC_LIST_CACHE_SIZE) {
     420        stubInfo.u.getByIdProtoList.listSize++;
     421       
     422        CodeLocationLabel lastProtoBegin = polymorphicStructureList->list[listIndex - 1].stubRoutine;
     423        ASSERT(!!lastProtoBegin);
     424
     425        CodeLocationLabel entryLabel;
     426       
     427        generateProtoChainAccessStub(exec, stubInfo, prototypeChain, count, offset, structure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToDone), lastProtoBegin, entryLabel);
     428       
     429        polymorphicStructureList->list[listIndex].set(*globalData, codeBlock->ownerExecutable(), entryLabel, structure);
     430       
     431        CodeLocationJump jumpLocation = stubInfo.callReturnLocation.jumpAtOffset(stubInfo.deltaCallToStructCheck);
     432        RepatchBuffer repatchBuffer(codeBlock);
     433        repatchBuffer.relink(jumpLocation, entryLabel);
     434       
     435        if (listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1))
     436            return true;
     437    }
     438   
     439    return false;
     440}
     441
     442void dfgBuildGetByIDProtoList(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
     443{
     444    bool dontChangeCall = tryBuildGetByIDProtoList(exec, baseValue, propertyName, slot, stubInfo);
     445    if (!dontChangeCall)
     446        dfgRepatchCall(exec->codeBlock(), stubInfo.callReturnLocation, operationGetById);
     447}
     448
    299449static V_DFGOperation_EJJI appropriatePutByIdFunction(const PutPropertySlot &slot, PutKind putKind)
    300450{
     
    352502            GPRReg baseGPR = static_cast<GPRReg>(stubInfo.baseGPR);
    353503            GPRReg valueGPR = static_cast<GPRReg>(stubInfo.valueGPR);
    354             GPRReg scratchGPR = static_cast<GPRReg>(stubInfo.u.unset.scratchGPR);
     504            GPRReg scratchGPR = static_cast<GPRReg>(stubInfo.scratchGPR);
    355505            bool needToRestoreScratch = false;
    356506           
     
    409559            stubInfo.stubRoutine = entryLabel;
    410560           
    411             CodeLocationLabel hotPathBegin = stubInfo.hotPathBegin;
    412561            RepatchBuffer repatchBuffer(codeBlock);
    413562            repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.deltaCallToStructCheck), entryLabel);
  • trunk/Source/JavaScriptCore/dfg/DFGRepatch.h

    r90877 r90950  
    3737void dfgRepatchGetMethod(ExecState*, JSValue, const Identifier&, const PropertySlot&, MethodCallLinkInfo&);
    3838void dfgBuildGetByIDList(ExecState*, JSValue, const Identifier&, const PropertySlot&, StructureStubInfo&);
     39void dfgBuildGetByIDProtoList(ExecState*, JSValue, const Identifier&, const PropertySlot&, StructureStubInfo&);
    3940void dfgRepatchPutByID(ExecState*, JSValue, const Identifier&, const PutPropertySlot&, StructureStubInfo&, PutKind);
    4041void dfgLinkFor(ExecState*, CallLinkInfo&, CodeBlock*, JSFunction* callee, MacroAssemblerCodePtr, CodeSpecializationKind);
  • trunk/Source/JavaScriptCore/runtime/JSObject.h

    r90673 r90950  
    221221        bool isUsingInlineStorage() const { return static_cast<const void*>(m_propertyStorage) == static_cast<const void*>(this + 1); }
    222222
     223        void* addressOfPropertyAtOffset(size_t offset)
     224        {
     225            return static_cast<void*>(&m_propertyStorage[offset]);
     226        }
     227
    223228        static const unsigned baseExternalStorageCapacity = 16;
    224229
Note: See TracChangeset for help on using the changeset viewer.