Title: [222891] trunk
Revision
222891
Author
[email protected]
Date
2017-10-04 19:47:59 -0700 (Wed, 04 Oct 2017)

Log Message

Make pertinent AccessCases watch the poly proto watchpoint
https://bugs.webkit.org/show_bug.cgi?id=177765

Reviewed by Keith Miller.

JSTests:

* microbenchmarks/poly-proto-and-non-poly-proto-same-ic.js: Added.
(assert):
(foo.C):
(foo):
(validate):
* stress/poly-proto-clear-stub.js: Added.
(assert):
(foo.C):
(foo):

Source/_javascript_Core:

This patch makes it so that stubs that encounter a structure with a
valid poly proto watchpoint will watch the poly proto watchpoint. This
ensures that if the watchpoint is fired, the stub will be cleared
and have a chance to regenerate. In an ideal world, this will lead
to the stub generating better code since it may never encounter the
non-poly proto structure again.

This patch also fixes a bug in the original poly proto code where
I accidentally had a condition inverted. The bad code caused a
stub that continually cached two structures which are structurally
equivalent but with different prototype objects to always clear itself.
The code should have been written differently. It should have only
cleared if the poly proto watchpoint *was not* fired. The code
accidentally cleared only if stub *was* fired.

* bytecode/AccessCase.cpp:
(JSC::AccessCase::commit):
* bytecode/PolymorphicAccess.cpp:
(JSC::PolymorphicAccess::addCases):
(WTF::printInternal):
* bytecode/PolymorphicAccess.h:
(JSC::AccessGenerationResult::shouldResetStubAndFireWatchpoints const):
(JSC::AccessGenerationResult::addWatchpointToFire):
(JSC::AccessGenerationResult::fireWatchpoints):
(JSC::AccessGenerationResult::shouldResetStub const): Deleted.
* bytecode/StructureStubInfo.cpp:
(JSC::StructureStubInfo::addAccessCase):
(JSC::StructureStubInfo::reset):
* bytecode/Watchpoint.h:
(JSC::InlineWatchpointSet::inflate):
* jit/Repatch.cpp:
(JSC::fireWatchpointsAndClearStubIfNeeded):
(JSC::tryCacheGetByID):
(JSC::repatchGetByID):
(JSC::tryCachePutByID):
(JSC::repatchPutByID):
(JSC::tryCacheIn):
(JSC::repatchIn):
(JSC::tryRepatchIn): Deleted.

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (222890 => 222891)


--- trunk/JSTests/ChangeLog	2017-10-05 00:02:05 UTC (rev 222890)
+++ trunk/JSTests/ChangeLog	2017-10-05 02:47:59 UTC (rev 222891)
@@ -1,3 +1,20 @@
+2017-10-04  Saam Barati  <[email protected]>
+
+        Make pertinent AccessCases watch the poly proto watchpoint
+        https://bugs.webkit.org/show_bug.cgi?id=177765
+
+        Reviewed by Keith Miller.
+
+        * microbenchmarks/poly-proto-and-non-poly-proto-same-ic.js: Added.
+        (assert):
+        (foo.C):
+        (foo):
+        (validate):
+        * stress/poly-proto-clear-stub.js: Added.
+        (assert):
+        (foo.C):
+        (foo):
+
 2017-10-04  Ryan Haddad  <[email protected]>
 
         Remove failure expectation for async-func-decl-dstr-obj-id-put-unresolvable-no-strict.js.

Added: trunk/JSTests/microbenchmarks/poly-proto-and-non-poly-proto-same-ic.js (0 => 222891)


--- trunk/JSTests/microbenchmarks/poly-proto-and-non-poly-proto-same-ic.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/poly-proto-and-non-poly-proto-same-ic.js	2017-10-05 02:47:59 UTC (rev 222891)
@@ -0,0 +1,111 @@
+function assert(b, m) {
+    if (!b)
+        throw new Error("Bad:" + m);
+}
+
+function foo() {
+    class C {
+        constructor() {
+            this.p0 = 0
+            this.p1 = 1
+            this.p2 = 2
+            this.p3 = 3
+            this.p4 = 4
+            this.p5 = 5
+            this.p6 = 6
+            this.p7 = 7
+            this.p8 = 8
+            this.p9 = 9
+            this.p10 = 10
+            this.p11 = 11
+            this.p12 = 12
+            this.p13 = 13
+            this.p14 = 14
+            this.p15 = 15
+            this.p16 = 16
+            this.p17 = 17
+            this.p18 = 18
+            this.p19 = 19
+            this.p20 = 20
+            this.p21 = 21
+            this.p22 = 22
+            this.p23 = 23
+            this.p24 = 24
+            this.p25 = 25
+            this.p26 = 26
+            this.p27 = 27
+            this.p28 = 28
+            this.p29 = 29
+            this.p30 = 30
+            this.p31 = 31
+            this.p32 = 32
+            this.p33 = 33
+            this.p34 = 34
+            this.p35 = 35
+            this.p36 = 36
+            this.p37 = 37
+            this.p38 = 38
+            this.p39 = 39
+        }
+    };
+
+    return new C;
+}
+
+let items = [];
+for (let i = 0; i < 15; ++i) {
+    items.push(foo());
+}
+
+function validate() {
+    for (let i = 0; i < items.length; ++i) {
+        item = items[i];
+        assert(item.p0 === 0)
+        assert(item.p1 === 1)
+        assert(item.p2 === 2)
+        assert(item.p3 === 3)
+        assert(item.p4 === 4)
+        assert(item.p5 === 5)
+        assert(item.p6 === 6)
+        assert(item.p7 === 7)
+        assert(item.p8 === 8)
+        assert(item.p9 === 9)
+        assert(item.p10 === 10)
+        assert(item.p11 === 11)
+        assert(item.p12 === 12)
+        assert(item.p13 === 13)
+        assert(item.p14 === 14)
+        assert(item.p15 === 15)
+        assert(item.p16 === 16)
+        assert(item.p17 === 17)
+        assert(item.p18 === 18)
+        assert(item.p19 === 19)
+        assert(item.p20 === 20)
+        assert(item.p21 === 21)
+        assert(item.p22 === 22)
+        assert(item.p23 === 23)
+        assert(item.p24 === 24)
+        assert(item.p25 === 25)
+        assert(item.p26 === 26)
+        assert(item.p27 === 27)
+        assert(item.p28 === 28)
+        assert(item.p29 === 29)
+        assert(item.p30 === 30)
+        assert(item.p31 === 31)
+        assert(item.p32 === 32)
+        assert(item.p33 === 33)
+        assert(item.p34 === 34)
+        assert(item.p35 === 35)
+        assert(item.p36 === 36)
+        assert(item.p37 === 37)
+        assert(item.p38 === 38)
+        assert(item.p39 === 39)
+    }
+}
+
+let start = Date.now();
+for (let i = 0; i < 10000; ++i)
+    validate();
+
+if (false)
+    print(Date.now() - start);

Added: trunk/JSTests/stress/poly-proto-clear-stub.js (0 => 222891)


