Ignore:
Timestamp:
Sep 1, 2008, 2:22:54 PM (17 years ago)
Author:
[email protected]
Message:

JavaScriptCore:

2008-09-01 Geoffrey Garen <[email protected]>

Reviewed by Darin Adler.

First cut at inline caching for access to vanilla JavaScript properties.


SunSpider says 4% faster. Tests heavy on dictionary-like access have
regressed a bit -- we have a lot of room to improve in this area,
but this patch is over-ripe as-is.


JSCells now have a StructureID that uniquely identifies their layout,
and holds their prototype.


JSValue::put takes a PropertySlot& argument, so it can fill in details
about where it put a value, for the sake of caching.

  • VM/CodeGenerator.cpp: (KJS::CodeGenerator::CodeGenerator): Avoid calling removeDirect if we can, since it disables inline caching in the global object. This can probably improve in the future.
  • kjs/JSGlobalObject.cpp: Nixed reset(), since it complicates caching, and wasn't really necessary.
  • kjs/JSObject.cpp: Tweaked getter / setter behavior not to rely on the IsGetterSetter flag, since the flag was buggy. This is necessary in order to avoid accidentally accessing a getter / setter as a normal property.


Also changed getter / setter creation to honor ReadOnly, matching Mozilla.


  • kjs/PropertyMap.cpp: Nixed clear(), since it complicates caching and isn't necessary.
  • kjs/Shell.cpp: Moved SamplingTool dumping outside the loop. This allows you to aggregate sampling of multiple files (or the same file repeatedly), which helped me track down regressions.
  • kjs/ustring.h: Moved IdentifierRepHash here to share it.

WebCore:

2008-09-01 Geoffrey Garen <[email protected]>

Reviewed by Darin Adler.

First cut at inline caching for access to vanilla JavaScript properties.

Updated for JavaScriptCore changes. Mostly mechanical addition of StructureIDs
to WebCore classes, and PutPropertySlot& arguments to put functions.

(WebCore::JSCSSStyleDeclaration::customPut): Be sure to play nice with
inline caching for global properties, so global assignment can be optimized.

  • ForwardingHeaders/kjs/StructureID.h: Added.
  • bindings/js/JSDOMBinding.h: (WebCore::DOMObject::DOMObject):
  • bindings/js/JSDOMWindowBase.cpp: (WebCore::JSDOMWindowBase::put):
  • bindings/js/JSDOMWindowBase.h:
  • bindings/js/JSDOMWindowCustom.h: (WebCore::JSDOMWindow::customPut):
  • bindings/js/JSDOMWindowShell.cpp: (WebCore::JSDOMWindowShell::JSDOMWindowShell): (WebCore::JSDOMWindowShell::put):
  • bindings/js/JSDOMWindowShell.h:
  • bindings/js/JSEventTargetBase.h: (WebCore::JSEventTargetBase::put):
  • bindings/js/JSEventTargetNode.h: (WebCore::JSEventTargetNode::put):
  • bindings/js/JSHTMLAppletElementCustom.cpp: (WebCore::JSHTMLAppletElement::customPut):
  • bindings/js/JSHTMLEmbedElementCustom.cpp: (WebCore::JSHTMLEmbedElement::customPut):
  • bindings/js/JSHTMLInputElementBase.cpp: (WebCore::JSHTMLInputElementBase::put):
  • bindings/js/JSHTMLInputElementBase.h:
  • bindings/js/JSHTMLObjectElementCustom.cpp: (WebCore::JSHTMLObjectElement::customPut):
  • bindings/js/JSHistoryCustom.cpp: (WebCore::JSHistory::customPut):
  • bindings/js/JSInspectedObjectWrapper.cpp: (WebCore::JSInspectedObjectWrapper::wrap): (WebCore::JSInspectedObjectWrapper::JSInspectedObjectWrapper):
  • bindings/js/JSInspectedObjectWrapper.h:
  • bindings/js/JSInspectorCallbackWrapper.cpp: (WebCore::JSInspectorCallbackWrapper::wrap): (WebCore::JSInspectorCallbackWrapper::JSInspectorCallbackWrapper):
  • bindings/js/JSInspectorCallbackWrapper.h:
  • bindings/js/JSLocationCustom.cpp: (WebCore::JSLocation::customPut):
  • bindings/js/JSPluginElementFunctions.cpp: (WebCore::runtimeObjectCustomPut):
  • bindings/js/JSPluginElementFunctions.h:
  • bindings/js/JSQuarantinedObjectWrapper.cpp: (WebCore::JSQuarantinedObjectWrapper::JSQuarantinedObjectWrapper): (WebCore::JSQuarantinedObjectWrapper::put):
  • bindings/js/JSQuarantinedObjectWrapper.h:
  • bindings/js/JSStorageCustom.cpp: (WebCore::JSStorage::customPut):
  • bindings/objc/WebScriptObject.mm: (-[WebScriptObject setValue:forKey:]):
  • bindings/scripts/CodeGeneratorJS.pm:
  • bridge/NP_jsobject.cpp: (_NPN_SetProperty):
  • bridge/jni/jni_jsobject.mm: (JavaJSObject::setMember):
  • bridge/objc/objc_class.mm: (KJS::Bindings::ObjcClass::fallbackObject):
  • bridge/objc/objc_runtime.h:
  • bridge/objc/objc_runtime.mm: (ObjcFallbackObjectImp::ObjcFallbackObjectImp): (ObjcFallbackObjectImp::put):
  • bridge/runtime.cpp: (KJS::Bindings::Instance::createRuntimeObject):
  • bridge/runtime_array.cpp: (RuntimeArray::put):
  • bridge/runtime_array.h:
  • bridge/runtime_object.cpp: (RuntimeObjectImp::RuntimeObjectImp): (RuntimeObjectImp::put):
  • bridge/runtime_object.h:

