Changeset 249247 in webkit


Ignore:
Timestamp:
Aug 28, 2019 11:49:28 PM (5 years ago)
Author:
mark.lam@apple.com
Message:

DFG/FTL: We should prefetch structures and do a loadLoadFence before doing PrototypeChainIsSane checks.
https://bugs.webkit.org/show_bug.cgi?id=201281
<rdar://problem/54028228>

Reviewed by Yusuke Suzuki and Saam Barati.

JSTests:

  • stress/structure-storedPrototype-should-only-assert-on-the-mutator-thread.js: Added.

Source/JavaScriptCore:

This (see title above) is already the preferred idiom used in most places in our
compiler, except for 2: DFG's SpeculativeJIT::compileGetByValOnString() and FTL's
compileStringCharAt(). Consider the following:

bool prototypeChainIsSane = false;
if (globalObject->stringPrototypeChainIsSane()) {

...
m_graph.registerAndWatchStructureTransition(globalObject->stringPrototype()->structure(vm()));
m_graph.registerAndWatchStructureTransition(globalObject->objectPrototype()->structure(vm()));

prototypeChainIsSane = globalObject->stringPrototypeChainIsSane();

}

What's essential for correctness here is that the stringPrototype and objectPrototype
structures be loaded before the loads in the second stringPrototypeChainIsSane()
check. Without a loadLoadFence before the second stringPrototypeChainIsSane()
check, we can't guarantee that. Elsewhere in the compiler, the preferred idiom
for doing this right is to pre-load the structures first, do a loadLoadFence, and
then do the IsSane check just once after e.g.

Structure* arrayPrototypeStructure = globalObject->arrayPrototype()->structure(m_vm);
Structure* objectPrototypeStructure = globalObject->objectPrototype()->structure(m_vm);

if (arrayPrototypeStructure->transitionWatchpointSetIsStillValid() has loadLoadFences.

&& objectPrototypeStructure->transitionWatchpointSetIsStillValid() has loadLoadFences.
&& globalObject->arrayPrototypeChainIsSane()) {

m_graph.registerAndWatchStructureTransition(arrayPrototypeStructure);
m_graph.registerAndWatchStructureTransition(objectPrototypeStructure);
...

}

This patch changes DFG's SpeculativeJIT::compileGetByValOnString() and FTL's
compileStringCharAt() to follow the same idiom.

We also fix a bad assertion in Structure::storedPrototype() and
Structure::storedPrototypeObject(). The assertion is only correct when those
methods are called from the mutator thread. The assertion has been updated to
only check its test condition if the current thread is the mutator thread.

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileGetByValOnString):

  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileStringCharAt):

  • runtime/StructureInlines.h:

(JSC::Structure::storedPrototype const):
(JSC::Structure::storedPrototypeObject const):