--- trunk/JSTests/stress/poly-proto-clear-stub.js	                        (rev 0)
+++ trunk/JSTests/stress/poly-proto-clear-stub.js	2017-10-05 02:47:59 UTC (rev 222891)
@@ -0,0 +1,101 @@
+function assert(b, m) {
+    if (!b)
+        throw new Error("Bad:" + m);
+}
+
+function foo() {
+    class C {
+        constructor() {
+            this.p0 = 0
+            this.p1 = 1
+            this.p2 = 2
+            this.p3 = 3
+            this.p4 = 4
+            this.p5 = 5
+            this.p6 = 6
+            this.p7 = 7
+            this.p8 = 8
+            this.p9 = 9
+            this.p10 = 10
+            this.p11 = 11
+            this.p12 = 12
+            this.p13 = 13
+            this.p14 = 14
+            this.p15 = 15
+            this.p16 = 16
+            this.p17 = 17
+            this.p18 = 18
+            this.p19 = 19
+            this.p20 = 20
+            this.p21 = 21
+            this.p22 = 22
+            this.p23 = 23
+            this.p24 = 24
+            this.p25 = 25
+            this.p26 = 26
+            this.p27 = 27
+            this.p28 = 28
+            this.p29 = 29
+            this.p30 = 30
+            this.p31 = 31
+            this.p32 = 32
+            this.p33 = 33
+            this.p34 = 34
+            this.p35 = 35
+            this.p36 = 36
+            this.p37 = 37
+            this.p38 = 38
+            this.p39 = 39
+        }
+    };
+
+    let item = new C;
+    for (let i = 0; i < 20; ++i) {
+        assert(item.p0 === 0)
+        assert(item.p1 === 1)
+        assert(item.p2 === 2)
+        assert(item.p3 === 3)
+        assert(item.p4 === 4)
+        assert(item.p5 === 5)
+        assert(item.p6 === 6)
+        assert(item.p7 === 7)
+        assert(item.p8 === 8)
+        assert(item.p9 === 9)
+        assert(item.p10 === 10)
+        assert(item.p11 === 11)
+        assert(item.p12 === 12)
+        assert(item.p13 === 13)
+        assert(item.p14 === 14)
+        assert(item.p15 === 15)
+        assert(item.p16 === 16)
+        assert(item.p17 === 17)
+        assert(item.p18 === 18)
+        assert(item.p19 === 19)
+        assert(item.p20 === 20)
+        assert(item.p21 === 21)
+        assert(item.p22 === 22)
+        assert(item.p23 === 23)
+        assert(item.p24 === 24)
+        assert(item.p25 === 25)
+        assert(item.p26 === 26)
+        assert(item.p27 === 27)
+        assert(item.p28 === 28)
+        assert(item.p29 === 29)
+        assert(item.p30 === 30)
+        assert(item.p31 === 31)
+        assert(item.p32 === 32)
+        assert(item.p33 === 33)
+        assert(item.p34 === 34)
+        assert(item.p35 === 35)
+        assert(item.p36 === 36)
+        assert(item.p37 === 37)
+        assert(item.p38 === 38)
+        assert(item.p39 === 39)
+    }
+}
+
+let start = Date.now();
+for (let i = 0; i < 1000; ++i)
+    foo();
+if (false)
+    print(Date.now() - start);

Modified: trunk/Source/_javascript_Core/ChangeLog (222890 => 222891)


--- trunk/Source/_javascript_Core/ChangeLog	2017-10-05 00:02:05 UTC (rev 222890)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-10-05 02:47:59 UTC (rev 222891)
@@ -1,3 +1,50 @@
+2017-10-04  Saam Barati  <[email protected]>
+
+        Make pertinent AccessCases watch the poly proto watchpoint
+        https://bugs.webkit.org/show_bug.cgi?id=177765
+
+        Reviewed by Keith Miller.
+
+        This patch makes it so that stubs that encounter a structure with a
+        valid poly proto watchpoint will watch the poly proto watchpoint. This
+        ensures that if the watchpoint is fired, the stub will be cleared
+        and have a chance to regenerate. In an ideal world, this will lead
+        to the stub generating better code since it may never encounter the
+        non-poly proto structure again.
+        
+        This patch also fixes a bug in the original poly proto code where
+        I accidentally had a condition inverted. The bad code caused a
+        stub that continually cached two structures which are structurally
+        equivalent but with different prototype objects to always clear itself.
+        The code should have been written differently. It should have only
+        cleared if the poly proto watchpoint *was not* fired. The code
+        accidentally cleared only if stub *was* fired.
+
+        * bytecode/AccessCase.cpp:
+        (JSC::AccessCase::commit):
+        * bytecode/PolymorphicAccess.cpp:
+        (JSC::PolymorphicAccess::addCases):
+        (WTF::printInternal):
+        * bytecode/PolymorphicAccess.h:
+        (JSC::AccessGenerationResult::shouldResetStubAndFireWatchpoints const):
+        (JSC::AccessGenerationResult::addWatchpointToFire):
+        (JSC::AccessGenerationResult::fireWatchpoints):
+        (JSC::AccessGenerationResult::shouldResetStub const): Deleted.
+        * bytecode/StructureStubInfo.cpp:
+        (JSC::StructureStubInfo::addAccessCase):
+        (JSC::StructureStubInfo::reset):
+        * bytecode/Watchpoint.h:
+        (JSC::InlineWatchpointSet::inflate):
+        * jit/Repatch.cpp:
+        (JSC::fireWatchpointsAndClearStubIfNeeded):
+        (JSC::tryCacheGetByID):
+        (JSC::repatchGetByID):
+        (JSC::tryCachePutByID):
+        (JSC::repatchPutByID):
+        (JSC::tryCacheIn):
+        (JSC::repatchIn):
+        (JSC::tryRepatchIn): Deleted.
+
 2017-10-04  Matt Baker  <[email protected]>
 
         Web Inspector: Improve CanvasManager recording events

Modified: trunk/Source/_javascript_Core/bytecode/AccessCase.cpp (222890 => 222891)


--- trunk/Source/_javascript_Core/bytecode/AccessCase.cpp	2017-10-05 00:02:05 UTC (rev 222890)
+++ trunk/Source/_javascript_Core/bytecode/AccessCase.cpp	2017-10-05 02:47:59 UTC (rev 222891)
@@ -136,8 +136,9 @@
     RELEASE_ASSERT(m_state == Primordial || m_state == Committed);
 
     Vector<WatchpointSet*, 2> result;
+    Structure* structure = this->structure();
 
-    if ((structure() && structure()->needImpurePropertyWatchpoint())
+    if ((structure && structure->needImpurePropertyWatchpoint())
         || m_conditionSet.needImpurePropertyWatchpoint()
         || (m_polyProtoAccessChain && m_polyProtoAccessChain->needImpurePropertyWatchpoint()))
         result.append(vm.ensureWatchpointSetForImpureProperty(ident));
@@ -145,6 +146,14 @@
     if (additionalSet())
         result.append(additionalSet());
 
+    if (structure
+        && structure->hasRareData()
+        && structure->rareData()->hasSharedPolyProtoWatchpoint()
+        && structure->rareData()->sharedPolyProtoWatchpoint()->isStillValid()) {
+        WatchpointSet* set = structure->rareData()->sharedPolyProtoWatchpoint()->inflate();
+        result.append(set);
+    }
+
     m_state = Committed;
 
     return result;

Modified: trunk/Source/_javascript_Core/bytecode/PolymorphicAccess.cpp (222890 => 222891)


--- trunk/Source/_javascript_Core/bytecode/PolymorphicAccess.cpp	2017-10-05 00:02:05 UTC (rev 222890)
+++ trunk/Source/_javascript_Core/bytecode/PolymorphicAccess.cpp	2017-10-05 02:47:59 UTC (rev 222891)
@@ -259,19 +259,18 @@
         return AccessGenerationResult::MadeNoChanges;
 
     bool shouldReset = false;
+    AccessGenerationResult resetResult(AccessGenerationResult::ResetStubAndFireWatchpoints);
     auto considerPolyProtoReset = [&] (Structure* a, Structure* b) {
         if (Structure::shouldConvertToPolyProto(a, b)) {
             // For now, we only reset if this is our first time invalidating this watchpoint.
-            // FIXME: We should probably have all that can watch for the poly proto
-            // watchpoint do it. This will allow stubs to clear out their non-poly-proto
-            // IC cases. This is likely to be faster if you build up a bunch of ICs, then
-            // trigger poly proto, then only access poly proto objects. If we do this, we'll
-            // need this code below to delay firing the watchpoint since it might cause
-            // us to deallocate ourselves:
-            // https://bugs.webkit.org/show_bug.cgi?id=177765
+            // The reason we don't immediately fire this watchpoint is that we may be already
+            // watching the poly proto watchpoint, which if fired, would destroy us. We let
+            // the person handling the result to do a delayed fire.
             ASSERT(a->rareData()->sharedPolyProtoWatchpoint().get() == b->rareData()->sharedPolyProtoWatchpoint().get());
-            shouldReset |= a->rareData()->sharedPolyProtoWatchpoint()->hasBeenInvalidated(); 
-            a->rareData()->sharedPolyProtoWatchpoint()->invalidate(vm, StringFireDetail("Detected poly proto optimization opportunity."));
+            if (a->rareData()->sharedPolyProtoWatchpoint()->isStillValid()) {
+                shouldReset = true;
+                resetResult.addWatchpointToFire(*a->rareData()->sharedPolyProtoWatchpoint(), StringFireDetail("Detected poly proto optimization opportunity."));
+            }
         }
     };
 
@@ -291,7 +290,7 @@
     }
 
     if (shouldReset)
-        return AccessGenerationResult::ResetStub;
+        return resetResult;
 
     // Now add things to the new list. Note that at this point, we will still have old cases that
     // may be replaced by the new ones. That's fine. We will sort that out when we regenerate.
@@ -625,8 +624,8 @@
     case AccessGenerationResult::GeneratedFinalCode:
         out.print("GeneratedFinalCode");
         return;
-    case AccessGenerationResult::ResetStub:
-        out.print("ResetStub");
+    case AccessGenerationResult::ResetStubAndFireWatchpoints:
+        out.print("ResetStubAndFireWatchpoints");
         return;
     }
     

Modified: trunk/Source/_javascript_Core/bytecode/PolymorphicAccess.h (222890 => 222891)


--- trunk/Source/_javascript_Core/bytecode/PolymorphicAccess.h	2017-10-05 00:02:05 UTC (rev 222890)
+++ trunk/Source/_javascript_Core/bytecode/PolymorphicAccess.h	2017-10-05 02:47:59 UTC (rev 222891)
@@ -53,12 +53,13 @@
         Buffered,
         GeneratedNewCode,
         GeneratedFinalCode, // Generated so much code that we never want to generate code again.
-        ResetStub // We found out some data that makes us want to start over fresh with this stub. Currently, this happens when we detect poly proto.
+        ResetStubAndFireWatchpoints // We found out some data that makes us want to start over fresh with this stub. Currently, this happens when we detect poly proto.
     };
     
-    AccessGenerationResult()
-    {
-    }
+
+    AccessGenerationResult() = default;
+    AccessGenerationResult(AccessGenerationResult&&) = default;
+    AccessGenerationResult& operator=(AccessGenerationResult&&) = default;
     
     AccessGenerationResult(Kind kind)
         : m_kind(kind)
@@ -99,7 +100,7 @@
     bool buffered() const { return m_kind == Buffered; }
     bool generatedNewCode() const { return m_kind == GeneratedNewCode; }
     bool generatedFinalCode() const { return m_kind == GeneratedFinalCode; }
-    bool shouldResetStub() const { return m_kind == ResetStub; }
+    bool shouldResetStubAndFireWatchpoints() const { return m_kind == ResetStubAndFireWatchpoints; }
     
     // If we gave up on this attempt to generate code, or if we generated the "final" code, then we
     // should give up after this.
@@ -108,10 +109,22 @@
     bool generatedSomeCode() const { return generatedNewCode() || generatedFinalCode(); }
     
     void dump(PrintStream&) const;
+
+    void addWatchpointToFire(InlineWatchpointSet& set, StringFireDetail detail)
+    {
+        m_watchpointsToFire.append(std::pair<InlineWatchpointSet&, StringFireDetail>(set, detail));
+    }
+    void fireWatchpoints(VM& vm)
+    {
+        ASSERT(m_kind == ResetStubAndFireWatchpoints);
+        for (auto& pair : m_watchpointsToFire)
+            pair.first.invalidate(vm, pair.second);
+    }
     
 private:
     Kind m_kind;
     MacroAssemblerCodePtr m_code;
+    Vector<std::pair<InlineWatchpointSet&, StringFireDetail>> m_watchpointsToFire;
 };
 
 class PolymorphicAccess {

Modified: trunk/Source/_javascript_Core/bytecode/StructureStubInfo.cpp (222890 => 222891)


--- trunk/Source/_javascript_Core/bytecode/StructureStubInfo.cpp	2017-10-05 00:02:05 UTC (rev 222890)
+++ trunk/Source/_javascript_Core/bytecode/StructureStubInfo.cpp	2017-10-05 02:47:59 UTC (rev 222891)
@@ -131,10 +131,8 @@
         if (StructureStubInfoInternal::verbose)
             dataLog("Had stub, result: ", result, "\n");
 
-        if (result.shouldResetStub()) {
-            reset(codeBlock);
+        if (result.shouldResetStubAndFireWatchpoints())
             return result;
-        }
 
         if (!result.buffered()) {
             bufferedStructures.clear();
@@ -157,10 +155,8 @@
         if (StructureStubInfoInternal::verbose)
             dataLog("Created stub, result: ", result, "\n");
 
-        if (result.shouldResetStub()) {
-            reset(codeBlock);
+        if (result.shouldResetStubAndFireWatchpoints())
             return result;
-        }
 
         if (!result.buffered()) {
             bufferedStructures.clear();
@@ -212,10 +208,10 @@
 void StructureStubInfo::reset(CodeBlock* codeBlock)
 {
     bufferedStructures.clear();
-    
+
     if (cacheType == CacheType::Unset)
         return;
-    
+
     if (Options::verboseOSR()) {
         // This can be called from GC destructor calls, so we don't try to do a full dump
         // of the CodeBlock.

Modified: trunk/Source/_javascript_Core/bytecode/Watchpoint.h (222890 => 222891)


--- trunk/Source/_javascript_Core/bytecode/Watchpoint.h	2017-10-05 00:02:05 UTC (rev 222890)
+++ trunk/Source/_javascript_Core/bytecode/Watchpoint.h	2017-10-05 02:47:59 UTC (rev 222891)
@@ -378,6 +378,17 @@
             return fat()->isBeingWatched();
         return false;
     }
+
+    // We expose this because sometimes a client knows its about to start
+    // watching this InlineWatchpointSet, hence it'll become inflated regardless.
+    // Such clients may find it useful to have a WatchpointSet* pointer, for example,
+    // if they collect a Vector of WatchpointSet*.
+    WatchpointSet* inflate()
+    {
+        if (LIKELY(isFat()))
+            return fat();
+        return inflateSlow();
+    }
     
 private:
     static const uintptr_t IsThinFlag        = 1;
@@ -418,13 +429,6 @@
         return fat(m_data);
     }
     
-    WatchpointSet* inflate()
-    {
-        if (LIKELY(isFat()))
-            return fat();
-        return inflateSlow();
-    }
-    
     JS_EXPORT_PRIVATE WatchpointSet* inflateSlow();
     JS_EXPORT_PRIVATE void freeFat();
     

Modified: trunk/Source/_javascript_Core/jit/Repatch.cpp (222890 => 222891)


--- trunk/Source/_javascript_Core/jit/Repatch.cpp	2017-10-05 00:02:05 UTC (rev 222890)
+++ trunk/Source/_javascript_Core/jit/Repatch.cpp	2017-10-05 02:47:59 UTC (rev 222891)
@@ -140,6 +140,14 @@
 #endif
 }
 
+ALWAYS_INLINE static void fireWatchpointsAndClearStubIfNeeded(VM& vm, StructureStubInfo& stubInfo, CodeBlock* codeBlock, AccessGenerationResult& result)
+{
+    if (result.shouldResetStubAndFireWatchpoints()) {
+        result.fireWatchpoints(vm);
+        stubInfo.reset(codeBlock);
+    }
+}
+
 inline FunctionPtr appropriateOptimizingGetByIdFunction(GetByIDKind kind)
 {
     if (kind == GetByIDKind::Normal)
@@ -158,198 +166,206 @@
     return operationTryGetById;
 }
 
-static InlineCacheAction tryCacheGetByID(const GCSafeConcurrentJSLocker& locker, ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo, GetByIDKind kind)
+static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo, GetByIDKind kind)
 {
-    if (forceICFailure(exec))
-        return GiveUpOnCache;
-    
-    // FIXME: Cache property access for immediates.
-    if (!baseValue.isCell())
-        return GiveUpOnCache;
-
-    CodeBlock* codeBlock = exec->codeBlock();
     VM& vm = exec->vm();
+    AccessGenerationResult result;
 
-    std::unique_ptr<AccessCase> newCase;
+    {
+        GCSafeConcurrentJSLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
 
-    if (propertyName == vm.propertyNames->length) {
-        if (isJSArray(baseValue)) {
-            if (stubInfo.cacheType == CacheType::Unset
-                && slot.slotBase() == baseValue
-                && InlineAccess::isCacheableArrayLength(stubInfo, jsCast<JSArray*>(baseValue))) {
+        if (forceICFailure(exec))
+            return GiveUpOnCache;
+        
+        // FIXME: Cache property access for immediates.
+        if (!baseValue.isCell())
+            return GiveUpOnCache;
 
-                bool generatedCodeInline = InlineAccess::generateArrayLength(stubInfo, jsCast<JSArray*>(baseValue));
-                if (generatedCodeInline) {
-                    ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation(), appropriateOptimizingGetByIdFunction(kind));
-                    stubInfo.initArrayLength();
-                    return RetryCacheLater;
+        CodeBlock* codeBlock = exec->codeBlock();
+
+        std::unique_ptr<AccessCase> newCase;
+
+        if (propertyName == vm.propertyNames->length) {
+            if (isJSArray(baseValue)) {
+                if (stubInfo.cacheType == CacheType::Unset
+                    && slot.slotBase() == baseValue
+                    && InlineAccess::isCacheableArrayLength(stubInfo, jsCast<JSArray*>(baseValue))) {
+
+                    bool generatedCodeInline = InlineAccess::generateArrayLength(stubInfo, jsCast<JSArray*>(baseValue));
+                    if (generatedCodeInline) {
+                        ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation(), appropriateOptimizingGetByIdFunction(kind));
+                        stubInfo.initArrayLength();
+                        return RetryCacheLater;
+                    }
                 }
+
+                newCase = AccessCase::create(vm, codeBlock, AccessCase::ArrayLength);
+            } else if (isJSString(baseValue))
+                newCase = AccessCase::create(vm, codeBlock, AccessCase::StringLength);
+            else if (DirectArguments* arguments = jsDynamicCast<DirectArguments*>(vm, baseValue)) {
+                // If there were overrides, then we can handle this as a normal property load! Guarding
+                // this with such a check enables us to add an IC case for that load if needed.
+                if (!arguments->overrodeThings())
+                    newCase = AccessCase::create(vm, codeBlock, AccessCase::DirectArgumentsLength);
+            } else if (ScopedArguments* arguments = jsDynamicCast<ScopedArguments*>(vm, baseValue)) {
+                // Ditto.
+                if (!arguments->overrodeThings())
+                    newCase = AccessCase::create(vm, codeBlock, AccessCase::ScopedArgumentsLength);
             }
+        }
 
-            newCase = AccessCase::create(vm, codeBlock, AccessCase::ArrayLength);
-        } else if (isJSString(baseValue))
-            newCase = AccessCase::create(vm, codeBlock, AccessCase::StringLength);
-        else if (DirectArguments* arguments = jsDynamicCast<DirectArguments*>(vm, baseValue)) {
-            // If there were overrides, then we can handle this as a normal property load! Guarding
-            // this with such a check enables us to add an IC case for that load if needed.
-            if (!arguments->overrodeThings())
-                newCase = AccessCase::create(vm, codeBlock, AccessCase::DirectArgumentsLength);
-        } else if (ScopedArguments* arguments = jsDynamicCast<ScopedArguments*>(vm, baseValue)) {
-            // Ditto.
-            if (!arguments->overrodeThings())
-                newCase = AccessCase::create(vm, codeBlock, AccessCase::ScopedArgumentsLength);
+        if (!propertyName.isSymbol() && isJSModuleNamespaceObject(baseValue) && !slot.isUnset()) {
+            if (auto moduleNamespaceSlot = slot.moduleNamespaceSlot())
+                newCase = ModuleNamespaceAccessCase::create(vm, codeBlock, jsCast<JSModuleNamespaceObject*>(baseValue), moduleNamespaceSlot->environment, ScopeOffset(moduleNamespaceSlot->scopeOffset));
         }
-    }
+        
+        if (!newCase) {
+            if (!slot.isCacheable() && !slot.isUnset())
+                return GiveUpOnCache;
 
-    if (!propertyName.isSymbol() && isJSModuleNamespaceObject(baseValue) && !slot.isUnset()) {
-        if (auto moduleNamespaceSlot = slot.moduleNamespaceSlot())
-            newCase = ModuleNamespaceAccessCase::create(vm, codeBlock, jsCast<JSModuleNamespaceObject*>(baseValue), moduleNamespaceSlot->environment, ScopeOffset(moduleNamespaceSlot->scopeOffset));
-    }
-    
-    if (!newCase) {
-        if (!slot.isCacheable() && !slot.isUnset())
-            return GiveUpOnCache;
+            ObjectPropertyConditionSet conditionSet;
+            JSCell* baseCell = baseValue.asCell();
+            Structure* structure = baseCell->structure(vm);
 
-        ObjectPropertyConditionSet conditionSet;
-        JSCell* baseCell = baseValue.asCell();
-        Structure* structure = baseCell->structure(vm);
+            bool loadTargetFromProxy = false;
+            if (baseCell->type() == PureForwardingProxyType) {
+                baseValue = jsCast<JSProxy*>(baseCell)->target();
+                baseCell = baseValue.asCell();
+                structure = baseCell->structure(vm);
+                loadTargetFromProxy = true;
+            }
 
-        bool loadTargetFromProxy = false;
-        if (baseCell->type() == PureForwardingProxyType) {
-            baseValue = jsCast<JSProxy*>(baseCell)->target();
-            baseCell = baseValue.asCell();
-            structure = baseCell->structure(vm);
-            loadTargetFromProxy = true;
-        }
+            InlineCacheAction action = "" baseCell);
+            if (action != AttemptToCache)
+                return action;
 
-        InlineCacheAction action = "" baseCell);
-        if (action != AttemptToCache)
-            return action;
+            // Optimize self access.
+            if (stubInfo.cacheType == CacheType::Unset
+                && slot.isCacheableValue()
+                && slot.slotBase() == baseValue
+                && !slot.watchpointSet()
+                && !structure->needImpurePropertyWatchpoint()
+                && !loadTargetFromProxy) {
 
-        // Optimize self access.
-        if (stubInfo.cacheType == CacheType::Unset
-            && slot.isCacheableValue()
-            && slot.slotBase() == baseValue
-            && !slot.watchpointSet()
-            && !structure->needImpurePropertyWatchpoint()
-            && !loadTargetFromProxy) {
-
-            bool generatedCodeInline = InlineAccess::generateSelfPropertyAccess(stubInfo, structure, slot.cachedOffset());
-            if (generatedCodeInline) {
-                LOG_IC((ICEvent::GetByIdSelfPatch, structure->classInfo(), propertyName));
-                structure->startWatchingPropertyForReplacements(vm, slot.cachedOffset());
-                ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation(), appropriateOptimizingGetByIdFunction(kind));
-                stubInfo.initGetByIdSelf(codeBlock, structure, slot.cachedOffset());
-                return RetryCacheLater;
+                bool generatedCodeInline = InlineAccess::generateSelfPropertyAccess(stubInfo, structure, slot.cachedOffset());
+                if (generatedCodeInline) {
+                    LOG_IC((ICEvent::GetByIdSelfPatch, structure->classInfo(), propertyName));
+                    structure->startWatchingPropertyForReplacements(vm, slot.cachedOffset());
+                    ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation(), appropriateOptimizingGetByIdFunction(kind));
+                    stubInfo.initGetByIdSelf(codeBlock, structure, slot.cachedOffset());
+                    return RetryCacheLater;
+                }
             }
-        }
 
-        std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
+            std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
 
-        PropertyOffset offset = slot.isUnset() ? invalidOffset : slot.cachedOffset();
+            PropertyOffset offset = slot.isUnset() ? invalidOffset : slot.cachedOffset();
 
-        if (slot.isUnset() || slot.slotBase() != baseValue) {
-            if (structure->typeInfo().prohibitsPropertyCaching())
-                return GiveUpOnCache;
+            if (slot.isUnset() || slot.slotBase() != baseValue) {
+                if (structure->typeInfo().prohibitsPropertyCaching())
+                    return GiveUpOnCache;
 
-            if (structure->isDictionary()) {
-                if (structure->hasBeenFlattenedBefore())
+                if (structure->isDictionary()) {
+                    if (structure->hasBeenFlattenedBefore())
+                        return GiveUpOnCache;
+                    structure->flattenDictionaryStructure(vm, jsCast<JSObject*>(baseCell));
+                }
+
+                if (slot.isUnset() && structure->typeInfo().getOwnPropertySlotIsImpureForPropertyAbsence())
                     return GiveUpOnCache;
-                structure->flattenDictionaryStructure(vm, jsCast<JSObject*>(baseCell));
-            }
 
-            if (slot.isUnset() && structure->typeInfo().getOwnPropertySlotIsImpureForPropertyAbsence())
-                return GiveUpOnCache;
+                bool usesPolyProto;
+                prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), baseCell, slot, usesPolyProto);
+                if (!prototypeAccessChain) {
+                    // It's invalid to access this prototype property.
+                    return GiveUpOnCache;
+                }
 
-            bool usesPolyProto;
-            prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), baseCell, slot, usesPolyProto);
-            if (!prototypeAccessChain) {
-                // It's invalid to access this prototype property.
-                return GiveUpOnCache;
-            }
+                if (!usesPolyProto) {
+                    // We use ObjectPropertyConditionSet instead for faster accesses.
+                    prototypeAccessChain = nullptr;
 
-            if (!usesPolyProto) {
-                // We use ObjectPropertyConditionSet instead for faster accesses.
-                prototypeAccessChain = nullptr;
+                    if (slot.isUnset()) {
+                        conditionSet = generateConditionsForPropertyMiss(
+                            vm, codeBlock, exec, structure, propertyName.impl());
+                    } else {
+                        conditionSet = generateConditionsForPrototypePropertyHit(
+                            vm, codeBlock, exec, structure, slot.slotBase(),
+                            propertyName.impl());
+                    }
 
-                if (slot.isUnset()) {
-                    conditionSet = generateConditionsForPropertyMiss(
-                        vm, codeBlock, exec, structure, propertyName.impl());
-                } else {
-                    conditionSet = generateConditionsForPrototypePropertyHit(
-                        vm, codeBlock, exec, structure, slot.slotBase(),
-                        propertyName.impl());
+                    if (!conditionSet.isValid())
+                        return GiveUpOnCache;
                 }
 
-                if (!conditionSet.isValid())
-                    return GiveUpOnCache;
+                offset = slot.isUnset() ? invalidOffset : slot.cachedOffset();
             }
 
-            offset = slot.isUnset() ? invalidOffset : slot.cachedOffset();
-        }
+            JSFunction* getter = nullptr;
+            if (slot.isCacheableGetter())
+                getter = jsDynamicCast<JSFunction*>(vm, slot.getterSetter()->getter());
 
-        JSFunction* getter = nullptr;
-        if (slot.isCacheableGetter())
-            getter = jsDynamicCast<JSFunction*>(vm, slot.getterSetter()->getter());
+            std::optional<DOMAttributeAnnotation> domAttribute;
+            if (slot.isCacheableCustom() && slot.domAttribute())
+                domAttribute = slot.domAttribute();
 
-        std::optional<DOMAttributeAnnotation> domAttribute;
-        if (slot.isCacheableCustom() && slot.domAttribute())
-            domAttribute = slot.domAttribute();
-
-        if (kind == GetByIDKind::Try) {
-            AccessCase::AccessType type;
-            if (slot.isCacheableValue())
-                type = AccessCase::Load;
-            else if (slot.isUnset())
-                type = AccessCase::Miss;
-            else if (slot.isCacheableGetter())
-                type = AccessCase::GetGetter;
-            else
-                RELEASE_ASSERT_NOT_REACHED();
-
-            newCase = ProxyableAccessCase::create(vm, codeBlock, type, offset, structure, conditionSet, loadTargetFromProxy, slot.watchpointSet(), WTFMove(prototypeAccessChain));
-        } else if (!loadTargetFromProxy && getter && IntrinsicGetterAccessCase::canEmitIntrinsicGetter(getter, structure) && !prototypeAccessChain) {
-            // FIXME: We should make this work with poly proto, but for our own sanity, we probably
-            // want to do a pointer check on the actual getter. A good time to make this work would
-            // be when we can inherit from builtin types in poly proto fashion:
-            // https://bugs.webkit.org/show_bug.cgi?id=177318
-            newCase = IntrinsicGetterAccessCase::create(vm, codeBlock, slot.cachedOffset(), structure, conditionSet, getter);
-        } else {
-            if (slot.isCacheableValue() || slot.isUnset()) {
-                newCase = ProxyableAccessCase::create(vm, codeBlock, slot.isUnset() ? AccessCase::Miss : AccessCase::Load,
-                    offset, structure, conditionSet, loadTargetFromProxy, slot.watchpointSet(), WTFMove(prototypeAccessChain));
-            } else {
+            if (kind == GetByIDKind::Try) {
                 AccessCase::AccessType type;
-                if (slot.isCacheableGetter())
-                    type = AccessCase::Getter;
-                else if (slot.attributes() & PropertyAttribute::CustomAccessor)
-                    type = AccessCase::CustomAccessorGetter;
+                if (slot.isCacheableValue())
+                    type = AccessCase::Load;
+                else if (slot.isUnset())
+                    type = AccessCase::Miss;
+                else if (slot.isCacheableGetter())
+                    type = AccessCase::GetGetter;
                 else
-                    type = AccessCase::CustomValueGetter;
+                    RELEASE_ASSERT_NOT_REACHED();
 
-                if (kind == GetByIDKind::WithThis && type == AccessCase::CustomAccessorGetter && domAttribute)
-                    return GiveUpOnCache;
+                newCase = ProxyableAccessCase::create(vm, codeBlock, type, offset, structure, conditionSet, loadTargetFromProxy, slot.watchpointSet(), WTFMove(prototypeAccessChain));
+            } else if (!loadTargetFromProxy && getter && IntrinsicGetterAccessCase::canEmitIntrinsicGetter(getter, structure) && !prototypeAccessChain) {
+                // FIXME: We should make this work with poly proto, but for our own sanity, we probably
+                // want to do a pointer check on the actual getter. A good time to make this work would
+                // be when we can inherit from builtin types in poly proto fashion:
+                // https://bugs.webkit.org/show_bug.cgi?id=177318
+                newCase = IntrinsicGetterAccessCase::create(vm, codeBlock, slot.cachedOffset(), structure, conditionSet, getter);
+            } else {
+                if (slot.isCacheableValue() || slot.isUnset()) {
+                    newCase = ProxyableAccessCase::create(vm, codeBlock, slot.isUnset() ? AccessCase::Miss : AccessCase::Load,
+                        offset, structure, conditionSet, loadTargetFromProxy, slot.watchpointSet(), WTFMove(prototypeAccessChain));
+                } else {
+                    AccessCase::AccessType type;
+                    if (slot.isCacheableGetter())
+                        type = AccessCase::Getter;
+                    else if (slot.attributes() & PropertyAttribute::CustomAccessor)
+                        type = AccessCase::CustomAccessorGetter;
+                    else
+                        type = AccessCase::CustomValueGetter;
 
-                newCase = GetterSetterAccessCase::create(
-                    vm, codeBlock, type, offset, structure, conditionSet, loadTargetFromProxy,
-                    slot.watchpointSet(), slot.isCacheableCustom() ? slot.customGetter() : nullptr,
-                    slot.isCacheableCustom() ? slot.slotBase() : nullptr,
-                    domAttribute, WTFMove(prototypeAccessChain));
+                    if (kind == GetByIDKind::WithThis && type == AccessCase::CustomAccessorGetter && domAttribute)
+                        return GiveUpOnCache;
+
+                    newCase = GetterSetterAccessCase::create(
+                        vm, codeBlock, type, offset, structure, conditionSet, loadTargetFromProxy,
+                        slot.watchpointSet(), slot.isCacheableCustom() ? slot.customGetter() : nullptr,
+                        slot.isCacheableCustom() ? slot.slotBase() : nullptr,
+                        domAttribute, WTFMove(prototypeAccessChain));
+                }
             }
         }
-    }
 
-    LOG_IC((ICEvent::GetByIdAddAccessCase, baseValue.classInfoOrNull(vm), propertyName));
+        LOG_IC((ICEvent::GetByIdAddAccessCase, baseValue.classInfoOrNull(vm), propertyName));
 
-    AccessGenerationResult result = stubInfo.addAccessCase(locker, codeBlock, propertyName, WTFMove(newCase));
+        result = stubInfo.addAccessCase(locker, codeBlock, propertyName, WTFMove(newCase));
 
-    if (result.generatedSomeCode()) {
-        LOG_IC((ICEvent::GetByIdReplaceWithJump, baseValue.classInfoOrNull(vm), propertyName));
-        
-        RELEASE_ASSERT(result.code());
-        InlineAccess::rewireStubAsJump(stubInfo, CodeLocationLabel(result.code()));
+        if (result.generatedSomeCode()) {
+            LOG_IC((ICEvent::GetByIdReplaceWithJump, baseValue.classInfoOrNull(vm), propertyName));
+            
+            RELEASE_ASSERT(result.code());
+            InlineAccess::rewireStubAsJump(stubInfo, CodeLocationLabel(result.code()));
+        }
     }
-    
+
+    fireWatchpointsAndClearStubIfNeeded(vm, stubInfo, exec->codeBlock(), result);
+
     return result.shouldGiveUpNow() ? GiveUpOnCache : RetryCacheLater;
 }
 