LayoutTests:

2008-09-01 Geoffrey Garen <[email protected]>

Reviewed by Darin Adler.

First cut at inline caching for access to vanilla JavaScript properties.


Tests for things I broke along the way.


  • fast/dom/getter-on-window-object2-expected.txt:
  • fast/js/pic: Added.
  • fast/js/pic/cached-deleted-properties-expected.txt: Added.
  • fast/js/pic/cached-deleted-properties.html: Added.
  • fast/js/pic/cached-getter-dictionary-and-proto-expected.txt: Added.
  • fast/js/pic/cached-getter-dictionary-and-proto.html: Added.
  • fast/js/pic/cached-getter-setter-expected.txt: Added.
  • fast/js/pic/cached-getter-setter.html: Added.
  • fast/js/pic/cached-prototype-setter-expected.txt: Added.
  • fast/js/pic/cached-prototype-setter.html: Added.
  • fast/js/pic/cached-single-entry-transition-expected.txt: Added.
  • fast/js/pic/cached-single-entry-transition.html: Added.
  • fast/js/pic/get-empty-string-expected.txt: Added.
  • fast/js/pic/get-empty-string.html: Added.
  • fast/js/pic/get-set-proxy-object-expected.txt: Added.
  • fast/js/pic/get-set-proxy-object.html: Added.
  • fast/js/pic/rehash-poisons-structure-expected.txt: Added.
  • fast/js/pic/rehash-poisons-structure.html: Added.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/kjs/PropertyMap.cpp

    r35027 r36016  
    2222#include "PropertyMap.h"
    2323
    24 #include "JSObject.h"
    2524#include "protect.h"
    2625#include "PropertyNameArray.h"
     
    4443#define USE_SINGLE_ENTRY 1
    4544
    46 // 2/28/2006 ggaren: command-line JS iBench says that USE_SINGLE_ENTRY is a
    47 // 3.2% performance boost.
    48 
    4945namespace KJS {
    5046
     
    8177#endif
    8278
    83 struct PropertyMapEntry {
    84     UString::Rep* key;
    85     JSValue* value;
    86     unsigned attributes;
    87     unsigned index;
    88 
    89     PropertyMapEntry(UString::Rep* k, JSValue* v, int a)
    90         : key(k)
    91         , value(v)
    92         , attributes(a)
    93         , index(0)
    94     {
    95     }
    96 };
    97 
    98 // lastIndexUsed is an ever-increasing index used to identify the order items
    99 // were inserted into the property map. It's required that getEnumerablePropertyNames
    100 // return the properties in the order they were added for compatibility with other
    101 // browsers' JavaScript implementations.
    102 struct PropertyMapHashTable {
    103     unsigned sizeMask;
    104     unsigned size;
    105     unsigned keyCount;
    106     unsigned deletedSentinelCount;
    107     unsigned lastIndexUsed;
    108     unsigned entryIndicies[1];
    109 
    110     PropertyMapEntry* entries()
    111     {
    112         // The entries vector comes after the indices vector.
    113         // The 0th item in the entries vector is not really used; it has to
    114         // have a 0 in its key to allow the hash table lookup to handle deleted
    115         // sentinels without any special-case code, but the other fields are unused.
    116         return reinterpret_cast<PropertyMapEntry*>(&entryIndicies[size]);
    117     }
    118 
    119     static size_t allocationSize(unsigned size)
    120     {
    121         // We never let a hash table get more than half full,
    122         // So the number of indices we need is the size of the hash table.
    123         // But the number of entries is half that (plus one for the deleted sentinel).
    124         return sizeof(PropertyMapHashTable)
    125             + (size - 1) * sizeof(unsigned)
    126             + (1 + size / 2) * sizeof(PropertyMapEntry);
    127     }
    128 };
    129 
    13079static const unsigned emptyEntryIndex = 0;
    13180static const unsigned deletedSentinelIndex = 1;
     
    157106}
    158107
    159 void PropertyMap::clear()
    160 {
    161     if (!m_usingTable) {
    162 #if USE_SINGLE_ENTRY
    163         if (m_singleEntryKey) {
    164             m_singleEntryKey->deref();
    165             m_singleEntryKey = 0;
    166         }
    167 #endif
    168         return;
    169     }
    170 
    171     unsigned entryCount = m_u.table->keyCount + m_u.table->deletedSentinelCount;
    172     for (unsigned i = 1; i <= entryCount; i++) {
    173         if (UString::Rep* key = m_u.table->entries()[i].key)
    174             key->deref();
    175     }
    176     for (unsigned i = 0; i < m_u.table->size; i++)
    177         m_u.table->entryIndicies[i] = emptyEntryIndex;
    178     m_u.table->keyCount = 0;
    179     m_u.table->deletedSentinelCount = 0;
    180 }
    181 
    182108JSValue* PropertyMap::get(const Identifier& propertyName, unsigned& attributes) const
    183109{
     
    388314}
    389315
    390 void PropertyMap::put(const Identifier& propertyName, JSValue* value, unsigned attributes, bool checkReadOnly)
     316void PropertyMap::put(const Identifier& propertyName, JSValue* value, unsigned attributes, bool checkReadOnly, JSObject* slotBase, PutPropertySlot& slot)
    391317{
    392318    ASSERT(!propertyName.isNull());
     
    405331            m_singleEntryAttributes = static_cast<short>(attributes);
    406332            checkConsistency();
     333            slot.setNewProperty(slotBase, KJS_INVALID_OFFSET);
    407334            return;
    408335        }
     
    414341#endif
    415342
    416     if (!m_usingTable || (m_u.table->keyCount + m_u.table->deletedSentinelCount) * 2 >= m_u.table->size)
     343    if (!m_usingTable)
    417344        expand();
    418345
     
    439366            m_u.table->entries()[entryIndex - 1].value = value;
    440367            // Attributes are intentionally not updated.
     368            slot.setExistingProperty(slotBase, offsetForTableLocation(&m_u.table->entries()[entryIndex - 1].value));
    441369            return;
    442370        } else if (entryIndex == deletedSentinelIndex) {
     
    486414    ++m_u.table->keyCount;
    487415
     416    if ((m_u.table->keyCount + m_u.table->deletedSentinelCount) * 2 >= m_u.table->size)
     417        expand();
     418
    488419    checkConsistency();
     420    slot.setNewProperty(slotBase, offsetForTableLocation(&m_u.table->entries()[entryIndex - 1].value));
    489421}
    490422
     
    696628        return +1;
    697629    return 0;
    698 }
    699 
    700 bool PropertyMap::containsGettersOrSetters() const
    701 {
    702     if (!m_usingTable) {
    703 #if USE_SINGLE_ENTRY
    704         return !!(m_singleEntryAttributes & IsGetterSetter);
    705 #else
    706         return false;
    707 #endif
    708     }
    709 
    710     unsigned entryCount = m_u.table->keyCount + m_u.table->deletedSentinelCount;
    711     for (unsigned i = 1; i <= entryCount; i++) {
    712         if (m_u.table->entries()[i].attributes & IsGetterSetter)
    713             return true;
    714     }
    715 
    716     return false;
    717630}
    718631
Note: See TracChangeset for help on using the changeset viewer.