GetByStatus should not say it took the slow path for multiple identifiers and should...
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Nov 2019 05:41:56 +0000 (05:41 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Nov 2019 05:41:56 +0000 (05:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=204435

Reviewed by Tadeu Zagallo.

JSTests:

* microbenchmarks/get-by-val-polymorphic-ic-1.js: Added.
(assert):
(test1.foo):
(test1):
* microbenchmarks/get-by-val-polymorphic-ic-2.js: Added.
(assert):
(test2.foo):
(test2):
* microbenchmarks/get-by-val-polymorphic-ic-3.js: Added.
(assert):
(test3.foo):
(test3.args):
(test3):
* microbenchmarks/get-by-val-polymorphic-ic-4.js: Added.
(assert):
(test4.foo):
(test4.capture):
(test4.args):
(test4):
* microbenchmarks/get-by-val-polymorphic-ic-5.js: Added.
(assert):
(test5.foo):
(test5.capture):
(test5.args):
(test5):
* microbenchmarks/get-by-val-polymorphic-ic-6.js: Added.
(assert):
(test6.foo):
(test6.capture):
(test6.args):
(test6):
* microbenchmarks/get-by-val-polymorphic-ic-7.js: Added.
(assert):
(test7.foo):
(test7):
* microbenchmarks/get-by-val-polymorphic-ic-8.js: Added.
(assert):
(test7.foo):
(test7):
* microbenchmarks/get-by-val-polymorphic-ic-9.js: Added.
(assert):
(test7.foo):
(test7):

Source/JavaScriptCore:

I discovered some issues with get by val ICs when re-running the microbenchmarks
I wrote. I noticed that we were faster when not running with the DFG. The reason
for this is that we were only emitting a get by val IC in the DFG/FTL when we
observe the GetByStatus says it didn't "go to the slow path". The logic in GetByStatus
for building up a variant list was wrong for ICs with multiple identifiers. We have
a consistency check when building up the list to ensure that no two variants have
structure sets which overlap, because we wouldn't know which one to choose. However,
we were accidentally saying two GetByIdVariants overlap when they had different identifiers.
This patch fixes that bug by also doing an identifier comparison check. Two GetByIdVariants
with different identifiers do not overlap.

We also used to say a GetByStatus "goes to the slow path" if any of the cases were an
array-like load. I wrote that code thinking that ArrayProfile would just handle it.
However, sometimes we have a get by val IC that both has string properties and int32 properties.
In these kinds of scenarios, an IC is super profitable. This patch now distinguishes
between a GetByStatus saying "we're a slow path" and if we actually observed the StructureStubInfo
go to the slow path. In the DFG/FTL, we only forgo emitting a get by val IC when observing a
prior StructureStubInfo that went to the slow path.

I also realized are call to StructureStubInfo::considerCaching was wrong for get by val ICs.
We were only considering the Structure in isolation, not the { Structure, Identifier }
pair. For get by val, we need to  consider the pair together, since {s1, "a"}
and {s1, "b"} will be two different access cases.

This patch demonstrates that on these microbenchmarks, get by val ICs can
be between 50-200% faster.

* bytecode/GetByIdVariant.cpp:
(JSC::GetByIdVariant::dumpInContext const):
* bytecode/GetByIdVariant.h:
(JSC::GetByIdVariant::overlaps):
* bytecode/GetByStatus.cpp:
(JSC::GetByStatus::GetByStatus):
(JSC::GetByStatus::computeForStubInfoWithoutExitSiteFeedback):
(JSC::GetByStatus::makesCalls const):
(JSC::GetByStatus::slowVersion const):
(JSC::GetByStatus::merge):
(JSC::GetByStatus::dump const):
* bytecode/GetByStatus.h:
(JSC::GetByStatus::GetByStatus):
(JSC::GetByStatus::takesSlowPath const):
(JSC::GetByStatus::observedStructureStubInfoSlowPath const):
* bytecode/ICStatusUtils.h:
(JSC::appendICStatusVariant):
* bytecode/InByIdVariant.h:
(JSC::InByIdVariant::overlaps):
* bytecode/InstanceOfVariant.h:
(JSC::InstanceOfVariant::overlaps):
* bytecode/PutByIdVariant.h:
(JSC::PutByIdVariant::overlaps):
* bytecode/StructureStubInfo.cpp:
(JSC::StructureStubInfo::visitWeakReferences):
* bytecode/StructureStubInfo.h:
(JSC::StructureStubInfo::considerCaching):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* jit/JITOperations.cpp:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@252763 268f45cc-cd09-0410-ab3c-d52691b4dbfc

23 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/get-by-val-polymorphic-ic-1.js [new file with mode: 0644]
JSTests/microbenchmarks/get-by-val-polymorphic-ic-2.js [new file with mode: 0644]
JSTests/microbenchmarks/get-by-val-polymorphic-ic-3.js [new file with mode: 0644]
JSTests/microbenchmarks/get-by-val-polymorphic-ic-4.js [new file with mode: 0644]
JSTests/microbenchmarks/get-by-val-polymorphic-ic-5.js [new file with mode: 0644]
JSTests/microbenchmarks/get-by-val-polymorphic-ic-6.js [new file with mode: 0644]
JSTests/microbenchmarks/get-by-val-polymorphic-ic-7.js [new file with mode: 0644]
JSTests/microbenchmarks/get-by-val-polymorphic-ic-8.js [new file with mode: 0644]
JSTests/microbenchmarks/get-by-val-polymorphic-ic-9.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/GetByIdVariant.cpp
Source/JavaScriptCore/bytecode/GetByIdVariant.h
Source/JavaScriptCore/bytecode/GetByStatus.cpp
Source/JavaScriptCore/bytecode/GetByStatus.h
Source/JavaScriptCore/bytecode/ICStatusUtils.h
Source/JavaScriptCore/bytecode/InByIdVariant.h
Source/JavaScriptCore/bytecode/InstanceOfVariant.h
Source/JavaScriptCore/bytecode/PutByIdVariant.h
Source/JavaScriptCore/bytecode/StructureStubInfo.cpp
Source/JavaScriptCore/bytecode/StructureStubInfo.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/jit/JITOperations.cpp

index 131e015..35d280b 100644 (file)
@@ -1,3 +1,54 @@
+2019-11-21  Saam Barati  <sbarati@apple.com>
+
+        GetByStatus should not say it took the slow path for multiple identifiers and should have a way to indicate if the StructureStubInfo it saw took the slow path
+        https://bugs.webkit.org/show_bug.cgi?id=204435
+
+        Reviewed by Tadeu Zagallo.
+
+        * microbenchmarks/get-by-val-polymorphic-ic-1.js: Added.
+        (assert):
+        (test1.foo):
+        (test1):
+        * microbenchmarks/get-by-val-polymorphic-ic-2.js: Added.
+        (assert):
+        (test2.foo):
+        (test2):
+        * microbenchmarks/get-by-val-polymorphic-ic-3.js: Added.
+        (assert):
+        (test3.foo):
+        (test3.args):
+        (test3):
+        * microbenchmarks/get-by-val-polymorphic-ic-4.js: Added.
+        (assert):
+        (test4.foo):
+        (test4.capture):
+        (test4.args):
+        (test4):
+        * microbenchmarks/get-by-val-polymorphic-ic-5.js: Added.
+        (assert):
+        (test5.foo):
+        (test5.capture):
+        (test5.args):
+        (test5):
+        * microbenchmarks/get-by-val-polymorphic-ic-6.js: Added.
+        (assert):
+        (test6.foo):
+        (test6.capture):
+        (test6.args):
+        (test6):
+        * microbenchmarks/get-by-val-polymorphic-ic-7.js: Added.
+        (assert):
+        (test7.foo):
+        (test7):
+        * microbenchmarks/get-by-val-polymorphic-ic-8.js: Added.
+        (assert):
+        (test7.foo):
+        (test7):
+        * microbenchmarks/get-by-val-polymorphic-ic-9.js: Added.
+        (assert):
+        (test7.foo):
+        (test7):
+
 2019-11-21  Mark Lam  <mark.lam@apple.com>
 
         Fix broken String.prototype.replace() and replaceAll().
diff --git a/JSTests/microbenchmarks/get-by-val-polymorphic-ic-1.js b/JSTests/microbenchmarks/get-by-val-polymorphic-ic-1.js
new file mode 100644 (file)
index 0000000..c0895b0
--- /dev/null
@@ -0,0 +1,21 @@
+function assert(b, m) {
+    if (!b)
+        throw new Error(m);
+}
+
+function test1() {
+    function foo(o, i) {
+        return o[i];
+    }
+    noInline(foo);
+
+    let o = {a: 42, b: 43};
+    let start = Date.now();
+    for (let i = 0; i < 10000000; ++i) {
+        assert(foo(o, "a") === 42);
+        assert(foo(o, "b") === 43);
+        assert(foo(o, "c") === undefined);
+        assert(foo(o, "d") === undefined);
+    }
+}
+test1();
diff --git a/JSTests/microbenchmarks/get-by-val-polymorphic-ic-2.js b/JSTests/microbenchmarks/get-by-val-polymorphic-ic-2.js
new file mode 100644 (file)
index 0000000..3ba8917
--- /dev/null
@@ -0,0 +1,32 @@
+function assert(b, m) {
+    if (!b)
+        throw new Error(m);
+}
+
+function test2() {
+    function foo(o, i) {
+        return o[i];
+    }
+    noInline(foo);
+
+    let t1 = {};
+    let t2 = {};
+
+    let o1 = [t1];
+    let o2 = [10];
+    let o3 = [10.5];
+    let o4 = [t2];
+    let o5 = {x:42}
+    ensureArrayStorage(o4);
+
+    let start = Date.now();
+    for (let i = 0; i < 8000000; ++i) {
+        assert(foo(o1, 0) === t1);
+        assert(foo(o2, 0) === 10);
+        assert(foo(o3, 0) === 10.5);
+        assert(foo(o4, 0) === t2);
+        assert(foo(o5, "x") === 42);
+        assert(foo(o5, "x") === 42);
+    }
+}
+test2();
diff --git a/JSTests/microbenchmarks/get-by-val-polymorphic-ic-3.js b/JSTests/microbenchmarks/get-by-val-polymorphic-ic-3.js
new file mode 100644 (file)
index 0000000..cdca1d7
--- /dev/null
@@ -0,0 +1,28 @@
+function assert(b, m) {
+    if (!b)
+        throw new Error(m);
+}
+
+function test3() {
+    function foo(o, i) {
+        return o[i];
+    }
+    noInline(foo);
+
+    function args(a, b) {
+        return arguments;
+    }
+
+    let o1 = args(20, 30);
+    let o2 = [10];
+    let o3 = {x:42}
+
+    let start = Date.now();
+    for (let i = 0; i < 8000000; ++i) {
+        assert(foo(o1, 0) === 20);
+        assert(foo(o1, 1) === 30);
+        assert(foo(o2, 0) === 10);
+        assert(foo(o3, "x") === 42);
+    }
+}
+test3();
diff --git a/JSTests/microbenchmarks/get-by-val-polymorphic-ic-4.js b/JSTests/microbenchmarks/get-by-val-polymorphic-ic-4.js
new file mode 100644 (file)
index 0000000..9d1bdbc
--- /dev/null
@@ -0,0 +1,32 @@
+function assert(b, m) {
+    if (!b)
+        throw new Error(m);
+}
+
+function test4() {
+    // scoped arguments
+    function foo(o, i) {
+        return o[i];
+    }
+    noInline(foo);
+
+    function args(a, b) {
+        function capture() { 
+            return a + b;
+        }
+        return arguments;
+    }
+
+    let o1 = args(20, 30, 40);
+    let o3 = {x:42}
+
+    let start = Date.now();
+    for (let i = 0; i < 8000000; ++i) {
+        assert(foo(o1, 0) === 20);
+        assert(foo(o1, 1) === 30);
+        assert(foo(o1, 2) === 40);
+        assert(foo(o1, 3) === undefined);
+        assert(foo(o3, "x") === 42);
+    }
+}
+test4();
diff --git a/JSTests/microbenchmarks/get-by-val-polymorphic-ic-5.js b/JSTests/microbenchmarks/get-by-val-polymorphic-ic-5.js
new file mode 100644 (file)
index 0000000..7884733
--- /dev/null
@@ -0,0 +1,36 @@
+function assert(b, m) {
+    if (!b)
+        throw new Error(m);
+}
+
+function test5() {
+    function foo(o, i) {
+        return o[i];
+    }
+    noInline(foo);
+
+    function args(a, b) {
+        function capture() { 
+            return a + b;
+        }
+        return arguments;
+    }
+
+    let o1 = new Uint8Array([1, 2]);
+    let o2 = new Uint32Array([3, 4]);
+    let o3 = new Int32Array([5, 6]);
+    let o4 = new Float32Array([7, 8]);
+
+    let start = Date.now();
+    for (let i = 0; i < 8000000; ++i) {
+        assert(foo(o1, 0) === 1);
+        assert(foo(o1, 1) === 2);
+        assert(foo(o2, 0) === 3);
+        assert(foo(o2, 1) === 4);
+        assert(foo(o3, 0) === 5);
+        assert(foo(o3, 1) === 6);
+        assert(foo(o4, 0) === 7);
+        assert(foo(o4, 1) === 8);
+    }
+}
+test5();
diff --git a/JSTests/microbenchmarks/get-by-val-polymorphic-ic-6.js b/JSTests/microbenchmarks/get-by-val-polymorphic-ic-6.js
new file mode 100644 (file)
index 0000000..897d3e5
--- /dev/null
@@ -0,0 +1,34 @@
+function assert(b, m) {
+    if (!b)
+        throw new Error(m);
+}
+
+function test6() {
+    function foo(o, i) {
+        return o[i];
+    }
+    noInline(foo);
+
+    function args(a, b) {
+        function capture() { 
+            return a + b;
+        }
+        return arguments;
+    }
+
+    let o1 = "abc";
+    let o2 = "\u2713a";
+    let o3 = new Uint32Array([3, 4]);
+
+    let start = Date.now();
+    for (let i = 0; i < 2000000; ++i) {
+        assert(foo(o1, 0) === "a");
+        assert(foo(o1, 1) === "b");
+        assert(foo(o1, 2) === "c");
+        assert(foo(o2, 0) === "\u2713");
+        assert(foo(o2, 1) === "a");
+        assert(foo(o3, 0) === 3);
+        assert(foo(o3, 1) === 4);
+    }
+}
+test6();
diff --git a/JSTests/microbenchmarks/get-by-val-polymorphic-ic-7.js b/JSTests/microbenchmarks/get-by-val-polymorphic-ic-7.js
new file mode 100644 (file)
index 0000000..d2d4b93
--- /dev/null
@@ -0,0 +1,21 @@
+function assert(b, m) {
+    if (!b)
+        throw new Error(m);
+}
+
+function test7() {
+    function foo(o, i) {
+        return o[i];
+    }
+    noInline(foo);
+
+    let s1 = Symbol();
+    let s2 = Symbol();
+    let o = {[s1]: 42, [s2]: 43};
+    let start = Date.now();
+    for (let i = 0; i < 10000000; ++i) {
+        assert(foo(o, s1) === 42);
+        assert(foo(o, s2) === 43);
+    }
+}
+test7();
diff --git a/JSTests/microbenchmarks/get-by-val-polymorphic-ic-8.js b/JSTests/microbenchmarks/get-by-val-polymorphic-ic-8.js
new file mode 100644 (file)
index 0000000..19d343e
--- /dev/null
@@ -0,0 +1,22 @@
+function assert(b, m) {
+    if (!b)
+        throw new Error(m);
+}
+
+function test7() {
+    function foo(o, i) {
+        return o[i];
+    }
+    noInline(foo);
+
+    let s1 = Symbol();
+    let s2 = Symbol();
+    let o = {[s1]: 42, [s2]: 43, o:32};
+    let start = Date.now();
+    for (let i = 0; i < 10000000; ++i) {
+        assert(foo(o, s1) === 42);
+        assert(foo(o, s2) === 43);
+        assert(foo(o, "o") === 32);
+    }
+}
+test7();
diff --git a/JSTests/microbenchmarks/get-by-val-polymorphic-ic-9.js b/JSTests/microbenchmarks/get-by-val-polymorphic-ic-9.js
new file mode 100644 (file)
index 0000000..e634f02
--- /dev/null
@@ -0,0 +1,24 @@
+function assert(b, m) {
+    if (!b)
+        throw new Error(m);
+}
+
+function test7() {
+    function foo(o, i) {
+        return o[i];
+    }
+    noInline(foo);
+
+    let s1 = Symbol();
+    let s2 = Symbol();
+    let o = {[s1]: 42, [s2]: 43, o:32};
+    o[0] = 1337;
+    let start = Date.now();
+    for (let i = 0; i < 10000000; ++i) {
+        assert(foo(o, s1) === 42);
+        assert(foo(o, s2) === 43);
+        assert(foo(o, "o") === 32);
+        assert(foo(o, 0) === 1337);
+    }
+}
+test7();
index fae231e..9025ec2 100644 (file)
@@ -1,3 +1,68 @@
+2019-11-21  Saam Barati  <sbarati@apple.com>
+
+        GetByStatus should not say it took the slow path for multiple identifiers and should have a way to indicate if the StructureStubInfo it saw took the slow path
+        https://bugs.webkit.org/show_bug.cgi?id=204435
+
+        Reviewed by Tadeu Zagallo.
+
+        I discovered some issues with get by val ICs when re-running the microbenchmarks
+        I wrote. I noticed that we were faster when not running with the DFG. The reason
+        for this is that we were only emitting a get by val IC in the DFG/FTL when we
+        observe the GetByStatus says it didn't "go to the slow path". The logic in GetByStatus
+        for building up a variant list was wrong for ICs with multiple identifiers. We have
+        a consistency check when building up the list to ensure that no two variants have
+        structure sets which overlap, because we wouldn't know which one to choose. However,
+        we were accidentally saying two GetByIdVariants overlap when they had different identifiers.
+        This patch fixes that bug by also doing an identifier comparison check. Two GetByIdVariants
+        with different identifiers do not overlap.
+        
+        We also used to say a GetByStatus "goes to the slow path" if any of the cases were an
+        array-like load. I wrote that code thinking that ArrayProfile would just handle it.
+        However, sometimes we have a get by val IC that both has string properties and int32 properties.
+        In these kinds of scenarios, an IC is super profitable. This patch now distinguishes
+        between a GetByStatus saying "we're a slow path" and if we actually observed the StructureStubInfo
+        go to the slow path. In the DFG/FTL, we only forgo emitting a get by val IC when observing a
+        prior StructureStubInfo that went to the slow path.
+        
+        I also realized are call to StructureStubInfo::considerCaching was wrong for get by val ICs.
+        We were only considering the Structure in isolation, not the { Structure, Identifier }
+        pair. For get by val, we need to  consider the pair together, since {s1, "a"}
+        and {s1, "b"} will be two different access cases.
+        
+        This patch demonstrates that on these microbenchmarks, get by val ICs can
+        be between 50-200% faster.
+
+        * bytecode/GetByIdVariant.cpp:
+        (JSC::GetByIdVariant::dumpInContext const):
+        * bytecode/GetByIdVariant.h:
+        (JSC::GetByIdVariant::overlaps):
+        * bytecode/GetByStatus.cpp:
+        (JSC::GetByStatus::GetByStatus):
+        (JSC::GetByStatus::computeForStubInfoWithoutExitSiteFeedback):
+        (JSC::GetByStatus::makesCalls const):
+        (JSC::GetByStatus::slowVersion const):
+        (JSC::GetByStatus::merge):
+        (JSC::GetByStatus::dump const):
+        * bytecode/GetByStatus.h:
+        (JSC::GetByStatus::GetByStatus):
+        (JSC::GetByStatus::takesSlowPath const):
+        (JSC::GetByStatus::observedStructureStubInfoSlowPath const):
+        * bytecode/ICStatusUtils.h:
+        (JSC::appendICStatusVariant):
+        * bytecode/InByIdVariant.h:
+        (JSC::InByIdVariant::overlaps):
+        * bytecode/InstanceOfVariant.h:
+        (JSC::InstanceOfVariant::overlaps):
+        * bytecode/PutByIdVariant.h:
+        (JSC::PutByIdVariant::overlaps):
+        * bytecode/StructureStubInfo.cpp:
+        (JSC::StructureStubInfo::visitWeakReferences):
+        * bytecode/StructureStubInfo.h:
+        (JSC::StructureStubInfo::considerCaching):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * jit/JITOperations.cpp:
+
 2019-11-21  Mark Lam  <mark.lam@apple.com>
 
         Fix broken String.prototype.replace() and replaceAll().
index 04c7a3a..588dea2 100644 (file)
@@ -181,13 +181,13 @@ void GetByIdVariant::dump(PrintStream& out) const
 
 void GetByIdVariant::dumpInContext(PrintStream& out, DumpContext* context) const
 {
+    out.print("<");
+    out.print("id='", m_identifier ? *m_identifier : Identifier(), "', ");
     if (!isSet()) {
-        out.print("<empty>");
+        out.print("empty>");
         return;
     }
-    
-    out.print(
-        "<", inContext(structureSet(), context), ", ", inContext(m_conditionSet, context));
+    out.print(inContext(structureSet(), context), ", ", inContext(m_conditionSet, context));
     out.print(", offset = ", offset());
     if (m_callLinkStatus)
         out.print(", call = ", *m_callLinkStatus);
index 6865e57..83d978f 100644 (file)
@@ -83,6 +83,17 @@ public:
     void dumpInContext(PrintStream&, DumpContext*) const;
 
     Box<Identifier> identifier() const { return m_identifier; }
+
+    bool overlaps(const GetByIdVariant& other)
+    {
+        if (!!m_identifier != !!other.m_identifier)
+            return true;
+        if (m_identifier) {
+            if (m_identifier->impl() != other.m_identifier->impl())
+                return false;
+        }
+        return structureSet().overlaps(other.structureSet());
+    }
     
 private:
     friend class GetByStatus;
index c8e9c41..182c490 100644 (file)
@@ -52,7 +52,7 @@ bool GetByStatus::appendVariant(const GetByIdVariant& variant)
     return appendICStatusVariant(m_variants, variant);
 }
 
-GetByStatus GetByStatus::computeFromLLInt(CodeBlock* profiledBlock, BytecodeIndex bytecodeIndex)
+GetByStatus GetByStatus::computeFromLLInt(CodeBlock* profiledBlock, BytecodeIndex bytecodeIndex, TrackIdentifiers trackIdentifiers)
 {
     VM& vm = profiledBlock->vm();
     
@@ -91,6 +91,8 @@ GetByStatus GetByStatus::computeFromLLInt(CodeBlock* profiledBlock, BytecodeInde
     }
     }
 
+    ASSERT_UNUSED(trackIdentifiers, trackIdentifiers == TrackIdentifiers::No); // We could make this work in the future, but nobody needs it right now.
+
     if (!structureID)
         return GetByStatus(NoInformation, false);
 
@@ -111,7 +113,7 @@ GetByStatus GetByStatus::computeFromLLInt(CodeBlock* profiledBlock, BytecodeInde
     return result;
 }
 
-GetByStatus GetByStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, BytecodeIndex bytecodeIndex, ExitFlag didExit, CallLinkStatus::ExitSiteData callExitSiteData)
+GetByStatus GetByStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, BytecodeIndex bytecodeIndex, ExitFlag didExit, CallLinkStatus::ExitSiteData callExitSiteData, TrackIdentifiers trackIdentifiers)
 {
     ConcurrentJSLocker locker(profiledBlock->m_lock);
 
@@ -119,7 +121,7 @@ GetByStatus GetByStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map,
 
 #if ENABLE(DFG_JIT)
     result = computeForStubInfoWithoutExitSiteFeedback(
-        locker, profiledBlock, map.get(CodeOrigin(bytecodeIndex)).stubInfo, callExitSiteData);
+        locker, profiledBlock, map.get(CodeOrigin(bytecodeIndex)).stubInfo, callExitSiteData, trackIdentifiers);
     
     if (didExit)
         return result.slowVersion();