@@ -356,9 +372,8 @@
 void repatchGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo, GetByIDKind kind)
 {
     SuperSamplerScope superSamplerScope(false);
-    GCSafeConcurrentJSLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
     
-    if (tryCacheGetByID(locker, exec, baseValue, propertyName, slot, stubInfo, kind) == GiveUpOnCache)
+    if (tryCacheGetByID(exec, baseValue, propertyName, slot, stubInfo, kind) == GiveUpOnCache)
         ftlThunkAwareRepatchCall(exec->codeBlock(), stubInfo.slowPathCallLocation(), appropriateGenericGetByIdFunction(kind));
 }
 
@@ -386,160 +401,167 @@
     return operationPutByIdNonStrictOptimize;
 }
 
-static InlineCacheAction tryCachePutByID(const GCSafeConcurrentJSLocker& locker, ExecState* exec, JSValue baseValue, Structure* structure, const Identifier& ident, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
+static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Structure* structure, const Identifier& ident, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
 {
-    if (forceICFailure(exec))
-        return GiveUpOnCache;
-    
-    CodeBlock* codeBlock = exec->codeBlock();
     VM& vm = exec->vm();
+    AccessGenerationResult result;
+    {
+        GCSafeConcurrentJSLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
 
-    if (!baseValue.isCell())
-        return GiveUpOnCache;
-    
-    if (!slot.isCacheablePut() && !slot.isCacheableCustom() && !slot.isCacheableSetter())
-        return GiveUpOnCache;
+        if (forceICFailure(exec))
+            return GiveUpOnCache;
+        
+        CodeBlock* codeBlock = exec->codeBlock();
 
-    if (!structure->propertyAccessesAreCacheable())
-        return GiveUpOnCache;
+        if (!baseValue.isCell())
+            return GiveUpOnCache;
+        
+        if (!slot.isCacheablePut() && !slot.isCacheableCustom() && !slot.isCacheableSetter())
+            return GiveUpOnCache;
 
-    std::unique_ptr<AccessCase> newCase;
-    JSCell* baseCell = baseValue.asCell();
+        if (!structure->propertyAccessesAreCacheable())
+            return GiveUpOnCache;
 
-    if (slot.base() == baseValue && slot.isCacheablePut()) {
-        if (slot.type() == PutPropertySlot::ExistingProperty) {
-            structure->didCachePropertyReplacement(vm, slot.cachedOffset());
-        
-            if (stubInfo.cacheType == CacheType::Unset
-                && InlineAccess::canGenerateSelfPropertyReplace(stubInfo, slot.cachedOffset())
-                && !structure->needImpurePropertyWatchpoint()
-                && !structure->inferredTypeFor(ident.impl())) {
-                
-                bool generatedCodeInline = InlineAccess::generateSelfPropertyReplace(stubInfo, structure, slot.cachedOffset());
-                if (generatedCodeInline) {
-                    LOG_IC((ICEvent::PutByIdSelfPatch, structure->classInfo(), ident));
-                    ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation(), appropriateOptimizingPutByIdFunction(slot, putKind));
-                    stubInfo.initPutByIdReplace(codeBlock, structure, slot.cachedOffset());
-                    return RetryCacheLater;
+        std::unique_ptr<AccessCase> newCase;
+        JSCell* baseCell = baseValue.asCell();
+
+        if (slot.base() == baseValue && slot.isCacheablePut()) {
+            if (slot.type() == PutPropertySlot::ExistingProperty) {
+                structure->didCachePropertyReplacement(vm, slot.cachedOffset());
+            
+                if (stubInfo.cacheType == CacheType::Unset
+                    && InlineAccess::canGenerateSelfPropertyReplace(stubInfo, slot.cachedOffset())
+                    && !structure->needImpurePropertyWatchpoint()
+                    && !structure->inferredTypeFor(ident.impl())) {
+                    
+                    bool generatedCodeInline = InlineAccess::generateSelfPropertyReplace(stubInfo, structure, slot.cachedOffset());
+                    if (generatedCodeInline) {
+                        LOG_IC((ICEvent::PutByIdSelfPatch, structure->classInfo(), ident));
+                        ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation(), appropriateOptimizingPutByIdFunction(slot, putKind));
+                        stubInfo.initPutByIdReplace(codeBlock, structure, slot.cachedOffset());
+                        return RetryCacheLater;
+                    }
                 }
-            }
 
-            newCase = AccessCase::create(vm, codeBlock, AccessCase::Replace, slot.cachedOffset(), structure);
-        } else {
-            ASSERT(slot.type() == PutPropertySlot::NewProperty);
+                newCase = AccessCase::create(vm, codeBlock, AccessCase::Replace, slot.cachedOffset(), structure);
+            } else {
+                ASSERT(slot.type() == PutPropertySlot::NewProperty);
 
-            if (!structure->isObject())
-                return GiveUpOnCache;
-
-            if (structure->isDictionary()) {
-                if (structure->hasBeenFlattenedBefore())
+                if (!structure->isObject())
                     return GiveUpOnCache;
-                structure->flattenDictionaryStructure(vm, jsCast<JSObject*>(baseValue));
-            }
 
-            PropertyOffset offset;
-            Structure* newStructure =
-                Structure::addPropertyTransitionToExistingStructureConcurrently(
-                    structure, ident.impl(), 0, offset);
-            if (!newStructure || !newStructure->propertyAccessesAreCacheable())
-                return GiveUpOnCache;
+                if (structure->isDictionary()) {
+                    if (structure->hasBeenFlattenedBefore())
+                        return GiveUpOnCache;
+                    structure->flattenDictionaryStructure(vm, jsCast<JSObject*>(baseValue));
+                }
 
-            ASSERT(newStructure->previousID() == structure);
-            ASSERT(!newStructure->isDictionary());
-            ASSERT(newStructure->isObject());
-            
-            std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
-            ObjectPropertyConditionSet conditionSet;
-            if (putKind == NotDirect) {
-                bool usesPolyProto;
-                prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), baseCell, nullptr, usesPolyProto);
-                if (!prototypeAccessChain) {
-                    // It's invalid to access this prototype property.
+                PropertyOffset offset;
+                Structure* newStructure =
+                    Structure::addPropertyTransitionToExistingStructureConcurrently(
+                        structure, ident.impl(), 0, offset);
+                if (!newStructure || !newStructure->propertyAccessesAreCacheable())
                     return GiveUpOnCache;
-                }
 
-                if (!usesPolyProto) {
-                    prototypeAccessChain = nullptr;
-                    conditionSet =
-                        generateConditionsForPropertySetterMiss(
-                            vm, codeBlock, exec, newStructure, ident.impl());
-                    if (!conditionSet.isValid())
+                ASSERT(newStructure->previousID() == structure);
+                ASSERT(!newStructure->isDictionary());
+                ASSERT(newStructure->isObject());
+                
+                std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
+                ObjectPropertyConditionSet conditionSet;
+                if (putKind == NotDirect) {
+                    bool usesPolyProto;
+                    prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), baseCell, nullptr, usesPolyProto);
+                    if (!prototypeAccessChain) {
+                        // It's invalid to access this prototype property.
                         return GiveUpOnCache;
+                    }
+
+                    if (!usesPolyProto) {
+                        prototypeAccessChain = nullptr;
+                        conditionSet =
+                            generateConditionsForPropertySetterMiss(
+                                vm, codeBlock, exec, newStructure, ident.impl());
+                        if (!conditionSet.isValid())
+                            return GiveUpOnCache;
+                    }
+
                 }
 
+                newCase = AccessCase::create(vm, codeBlock, offset, structure, newStructure, conditionSet, WTFMove(prototypeAccessChain));
             }
+        } else if (slot.isCacheableCustom() || slot.isCacheableSetter()) {
+            if (slot.isCacheableCustom()) {
+                ObjectPropertyConditionSet conditionSet;
+                std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
 
-            newCase = AccessCase::create(vm, codeBlock, offset, structure, newStructure, conditionSet, WTFMove(prototypeAccessChain));
-        }
-    } else if (slot.isCacheableCustom() || slot.isCacheableSetter()) {
-        if (slot.isCacheableCustom()) {
-            ObjectPropertyConditionSet conditionSet;
-            std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
+                if (slot.base() != baseValue) {
+                    bool usesPolyProto;
+                    prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), baseCell, slot.base(), usesPolyProto);
+                    if (!prototypeAccessChain) {
+                        // It's invalid to access this prototype property.
+                        return GiveUpOnCache;
+                    }
 
-            if (slot.base() != baseValue) {
-                bool usesPolyProto;
-                prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), baseCell, slot.base(), usesPolyProto);
-                if (!prototypeAccessChain) {
-                    // It's invalid to access this prototype property.
-                    return GiveUpOnCache;
+                    if (!usesPolyProto) {
+                        prototypeAccessChain = nullptr;
+                        conditionSet =
+                            generateConditionsForPrototypePropertyHit(
+                                vm, codeBlock, exec, structure, slot.base(), ident.impl());
+                        if (!conditionSet.isValid())
+                            return GiveUpOnCache;
+                    }
                 }
 
-                if (!usesPolyProto) {
-                    prototypeAccessChain = nullptr;
-                    conditionSet =
-                        generateConditionsForPrototypePropertyHit(
-                            vm, codeBlock, exec, structure, slot.base(), ident.impl());
-                    if (!conditionSet.isValid())
+                newCase = GetterSetterAccessCase::create(
+                    vm, codeBlock, slot.isCustomAccessor() ? AccessCase::CustomAccessorSetter : AccessCase::CustomValueSetter, structure, invalidOffset,
+                    conditionSet, WTFMove(prototypeAccessChain), slot.customSetter(), slot.base());
+            } else {
+                ObjectPropertyConditionSet conditionSet;
+                std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
+                PropertyOffset offset = slot.cachedOffset();
+
+                if (slot.base() != baseValue) {
+                    bool usesPolyProto;
+                    prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), baseCell, slot.base(), usesPolyProto);
+                    if (!prototypeAccessChain) {
+                        // It's invalid to access this prototype property.
                         return GiveUpOnCache;
-                }
-            }
+                    }
 
