Title: [256977] branches/safari-609.1.20.1-branch
Revision
256977
Author
repst...@apple.com
Date
2020-02-19 15:38:15 -0800 (Wed, 19 Feb 2020)

Log Message

Cherry-pick r256779. rdar://problem/59576798

    [JSC] JITThunk should be HashSet<Weak<NativeExecutable>> with appropriate GC weakness handling
    https://bugs.webkit.org/show_bug.cgi?id=207715

    Reviewed by Darin Adler.

    JSTests:

    * stress/stress-jitthunks.js: Added.
    (let.set newGlobal):
    (set catch):

    Source/_javascript_Core:

    This patch refines JITThunks GC-aware Weak hash map for NativeExecutable. Previously, we have
    HashMap<std::tuple<TaggedNativeFunction, TaggedNativeFunction, String>, Weak<NativeExecutable>> table.
    But this is not good because the first tuple's information is already in NativeExecutable.
    But we were using this design since Weak<NativeExecutable> can be nullified because of Weak<>. If this
    happens, we could have invalid Entry in HashMap which does not have corresponding values. This will
    cause crash when rehasing requires hash code for this entry.

    But this HashMap is very bad in terms of memory usage. Each entry has 32 bytes, and this table gets enough
    large. We identified that this table is consuming much memory in Membuster. So it is worth designing
    carefully crafted data structure which only holds Weak<NativeExecutable> by leveraging the deep interaction
    with our GC implementation.

    This patch implements new design of JITThunks, which uses HashSet<Weak<NativeExecutable>> and carefully crafted
    HashTraits / KeyTraits to handle Weak<> well.

    1. Each Weak should have finalizer, and this finalizer should remove dead Weak<NativeExecutable> from HashSet.

        This is ensuring that all the keys in HashSet is, even if Weak<> is saying it is Dead, it still has an way
        to access content of NativeExecutable if the content is not a JS objects. For example, we can get function
        pointer from dead Weak<NativeExecutable> if it is not yet finalized. Since we remove all finalized Weak<>
        from the table, this finalizer mechanism allows us to access function pointers etc. from Weak<NativeExecutable>
        so long as it is held in this table.

    2. Getting NativeExecutable* from JITThunks should have special protocol.

        When getting NativeExecutable* from JITThunks, we do the following,

        1. First, we check we have an Entry in JITThunks. If it does not exist, we should insert it anyway.
            1.1. If it exists, we should check whether this Weak<NativeExecutable> is dead or not. It is possible that
                 dead one is still in the table because "dead" does not mean that it is "finalized". Until finalizing happens (and
                 it can be delayed by incremental-sweeper), Weak<NativeExecutable> can be dead but still accessible. So the table
                 is still holding dead one. If we get dead one, we should insert a new one.
            1.2. If it is not dead, we return it.
        2. Second, we create a new NativeExecutable and insert it. In that case, it is possible that the table already has Weak<NativeExecutable>,
           but it is dead. In that case, we need to explicitly replace it with newly created one since old one is holding old content. If we
           replaced, finalizer of Weak<> will not be invoked since it immediately deallocates Weak<>. So, it does not happen that this newly
           inserted NativeExecutable* is removed by the finalizer registered by the old Weak<>.

    This change makes memory usage of JITThunks table 1/4.

    * heap/Weak.cpp:
    (JSC::weakClearSlowCase):
    * heap/Weak.h:
    (JSC::Weak::Weak):
    (JSC::Weak::isHashTableEmptyValue const):
    (JSC::Weak::unsafeImpl const):
    (WTF::HashTraits<JSC::Weak<T>>::isEmptyValue):
    * heap/WeakInlines.h:
    (JSC::Weak<T>::Weak):
    * jit/JITThunks.cpp:
    (JSC::JITThunks::JITThunks):
    (JSC::JITThunks::WeakNativeExecutableHash::hash):
    (JSC::JITThunks::WeakNativeExecutableHash::equal):
    (JSC::JITThunks::HostKeySearcher::hash):
    (JSC::JITThunks::HostKeySearcher::equal):
    (JSC::JITThunks::NativeExecutableTranslator::hash):
    (JSC::JITThunks::NativeExecutableTranslator::equal):
    (JSC::JITThunks::NativeExecutableTranslator::translate):
    (JSC::JITThunks::finalize):
    (JSC::JITThunks::hostFunctionStub):
    (JSC::JITThunks::clearHostFunctionStubs): Deleted.
    * jit/JITThunks.h:
    * runtime/NativeExecutable.h:
    * tools/JSDollarVM.cpp:
    (JSC::functionGCSweepAsynchronously):
    (JSC::functionCreateEmptyFunctionWithName):
    (JSC::JSDollarVM::finishCreation):

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

Modified Paths

Added Paths

Diff

Modified: branches/safari-609.1.20.1-branch/JSTests/ChangeLog (256976 => 256977)


--- branches/safari-609.1.20.1-branch/JSTests/ChangeLog	2020-02-19 23:38:08 UTC (rev 256976)
+++ branches/safari-609.1.20.1-branch/JSTests/ChangeLog	2020-02-19 23:38:15 UTC (rev 256977)
@@ -1,5 +1,103 @@
 2020-02-19  Alan Coon  <alanc...@apple.com>
 
+        Cherry-pick r256779. rdar://problem/59576798
+
+    [JSC] JITThunk should be HashSet<Weak<NativeExecutable>> with appropriate GC weakness handling
+    https://bugs.webkit.org/show_bug.cgi?id=207715
+    
+    Reviewed by Darin Adler.
+    
+    JSTests:
+    
+    * stress/stress-jitthunks.js: Added.
+    (let.set newGlobal):
+    (set catch):
+    
+    Source/_javascript_Core:
+    
+    This patch refines JITThunks GC-aware Weak hash map for NativeExecutable. Previously, we have
+    HashMap<std::tuple<TaggedNativeFunction, TaggedNativeFunction, String>, Weak<NativeExecutable>> table.
+    But this is not good because the first tuple's information is already in NativeExecutable.
+    But we were using this design since Weak<NativeExecutable> can be nullified because of Weak<>. If this
+    happens, we could have invalid Entry in HashMap which does not have corresponding values. This will
+    cause crash when rehasing requires hash code for this entry.
+    
+    But this HashMap is very bad in terms of memory usage. Each entry has 32 bytes, and this table gets enough
+    large. We identified that this table is consuming much memory in Membuster. So it is worth designing
+    carefully crafted data structure which only holds Weak<NativeExecutable> by leveraging the deep interaction
+    with our GC implementation.
+    
+    This patch implements new design of JITThunks, which uses HashSet<Weak<NativeExecutable>> and carefully crafted
+    HashTraits / KeyTraits to handle Weak<> well.
+    
+    1. Each Weak should have finalizer, and this finalizer should remove dead Weak<NativeExecutable> from HashSet.
+    
+        This is ensuring that all the keys in HashSet is, even if Weak<> is saying it is Dead, it still has an way
+        to access content of NativeExecutable if the content is not a JS objects. For example, we can get function
+        pointer from dead Weak<NativeExecutable> if it is not yet finalized. Since we remove all finalized Weak<>
+        from the table, this finalizer mechanism allows us to access function pointers etc. from Weak<NativeExecutable>
+        so long as it is held in this table.
+    
+    2. Getting NativeExecutable* from JITThunks should have special protocol.
+    
+        When getting NativeExecutable* from JITThunks, we do the following,
+    
+        1. First, we check we have an Entry in JITThunks. If it does not exist, we should insert it anyway.
+            1.1. If it exists, we should check whether this Weak<NativeExecutable> is dead or not. It is possible that
+                 dead one is still in the table because "dead" does not mean that it is "finalized". Until finalizing happens (and
+                 it can be delayed by incremental-sweeper), Weak<NativeExecutable> can be dead but still accessible. So the table
+                 is still holding dead one. If we get dead one, we should insert a new one.
+            1.2. If it is not dead, we return it.
+        2. Second, we create a new NativeExecutable and insert it. In that case, it is possible that the table already has Weak<NativeExecutable>,
+           but it is dead. In that case, we need to explicitly replace it with newly created one since old one is holding old content. If we
+           replaced, finalizer of Weak<> will not be invoked since it immediately deallocates Weak<>. So, it does not happen that this newly
+           inserted NativeExecutable* is removed by the finalizer registered by the old Weak<>.
+    
+    This change makes memory usage of JITThunks table 1/4.
+    
+    * heap/Weak.cpp:
+    (JSC::weakClearSlowCase):
+    * heap/Weak.h:
+    (JSC::Weak::Weak):
+    (JSC::Weak::isHashTableEmptyValue const):
+    (JSC::Weak::unsafeImpl const):
+    (WTF::HashTraits<JSC::Weak<T>>::isEmptyValue):
+    * heap/WeakInlines.h:
+    (JSC::Weak<T>::Weak):
+    * jit/JITThunks.cpp:
+    (JSC::JITThunks::JITThunks):
+    (JSC::JITThunks::WeakNativeExecutableHash::hash):
+    (JSC::JITThunks::WeakNativeExecutableHash::equal):
+    (JSC::JITThunks::HostKeySearcher::hash):
+    (JSC::JITThunks::HostKeySearcher::equal):
+    (JSC::JITThunks::NativeExecutableTranslator::hash):
+    (JSC::JITThunks::NativeExecutableTranslator::equal):
+    (JSC::JITThunks::NativeExecutableTranslator::translate):
+    (JSC::JITThunks::finalize):
+    (JSC::JITThunks::hostFunctionStub):
+    (JSC::JITThunks::clearHostFunctionStubs): Deleted.
+    * jit/JITThunks.h:
+    * runtime/NativeExecutable.h:
+    * tools/JSDollarVM.cpp:
+    (JSC::functionGCSweepAsynchronously):
+    (JSC::functionCreateEmptyFunctionWithName):
+    (JSC::JSDollarVM::finishCreation):
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@256779 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2020-02-17  Yusuke Suzuki  <ysuz...@apple.com>
+
+            [JSC] JITThunk should be HashSet<Weak<NativeExecutable>> with appropriate GC weakness handling
+            https://bugs.webkit.org/show_bug.cgi?id=207715
+
+            Reviewed by Darin Adler.
+
+            * stress/stress-jitthunks.js: Added.
+            (let.set newGlobal):
+            (set catch):
+
+2020-02-19  Alan Coon  <alanc...@apple.com>
+
         Cherry-pick r256766. rdar://problem/59576813
 
     [Wasm] REGRESSION(r256665): Wasm->JS call IC needs to save memory size register

Added: branches/safari-609.1.20.1-branch/JSTests/stress/stress-jitthunks.js (0 => 256977)