@@ -130,12 +132,33 @@ GetByStatus GetByStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map,
 #endif
 
     if (!result)
-        return computeFromLLInt(profiledBlock, bytecodeIndex);
+        return computeFromLLInt(profiledBlock, bytecodeIndex, trackIdentifiers);
     
     return result;
 }
 
 #if ENABLE(JIT)
+GetByStatus::GetByStatus(StubInfoSummary summary, StructureStubInfo& stubInfo)
+    : m_wasSeenInJIT(true)
+{
+    switch (summary) {
+    case StubInfoSummary::NoInformation:
+        m_state = NoInformation;
+        return;
+    case StubInfoSummary::Simple:
+    case StubInfoSummary::MakesCalls:
+        RELEASE_ASSERT_NOT_REACHED();
+        return;
+    case StubInfoSummary::TakesSlowPath:
+        m_state = stubInfo.tookSlowPath ? ObservedTakesSlowPath : LikelyTakesSlowPath;
+        return;
+    case StubInfoSummary::TakesSlowPathAndMakesCalls:
+        m_state = stubInfo.tookSlowPath ? ObservedSlowPathAndMakesCalls : MakesCalls;
+        return;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
 GetByStatus::GetByStatus(const ModuleNamespaceAccessCase& accessCase)
     : m_moduleNamespaceData(Box<ModuleNamespaceData>::create(ModuleNamespaceData { accessCase.moduleNamespaceObject(), accessCase.moduleEnvironment(), accessCase.scopeOffset(), accessCase.identifier() }))
     , m_state(ModuleNamespace)
@@ -144,11 +167,11 @@ GetByStatus::GetByStatus(const ModuleNamespaceAccessCase& accessCase)
 }
 
 GetByStatus GetByStatus::computeForStubInfoWithoutExitSiteFeedback(
-    const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, CallLinkStatus::ExitSiteData callExitSiteData)
+    const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, CallLinkStatus::ExitSiteData callExitSiteData, TrackIdentifiers trackIdentifiers)
 {
     StubInfoSummary summary = StructureStubInfo::summary(stubInfo);
     if (!isInlineable(summary))
-        return GetByStatus(summary);
+        return GetByStatus(summary, *stubInfo);
     
     // Finally figure out if we can derive an access strategy.
     GetByStatus result;
@@ -161,17 +184,19 @@ GetByStatus GetByStatus::computeForStubInfoWithoutExitSiteFeedback(
     case CacheType::GetByIdSelf: {
         Structure* structure = stubInfo->u.byIdSelf.baseObjectStructure.get();
         if (structure->takesSlowPathInDFGForImpureProperty())
-            return GetByStatus(JSC::slowVersion(summary));
+            return GetByStatus(JSC::slowVersion(summary), *stubInfo);
         Box<Identifier> identifier = stubInfo->getByIdSelfIdentifier();
         UniquedStringImpl* uid = identifier->impl();
         RELEASE_ASSERT(uid);
+        if (trackIdentifiers == TrackIdentifiers::No)
+            identifier = nullptr;
         GetByIdVariant variant(WTFMove(identifier));
         unsigned attributes;
         variant.m_offset = structure->getConcurrently(uid, attributes);
         if (!isValidOffset(variant.m_offset))
-            return GetByStatus(JSC::slowVersion(summary));
+            return GetByStatus(JSC::slowVersion(summary), *stubInfo);
         if (attributes & PropertyAttribute::CustomAccessorOrValue)
-            return GetByStatus(JSC::slowVersion(summary));
+            return GetByStatus(JSC::slowVersion(summary), *stubInfo);
         
         variant.m_structureSet.add(structure);
         bool didAppend = result.appendVariant(variant);
@@ -194,16 +219,16 @@ GetByStatus GetByStatus::computeForStubInfoWithoutExitSiteFeedback(
         for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) {
             const AccessCase& access = list->at(listIndex);
             if (access.viaProxy())
-                return GetByStatus(JSC::slowVersion(summary));
+                return GetByStatus(JSC::slowVersion(summary), *stubInfo);
 
             if (access.usesPolyProto())
-                return GetByStatus(JSC::slowVersion(summary));
+                return GetByStatus(JSC::slowVersion(summary), *stubInfo);
 
             if (!access.requiresIdentifierNameMatch()) {
                 // FIXME: We could use this for indexed loads in the future. This is pretty solid profiling
                 // information, and probably better than ArrayProfile when it's available.
                 // https://bugs.webkit.org/show_bug.cgi?id=204215
-                return GetByStatus(JSC::slowVersion(summary));
+                return GetByStatus(JSC::slowVersion(summary), *stubInfo);
             }
             
             Structure* structure = access.structure();
@@ -214,7 +239,7 @@ GetByStatus GetByStatus::computeForStubInfoWithoutExitSiteFeedback(
                 // shouldn't have to use value profiling to discover something that the AccessCase
                 // could have told us. But, it works well enough. So, our only concern here is to not
                 // crash on null structure.
-                return GetByStatus(JSC::slowVersion(summary));
+                return GetByStatus(JSC::slowVersion(summary), *stubInfo);
             }
             
             ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor(
@@ -225,7 +250,7 @@ GetByStatus GetByStatus::computeForStubInfoWithoutExitSiteFeedback(
                 continue;
                  
             case ComplexGetStatus::TakesSlowPath:
-                return GetByStatus(JSC::slowVersion(summary));
+                return GetByStatus(JSC::slowVersion(summary), *stubInfo);
                  
             case ComplexGetStatus::Inlineable: {
                 std::unique_ptr<CallLinkStatus> callLinkStatus;
@@ -255,7 +280,7 @@ GetByStatus GetByStatus::computeForStubInfoWithoutExitSiteFeedback(
                 case AccessCase::CustomAccessorGetter: {
                     customAccessorGetter = access.as<GetterSetterAccessCase>().customAccessor();
                     if (!access.as<GetterSetterAccessCase>().domAttribute())
-                        return GetByStatus(JSC::slowVersion(summary));
+                        return GetByStatus(JSC::slowVersion(summary), *stubInfo);
                     domAttribute = WTF::makeUnique<DOMAttributeAnnotation>(*access.as<GetterSetterAccessCase>().domAttribute());
                     haveDOMAttribute = true;
                     result.m_state = Custom;
@@ -264,28 +289,28 @@ GetByStatus GetByStatus::computeForStubInfoWithoutExitSiteFeedback(
                 default: {
                     // FIXME: It would be totally sweet to support more of these at some point in the
                     // future. https://bugs.webkit.org/show_bug.cgi?id=133052
-                    return GetByStatus(JSC::slowVersion(summary));
+                    return GetByStatus(JSC::slowVersion(summary), *stubInfo);
                 } }
 
                 ASSERT((AccessCase::Miss == access.type()) == (access.offset() == invalidOffset));
                 GetByIdVariant variant(
-                    access.identifier(), StructureSet(structure), complexGetStatus.offset(),
+                    trackIdentifiers == TrackIdentifiers::Yes ? access.identifier() : Box<Identifier>(nullptr), StructureSet(structure), complexGetStatus.offset(),
                     complexGetStatus.conditionSet(), WTFMove(callLinkStatus),
                     intrinsicFunction,
                     customAccessorGetter,
                     WTFMove(domAttribute));
 
                 if (!result.appendVariant(variant))
-                    return GetByStatus(JSC::slowVersion(summary));
+                    return GetByStatus(JSC::slowVersion(summary), *stubInfo);
 
                 if (haveDOMAttribute) {
                     // Give up when custom accesses are not merged into one.
                     if (result.numVariants() != 1)
-                        return GetByStatus(JSC::slowVersion(summary));
+                        return GetByStatus(JSC::slowVersion(summary), *stubInfo);
                 } else {
                     // Give up when custom access and simple access are mixed.
                     if (result.m_state == Custom)
-                        return GetByStatus(JSC::slowVersion(summary));
+                        return GetByStatus(JSC::slowVersion(summary), *stubInfo);
                 }
                 break;
             } }
@@ -295,7 +320,7 @@ GetByStatus GetByStatus::computeForStubInfoWithoutExitSiteFeedback(
     }
         
     default:
-        return GetByStatus(JSC::slowVersion(summary));
+        return GetByStatus(JSC::slowVersion(summary), *stubInfo);
     }
     
     RELEASE_ASSERT_NOT_REACHED();
@@ -304,7 +329,7 @@ GetByStatus GetByStatus::computeForStubInfoWithoutExitSiteFeedback(
 
 GetByStatus GetByStatus::computeFor(
     CodeBlock* profiledBlock, ICStatusMap& baselineMap,
-    ICStatusContextStack& icContextStack, CodeOrigin codeOrigin)
+    ICStatusContextStack& icContextStack, CodeOrigin codeOrigin, TrackIdentifiers trackIdentifiers)
 {
     BytecodeIndex bytecodeIndex = codeOrigin.bytecodeIndex();
     CallLinkStatus::ExitSiteData callExitSiteData = CallLinkStatus::computeExitSiteData(profiledBlock, bytecodeIndex);
@@ -319,7 +344,7 @@ GetByStatus GetByStatus::computeFor(
                 // inlined and not-inlined.
                 GetByStatus baselineResult = computeFor(
                     profiledBlock, baselineMap, bytecodeIndex, didExit,
-                    callExitSiteData);
+                    callExitSiteData, trackIdentifiers);
                 baselineResult.merge(result);
                 return baselineResult;
             }
@@ -333,7 +358,7 @@ GetByStatus GetByStatus::computeFor(
             {
                 ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock);
                 result = computeForStubInfoWithoutExitSiteFeedback(
-                    locker, context->optimizedCodeBlock, status.stubInfo, callExitSiteData);
+                    locker, context->optimizedCodeBlock, status.stubInfo, callExitSiteData, trackIdentifiers);
             }
             if (result.isSet())
                 return bless(result);
@@ -343,7 +368,7 @@ GetByStatus GetByStatus::computeFor(
             return bless(*status.getStatus);
     }
     
-    return computeFor(profiledBlock, baselineMap, bytecodeIndex, didExit, callExitSiteData);
+    return computeFor(profiledBlock, baselineMap, bytecodeIndex, didExit, callExitSiteData, trackIdentifiers);
 }
 
 GetByStatus GetByStatus::computeFor(const StructureSet& set, UniquedStringImpl* uid)
@@ -359,7 +384,7 @@ GetByStatus GetByStatus::computeFor(const StructureSet& set, UniquedStringImpl*
         return GetByStatus();
 
     if (parseIndex(*uid))
-        return GetByStatus(TakesSlowPath);
+        return GetByStatus(LikelyTakesSlowPath);
     
     GetByStatus result;
     result.m_state = Simple;
@@ -367,22 +392,22 @@ GetByStatus GetByStatus::computeFor(const StructureSet& set, UniquedStringImpl*
     for (unsigned i = 0; i < set.size(); ++i) {
         Structure* structure = set[i];
         if (structure->typeInfo().overridesGetOwnPropertySlot() && structure->typeInfo().type() != GlobalObjectType)
-            return GetByStatus(TakesSlowPath);
+            return GetByStatus(LikelyTakesSlowPath);
         
         if (!structure->propertyAccessesAreCacheable())
-            return GetByStatus(TakesSlowPath);
+            return GetByStatus(LikelyTakesSlowPath);
         
         unsigned attributes;
         PropertyOffset offset = structure->getConcurrently(uid, attributes);
         if (!isValidOffset(offset))
-            return GetByStatus(TakesSlowPath); // It's probably a prototype lookup. Give up on life for now, even though we could totally be way smarter about it.
+            return GetByStatus(LikelyTakesSlowPath); // It's probably a prototype lookup. Give up on life for now, even though we could totally be way smarter about it.
         if (attributes & PropertyAttribute::Accessor)
             return GetByStatus(MakesCalls); // We could be smarter here, like strength-reducing this to a Call.
         if (attributes & PropertyAttribute::CustomAccessorOrValue)
-            return GetByStatus(TakesSlowPath);
+            return GetByStatus(LikelyTakesSlowPath);
         
         if (!result.appendVariant(GetByIdVariant(nullptr, structure, offset)))
-            return GetByStatus(TakesSlowPath);
+            return GetByStatus(LikelyTakesSlowPath);
     }
     
     return result;
@@ -393,7 +418,8 @@ bool GetByStatus::makesCalls() const
 {
     switch (m_state) {
     case NoInformation:
-    case TakesSlowPath:
+    case LikelyTakesSlowPath:
+    case ObservedTakesSlowPath:
     case Custom:
     case ModuleNamespace:
         return false;
@@ -404,6 +430,7 @@ bool GetByStatus::makesCalls() const
         }
         return false;
     case MakesCalls:
+    case ObservedSlowPathAndMakesCalls:
         return true;
     }
     RELEASE_ASSERT_NOT_REACHED();
@@ -413,7 +440,9 @@ bool GetByStatus::makesCalls() const
 
 GetByStatus GetByStatus::slowVersion() const
 {
-    return GetByStatus(makesCalls() ? MakesCalls : TakesSlowPath, wasSeenInJIT());
+    if (observedStructureStubInfoSlowPath())
+        return GetByStatus(makesCalls() ? ObservedSlowPathAndMakesCalls : ObservedTakesSlowPath, wasSeenInJIT());
+    return GetByStatus(makesCalls() ? MakesCalls : LikelyTakesSlowPath, wasSeenInJIT());
 }
 
 void GetByStatus::merge(const GetByStatus& other)
@@ -422,7 +451,10 @@ void GetByStatus::merge(const GetByStatus& other)
         return;
     
     auto mergeSlow = [&] () {
-        *this = GetByStatus((makesCalls() || other.makesCalls()) ? MakesCalls : TakesSlowPath);
+        if (observedStructureStubInfoSlowPath() || other.observedStructureStubInfoSlowPath())
+            *this = GetByStatus((makesCalls() || other.makesCalls()) ? ObservedSlowPathAndMakesCalls : ObservedTakesSlowPath);
+        else
+            *this = GetByStatus((makesCalls() || other.makesCalls()) ? MakesCalls : LikelyTakesSlowPath);
     };
     
     switch (m_state) {
@@ -456,8 +488,10 @@ void GetByStatus::merge(const GetByStatus& other)
         
         return;
         
-    case TakesSlowPath:
+    case LikelyTakesSlowPath:
+    case ObservedTakesSlowPath:
     case MakesCalls:
+    case ObservedSlowPathAndMakesCalls:
         return mergeSlow();
     }
     
@@ -537,12 +571,18 @@ void GetByStatus::dump(PrintStream& out) const
     case ModuleNamespace:
         out.print("ModuleNamespace");
         break;
-    case TakesSlowPath:
-        out.print("TakesSlowPath");
+    case LikelyTakesSlowPath:
+        out.print("LikelyTakesSlowPath");
+        break;
+    case ObservedTakesSlowPath:
+        out.print("ObservedTakesSlowPath");
         break;
     case MakesCalls:
         out.print("MakesCalls");
         break;
+    case ObservedSlowPathAndMakesCalls:
+        out.print("ObservedSlowPathAndMakesCalls");
+        break;
     }
     out.print(", ", listDump(m_variants), ", seenInJIT = ", m_wasSeenInJIT, ")");
 }
index 22b7bbe..e133eaa 100644 (file)
@@ -56,10 +56,19 @@ public:
         Custom,
         // It's cached for an access to a module namespace object's binding.
         ModuleNamespace,
-        // It's known to often take slow path.
-        TakesSlowPath,
-        // It's known to take paths that make calls.
+        // It will likely take the slow path.
+        LikelyTakesSlowPath,
+        // It's known to take slow path. We also observed that the slow path was taken on StructureStubInfo.
+        ObservedTakesSlowPath,
+        // It will likely take the slow path and will make calls.
         MakesCalls,
+        // It known to take paths that make calls. We also observed that the slow path was taken on StructureStubInfo.
+        ObservedSlowPathAndMakesCalls ,
+    };
+
+    enum class TrackIdentifiers : uint8_t {
+        No,  // Used for get_by_id
+        Yes, // Used for get_by_val
     };
 
     GetByStatus()
@@ -70,29 +79,10 @@ public:
     explicit GetByStatus(State state)
         : m_state(state)
     {
-        ASSERT(state == NoInformation || state == TakesSlowPath || state == MakesCalls);
+        ASSERT(state == NoInformation || state == LikelyTakesSlowPath || state == ObservedTakesSlowPath || state == MakesCalls || state == ObservedSlowPathAndMakesCalls);
     }
     
-    explicit GetByStatus(StubInfoSummary summary)
-        : m_wasSeenInJIT(true)
-    {
-        switch (summary) {
-        case StubInfoSummary::NoInformation:
-            m_state = NoInformation;
-            return;
-        case StubInfoSummary::Simple:
-        case StubInfoSummary::MakesCalls:
-            RELEASE_ASSERT_NOT_REACHED();
-            return;
-        case StubInfoSummary::TakesSlowPath:
-            m_state = TakesSlowPath;
-            return;
-        case StubInfoSummary::TakesSlowPathAndMakesCalls:
-            m_state = MakesCalls;
-            return;
-        }
-        RELEASE_ASSERT_NOT_REACHED();
-    }
+    explicit GetByStatus(StubInfoSummary, StructureStubInfo&);
     
     GetByStatus(
         State state, bool wasSeenInJIT)
@@ -101,7 +91,7 @@ public:
     {
     }
     
-    static GetByStatus computeFor(CodeBlock* baselineBlock, ICStatusMap& baselineMap, ICStatusContextStack& dfgContextStack, CodeOrigin);
+    static GetByStatus computeFor(CodeBlock* baselineBlock, ICStatusMap& baselineMap, ICStatusContextStack& dfgContextStack, CodeOrigin, TrackIdentifiers);
     static GetByStatus computeFor(const StructureSet&, UniquedStringImpl*);
 
     State state() const { return m_state; }
@@ -117,15 +107,14 @@ public:
     const GetByIdVariant& at(size_t index) const { return m_variants[index]; }
     const GetByIdVariant& operator[](size_t index) const { return at(index); }
 
-    bool takesSlowPath() const { return m_state == TakesSlowPath || m_state == MakesCalls || m_state == Custom || m_state == ModuleNamespace; }
+    bool takesSlowPath() const { return m_state == LikelyTakesSlowPath || m_state == ObservedTakesSlowPath || m_state == MakesCalls || m_state == ObservedSlowPathAndMakesCalls || m_state == Custom || m_state == ModuleNamespace; }
+    bool observedStructureStubInfoSlowPath() const { return m_state == ObservedTakesSlowPath || m_state == ObservedSlowPathAndMakesCalls; }
     bool makesCalls() const;
     
     GetByStatus slowVersion() const;
     
     bool wasSeenInJIT() const { return m_wasSeenInJIT; }
     
-    void merge(const GetByStatus&);
-    
     // Attempts to reduce the set of variants to fit the given structure set. This may be approximate.
     void filter(const StructureSet&);
 
@@ -143,13 +132,15 @@ public:
     Box<Identifier> singleIdentifier() const;
     
 private:
+    void merge(const GetByStatus&);
+    
 #if ENABLE(JIT)
     GetByStatus(const ModuleNamespaceAccessCase&);
     static GetByStatus computeForStubInfoWithoutExitSiteFeedback(
-        const ConcurrentJSLocker&, CodeBlock* profiledBlock, StructureStubInfo*, CallLinkStatus::ExitSiteData);
+        const ConcurrentJSLocker&, CodeBlock* profiledBlock, StructureStubInfo*, CallLinkStatus::ExitSiteData, TrackIdentifiers);
 #endif
-    static GetByStatus computeFromLLInt(CodeBlock*, BytecodeIndex);
-    static GetByStatus computeFor(CodeBlock*, ICStatusMap&, BytecodeIndex, ExitFlag, CallLinkStatus::ExitSiteData);
+    static GetByStatus computeFromLLInt(CodeBlock*, BytecodeIndex, TrackIdentifiers);
+    static GetByStatus computeFor(CodeBlock*, ICStatusMap&, BytecodeIndex, ExitFlag, CallLinkStatus::ExitSiteData, TrackIdentifiers);
 
     struct ModuleNamespaceData {
         JSModuleNamespaceObject* m_moduleNamespaceObject { nullptr };
index f37dd9c..f81fab0 100644 (file)
@@ -43,7 +43,7 @@ bool appendICStatusVariant(VariantVectorType& variants, const VariantType& varia
             for (unsigned j = 0; j < variants.size(); ++j) {
                 if (i == j)
                     continue;
-                if (variants[j].structureSet().overlaps(mergedVariant.structureSet()))
+                if (variants[j].overlaps(mergedVariant))
                     return false;
             }
             return true;
@@ -54,7 +54,7 @@ bool appendICStatusVariant(VariantVectorType& variants, const VariantType& varia
     // overlap but it's possible that an inline cache got into a weird state. We are
     // defensive and bail if we detect crazy.
     for (unsigned i = 0; i < variants.size(); ++i) {
-        if (variants[i].structureSet().overlaps(variant.structureSet()))
+        if (variants[i].overlaps(variant))
             return false;
     }
     
index 589f9ff..51bf25a 100644 (file)
@@ -63,6 +63,11 @@ public:
     void dump(PrintStream&) const;
     void dumpInContext(PrintStream&, DumpContext*) const;
 
+    bool overlaps(const InByIdVariant& other)
+    {
+        return structureSet().overlaps(other.structureSet());
+    }
+
 private:
     friend class InByIdStatus;
 
index 1e5f790..bdaff5a 100644 (file)
@@ -53,6 +53,11 @@ public:
     
     void dump(PrintStream&) const;
     void dumpInContext(PrintStream&, DumpContext*) const;
+
+    bool overlaps(const InstanceOfVariant& other)
+    {
+        return structureSet().overlaps(other.structureSet());
+    }
     
 private:
     friend class InstanceOfStatus;
index f9b0168..8f6090f 100644 (file)
@@ -138,6 +138,11 @@ public:
     void dump(PrintStream&) const;
     void dumpInContext(PrintStream&, DumpContext*) const;
 
+    bool overlaps(const PutByIdVariant& other)
+    {
+        return structureSet().overlaps(other.structureSet());
+    }
+
 private:
     bool attemptToMergeTransitionWithReplace(const PutByIdVariant& replace);
     
index f1392bb..a930996 100644 (file)
@@ -283,9 +283,10 @@ void StructureStubInfo::visitWeakReferences(CodeBlock* codeBlock)
 {
     VM& vm = codeBlock->vm();
     
-    bufferedStructures.genericFilter(
-        [&] (Structure* structure) -> bool {
-            return vm.heap.isMarked(structure);
+    bufferedStructures.removeIf(
+        [&] (auto& pair) -> bool {
+            Structure* structure = pair.first;
+            return !vm.heap.isMarked(structure);
         });
 
     switch (m_cacheType) {
index 34b39c6..578e597 100644 (file)
@@ -94,7 +94,7 @@ public:
     // This returns true if it has marked everything that it will ever mark.
     bool propagateTransitions(SlotVisitor&);
         
-    ALWAYS_INLINE bool considerCaching(VM& vm, CodeBlock* codeBlock, Structure* structure)
+    ALWAYS_INLINE bool considerCaching(VM& vm, CodeBlock* codeBlock, Structure* structure, UniquedStringImpl* impl = nullptr)
     {
         DisallowGC disallowGC;
 
@@ -154,7 +154,7 @@ public:
             // NOTE: This will behave oddly for InstanceOf if the user varies the prototype but not
             // the base's structure. That seems unlikely for the canonical use of instanceof, where
             // the prototype is fixed.
-            bool isNewlyAdded = bufferedStructures.add(structure);
+            bool isNewlyAdded = bufferedStructures.add({ structure, impl }).isNewEntry;
             if (isNewlyAdded)
                 vm.heap.writeBarrier(codeBlock);
             return isNewlyAdded;
@@ -188,11 +188,13 @@ public:
         return m_getByIdSelfIdentifier;
     }
     
+private:
     // Represents those structures that already have buffered AccessCases in the PolymorphicAccess.
     // Note that it's always safe to clear this. If we clear it prematurely, then if we see the same
     // structure again during this buffering countdown, we will create an AccessCase object for it.
     // That's not so bad - we'll get rid of the redundant ones once we regenerate.
-    StructureSet bufferedStructures;
+    HashSet<std::pair<Structure*, RefPtr<UniquedStringImpl>>> bufferedStructures;
+public:
     
     struct {
         CodeLocationLabel<JITStubRoutinePtrTag> start; // This is either the start of the inline IC for *byId caches. or the location of patchable jump for 'instanceof' caches.
index 816cc53..aaeab19 100644 (file)
@@ -4713,7 +4713,7 @@ void ByteCodeParser::parseGetById(const Instruction* currentInstruction)
     GetByStatus getByStatus = GetByStatus::computeFor(
         m_inlineStackTop->m_profiledBlock,
         m_inlineStackTop->m_baselineMap, m_icContextStack,
-        currentCodeOrigin());
+        currentCodeOrigin(), GetByStatus::TrackIdentifiers::No);
 
     AccessType type = AccessType::GetById;
     unsigned opcodeLength = currentInstruction->size();
@@ -5622,7 +5622,7 @@ void ByteCodeParser::parseBlock(unsigned limit)
             Node* base = get(bytecode.m_base);
             Node* property = get(bytecode.m_property);
             bool shouldCompileAsGetById = false;
-            GetByStatus getByStatus = GetByStatus::computeFor(m_inlineStackTop->m_profiledBlock, m_inlineStackTop->m_baselineMap, m_icContextStack, currentCodeOrigin());
+            GetByStatus getByStatus = GetByStatus::computeFor(m_inlineStackTop->m_profiledBlock, m_inlineStackTop->m_baselineMap, m_icContextStack, currentCodeOrigin(), GetByStatus::TrackIdentifiers::Yes);
             unsigned identifierNumber = 0;
             {
                 // FIXME: When the bytecode is not compiled in the baseline JIT, byValInfo becomes null.
@@ -5655,7 +5655,7 @@ void ByteCodeParser::parseBlock(unsigned limit)
                 Node* getByVal = addToGraph(Node::VarArg, GetByVal, OpInfo(arrayMode.asWord()), OpInfo(prediction));
                 m_exitOK = false; // GetByVal must be treated as if it clobbers exit state, since FixupPhase may make it generic.
                 set(bytecode.m_dst, getByVal);
-                if (getByStatus.takesSlowPath())
+                if (getByStatus.observedStructureStubInfoSlowPath())
                     m_graph.m_slowGetByVal.add(getByVal);
             }
 
index 202e91c..b358b1a 100644 (file)
@@ -2036,7 +2036,7 @@ EncodedJSValue JIT_OPERATION operationGetByValOptimize(JSGlobalObject* globalObj
             return JSValue::encode(baseValue.getPropertySlot(globalObject, propertyName, [&] (bool found, PropertySlot& slot) -> JSValue {
                 LOG_IC((ICEvent::OperationGetByValOptimize, baseValue.classInfoOrNull(vm), propertyName, baseValue == slot.slotBase())); 
                 
-                if (stubInfo->considerCaching(vm, codeBlock, baseValue.structureOrNull()))
+                if (stubInfo->considerCaching(vm, codeBlock, baseValue.structureOrNull(), propertyName.impl()))
                     repatchGetBy(globalObject, codeBlock, baseValue, propertyName, slot, *stubInfo, GetByKind::NormalByVal);
                 return found ? slot.getValue(globalObject, propertyName) : jsUndefined();
             }));