-            newCase = GetterSetterAccessCase::create(
-                vm, codeBlock, slot.isCustomAccessor() ? AccessCase::CustomAccessorSetter : AccessCase::CustomValueSetter, structure, invalidOffset,
-                conditionSet, WTFMove(prototypeAccessChain), slot.customSetter(), slot.base());
-        } else {
-            ObjectPropertyConditionSet conditionSet;
-            std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
-            PropertyOffset offset = slot.cachedOffset();
+                    if (!usesPolyProto) {
+                        prototypeAccessChain = nullptr;
+                        conditionSet =
+                            generateConditionsForPrototypePropertyHit(
+                                vm, codeBlock, exec, structure, slot.base(), ident.impl());
+                        if (!conditionSet.isValid())
+                            return GiveUpOnCache;
 
-            if (slot.base() != baseValue) {
-                bool usesPolyProto;
-                prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), baseCell, slot.base(), usesPolyProto);
-                if (!prototypeAccessChain) {
-                    // It's invalid to access this prototype property.
-                    return GiveUpOnCache;
-                }
+                        RELEASE_ASSERT(offset == conditionSet.slotBaseCondition().offset());
+                    }
 
-                if (!usesPolyProto) {
-                    prototypeAccessChain = nullptr;
-                    conditionSet =
-                        generateConditionsForPrototypePropertyHit(
-                            vm, codeBlock, exec, structure, slot.base(), ident.impl());
-                    if (!conditionSet.isValid())
-                        return GiveUpOnCache;
-
-                    RELEASE_ASSERT(offset == conditionSet.slotBaseCondition().offset());
                 }
 
