Ignore:
Timestamp:
Nov 25, 2008, 3:07:30 PM (17 years ago)
Author:
[email protected]
Message:

2008-11-24 Gavin Barraclough <[email protected]>

Reviewed by Geoff Garen.

Polymorpic caching for get by id chain. Similar to the polymorphic caching already implemented
for self and proto accesses (implemented by allowing multiple trampolines to be JIT genertaed,
and linked together) - the get by id chain caching is implemented as a genericization of the
proto list caching, allowing cached access lists to contain a mix of proto and proto chain
accesses (since in JS style inheritance hierarchies you may commonly see a mix of properties
being overridden on the direct prototype, or higher up its prototype chain).

In order to allow this patch to compile there is a fix to appease gcc 4.2 compiler issues
(removing the jumps between fall-through cases in privateExecute).


This patch also removes redundant immediate checking from the reptach code, and fixes a related
memory leak (failure to deallocate trampolines).

~2% progression on v8 tests (bulk on the win on deltablue)

  • bytecode/Instruction.h: (JSC::PolymorphicAccessStructureList::PolymorphicStubInfo::): (JSC::PolymorphicAccessStructureList::PolymorphicStubInfo::set): (JSC::PolymorphicAccessStructureList::PolymorphicAccessStructureList): (JSC::PolymorphicAccessStructureList::derefStructures):
  • interpreter/Interpreter.cpp: (JSC::countPrototypeChainEntriesAndCheckForProxies): (JSC::Interpreter::tryCacheGetByID): (JSC::Interpreter::privateExecute): (JSC::Interpreter::tryCTICacheGetByID): (JSC::Interpreter::cti_op_get_by_id_self_fail): (JSC::getPolymorphicAccessStructureListSlot): (JSC::Interpreter::cti_op_get_by_id_proto_list):
  • interpreter/Interpreter.h:
  • jit/JIT.cpp: (JSC::JIT::privateCompileGetByIdProto): (JSC::JIT::privateCompileGetByIdSelfList): (JSC::JIT::privateCompileGetByIdProtoList): (JSC::JIT::privateCompileGetByIdChainList): (JSC::JIT::privateCompileGetByIdChain): (JSC::JIT::privateCompilePatchGetArrayLength):
  • jit/JIT.h: (JSC::JIT::compileGetByIdChainList):
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/interpreter/Interpreter.cpp

    r38688 r38763  
    8484// Preferred number of milliseconds between each timeout check
    8585static const int preferredScriptCheckTimeInterval = 1000;
    86 
    87 #if HAVE(COMPUTED_GOTO)
    88 static void* op_throw_end_indirect;
    89 static void* op_call_indirect;
    90 #endif
    9186
    9287#if ENABLE(JIT)
     
    13241319}
    13251320
     1321static size_t countPrototypeChainEntriesAndCheckForProxies(CallFrame* callFrame, JSValue* baseValue, const PropertySlot& slot)
     1322{
     1323    JSObject* o = asObject(baseValue);
     1324    size_t count = 0;
     1325
     1326    while (slot.slotBase() != o) {
     1327        JSValue* v = o->structure()->prototypeForLookup(callFrame);
     1328
     1329        // If we didn't find slotBase in baseValue's prototype chain, then baseValue
     1330        // must be a proxy for another object.
     1331
     1332        if (v->isNull())
     1333            return 0;
     1334
     1335        o = asObject(v);
     1336
     1337        // Heavy access to a prototype is a good indication that it's not being
     1338        // used as a dictionary.
     1339        if (o->structure()->isDictionary()) {
     1340            RefPtr<Structure> transition = Structure::fromDictionaryTransition(o->structure());
     1341            o->setStructure(transition.release());
     1342            asObject(baseValue)->structure()->setCachedPrototypeChain(0);
     1343        }
     1344
     1345        ++count;
     1346    }
     1347   
     1348    ASSERT(count);
     1349    return count;
     1350}
     1351
    13261352NEVER_INLINE void Interpreter::tryCacheGetByID(CallFrame* callFrame, CodeBlock* codeBlock, Instruction* vPC, JSValue* baseValue, const Identifier& propertyName, const PropertySlot& slot)
    13271353{
     
    14041430    }
    14051431
    1406     size_t count = 0;
    1407     JSObject* o = asObject(baseValue);
    1408     while (slot.slotBase() != o) {
    1409         JSValue* v = o->structure()->prototypeForLookup(callFrame);
    1410 
    1411         // If we didn't find base in baseValue's prototype chain, then baseValue
    1412         // must be a proxy for another object.
    1413         if (v->isNull()) {
    1414             vPC[0] = getOpcode(op_get_by_id_generic);
    1415             return;
    1416         }
    1417 
    1418         o = asObject(v);
    1419 
    1420         // Heavy access to a prototype is a good indication that it's not being
    1421         // used as a dictionary.
    1422         if (o->structure()->isDictionary()) {
    1423             RefPtr<Structure> transition = Structure::fromDictionaryTransition(o->structure());
    1424             o->setStructure(transition.release());
    1425             asObject(baseValue)->structure()->setCachedPrototypeChain(0);
    1426         }
    1427 
    1428         ++count;
     1432    size_t count = countPrototypeChainEntriesAndCheckForProxies(callFrame, baseValue, slot);
     1433    if (!count) {
     1434        vPC[0] = getOpcode(op_get_by_id_generic);
     1435        return;
    14291436    }
    14301437
     
    14631470            #undef ADD_OPCODE_ID
    14641471            ASSERT(m_opcodeIDTable.size() == numOpcodeIDs);
    1465             op_throw_end_indirect = &&op_throw_end;
    1466             op_call_indirect = &&op_call;
    14671472        #endif // HAVE(COMPUTED_GOTO)
    14681473        return noValue();
     
    33153320        // We didn't find the blessed version of eval, so process this
    33163321        // instruction as a normal function call.
    3317 
    3318 #if HAVE(COMPUTED_GOTO)
    3319         // Hack around gcc performance quirk by performing an indirect goto
    3320         // in order to set the vPC -- attempting to do so directly results in a
    3321         // significant regression.
    3322         goto *op_call_indirect; // indirect goto -> op_call
    3323 #endif
    33243322        // fall through to op_call
    33253323    }
     
    38023800            return jsNull();
    38033801        }
    3804 
    3805 #if HAVE(COMPUTED_GOTO)
    3806         // Hack around gcc performance quirk by performing an indirect goto
    3807         // in order to set the vPC -- attempting to do so directly results in a
    3808         // significant regression.
    3809         goto *op_throw_end_indirect; // indirect goto -> op_throw_end
    3810     }
    3811     op_throw_end: {
    3812 #endif
    38133802
    38143803        vPC = handlerVPC;
     
    42284217    }
    42294218
    4230     size_t count = 0;
    4231     JSObject* o = asObject(baseValue);
    4232     while (slot.slotBase() != o) {
    4233         JSValue* v = o->structure()->prototypeForLookup(callFrame);
    4234 
    4235         // If we didn't find slotBase in baseValue's prototype chain, then baseValue
    4236         // must be a proxy for another object.
    4237 
    4238         if (v->isNull()) {
    4239             vPC[0] = getOpcode(op_get_by_id_generic);
    4240             return;
    4241         }
    4242 
    4243         o = asObject(v);
    4244 
    4245         // Heavy access to a prototype is a good indication that it's not being
    4246         // used as a dictionary.
    4247         if (o->structure()->isDictionary()) {
    4248             RefPtr<Structure> transition = Structure::fromDictionaryTransition(o->structure());
    4249             o->setStructure(transition.release());
    4250             asObject(baseValue)->structure()->setCachedPrototypeChain(0);
    4251         }
    4252 
    4253         ++count;
     4219    size_t count = countPrototypeChainEntriesAndCheckForProxies(callFrame, baseValue, slot);
     4220    if (!count) {
     4221        vPC[0] = getOpcode(op_get_by_id_generic);
     4222        return;
    42544223    }
    42554224
     
    42574226    if (!chain)
    42584227        chain = cachePrototypeChain(callFrame, structure);
    4259 
    42604228    ASSERT(chain);
     4229
    42614230    vPC[0] = getOpcode(op_get_by_id_chain);
    42624231    vPC[4] = structure;
     
    46214590        if (vPC[0].u.opcode == ARG_globalData->interpreter->getOpcode(op_get_by_id_self)) {
    46224591            ASSERT(!stubInfo->stubRoutine);
    4623             polymorphicStructureList = new PolymorphicAccessStructureList(vPC[4].u.structure, 0, vPC[5].u.operand, 0);
     4592            polymorphicStructureList = new PolymorphicAccessStructureList(vPC[5].u.operand, 0, vPC[4].u.structure);
    46244593
    46254594            vPC[0] = ARG_globalData->interpreter->getOpcode(op_get_by_id_self_list);
     
    46434612}
    46444613
     4614static PolymorphicAccessStructureList* getPolymorphicAccessStructureListSlot(Interpreter* interpreter, StructureStubInfo* stubInfo, Instruction* vPC, int& listIndex)
     4615{
     4616    PolymorphicAccessStructureList* prototypeStructureList;
     4617    listIndex = 1;
     4618
     4619    if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_proto)) {
     4620        prototypeStructureList = new PolymorphicAccessStructureList(vPC[6].u.operand, stubInfo->stubRoutine, vPC[4].u.structure, vPC[5].u.structure);
     4621        stubInfo->stubRoutine = 0;
     4622
     4623        vPC[0] = interpreter->getOpcode(op_get_by_id_proto_list);
     4624        vPC[4] = prototypeStructureList;
     4625        vPC[5] = 2;
     4626    } else if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_chain)) {
     4627        prototypeStructureList = new PolymorphicAccessStructureList(vPC[6].u.operand, stubInfo->stubRoutine, vPC[4].u.structure, vPC[5].u.structureChain);
     4628        stubInfo->stubRoutine = 0;
     4629
     4630        vPC[0] = interpreter->getOpcode(op_get_by_id_proto_list);
     4631        vPC[4] = prototypeStructureList;
     4632        vPC[5] = 2;
     4633    } else {
     4634        ASSERT(vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_proto_list));
     4635        prototypeStructureList = vPC[4].u.polymorphicStructures;
     4636        listIndex = vPC[5].u.operand;
     4637        vPC[5] = listIndex + 1;
     4638
     4639        ASSERT(listIndex < POLYMORPHIC_LIST_CACHE_SIZE);
     4640    }
     4641   
     4642    return prototypeStructureList;
     4643}
     4644
    46454645JSValue* Interpreter::cti_op_get_by_id_proto_list(CTI_ARGS)
    46464646{
     
    46554655    CHECK_FOR_EXCEPTION();
    46564656
    4657     if (baseValue->isObject()
    4658         && slot.isCacheable()
    4659         && !asCell(baseValue)->structure()->isDictionary()
    4660         && slot.slotBase() == asCell(baseValue)->structure()->prototypeForLookup(callFrame)) {
    4661 
    4662         JSCell* baseCell = asCell(baseValue);
    4663         Structure* structure = baseCell->structure();
    4664         CodeBlock* codeBlock = callFrame->codeBlock();
    4665         unsigned vPCIndex = codeBlock->ctiReturnAddressVPCMap.get(CTI_RETURN_ADDRESS);
    4666         Instruction* vPC = codeBlock->instructions.begin() + vPCIndex;
    4667 
    4668         ASSERT(slot.slotBase()->isObject());
    4669 
    4670         JSObject* slotBaseObject = asObject(slot.slotBase());
    4671 
     4657    if (!baseValue->isObject() || !slot.isCacheable() || asCell(baseValue)->structure()->isDictionary()) {
     4658        ctiRepatchCallByReturnAddress(CTI_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_proto_fail));
     4659        return result;
     4660    }
     4661
     4662    Structure* structure = asCell(baseValue)->structure();
     4663    CodeBlock* codeBlock = callFrame->codeBlock();
     4664    Instruction* vPC = codeBlock->instructions.begin() + codeBlock->ctiReturnAddressVPCMap.get(CTI_RETURN_ADDRESS);
     4665
     4666    ASSERT(slot.slotBase()->isObject());
     4667    JSObject* slotBaseObject = asObject(slot.slotBase());
     4668
     4669    if (slot.slotBase() == baseValue)
     4670        ctiRepatchCallByReturnAddress(CTI_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_proto_fail));
     4671    else if (slot.slotBase() == asCell(baseValue)->structure()->prototypeForLookup(callFrame)) {
    46724672        // Heavy access to a prototype is a good indication that it's not being
    46734673        // used as a dictionary.
     
    46794679
    46804680        StructureStubInfo* stubInfo = &codeBlock->getStubInfo(CTI_RETURN_ADDRESS);
    4681 
    4682         PolymorphicAccessStructureList* prototypeStructureList;
    4683         int listIndex = 1;
    4684 
    4685         if (vPC[0].u.opcode == ARG_globalData->interpreter->getOpcode(op_get_by_id_proto)) {
    4686             prototypeStructureList = new PolymorphicAccessStructureList(vPC[4].u.structure, vPC[5].u.structure, vPC[6].u.operand, stubInfo->stubRoutine);
    4687             stubInfo->stubRoutine = 0;
    4688 
    4689             vPC[0] = ARG_globalData->interpreter->getOpcode(op_get_by_id_proto_list);
    4690             vPC[4] = prototypeStructureList;
    4691             vPC[5] = 2;
    4692         } else {
    4693             prototypeStructureList = vPC[4].u.polymorphicStructures;
    4694             listIndex = vPC[5].u.operand;
    4695 
    4696             vPC[5] = listIndex + 1;
    4697         }
     4681        int listIndex;
     4682        PolymorphicAccessStructureList* prototypeStructureList = getPolymorphicAccessStructureListSlot(ARG_globalData->interpreter, stubInfo, vPC, listIndex);
    46984683
    46994684        JIT::compileGetByIdProtoList(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, prototypeStructureList, listIndex, structure, slotBaseObject->structure(), slot.cachedOffset());
     
    47014686        if (listIndex == (POLYMORPHIC_LIST_CACHE_SIZE - 1))
    47024687            ctiRepatchCallByReturnAddress(CTI_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_proto_list_full));
    4703     } else {
     4688    } else if (size_t count = countPrototypeChainEntriesAndCheckForProxies(callFrame, baseValue, slot)) {
     4689        StructureChain* chain = structure->cachedPrototypeChain();
     4690        if (!chain)
     4691            chain = cachePrototypeChain(callFrame, structure);
     4692        ASSERT(chain);
     4693
     4694        StructureStubInfo* stubInfo = &codeBlock->getStubInfo(CTI_RETURN_ADDRESS);
     4695        int listIndex;
     4696        PolymorphicAccessStructureList* prototypeStructureList = getPolymorphicAccessStructureListSlot(ARG_globalData->interpreter, stubInfo, vPC, listIndex);
     4697
     4698        JIT::compileGetByIdChainList(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, prototypeStructureList, listIndex, structure, chain, count, slot.cachedOffset());
     4699
     4700        if (listIndex == (POLYMORPHIC_LIST_CACHE_SIZE - 1))
     4701            ctiRepatchCallByReturnAddress(CTI_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_proto_list_full));
     4702    } else
    47044703        ctiRepatchCallByReturnAddress(CTI_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_proto_fail));
    4705     }
     4704
    47064705    return result;
    47074706}
     
    47204719
    47214720JSValue* Interpreter::cti_op_get_by_id_proto_fail(CTI_ARGS)
    4722 {
    4723     CTI_STACK_HACK();
    4724 
    4725     JSValue* baseValue = ARG_src1;
    4726     PropertySlot slot(baseValue);
    4727     JSValue* result = baseValue->get(ARG_callFrame, *ARG_id2, slot);
    4728 
    4729     CHECK_FOR_EXCEPTION_AT_END();
    4730     return result;
    4731 }
    4732 
    4733 JSValue* Interpreter::cti_op_get_by_id_chain_fail(CTI_ARGS)
    47344721{
    47354722    CTI_STACK_HACK();
Note: See TracChangeset for help on using the changeset viewer.