- Revision
- 256961
- Author
- alanc...@apple.com
- Date
- 2020-02-19 15:08:02 -0800 (Wed, 19 Feb 2020)
Log Message
Cherry-pick r256779. rdar://problem/59551695
[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.0-branch/JSTests/ChangeLog (256960 => 256961)
--- branches/safari-609.1.20.0-branch/JSTests/ChangeLog 2020-02-19 23:07:57 UTC (rev 256960)
+++ branches/safari-609.1.20.0-branch/JSTests/ChangeLog 2020-02-19 23:08:02 UTC (rev 256961)
@@ -1,5 +1,103 @@
2020-02-19 Russell Epstein <repst...@apple.com>
+ Cherry-pick r256779. rdar://problem/59551695
+
+ [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 Russell Epstein <repst...@apple.com>
+
Cherry-pick r256766. rdar://problem/59551706
[Wasm] REGRESSION(r256665): Wasm->JS call IC needs to save memory size register
Added: branches/safari-609.1.20.0-branch/JSTests/stress/stress-jitthunks.js (0 => 256961)
--- branches/safari-609.1.20.0-branch/JSTests/stress/stress-jitthunks.js (rev 0)
+++ branches/safari-609.1.20.0-branch/JSTests/stress/stress-jitthunks.js 2020-02-19 23:08:02 UTC (rev 256961)
@@ -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.0-branch/Source/_javascript_Core/ChangeLog (256960 => 256961)
--- branches/safari-609.1.20.0-branch/Source/_javascript_Core/ChangeLog 2020-02-19 23:07:57 UTC (rev 256960)
+++ branches/safari-609.1.20.0-branch/Source/_javascript_Core/ChangeLog 2020-02-19 23:08:02 UTC (rev 256961)
@@ -1,5 +1,167 @@
2020-02-19 Russell Epstein <repst...@apple.com>
+ Cherry-pick r256779. rdar://problem/59551695
+
+ [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 Russell Epstein <repst...@apple.com>
+
Cherry-pick r256766. rdar://problem/59551706
[Wasm] REGRESSION(r256665): Wasm->JS call IC needs to save memory size register
Modified: branches/safari-609.1.20.0-branch/Source/_javascript_Core/heap/Weak.cpp (256960 => 256961)
--- branches/safari-609.1.20.0-branch/Source/_javascript_Core/heap/Weak.cpp 2020-02-19 23:07:57 UTC (rev 256960)
+++ branches/safari-609.1.20.0-branch/Source/_javascript_Core/heap/Weak.cpp 2020-02-19 23:08:02 UTC (rev 256961)
@@ -36,7 +36,7 @@
ASSERT(impl);
WeakSet::deallocate(impl);
- impl = 0;
+ impl = nullptr;
}
} // namespace JSC
Modified: branches/safari-609.1.20.0-branch/Source/_javascript_Core/heap/Weak.h (256960 => 256961)
--- branches/safari-609.1.20.0-branch/Source/_javascript_Core/heap/Weak.h 2020-02-19 23:07:57 UTC (rev 256960)
+++ branches/safari-609.1.20.0-branch/Source/_javascript_Core/heap/Weak.h 2020-02-19 23:08:02 UTC (rev 256961)
@@ -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.0-branch/Source/_javascript_Core/heap/WeakInlines.h (256960 => 256961)
--- branches/safari-609.1.20.0-branch/Source/_javascript_Core/heap/WeakInlines.h 2020-02-19 23:07:57 UTC (rev 256960)
+++ branches/safari-609.1.20.0-branch/Source/_javascript_Core/heap/WeakInlines.h 2020-02-19 23:08:02 UTC (rev 256961)
@@ -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.0-branch/Source/_javascript_Core/jit/JITThunks.cpp (256960 => 256961)
--- branches/safari-609.1.20.0-branch/Source/_javascript_Core/jit/JITThunks.cpp 2020-02-19 23:07:57 UTC (rev 256960)
+++ branches/safari-609.1.20.0-branch/Source/_javascript_Core/jit/JITThunks.cpp 2020-02-19 23:08:02 UTC (rev 256961)
@@ -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.0-branch/Source/_javascript_Core/jit/JITThunks.h (256960 => 256961)
--- branches/safari-609.1.20.0-branch/Source/_javascript_Core/jit/JITThunks.h 2020-02-19 23:07:57 UTC (rev 256960)
+++ branches/safari-609.1.20.0-branch/Source/_javascript_Core/jit/JITThunks.h 2020-02-19 23:08:02 UTC (rev 256961)
@@ -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.0-branch/Source/_javascript_Core/runtime/NativeExecutable.h (256960 => 256961)
--- branches/safari-609.1.20.0-branch/Source/_javascript_Core/runtime/NativeExecutable.h 2020-02-19 23:07:57 UTC (rev 256960)
+++ branches/safari-609.1.20.0-branch/Source/_javascript_Core/runtime/NativeExecutable.h 2020-02-19 23:08:02 UTC (rev 256961)
@@ -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.0-branch/Source/_javascript_Core/tools/JSDollarVM.cpp (256960 => 256961)
--- branches/safari-609.1.20.0-branch/Source/_javascript_Core/tools/JSDollarVM.cpp 2020-02-19 23:07:57 UTC (rev 256960)
+++ branches/safari-609.1.20.0-branch/Source/_javascript_Core/tools/JSDollarVM.cpp 2020-02-19 23:08:02 UTC (rev 256961)
@@ -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);