--- branches/safari-609.1.20.1-branch/JSTests/stress/stress-jitthunks.js	                        (rev 0)
+++ branches/safari-609.1.20.1-branch/JSTests/stress/stress-jitthunks.js	2020-02-19 23:38:15 UTC (rev 256977)
@@ -0,0 +1,20 @@
+{
+    let functions = [];
+    for (var i = 0; i < 1e5; ++i)
+        functions.push($vm.createEmptyFunctionWithName(i));
+    let newGlobal = $vm.createGlobalObject();
+    newGlobal.WeakMap.prototype.set;
+    newGlobal.WeakMap.prototype.set = null; // Remove reference!
+    $vm.gcSweepAsynchronously(); // Mark WeakMap#set dead, but since we have so many NativeExecutables, it is dead, but still in JITThunks.
+    newGlobal = $vm.createGlobalObject();
+    let set = newGlobal.WeakMap.prototype.set; // Accessing to HashTable, which found a dead previous NativeExecutable, and replace it with new one.
+    $vm.gc(); // This does not invoke finalizer for WeakMap#set since we already replaced it. And previous one is finally destroyed.
+    try {
+        set(); // Of course, it works. It is using a new NativeExecutable for WeakMap#set, not using dead one.
+    } catch { };
+    for (var i = 0; i < 1e3; ++i)
+        functions.push($vm.createEmptyFunctionWithName(i));
+    try {
+        set();
+    } catch { };
+}

Modified: branches/safari-609.1.20.1-branch/Source/_javascript_Core/ChangeLog (256976 => 256977)


--- branches/safari-609.1.20.1-branch/Source/_javascript_Core/ChangeLog	2020-02-19 23:38:08 UTC (rev 256976)
+++ branches/safari-609.1.20.1-branch/Source/_javascript_Core/ChangeLog	2020-02-19 23:38:15 UTC (rev 256977)
@@ -1,5 +1,167 @@
 2020-02-19  Alan Coon  <alanc...@apple.com>
 
+        Cherry-pick r256779. rdar://problem/59576798
+
+    [JSC] JITThunk should be HashSet<Weak<NativeExecutable>> with appropriate GC weakness handling
+    https://bugs.webkit.org/show_bug.cgi?id=207715
+    
+    Reviewed by Darin Adler.
+    
+    JSTests:
+    
+    * stress/stress-jitthunks.js: Added.
+    (let.set newGlobal):
+    (set catch):
+    
+    Source/_javascript_Core:
+    
+    This patch refines JITThunks GC-aware Weak hash map for NativeExecutable. Previously, we have
+    HashMap<std::tuple<TaggedNativeFunction, TaggedNativeFunction, String>, Weak<NativeExecutable>> table.
+    But this is not good because the first tuple's information is already in NativeExecutable.
+    But we were using this design since Weak<NativeExecutable> can be nullified because of Weak<>. If this
+    happens, we could have invalid Entry in HashMap which does not have corresponding values. This will
+    cause crash when rehasing requires hash code for this entry.
+    
+    But this HashMap is very bad in terms of memory usage. Each entry has 32 bytes, and this table gets enough
+    large. We identified that this table is consuming much memory in Membuster. So it is worth designing
+    carefully crafted data structure which only holds Weak<NativeExecutable> by leveraging the deep interaction
+    with our GC implementation.
+    
+    This patch implements new design of JITThunks, which uses HashSet<Weak<NativeExecutable>> and carefully crafted
+    HashTraits / KeyTraits to handle Weak<> well.
+    
+    1. Each Weak should have finalizer, and this finalizer should remove dead Weak<NativeExecutable> from HashSet.
+    
+        This is ensuring that all the keys in HashSet is, even if Weak<> is saying it is Dead, it still has an way
+        to access content of NativeExecutable if the content is not a JS objects. For example, we can get function
+        pointer from dead Weak<NativeExecutable> if it is not yet finalized. Since we remove all finalized Weak<>
+        from the table, this finalizer mechanism allows us to access function pointers etc. from Weak<NativeExecutable>
+        so long as it is held in this table.
+    
+    2. Getting NativeExecutable* from JITThunks should have special protocol.
+    
+        When getting NativeExecutable* from JITThunks, we do the following,
+    
+        1. First, we check we have an Entry in JITThunks. If it does not exist, we should insert it anyway.
+            1.1. If it exists, we should check whether this Weak<NativeExecutable> is dead or not. It is possible that
+                 dead one is still in the table because "dead" does not mean that it is "finalized". Until finalizing happens (and
+                 it can be delayed by incremental-sweeper), Weak<NativeExecutable> can be dead but still accessible. So the table
+                 is still holding dead one. If we get dead one, we should insert a new one.
+            1.2. If it is not dead, we return it.
+        2. Second, we create a new NativeExecutable and insert it. In that case, it is possible that the table already has Weak<NativeExecutable>,
+           but it is dead. In that case, we need to explicitly replace it with newly created one since old one is holding old content. If we
+           replaced, finalizer of Weak<> will not be invoked since it immediately deallocates Weak<>. So, it does not happen that this newly
+           inserted NativeExecutable* is removed by the finalizer registered by the old Weak<>.
+    
+    This change makes memory usage of JITThunks table 1/4.
+    
+    * heap/Weak.cpp:
+    (JSC::weakClearSlowCase):
+    * heap/Weak.h:
+    (JSC::Weak::Weak):
+    (JSC::Weak::isHashTableEmptyValue const):
+    (JSC::Weak::unsafeImpl const):
+    (WTF::HashTraits<JSC::Weak<T>>::isEmptyValue):
+    * heap/WeakInlines.h:
+    (JSC::Weak<T>::Weak):
+    * jit/JITThunks.cpp:
+    (JSC::JITThunks::JITThunks):
+    (JSC::JITThunks::WeakNativeExecutableHash::hash):
+    (JSC::JITThunks::WeakNativeExecutableHash::equal):
+    (JSC::JITThunks::HostKeySearcher::hash):
+    (JSC::JITThunks::HostKeySearcher::equal):
+    (JSC::JITThunks::NativeExecutableTranslator::hash):
+    (JSC::JITThunks::NativeExecutableTranslator::equal):
+    (JSC::JITThunks::NativeExecutableTranslator::translate):
+    (JSC::JITThunks::finalize):
+    (JSC::JITThunks::hostFunctionStub):
+    (JSC::JITThunks::clearHostFunctionStubs): Deleted.
+    * jit/JITThunks.h:
+    * runtime/NativeExecutable.h:
+    * tools/JSDollarVM.cpp:
+    (JSC::functionGCSweepAsynchronously):
+    (JSC::functionCreateEmptyFunctionWithName):
+    (JSC::JSDollarVM::finishCreation):
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@256779 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2020-02-17  Yusuke Suzuki  <ysuz...@apple.com>
+
+            [JSC] JITThunk should be HashSet<Weak<NativeExecutable>> with appropriate GC weakness handling
+            https://bugs.webkit.org/show_bug.cgi?id=207715
+
+            Reviewed by Darin Adler.
+
+            This patch refines JITThunks GC-aware Weak hash map for NativeExecutable. Previously, we have
+            HashMap<std::tuple<TaggedNativeFunction, TaggedNativeFunction, String>, Weak<NativeExecutable>> table.
+            But this is not good because the first tuple's information is already in NativeExecutable.
+            But we were using this design since Weak<NativeExecutable> can be nullified because of Weak<>. If this
+            happens, we could have invalid Entry in HashMap which does not have corresponding values. This will
+            cause crash when rehasing requires hash code for this entry.
+
+            But this HashMap is very bad in terms of memory usage. Each entry has 32 bytes, and this table gets enough
+            large. We identified that this table is consuming much memory in Membuster. So it is worth designing
+            carefully crafted data structure which only holds Weak<NativeExecutable> by leveraging the deep interaction
+            with our GC implementation.
+
+            This patch implements new design of JITThunks, which uses HashSet<Weak<NativeExecutable>> and carefully crafted
+            HashTraits / KeyTraits to handle Weak<> well.
+
+            1. Each Weak should have finalizer, and this finalizer should remove dead Weak<NativeExecutable> from HashSet.
+
+                This is ensuring that all the keys in HashSet is, even if Weak<> is saying it is Dead, it still has an way
+                to access content of NativeExecutable if the content is not a JS objects. For example, we can get function
+                pointer from dead Weak<NativeExecutable> if it is not yet finalized. Since we remove all finalized Weak<>
+                from the table, this finalizer mechanism allows us to access function pointers etc. from Weak<NativeExecutable>
+                so long as it is held in this table.
+
+            2. Getting NativeExecutable* from JITThunks should have special protocol.
+
+                When getting NativeExecutable* from JITThunks, we do the following,
+
+                1. First, we check we have an Entry in JITThunks. If it does not exist, we should insert it anyway.
+                    1.1. If it exists, we should check whether this Weak<NativeExecutable> is dead or not. It is possible that
+                         dead one is still in the table because "dead" does not mean that it is "finalized". Until finalizing happens (and
+                         it can be delayed by incremental-sweeper), Weak<NativeExecutable> can be dead but still accessible. So the table
+                         is still holding dead one. If we get dead one, we should insert a new one.
+                    1.2. If it is not dead, we return it.
+                2. Second, we create a new NativeExecutable and insert it. In that case, it is possible that the table already has Weak<NativeExecutable>,
+                   but it is dead. In that case, we need to explicitly replace it with newly created one since old one is holding old content. If we
+                   replaced, finalizer of Weak<> will not be invoked since it immediately deallocates Weak<>. So, it does not happen that this newly
+                   inserted NativeExecutable* is removed by the finalizer registered by the old Weak<>.
+
+            This change makes memory usage of JITThunks table 1/4.
+
+            * heap/Weak.cpp:
+            (JSC::weakClearSlowCase):
+            * heap/Weak.h:
+            (JSC::Weak::Weak):
+            (JSC::Weak::isHashTableEmptyValue const):
+            (JSC::Weak::unsafeImpl const):
+            (WTF::HashTraits<JSC::Weak<T>>::isEmptyValue):
+            * heap/WeakInlines.h:
+            (JSC::Weak<T>::Weak):
+            * jit/JITThunks.cpp:
+            (JSC::JITThunks::JITThunks):
+            (JSC::JITThunks::WeakNativeExecutableHash::hash):
+            (JSC::JITThunks::WeakNativeExecutableHash::equal):
+            (JSC::JITThunks::HostKeySearcher::hash):
+            (JSC::JITThunks::HostKeySearcher::equal):
+            (JSC::JITThunks::NativeExecutableTranslator::hash):
+            (JSC::JITThunks::NativeExecutableTranslator::equal):
+            (JSC::JITThunks::NativeExecutableTranslator::translate):
+            (JSC::JITThunks::finalize):
+            (JSC::JITThunks::hostFunctionStub):
+            (JSC::JITThunks::clearHostFunctionStubs): Deleted.
+            * jit/JITThunks.h:
+            * runtime/NativeExecutable.h:
+            * tools/JSDollarVM.cpp:
+            (JSC::functionGCSweepAsynchronously):
+            (JSC::functionCreateEmptyFunctionWithName):
+            (JSC::JSDollarVM::finishCreation):
+
+2020-02-19  Alan Coon  <alanc...@apple.com>
+
         Cherry-pick r256766. rdar://problem/59576813
 
     [Wasm] REGRESSION(r256665): Wasm->JS call IC needs to save memory size register

Modified: branches/safari-609.1.20.1-branch/Source/_javascript_Core/heap/Weak.cpp (256976 => 256977)


--- branches/safari-609.1.20.1-branch/Source/_javascript_Core/heap/Weak.cpp	2020-02-19 23:38:08 UTC (rev 256976)
+++ branches/safari-609.1.20.1-branch/Source/_javascript_Core/heap/Weak.cpp	2020-02-19 23:38:15 UTC (rev 256977)
@@ -36,7 +36,7 @@
     ASSERT(impl);
 
     WeakSet::deallocate(impl);
-    impl = 0;
+    impl = nullptr;
 }
 
 } // namespace JSC

Modified: branches/safari-609.1.20.1-branch/Source/_javascript_Core/heap/Weak.h (256976 => 256977)


--- branches/safari-609.1.20.1-branch/Source/_javascript_Core/heap/Weak.h	2020-02-19 23:38:08 UTC (rev 256976)
+++ branches/safari-609.1.20.1-branch/Source/_javascript_Core/heap/Weak.h	2020-02-19 23:38:15 UTC (rev 256977)
@@ -42,23 +42,20 @@
 template<typename T> class Weak {
     WTF_MAKE_NONCOPYABLE(Weak);
 public:
-    Weak()
-        : m_impl(0)
-    {
-    }
+    Weak() = default;
 
-    Weak(std::nullptr_t)
-        : m_impl(0)
+    constexpr Weak(std::nullptr_t)
+        : Weak()
     {
     }
 
-    inline Weak(T*, WeakHandleOwner* = 0, void* context = 0);
+    Weak(T*, WeakHandleOwner* = nullptr, void* context = nullptr);
 
-    enum HashTableDeletedValueTag { HashTableDeletedValue };
-    inline bool isHashTableDeletedValue() const;
-    inline Weak(HashTableDeletedValueTag);
+    bool isHashTableDeletedValue() const;
+    Weak(WTF::HashTableDeletedValueType);
+    constexpr bool isHashTableEmptyValue() const { return !m_impl; }
 
-    inline Weak(Weak&&);
+    Weak(Weak&&);
 
     ~Weak()
     {
@@ -79,6 +76,7 @@
     inline explicit operator bool() const;
 
     inline WeakImpl* leakImpl() WARN_UNUSED_RETURN;
+    WeakImpl* unsafeImpl() const { return m_impl; }
     void clear()
     {
         if (!m_impl)
@@ -89,7 +87,7 @@
 private:
     static inline WeakImpl* hashTableDeletedValue();
 
-    WeakImpl* m_impl;
+    WeakImpl* m_impl { nullptr };
 };
 
 } // namespace JSC
@@ -106,6 +104,9 @@
     typedef std::nullptr_t EmptyValueType;
     static EmptyValueType emptyValue() { return nullptr; }
 
+    static constexpr bool hasIsEmptyValueFunction = true;
+    static bool isEmptyValue(const JSC::Weak<T>& value) { return value.isHashTableEmptyValue(); }
+
     typedef T* PeekType;
     static PeekType peek(const StorageType& value) { return value.get(); }
     static PeekType peek(EmptyValueType) { return PeekType(); }

Modified: branches/safari-609.1.20.1-branch/Source/_javascript_Core/heap/WeakInlines.h (256976 => 256977)


--- branches/safari-609.1.20.1-branch/Source/_javascript_Core/heap/WeakInlines.h	2020-02-19 23:38:08 UTC (rev 256976)
+++ branches/safari-609.1.20.1-branch/Source/_javascript_Core/heap/WeakInlines.h	2020-02-19 23:38:15 UTC (rev 256977)
@@ -41,7 +41,7 @@
     return m_impl == hashTableDeletedValue();
 }
 
-template<typename T> inline Weak<T>::Weak(typename Weak<T>::HashTableDeletedValueTag)
+template<typename T> inline Weak<T>::Weak(WTF::HashTableDeletedValueType)
     : m_impl(hashTableDeletedValue())
 {
 }

Modified: branches/safari-609.1.20.1-branch/Source/_javascript_Core/jit/JITThunks.cpp (256976 => 256977)


--- branches/safari-609.1.20.1-branch/Source/_javascript_Core/jit/JITThunks.cpp	2020-02-19 23:38:08 UTC (rev 256976)
+++ branches/safari-609.1.20.1-branch/Source/_javascript_Core/jit/JITThunks.cpp	2020-02-19 23:38:15 UTC (rev 256977)
@@ -37,7 +37,6 @@
 namespace JSC {
 
 JITThunks::JITThunks()
-    : m_hostFunctionStubMap(makeUnique<HostFunctionStubMap>())
 {
 }
 
@@ -45,6 +44,53 @@
 {
 }
 
+static inline NativeExecutable& getMayBeDyingNativeExecutable(const Weak<NativeExecutable>& weak)
+{
+    // This never gets Deleted / Empty slots.
+    WeakImpl* impl = weak.unsafeImpl();
+    ASSERT(impl);
+    // We have a callback removing entry when finalizing. This means that we never hold Deallocated entry in HashSet.
+    ASSERT(impl->state() != WeakImpl::State::Deallocated);
+    // Never use jsCast here. This is possible that this value is "Dead" but not "Finalized" yet. In this case,
+    // we can still access to non-JS data, as we are doing in a finalize callback.
+    auto* executable = static_cast<NativeExecutable*>(impl->jsValue().asCell());
+    ASSERT(executable);
+    return *executable;
+}
+
+inline unsigned JITThunks::WeakNativeExecutableHash::hash(NativeExecutable* executable)
+{
+    return hash(executable->function(), executable->constructor(), executable->name());
+}
+
+inline unsigned JITThunks::WeakNativeExecutableHash::hash(const Weak<NativeExecutable>& key)
+{
+    return hash(&getMayBeDyingNativeExecutable(key));
+}
+
+inline bool JITThunks::WeakNativeExecutableHash::equal(NativeExecutable& a, NativeExecutable& b)
+{
+    if (&a == &b)
+        return true;
+    return a.function() == b.function() && a.constructor() == b.constructor() && a.name() == b.name();
+}
+
+inline bool JITThunks::WeakNativeExecutableHash::equal(const Weak<NativeExecutable>& a, const Weak<NativeExecutable>& b)
+{
+    return equal(getMayBeDyingNativeExecutable(a), getMayBeDyingNativeExecutable(b));
+}
+
+inline bool JITThunks::WeakNativeExecutableHash::equal(const Weak<NativeExecutable>& a, NativeExecutable* bExecutable)
+{
+    return equal(getMayBeDyingNativeExecutable(a), *bExecutable);
+}
+
+inline bool JITThunks::WeakNativeExecutableHash::equal(const Weak<NativeExecutable>& a, const HostFunctionKey& b)
+{
+    auto& aExecutable = getMayBeDyingNativeExecutable(a);
+    return aExecutable.function() == std::get<0>(b) && aExecutable.constructor() == std::get<1>(b) && aExecutable.name() == std::get<2>(b);
+}
+
 MacroAssemblerCodePtr<JITThunkPtrTag> JITThunks::ctiNativeCall(VM& vm)
 {
     ASSERT(VM::canUseJIT());
@@ -102,10 +148,32 @@
     return entry->value;
 }
 
+struct JITThunks::HostKeySearcher {
+    static unsigned hash(const HostFunctionKey& key) { return WeakNativeExecutableHash::hash(key); }
+    static bool equal(const Weak<NativeExecutable>& a, const HostFunctionKey& b) { return WeakNativeExecutableHash::equal(a, b); }
+};
+
+struct JITThunks::NativeExecutableTranslator {
+    static unsigned hash(NativeExecutable* key) { return WeakNativeExecutableHash::hash(key); }
+    static bool equal(const Weak<NativeExecutable>& a, NativeExecutable* b) { return WeakNativeExecutableHash::equal(a, b); }
+    static void translate(Weak<NativeExecutable>& location, NativeExecutable* executable, unsigned)
+    {
+        location = Weak<NativeExecutable>(executable, executable->vm().jitStubs.get());
+    }
+};
+
 void JITThunks::finalize(Handle<Unknown> handle, void*)
 {
     auto* nativeExecutable = static_cast<NativeExecutable*>(handle.get().asCell());
-    weakRemove(*m_hostFunctionStubMap, std::make_tuple(nativeExecutable->function(), nativeExecutable->constructor(), nativeExecutable->name()), nativeExecutable);
+    auto hostFunctionKey = std::make_tuple(nativeExecutable->function(), nativeExecutable->constructor(), nativeExecutable->name());
+    {
+        DisallowGC disallowGC;
+        auto iterator = m_nativeExecutableSet.find<HostKeySearcher>(hostFunctionKey);
+        // Because this finalizer is called, this means that we still have dead Weak<> in m_nativeExecutableSet.
+        ASSERT(iterator != m_nativeExecutableSet.end());
+        ASSERT(iterator->unsafeImpl()->state() == WeakImpl::State::Finalized);
+        m_nativeExecutableSet.remove(iterator);
+    }
 }
 
 NativeExecutable* JITThunks::hostFunctionStub(VM& vm, TaggedNativeFunction function, TaggedNativeFunction constructor, const String& name)
@@ -118,8 +186,17 @@
     ASSERT(!isCompilationThread());    
     ASSERT(VM::canUseJIT());
 
-    if (NativeExecutable* nativeExecutable = m_hostFunctionStubMap->get(std::make_tuple(function, constructor, name)))
-        return nativeExecutable;
+    auto hostFunctionKey = std::make_tuple(function, constructor, name);
+    {
+        DisallowGC disallowGC;
+        auto iterator = m_nativeExecutableSet.find<HostKeySearcher>(hostFunctionKey);
+        if (iterator != m_nativeExecutableSet.end()) {
+            // It is possible that this returns Weak<> which is Dead, but not finalized.
+            // We should not use this reference to store value created in the subsequent sequence, since allocating NativeExecutable can cause GC, which changes this Set.
+            if (auto* executable = iterator->get())
+                return executable;
+        }
+    }
 
     RefPtr<JITCode> forCall;
     if (generator) {
@@ -133,7 +210,22 @@
     Ref<JITCode> forConstruct = adoptRef(*new NativeJITCode(MacroAssemblerCodeRef<JSEntryPtrTag>::createSelfManagedCodeRef(ctiNativeConstruct(vm).retagged<JSEntryPtrTag>()), JITType::HostCallThunk, NoIntrinsic));
     
     NativeExecutable* nativeExecutable = NativeExecutable::create(vm, forCall.releaseNonNull(), function, WTFMove(forConstruct), constructor, name);
-    weakAdd(*m_hostFunctionStubMap, std::make_tuple(function, constructor, name), Weak<NativeExecutable>(nativeExecutable, this));
+    {
+        DisallowGC disallowGC;
+        auto addResult = m_nativeExecutableSet.add<NativeExecutableTranslator>(nativeExecutable);
+        if (!addResult.isNewEntry) {
+            // Override the existing Weak<NativeExecutable> with the new one since it is dead.
+            ASSERT(!*addResult.iterator);
+            *addResult.iterator = Weak<NativeExecutable>(nativeExecutable, this);
+            ASSERT(*addResult.iterator);
+#if ASSERT_ENABLED
+            auto iterator = m_nativeExecutableSet.find<HostKeySearcher>(hostFunctionKey);
+            ASSERT(iterator != m_nativeExecutableSet.end());
+            ASSERT(iterator->get() == nativeExecutable);
+            ASSERT(iterator->unsafeImpl()->state() == WeakImpl::State::Live);
+#endif
+        }
+    }
     return nativeExecutable;
 }
 
@@ -142,11 +234,6 @@
     return hostFunctionStub(vm, function, callHostFunctionAsConstructor, generator, intrinsic, nullptr, name);
 }
 