+                newCase = GetterSetterAccessCase::create(
+                    vm, codeBlock, AccessCase::Setter, structure, offset, conditionSet, WTFMove(prototypeAccessChain));
             }
+        }
 
-            newCase = GetterSetterAccessCase::create(
-                vm, codeBlock, AccessCase::Setter, structure, offset, conditionSet, WTFMove(prototypeAccessChain));
+        LOG_IC((ICEvent::PutByIdAddAccessCase, structure->classInfo(), ident));
+        
+        result = stubInfo.addAccessCase(locker, codeBlock, ident, WTFMove(newCase));
+
+        if (result.generatedSomeCode()) {
+            LOG_IC((ICEvent::PutByIdReplaceWithJump, structure->classInfo(), ident));
+            
+            RELEASE_ASSERT(result.code());
+
+            InlineAccess::rewireStubAsJump(stubInfo, CodeLocationLabel(result.code()));
         }
     }
 
-    LOG_IC((ICEvent::PutByIdAddAccessCase, structure->classInfo(), ident));
-    
-    AccessGenerationResult result = stubInfo.addAccessCase(locker, codeBlock, ident, WTFMove(newCase));
-    
-    if (result.generatedSomeCode()) {
-        LOG_IC((ICEvent::PutByIdReplaceWithJump, structure->classInfo(), ident));
-        
-        RELEASE_ASSERT(result.code());
+    fireWatchpointsAndClearStubIfNeeded(vm, stubInfo, exec->codeBlock(), result);
 
-        InlineAccess::rewireStubAsJump(stubInfo, CodeLocationLabel(result.code()));
-    }
-    
     return result.shouldGiveUpNow() ? GiveUpOnCache : RetryCacheLater;
 }
 
