Commit 2df4d58a authored by Seth Brenith's avatar Seth Brenith Committed by V8 LUCI CQ

Fix rehashing of script compilation cache

The script compilation cache contains weak pointers to Script objects as
its keys. When doing a rehashing operation, any hash table needs the
ability to get the hash code for every entry in the table. However, if
the weak pointer was cleared from a key, there is no longer any way to
get the hash code for that entry.

In https://crrev.com/c/3597106 , I attempted to solve this problem by
deleting all entries whose keys contain cleared weak pointers prior to
rehashing, but the implementation has a bug: when resizing, the new
table is allocated after deleting the entries with cleared keys, so if
that allocation triggers a GC, the table can once again have entries
with cleared keys.

This could be solved in a variety of ways, such as:

1. Iterate the entries again and delete those with cleared keys, after
   allocating the new table but before calling Rehash() to copy data
   into that new table. This means we can't directly use
   HashTable::EnsureCapacity, which normally does both the allocation
   and the rehashing.
2. Return a bogus hash code for entries whose keys contain cleared weak
   pointers. This is simple but risks poor distribution of data after
   rehashing.
3. Implement custom rehashing which can avoid copying entries with
   cleared keys, rather than reusing the rehashing implementation from
   HashTable.
4. Include the hash value in every key, so a consistent hash value is
   available even after the weak Script pointer has been cleared.

The fourth option sounds like the lowest risk to me, so this change
implements that option.

Bug: v8:12808
Change-Id: I6b19b9c8af67dcfc31b74842ba581dd141e18845
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3654413Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#80637}
parent d812c9a9
...@@ -57,6 +57,7 @@ void CompilationCacheTable::SetEvalFeedbackValueAt(InternalIndex entry, ...@@ -57,6 +57,7 @@ void CompilationCacheTable::SetEvalFeedbackValueAt(InternalIndex entry,
class ScriptCacheKey : public HashTableKey { class ScriptCacheKey : public HashTableKey {
public: public:
enum Index { enum Index {
kHash,
kWeakScript, kWeakScript,
kEnd, kEnd,
}; };
...@@ -112,11 +113,6 @@ uint32_t CompilationCacheShape::EvalHash(String source, ...@@ -112,11 +113,6 @@ uint32_t CompilationCacheShape::EvalHash(String source,
return hash; return hash;
} }
uint32_t CompilationCacheShape::ScriptHash(String source) {
uint32_t hash = source.EnsureHash();
return hash;
}
uint32_t CompilationCacheShape::HashForObject(ReadOnlyRoots roots, uint32_t CompilationCacheShape::HashForObject(ReadOnlyRoots roots,
Object object) { Object object) {
// Eval: The key field contains the hash as a Number. // Eval: The key field contains the hash as a Number.
...@@ -129,12 +125,19 @@ uint32_t CompilationCacheShape::HashForObject(ReadOnlyRoots roots, ...@@ -129,12 +125,19 @@ uint32_t CompilationCacheShape::HashForObject(ReadOnlyRoots roots,
// Script. // Script.
if (object.IsWeakFixedArray()) { if (object.IsWeakFixedArray()) {
uint32_t result = static_cast<uint32_t>(Smi::ToInt(
WeakFixedArray::cast(object).Get(ScriptCacheKey::kHash).ToSmi()));
#ifdef DEBUG
base::Optional<String> script_key = base::Optional<String> script_key =
ScriptCacheKey::SourceFromObject(object); ScriptCacheKey::SourceFromObject(object);
// Rehashing should only happen after we've removed all of the keys if (script_key) {
// containing cleared weak refs. uint32_t source_hash;
DCHECK(script_key); if (script_key->TryGetHash(&source_hash)) {
return ScriptHash(*script_key); DCHECK_EQ(result, source_hash);
}
}
#endif
return result;
} }
// Eval: See EvalCacheKey::ToHandle for the encoding. // Eval: See EvalCacheKey::ToHandle for the encoding.
......
...@@ -226,8 +226,13 @@ class CodeKey : public HashTableKey { ...@@ -226,8 +226,13 @@ class CodeKey : public HashTableKey {
} // namespace } // namespace
ScriptCacheKey::ScriptCacheKey(Handle<String> source) ScriptCacheKey::ScriptCacheKey(Handle<String> source)
: HashTableKey(CompilationCacheShape::ScriptHash(*source)), : HashTableKey(source->EnsureHash()), source_(source) {
source_(source) {} // Hash values must fit within a Smi.
static_assert(Name::HashBits::kSize <= kSmiValueSize);
DCHECK_EQ(
static_cast<uint32_t>(Smi::ToInt(Smi::FromInt(static_cast<int>(Hash())))),
Hash());
}
bool ScriptCacheKey::IsMatch(Object other) { bool ScriptCacheKey::IsMatch(Object other) {
DisallowGarbageCollection no_gc; DisallowGarbageCollection no_gc;
...@@ -241,6 +246,8 @@ Handle<Object> ScriptCacheKey::AsHandle(Isolate* isolate, ...@@ -241,6 +246,8 @@ Handle<Object> ScriptCacheKey::AsHandle(Isolate* isolate,
// Any SharedFunctionInfo being stored in the script cache should have a // Any SharedFunctionInfo being stored in the script cache should have a
// Script. // Script.
DCHECK(shared->script().IsScript()); DCHECK(shared->script().IsScript());
array->Set(kHash,
MaybeObject::FromObject(Smi::FromInt(static_cast<int>(Hash()))));
array->Set(kWeakScript, array->Set(kWeakScript,
MaybeObject::MakeWeak(MaybeObject::FromObject(shared->script()))); MaybeObject::MakeWeak(MaybeObject::FromObject(shared->script())));
return array; return array;
...@@ -469,12 +476,8 @@ void CompilationCacheTable::RemoveEntry(InternalIndex entry) { ...@@ -469,12 +476,8 @@ void CompilationCacheTable::RemoveEntry(InternalIndex entry) {
ElementRemoved(); ElementRemoved();
// This table does not shrink upon deletion. The script cache depends on that // This table does not shrink upon deletion. The script cache depends on that
// fact, in two ways: // fact, because EnsureScriptTableCapacity calls RemoveEntry at a time when
// 1. EnsureScriptTableCapacity calls RemoveEntry, at a time when shrinking // shrinking the table would be counterproductive.
// the table would be counterproductive, and
// 2. CompilationCacheShape::HashForObject cannot produce a hash for keys that
// contain cleared weak pointers, so rehashing must only occur right after
// all such keys have been cleared.
} }
} // namespace internal } // namespace internal
......
...@@ -32,8 +32,6 @@ class CompilationCacheShape : public BaseShape<HashTableKey*> { ...@@ -32,8 +32,6 @@ class CompilationCacheShape : public BaseShape<HashTableKey*> {
static inline uint32_t EvalHash(String source, SharedFunctionInfo shared, static inline uint32_t EvalHash(String source, SharedFunctionInfo shared,
LanguageMode language_mode, int position); LanguageMode language_mode, int position);
static inline uint32_t ScriptHash(String source);
static inline uint32_t HashForObject(ReadOnlyRoots roots, Object object); static inline uint32_t HashForObject(ReadOnlyRoots roots, Object object);
static const int kPrefixSize = 0; static const int kPrefixSize = 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment