Ignore:
Timestamp:
Jul 22, 2018, 9:54:38 AM (7 years ago)
Author:
Yusuke Suzuki
Message:

[DFG] Fold GetByVal if the indexed value is non configurable and non writable
https://bugs.webkit.org/show_bug.cgi?id=186462

Reviewed by Saam Barati.

JSTests:

  • stress/folding-get-by-val-with-read-only-dont-delete-object.js: Added.

(shouldBe):
(test1):
(test2):
(test3):
(test4):
(test5):

  • stress/folding-get-by-val-with-read-only-dont-delete-runtime-array.js: Added.

(shouldBe):
(test1):
(test2):
(test5):

  • stress/folding-get-by-val-with-read-only-dont-delete.js: Added.

(shouldBe):
(test1):
(test2):
(test3):
(test4):
(test5):

Source/JavaScriptCore:

Non-special DontDelete | ReadOnly properties mean that it won't be changed. If DFG AI can retrieve this
property, AI can fold it into a constant. This type of property can be seen when we use ES6 tagged templates.
Tagged templates' callsite includes indexed properties whose attributes are DontDelete | ReadOnly.

This patch attempts to fold such properties into constant in DFG AI. The challenge is that DFG AI runs
concurrently with the mutator thread. In this patch, we insert WTF::storeStoreFence between value setting
and attributes setting. The attributes must be set after the corresponding value is set. If the loaded
attributes (with WTF::loadLoadFence) include DontDelete | ReadOnly, it means the given value won't be
changed and we can safely use it. We arrange our existing code to use this protocol.

Since GetByVal folding requires the correct Structure & Butterfly pairs, it is only enabled in x86 architecture
since it is TSO. So, our WTF::storeStoreFence in SparseArrayValueMap is also emitted only in x86.

This patch improves SixSpeed/template_string_tag.es6.

baseline patched

template_string_tag.es6 237.0301+-4.8374 9.8779+-0.3628 definitely 23.9960x faster

  • dfg/DFGAbstractInterpreterInlines.h:

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

  • runtime/JSArray.cpp:

(JSC::JSArray::setLengthWithArrayStorage):

  • runtime/JSObject.cpp:

(JSC::JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists):
(JSC::JSObject::deletePropertyByIndex):
(JSC::JSObject::getOwnPropertyNames):
(JSC::putIndexedDescriptor):
(JSC::JSObject::defineOwnIndexedProperty):
(JSC::JSObject::attemptToInterceptPutByIndexOnHoleForPrototype):
(JSC::JSObject::putIndexedDescriptor): Deleted.

  • runtime/JSObject.h:
  • runtime/SparseArrayValueMap.cpp:

(JSC::SparseArrayValueMap::SparseArrayValueMap):
(JSC::SparseArrayValueMap::add):
(JSC::SparseArrayValueMap::putDirect):
(JSC::SparseArrayValueMap::getConcurrently):
(JSC::SparseArrayEntry::get const):
(JSC::SparseArrayEntry::getConcurrently const):
(JSC::SparseArrayEntry::put):
(JSC::SparseArrayEntry::getNonSparseMode const):
(JSC::SparseArrayValueMap::visitChildren):
(JSC::SparseArrayValueMap::~SparseArrayValueMap): Deleted.

  • runtime/SparseArrayValueMap.h:

(JSC::SparseArrayEntry::SparseArrayEntry):
(JSC::SparseArrayEntry::attributes const):
(JSC::SparseArrayEntry::forceSet):
(JSC::SparseArrayEntry::asValue):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp

    r233122 r234089  
    4242SparseArrayValueMap::SparseArrayValueMap(VM& vm)
    4343    : Base(vm, vm.sparseArrayValueMapStructure.get())
    44     , m_flags(Normal)
    45     , m_reportedCapacity(0)
    46 {
    47 }
    48 
    49 SparseArrayValueMap::~SparseArrayValueMap()
    5044{
    5145}
     
    7973    {
    8074        auto locker = holdLock(cellLock());
    81         SparseArrayEntry entry;
    82         entry.setWithoutWriteBarrier(jsUndefined());
    83        
    84         result = m_map.add(i, entry);
     75        result = m_map.add(i, SparseArrayEntry());
    8576        capacity = m_map.capacity();
    8677    }
     
    146137    }
    147138
    148     if (entry.attributes & PropertyAttribute::ReadOnly)
     139    if (entry.attributes() & PropertyAttribute::ReadOnly)
    149140        return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
    150141
    151     entry.attributes = attributes;
    152     entry.set(vm, this, value);
     142    entry.forceSet(vm, this, value, attributes);
    153143    return true;
     144}
     145
     146JSValue SparseArrayValueMap::getConcurrently(unsigned i)
     147{
     148    auto locker = holdLock(cellLock());
     149    auto iterator = m_map.find(i);
     150    if (iterator == m_map.end())
     151        return JSValue();
     152    return iterator->value.getConcurrently();
    154153}
    155154
     
    160159
    161160    if (LIKELY(!value.isGetterSetter())) {
    162         slot.setValue(thisObject, attributes, value);
     161        slot.setValue(thisObject, m_attributes, value);
    163162        return;
    164163    }
    165164
    166     slot.setGetterSlot(thisObject, attributes, jsCast<GetterSetter*>(value));
     165    slot.setGetterSlot(thisObject, m_attributes, jsCast<GetterSetter*>(value));
    167166}
    168167
    169168void SparseArrayEntry::get(PropertyDescriptor& descriptor) const
    170169{
    171     descriptor.setDescriptor(Base::get(), attributes);
     170    descriptor.setDescriptor(Base::get(), m_attributes);
     171}
     172
     173JSValue SparseArrayEntry::getConcurrently() const
     174{
     175    // These attributes and value can be updated while executing getConcurrently.
     176    // But this is OK since attributes should be never weaken once it gets DontDelete and ReadOnly.
     177    // By emitting store-store-fence and load-load-fence between value setting and attributes setting,
     178    // we can ensure that the value is what we want once the attributes get ReadOnly & DontDelete:
     179    // once attributes get this state, the value should not be changed.
     180    unsigned attributes = m_attributes;
     181    Dependency attributesDependency = Dependency::fence(attributes);
     182    if (attributes & PropertyAttribute::Accessor)
     183        return JSValue();
     184
     185    if (!(attributes & PropertyAttribute::ReadOnly))
     186        return JSValue();
     187
     188    if (!(attributes & PropertyAttribute::DontDelete))
     189        return JSValue();
     190
     191    return attributesDependency.consume(this)->Base::get();
    172192}
    173193
     
    177197    auto scope = DECLARE_THROW_SCOPE(vm);
    178198
    179     if (!(attributes & PropertyAttribute::Accessor)) {
    180         if (attributes & PropertyAttribute::ReadOnly)
     199    if (!(m_attributes & PropertyAttribute::Accessor)) {
     200        if (m_attributes & PropertyAttribute::ReadOnly)
    181201            return typeError(exec, scope, shouldThrow, ReadonlyPropertyWriteError);
    182202
     
    191211JSValue SparseArrayEntry::getNonSparseMode() const
    192212{
    193     ASSERT(!attributes);
     213    ASSERT(!m_attributes);
    194214    return Base::get();
    195215}
     
    203223    iterator end = thisMap->m_map.end();
    204224    for (iterator it = thisMap->m_map.begin(); it != end; ++it)
    205         visitor.append(it->value);
     225        visitor.append(it->value.asValue());
    206226}
    207227
Note: See TracChangeset for help on using the changeset viewer.