-void JITThunks::clearHostFunctionStubs()
-{
-    m_hostFunctionStubMap = nullptr;
-}
-
 } // namespace JSC
 
 #endif // ENABLE(JIT)

Modified: branches/safari-609.1.20.1-branch/Source/_javascript_Core/jit/JITThunks.h (256976 => 256977)


--- branches/safari-609.1.20.1-branch/Source/_javascript_Core/jit/JITThunks.h	2020-02-19 23:38:08 UTC (rev 256976)
+++ branches/safari-609.1.20.1-branch/Source/_javascript_Core/jit/JITThunks.h	2020-02-19 23:38:15 UTC (rev 256977)
@@ -35,6 +35,7 @@
 #include "WeakHandleOwner.h"
 #include <tuple>
 #include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
 #include <wtf/text/StringHash.h>
 
 namespace JSC {
@@ -65,8 +66,6 @@
     NativeExecutable* hostFunctionStub(VM&, TaggedNativeFunction, TaggedNativeFunction constructor, ThunkGenerator, Intrinsic, const DOMJIT::Signature*, const String& name);
     NativeExecutable* hostFunctionStub(VM&, TaggedNativeFunction, ThunkGenerator, Intrinsic, const String& name);
 
-    void clearHostFunctionStubs();
-
 private:
     void finalize(Handle<Unknown>, void* context) override;
     
@@ -73,39 +72,43 @@
     typedef HashMap<ThunkGenerator, MacroAssemblerCodeRef<JITThunkPtrTag>> CTIStubMap;
     CTIStubMap m_ctiStubMap;
 
-    typedef std::tuple<TaggedNativeFunction, TaggedNativeFunction, String> HostFunctionKey;
+    using HostFunctionKey = std::tuple<TaggedNativeFunction, TaggedNativeFunction, String>;
 
-    struct HostFunctionHash {
+    struct WeakNativeExecutableHash {
+        static inline unsigned hash(const Weak<NativeExecutable>&);
+        static inline unsigned hash(NativeExecutable*);
         static unsigned hash(const HostFunctionKey& key)
         {
-            unsigned hash = WTF::pairIntHash(hashPointer(std::get<0>(key)), hashPointer(std::get<1>(key)));
-            if (!std::get<2>(key).isNull())
-                hash = WTF::pairIntHash(hash, DefaultHash<String>::Hash::hash(std::get<2>(key)));
-            return hash;
+            return hash(std::get<0>(key), std::get<1>(key), std::get<2>(key));
         }
-        static bool equal(const HostFunctionKey& a, const HostFunctionKey& b)
-        {
-            return (std::get<0>(a) == std::get<0>(b)) && (std::get<1>(a) == std::get<1>(b)) && (std::get<2>(a) == std::get<2>(b));
-        }
-        static constexpr bool safeToCompareToEmptyOrDeleted = true;
 
+        static inline bool equal(const Weak<NativeExecutable>&, const Weak<NativeExecutable>&);
+        static inline bool equal(const Weak<NativeExecutable>&, const HostFunctionKey&);
+        static inline bool equal(const Weak<NativeExecutable>&, NativeExecutable*);
+        static inline bool equal(NativeExecutable&, NativeExecutable&);
+        static constexpr bool safeToCompareToEmptyOrDeleted = false;
+
     private:
         static inline unsigned hashPointer(TaggedNativeFunction p)
         {
             return DefaultHash<TaggedNativeFunction>::Hash::hash(p);
         }
+
+        static unsigned hash(TaggedNativeFunction function, TaggedNativeFunction constructor, const String& name)
+        {
+            // FIXME: Use WTF::computeHash.
+            // https://bugs.webkit.org/show_bug.cgi?id=207835
+            unsigned hash = WTF::pairIntHash(hashPointer(function), hashPointer(constructor));
+            if (!name.isNull())
+                hash = WTF::pairIntHash(hash, DefaultHash<String>::Hash::hash(name));
+            return hash;
+        }
     };
+    struct HostKeySearcher;
+    struct NativeExecutableTranslator;
 
-    struct HostFunctionHashTrait : WTF::GenericHashTraits<HostFunctionKey> {
-        static constexpr bool emptyValueIsZero = true;
-        static EmptyValueType emptyValue() { return std::make_tuple(nullptr, nullptr, String()); }
-
-        static void constructDeletedValue(HostFunctionKey& slot) { std::get<0>(slot) = TaggedNativeFunction(-1); }
-        static bool isDeletedValue(const HostFunctionKey& value) { return std::get<0>(value) == TaggedNativeFunction(-1); }
-    };
-    
-    typedef HashMap<HostFunctionKey, Weak<NativeExecutable>, HostFunctionHash, HostFunctionHashTrait> HostFunctionStubMap;
-    std::unique_ptr<HostFunctionStubMap> m_hostFunctionStubMap;
+    using WeakNativeExecutableSet = HashSet<Weak<NativeExecutable>, WeakNativeExecutableHash>;
+    WeakNativeExecutableSet m_nativeExecutableSet;
     Lock m_lock;
 };
 

Modified: branches/safari-609.1.20.1-branch/Source/_javascript_Core/runtime/NativeExecutable.h (256976 => 256977)


--- branches/safari-609.1.20.1-branch/Source/_javascript_Core/runtime/NativeExecutable.h	2020-02-19 23:38:08 UTC (rev 256976)
+++ branches/safari-609.1.20.1-branch/Source/_javascript_Core/runtime/NativeExecutable.h	2020-02-19 23:38:15 UTC (rev 256977)
@@ -48,8 +48,8 @@
 
     CodeBlockHash hashFor(CodeSpecializationKind) const;
 
-    TaggedNativeFunction function() { return m_function; }
-    TaggedNativeFunction constructor() { return m_constructor; }
+    TaggedNativeFunction function() const { return m_function; }
+    TaggedNativeFunction constructor() const { return m_constructor; }
         
     TaggedNativeFunction nativeFunctionFor(CodeSpecializationKind kind)
     {

Modified: branches/safari-609.1.20.1-branch/Source/_javascript_Core/tools/JSDollarVM.cpp (256976 => 256977)


--- branches/safari-609.1.20.1-branch/Source/_javascript_Core/tools/JSDollarVM.cpp	2020-02-19 23:38:08 UTC (rev 256976)
+++ branches/safari-609.1.20.1-branch/Source/_javascript_Core/tools/JSDollarVM.cpp	2020-02-19 23:38:15 UTC (rev 256977)
@@ -1697,6 +1697,15 @@
     return JSValue::encode(jsUndefined());
 }
 
+// Runs a full GC, but sweep asynchronously.
+// Usage: $vm.gcSweepAsynchronously()
+static EncodedJSValue JSC_HOST_CALL functionGCSweepAsynchronously(JSGlobalObject* globalObject, CallFrame*)
+{
+    DollarVMAssertScope assertScope;
+    globalObject->vm().heap.collectNow(Async, CollectionScope::Full);
+    return JSValue::encode(jsUndefined());
+}
+
 // Dumps the hashes of all subspaces currently registered with the VM.
 // Usage: $vm.dumpSubspaceHashes()
 static EncodedJSValue JSC_HOST_CALL functionDumpSubspaceHashes(JSGlobalObject* globalObject, CallFrame*)
@@ -2305,6 +2314,19 @@
     return JSValue::encode(result);
 }
 
+static EncodedJSValue JSC_HOST_CALL functionCreateEmptyFunctionWithName(JSGlobalObject* globalObject, CallFrame* callFrame)
+{
+    DollarVMAssertScope assertScope;
+    VM& vm = globalObject->vm();
+    JSLockHolder lock(vm);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    const String name = callFrame->argument(0).toWTFString(globalObject);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+    RELEASE_AND_RETURN(scope, JSValue::encode(JSFunction::create(vm, globalObject, 1, name, functionCreateEmptyFunctionWithName)));
+}
+
 static EncodedJSValue JSC_HOST_CALL functionSetImpureGetterDelegate(JSGlobalObject* globalObject, CallFrame* callFrame)
 {
     DollarVMAssertScope assertScope;
@@ -2804,6 +2826,7 @@
     addFunction(vm, "noInline", functionNoInline, 1);
 
     addFunction(vm, "gc", functionGC, 0);
+    addFunction(vm, "gcSweepAsynchronously", functionGCSweepAsynchronously, 0);
     addFunction(vm, "edenGC", functionEdenGC, 0);
     addFunction(vm, "dumpSubspaceHashes", functionDumpSubspaceHashes, 0);
 
@@ -2850,6 +2873,7 @@
 #endif
     addFunction(vm, "createStaticCustomAccessor", functionCreateStaticCustomAccessor, 0);
     addFunction(vm, "createObjectDoingSideEffectPutWithoutCorrectSlotStatus", functionCreateObjectDoingSideEffectPutWithoutCorrectSlotStatus, 0);
+    addFunction(vm, "createEmptyFunctionWithName", functionCreateEmptyFunctionWithName, 1);
     addFunction(vm, "getPrivateProperty", functionGetPrivateProperty, 2);
     addFunction(vm, "setImpureGetterDelegate", functionSetImpureGetterDelegate, 2);
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to