Location:
trunk
Files:
1 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r249225 r249247  
     12019-08-28  Mark Lam  <mark.lam@apple.com>
     2
     3        DFG/FTL: We should prefetch structures and do a loadLoadFence before doing PrototypeChainIsSane checks.
     4        https://bugs.webkit.org/show_bug.cgi?id=201281
     5        <rdar://problem/54028228>
     6
     7        Reviewed by Yusuke Suzuki and Saam Barati.
     8
     9        * stress/structure-storedPrototype-should-only-assert-on-the-mutator-thread.js: Added.
     10
    1112019-08-28  Mark Lam  <mark.lam@apple.com>
    212
  • trunk/Source/JavaScriptCore/ChangeLog

    r249225 r249247  
     12019-08-28  Mark Lam  <mark.lam@apple.com>
     2
     3        DFG/FTL: We should prefetch structures and do a loadLoadFence before doing PrototypeChainIsSane checks.
     4        https://bugs.webkit.org/show_bug.cgi?id=201281
     5        <rdar://problem/54028228>
     6
     7        Reviewed by Yusuke Suzuki and Saam Barati.
     8
     9        This (see title above) is already the preferred idiom used in most places in our
     10        compiler, except for 2: DFG's SpeculativeJIT::compileGetByValOnString() and FTL's
     11        compileStringCharAt().  Consider the following:
     12
     13            bool prototypeChainIsSane = false;
     14            if (globalObject->stringPrototypeChainIsSane()) {
     15                ...
     16                m_graph.registerAndWatchStructureTransition(globalObject->stringPrototype()->structure(vm()));
     17                m_graph.registerAndWatchStructureTransition(globalObject->objectPrototype()->structure(vm()));
     18
     19                prototypeChainIsSane = globalObject->stringPrototypeChainIsSane();
     20            }
     21
     22        What's essential for correctness here is that the stringPrototype and objectPrototype
     23        structures be loaded before the loads in the second stringPrototypeChainIsSane()
     24        check.  Without a loadLoadFence before the second stringPrototypeChainIsSane()
     25        check, we can't guarantee that.  Elsewhere in the compiler, the preferred idiom
     26        for doing this right is to pre-load the structures first, do a loadLoadFence, and
     27        then do the IsSane check just once after e.g.
     28
     29            Structure* arrayPrototypeStructure = globalObject->arrayPrototype()->structure(m_vm);
     30            Structure* objectPrototypeStructure = globalObject->objectPrototype()->structure(m_vm);
     31
     32            if (arrayPrototypeStructure->transitionWatchpointSetIsStillValid() // has loadLoadFences.
     33                && objectPrototypeStructure->transitionWatchpointSetIsStillValid() // has loadLoadFences.
     34                && globalObject->arrayPrototypeChainIsSane()) {
     35
     36                m_graph.registerAndWatchStructureTransition(arrayPrototypeStructure);
     37                m_graph.registerAndWatchStructureTransition(objectPrototypeStructure);
     38                ...
     39            }
     40
     41        This patch changes DFG's SpeculativeJIT::compileGetByValOnString() and FTL's
     42        compileStringCharAt() to follow the same idiom.
     43
     44        We also fix a bad assertion in Structure::storedPrototype() and
     45        Structure::storedPrototypeObject().  The assertion is only correct when those
     46        methods are called from the mutator thread.  The assertion has been updated to
     47        only check its test condition if the current thread is the mutator thread.
     48
     49        * dfg/DFGSpeculativeJIT.cpp:
     50        (JSC::DFG::SpeculativeJIT::compileGetByValOnString):
     51        * ftl/FTLLowerDFGToB3.cpp:
     52        (JSC::FTL::DFG::LowerDFGToB3::compileStringCharAt):
     53        * runtime/StructureInlines.h:
     54        (JSC::Structure::storedPrototype const):
     55        (JSC::Structure::storedPrototypeObject const):
     56
    1572019-08-28  Mark Lam  <mark.lam@apple.com>
    258
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp

    r249184 r249247  
    22322232
    22332233        JSGlobalObject* globalObject = m_jit.globalObjectFor(node->origin.semantic);
    2234         bool prototypeChainIsSane = false;
     2234        Structure* stringPrototypeStructure = globalObject->stringPrototype()->structure(vm);
     2235        Structure* objectPrototypeStructure = globalObject->objectPrototype()->structure(vm);
     2236        WTF::loadLoadFence();
     2237
    22352238        if (globalObject->stringPrototypeChainIsSane()) {
    22362239            // FIXME: This could be captured using a Speculation mode that means "out-of-bounds
     
    22402243            // indexed properties either.
    22412244            // https://bugs.webkit.org/show_bug.cgi?id=144668
    2242             m_jit.graph().registerAndWatchStructureTransition(globalObject->stringPrototype()->structure(vm));
    2243             m_jit.graph().registerAndWatchStructureTransition(globalObject->objectPrototype()->structure(vm));
    2244             prototypeChainIsSane = globalObject->stringPrototypeChainIsSane();
    2245         }
    2246         if (prototypeChainIsSane) {
     2245            m_jit.graph().registerAndWatchStructureTransition(stringPrototypeStructure);
     2246            m_jit.graph().registerAndWatchStructureTransition(objectPrototypeStructure);
     2247
    22472248#if USE(JSVALUE64)
    22482249            addSlowPathGenerator(makeUnique<SaneStringGetByValSlowPathGenerator>(
  • trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp

    r249184 r249247  
    69796979        } else {
    69806980            JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
    6981            
    6982             bool prototypeChainIsSane = false;
     6981            Structure* stringPrototypeStructure = globalObject->stringPrototype()->structure(vm());
     6982            Structure* objectPrototypeStructure = globalObject->objectPrototype()->structure(vm());
     6983            WTF::loadLoadFence();
     6984
    69836985            if (globalObject->stringPrototypeChainIsSane()) {
    69846986                // FIXME: This could be captured using a Speculation mode that means
     
    69876989                // https://bugs.webkit.org/show_bug.cgi?id=144668
    69886990               
    6989                 m_graph.registerAndWatchStructureTransition(globalObject->stringPrototype()->structure(vm()));
    6990                 m_graph.registerAndWatchStructureTransition(globalObject->objectPrototype()->structure(vm()));
    6991 
    6992                 prototypeChainIsSane = globalObject->stringPrototypeChainIsSane();
    6993             }
    6994             if (prototypeChainIsSane) {
     6991                m_graph.registerAndWatchStructureTransition(stringPrototypeStructure);
     6992                m_graph.registerAndWatchStructureTransition(objectPrototypeStructure);
     6993
    69956994                LBasicBlock negativeIndex = m_out.newBlock();
    69966995                   
  • trunk/Source/JavaScriptCore/runtime/StructureInlines.h

    r249175 r249247  
    109109ALWAYS_INLINE JSValue Structure::storedPrototype(const JSObject* object) const
    110110{
    111     ASSERT(object->structure() == this);
     111    ASSERT(!isMainThread() || object->structure() == this);
    112112    if (hasMonoProto())
    113113        return storedPrototype();
     
    117117ALWAYS_INLINE JSObject* Structure::storedPrototypeObject(const JSObject* object) const
    118118{
    119     ASSERT(object->structure() == this);
     119    ASSERT(!isMainThread() || object->structure() == this);
    120120    if (hasMonoProto())
    121121        return storedPrototypeObject();
Note: See TracChangeset for help on using the changeset viewer.