@@ -546,35 +568,51 @@
 void repatchPutByID(ExecState* exec, JSValue baseValue, Structure* structure, const Identifier& propertyName, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
 {
     SuperSamplerScope superSamplerScope(false);
-    GCSafeConcurrentJSLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
     
-    if (tryCachePutByID(locker, exec, baseValue, structure, propertyName, slot, stubInfo, putKind) == GiveUpOnCache)
+    if (tryCachePutByID(exec, baseValue, structure, propertyName, slot, stubInfo, putKind) == GiveUpOnCache)
         ftlThunkAwareRepatchCall(exec->codeBlock(), stubInfo.slowPathCallLocation(), appropriateGenericPutByIdFunction(slot, putKind));
 }
 
-static InlineCacheAction tryRepatchIn(
-    const GCSafeConcurrentJSLocker& locker, ExecState* exec, JSCell* base, const Identifier& ident,
+static InlineCacheAction tryCacheIn(
+    ExecState* exec, JSCell* base, const Identifier& ident,
     bool wasFound, const PropertySlot& slot, StructureStubInfo& stubInfo)
 {
-    if (forceICFailure(exec))
-        return GiveUpOnCache;
-    
-    if (!base->structure()->propertyAccessesAreCacheable() || (!wasFound && !base->structure()->propertyAccessesAreCacheableForAbsence()))
-        return GiveUpOnCache;
-    
-    if (wasFound) {
-        if (!slot.isCacheable())
+    VM& vm = exec->vm();
+    AccessGenerationResult result;
+
+    {
+        GCSafeConcurrentJSLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
+        if (forceICFailure(exec))
             return GiveUpOnCache;
-    }
-    
-    CodeBlock* codeBlock = exec->codeBlock();
-    VM& vm = exec->vm();
-    Structure* structure = base->structure(vm);
-    
-    std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
-    ObjectPropertyConditionSet conditionSet;
-    if (wasFound) {
-        if (slot.slotBase() != base) {
+        
+        if (!base->structure()->propertyAccessesAreCacheable() || (!wasFound && !base->structure()->propertyAccessesAreCacheableForAbsence()))
+            return GiveUpOnCache;
+        
+        if (wasFound) {
+            if (!slot.isCacheable())
+                return GiveUpOnCache;
+        }
+        
+        CodeBlock* codeBlock = exec->codeBlock();
+        Structure* structure = base->structure(vm);
+        
+        std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
+        ObjectPropertyConditionSet conditionSet;
+        if (wasFound) {
+            if (slot.slotBase() != base) {
+                bool usesPolyProto;
+                prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), base, slot, usesPolyProto);
+                if (!prototypeAccessChain) {
+                    // It's invalid to access this prototype property.
+                    return GiveUpOnCache;
+                }
+                if (!usesPolyProto) {
+                    prototypeAccessChain = nullptr;
+                    conditionSet = generateConditionsForPrototypePropertyHit(
+                        vm, codeBlock, exec, structure, slot.slotBase(), ident.impl());
+                }
+            }
+        } else {
             bool usesPolyProto;
             prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), base, slot, usesPolyProto);
             if (!prototypeAccessChain) {
@@ -581,45 +619,35 @@
                 // It's invalid to access this prototype property.
                 return GiveUpOnCache;
             }
+
             if (!usesPolyProto) {
                 prototypeAccessChain = nullptr;
-                conditionSet = generateConditionsForPrototypePropertyHit(
-                    vm, codeBlock, exec, structure, slot.slotBase(), ident.impl());
+                conditionSet = generateConditionsForPropertyMiss(
+                    vm, codeBlock, exec, structure, ident.impl());
             }
         }
-    } else {
-        bool usesPolyProto;
-        prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), base, slot, usesPolyProto);
-        if (!prototypeAccessChain) {
-            // It's invalid to access this prototype property.
+        if (!conditionSet.isValid())
             return GiveUpOnCache;
-        }
 
-        if (!usesPolyProto) {
-            prototypeAccessChain = nullptr;
-            conditionSet = generateConditionsForPropertyMiss(
-                vm, codeBlock, exec, structure, ident.impl());
-        }
-    }
-    if (!conditionSet.isValid())
-        return GiveUpOnCache;
+        LOG_IC((ICEvent::InAddAccessCase, structure->classInfo(), ident));
 
-    LOG_IC((ICEvent::InAddAccessCase, structure->classInfo(), ident));
+        std::unique_ptr<AccessCase> newCase = AccessCase::create(
+            vm, codeBlock, wasFound ? AccessCase::InHit : AccessCase::InMiss, invalidOffset, structure, conditionSet, WTFMove(prototypeAccessChain));
 
-    std::unique_ptr<AccessCase> newCase = AccessCase::create(
-        vm, codeBlock, wasFound ? AccessCase::InHit : AccessCase::InMiss, invalidOffset, structure, conditionSet, WTFMove(prototypeAccessChain));
+        result = stubInfo.addAccessCase(locker, codeBlock, ident, WTFMove(newCase));
 
-    AccessGenerationResult result = stubInfo.addAccessCase(locker, codeBlock, ident, WTFMove(newCase));
-    
-    if (result.generatedSomeCode()) {
-        LOG_IC((ICEvent::InReplaceWithJump, structure->classInfo(), ident));
-        
-        RELEASE_ASSERT(result.code());
+        if (result.generatedSomeCode()) {
+            LOG_IC((ICEvent::InReplaceWithJump, structure->classInfo(), ident));
+            
+            RELEASE_ASSERT(result.code());
 
-        MacroAssembler::repatchJump(
-            stubInfo.patchableJumpForIn(),
-            CodeLocationLabel(result.code()));
+            MacroAssembler::repatchJump(
+                stubInfo.patchableJumpForIn(),
+                CodeLocationLabel(result.code()));
+        }
     }
+
+    fireWatchpointsAndClearStubIfNeeded(vm, stubInfo, exec->codeBlock(), result);
     
     return result.shouldGiveUpNow() ? GiveUpOnCache : RetryCacheLater;
 }
@@ -629,8 +657,7 @@
     const PropertySlot& slot, StructureStubInfo& stubInfo)
 {
     SuperSamplerScope superSamplerScope(false);
-    GCSafeConcurrentJSLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
-    if (tryRepatchIn(locker, exec, base, ident, wasFound, slot, stubInfo) == GiveUpOnCache)
+    if (tryCacheIn(exec, base, ident, wasFound, slot, stubInfo) == GiveUpOnCache)
         ftlThunkAwareRepatchCall(exec->codeBlock(), stubInfo.slowPathCallLocation(), operationIn);
 }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to