Changeset 95388 in webkit


Ignore:
Timestamp:
Sep 17, 2011 4:33:01 PM (13 years ago)
Author:
fpizlo@apple.com
Message:

method_check should repatch itself if it finds that the new structure(s)
are the result of transitions from the old structure(s)
https://bugs.webkit.org/show_bug.cgi?id=68294

Reviewed by Gavin Barraclough.

Previously a patched method_check would slow-path to get_by_id. Now it
slow-paths to method_check_update, which attempts to correct the
method_check due to structure transitions before bailing to get_by_id.

This is a 1-2% speed-up on some benchmarks and is not a slow-down
anywhere, leading to a 0.6% speed-up on the Kraken geomean.

  • jit/JITPropertyAccess.cpp:

(JSC::JIT::patchMethodCallProto):

  • jit/JITStubs.cpp:

(JSC::DEFINE_STUB_FUNCTION):

  • jit/JITStubs.h:
  • runtime/Structure.h:

(JSC::Structure::transitivelyTransitionedFrom):

Location:
trunk/Source/JavaScriptCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r95364 r95388  
     12011-09-16  Filip Pizlo  <fpizlo@apple.com>
     2
     3        method_check should repatch itself if it finds that the new structure(s)
     4        are the result of transitions from the old structure(s)
     5        https://bugs.webkit.org/show_bug.cgi?id=68294
     6
     7        Reviewed by Gavin Barraclough.
     8       
     9        Previously a patched method_check would slow-path to get_by_id. Now it
     10        slow-paths to method_check_update, which attempts to correct the
     11        method_check due to structure transitions before bailing to get_by_id.
     12       
     13        This is a 1-2% speed-up on some benchmarks and is not a slow-down
     14        anywhere, leading to a 0.6% speed-up on the Kraken geomean.
     15
     16        * jit/JITPropertyAccess.cpp:
     17        (JSC::JIT::patchMethodCallProto):
     18        * jit/JITStubs.cpp:
     19        (JSC::DEFINE_STUB_FUNCTION):
     20        * jit/JITStubs.h:
     21        * runtime/Structure.h:
     22        (JSC::Structure::transitivelyTransitionedFrom):
     23
    1242011-09-16  Ryosuke Niwa  <rniwa@webkit.org>
    225
  • trunk/Source/JavaScriptCore/jit/JITPropertyAccess.cpp

    r95273 r95388  
    10521052    RepatchBuffer repatchBuffer(codeBlock);
    10531053   
    1054     ASSERT(!methodCallLinkInfo.cachedStructure);
    10551054    CodeLocationDataLabelPtr structureLocation = methodCallLinkInfo.cachedStructure.location();
    10561055    methodCallLinkInfo.cachedStructure.set(globalData, structureLocation, codeBlock->ownerExecutable(), structure);
     
    10601059    methodCallLinkInfo.cachedPrototype.set(globalData, structureLocation.dataLabelPtrAtOffset(patchOffsetMethodCheckProtoObj), codeBlock->ownerExecutable(), proto);
    10611060    methodCallLinkInfo.cachedFunction.set(globalData, structureLocation.dataLabelPtrAtOffset(patchOffsetMethodCheckPutFunction), codeBlock->ownerExecutable(), callee);
    1062     repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id));
     1061    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_method_check_update));
    10631062}
    10641063
  • trunk/Source/JavaScriptCore/jit/JITStubs.cpp

    r95340 r95388  
    15581558    // Revert the get_by_id op back to being a regular get_by_id - allow it to cache like normal, if it needs to.
    15591559    ctiPatchCallByReturnAddress(codeBlock, STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id));
     1560    return JSValue::encode(result);
     1561}
     1562
     1563DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_method_check_update)
     1564{
     1565    STUB_INIT_STACK_FRAME(stackFrame);
     1566
     1567    CallFrame* callFrame = stackFrame.callFrame;
     1568    Identifier& ident = stackFrame.args[1].identifier();
     1569
     1570    JSValue baseValue = stackFrame.args[0].jsValue();
     1571    PropertySlot slot(baseValue);
     1572    JSValue result = baseValue.get(callFrame, ident, slot);
     1573    CHECK_FOR_EXCEPTION();
     1574
     1575    CodeBlock* codeBlock = stackFrame.callFrame->codeBlock();
     1576    MethodCallLinkInfo& methodCallLinkInfo = codeBlock->getMethodCallLinkInfo(STUB_RETURN_ADDRESS);
     1577
     1578    ASSERT(methodCallLinkInfo.seenOnce());
     1579
     1580    // If we successfully got something, then the base from which it is being accessed must
     1581    // be an object.  (Assertion to ensure asObject() call below is safe, which comes after
     1582    // an isCacheable() chceck.
     1583    ASSERT(!slot.isCacheableValue() || slot.slotBase().isObject());
     1584
     1585    // Check that:
     1586    //   * We're dealing with a JSCell,
     1587    //   * the property is cachable,
     1588    //   * it's not a dictionary
     1589    //   * there is a function cached.
     1590    Structure* structure;
     1591    JSCell* specific;
     1592    JSObject* slotBaseObject;
     1593    if (!(baseValue.isCell()
     1594          && slot.isCacheableValue()
     1595          && !(structure = baseValue.asCell()->structure())->isUncacheableDictionary()
     1596          && (slotBaseObject = asObject(slot.slotBase()))->getPropertySpecificValue(callFrame, ident, specific)
     1597          && specific
     1598          )
     1599        || (slot.slotBase() != structure->prototypeForLookup(callFrame)
     1600            && slot.slotBase() != baseValue)) {
     1601        // Revert the get_by_id op back to being a regular get_by_id - allow it to cache like normal, if it needs to.
     1602        ctiPatchCallByReturnAddress(codeBlock, STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id));
     1603        return JSValue::encode(result);
     1604    }
     1605   
     1606    // Now check if the situation has changed sufficiently that we should bail out of
     1607    // doing method_check optimizations entirely, or if it changed only slightly, in
     1608    // which case we can just repatch.
     1609   
     1610    JSValue proto = structure->prototypeForLookup(callFrame);
     1611   
     1612    bool previousWasProto = methodCallLinkInfo.cachedPrototype.get() != codeBlock->globalObject()->methodCallDummy();
     1613    bool currentIsProto = slot.slotBase() == proto;
     1614   
     1615    JSObject* callee = asObject(specific);
     1616   
     1617    if (previousWasProto != currentIsProto
     1618        || !structure->transitivelyTransitionedFrom(methodCallLinkInfo.cachedStructure.get())
     1619        || (previousWasProto && !slotBaseObject->structure()->transitivelyTransitionedFrom(methodCallLinkInfo.cachedPrototypeStructure.get()))
     1620        || specific != methodCallLinkInfo.cachedFunction.get()) {
     1621        ctiPatchCallByReturnAddress(codeBlock, STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id));
     1622        return JSValue::encode(result);
     1623    }
     1624   
     1625    // It makes sense to simply repatch the method_check.
     1626   
     1627    // Since we're accessing a prototype in a loop, it's a good bet that it
     1628    // should not be treated as a dictionary.
     1629    if (slotBaseObject->structure()->isDictionary())
     1630        slotBaseObject->flattenDictionaryObject(callFrame->globalData());
     1631   
     1632    // The result fetched should always be the callee!
     1633    ASSERT(result == JSValue(callee));
     1634   
     1635    // Check to see if the function is on the object's prototype. Patch up the code to optimize.
     1636    if (slot.slotBase() == proto) {
     1637        JIT::patchMethodCallProto(callFrame->globalData(), codeBlock, methodCallLinkInfo, callee, structure, slotBaseObject, STUB_RETURN_ADDRESS);
     1638        return JSValue::encode(result);
     1639    }
     1640   
     1641    ASSERT(slot.slotBase() == baseValue);
     1642   
     1643    // Since we generate the method-check to check both the structure and a prototype-structure (since this
     1644    // is the common case) we have a problem - we need to patch the prototype structure check to do something
     1645    // useful. We could try to nop it out altogether, but that's a little messy, so lets do something simpler
     1646    // for now. For now it performs a check on a special object on the global object only used for this
     1647    // purpose. The object is in no way exposed, and as such the check will always pass.
     1648    JIT::patchMethodCallProto(callFrame->globalData(), codeBlock, methodCallLinkInfo, callee, structure, callFrame->scopeChain()->globalObject->methodCallDummy(), STUB_RETURN_ADDRESS);
    15601649    return JSValue::encode(result);
    15611650}
  • trunk/Source/JavaScriptCore/jit/JITStubs.h

    r95310 r95388  
    335335    EncodedJSValue JIT_STUB cti_op_get_by_id_getter_stub(STUB_ARGS_DECLARATION);
    336336    EncodedJSValue JIT_STUB cti_op_get_by_id_method_check(STUB_ARGS_DECLARATION);
     337    EncodedJSValue JIT_STUB cti_op_get_by_id_method_check_update(STUB_ARGS_DECLARATION);
    337338    EncodedJSValue JIT_STUB cti_op_get_by_id_proto_fail(STUB_ARGS_DECLARATION);
    338339    EncodedJSValue JIT_STUB cti_op_get_by_id_proto_list(STUB_ARGS_DECLARATION);
  • trunk/Source/JavaScriptCore/runtime/Structure.h

    r95358 r95388  
    134134
    135135        Structure* previousID() const { ASSERT(structure()->classInfo() == &s_info); return m_previous.get(); }
     136        bool transitivelyTransitionedFrom(Structure* structureToFind);
    136137
    137138        void growPropertyStorageCapacity();
     
    353354    }
    354355
     356    inline bool Structure::transitivelyTransitionedFrom(Structure* structureToFind)
     357    {
     358        for (Structure* current = this; current; current = current->previousID()) {
     359            if (current == structureToFind)
     360                return true;
     361        }
     362        return false;
     363    }
     364
    355365} // namespace JSC
    356366
Note: See TracChangeset for help on using the changeset viewer.