Skip to content

Commit 6da8ead

Browse files
committed
[JSC] Propagate "singleton" invalidation to originating SymbolTable
rdar://178874394 https://bugs.webkit.org/show_bug.cgi?id=316472 Reviewed by Mark Lam. SymbolTable is maintaining InferredValue "singleton". This is a watchpoint to maintain whether this SymbolTable is singleton (it is never used for the different lexical environment again). If we figure out that this is singleton, then we can fold an environment into a constant in DFG, leveraging constant folding and which leads further more optimizations. But if we failed to speculate, then it gets fired, and it invalidates CodeBlock. If we can get better information about "this is not a singleton" before subscribing a watchpoint, we can improve DFG~'s quality due to precise information. This patch adds PropagateCloneInvalidationToOriginal flag to SymbolTable. When it is true, then when cloning, 1. If original SymbolTable's singleton is already invalidated, then we just makes the SymbolTable singleton invalidated from the beginning. 2. If not, we record this flag. 3. When SymbolTable's singleton gets invalidated, we propagate this invalidation to the originating SymbolTable's singleton. Since the original SymbolTable is stored in UnlinkedCodeBlock, we can share this information across multiple realms for the same code. We do not directly share the same watchpoint between them because it is natural to have multiple SymbolTable when you have two realms (navigation) and the previous SymbolTable is still alive because of GC timing etc. We would like to invalidate singleton only when one realm creates lexical environment with the same SymbolTable multiple times. * Source/JavaScriptCore/bytecode/CodeBlock.cpp: (JSC::CodeBlock::setConstantRegisters): * Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::BytecodeGenerator): (JSC::BytecodeGenerator::pushLexicalScopeInternal): * Source/JavaScriptCore/runtime/ModuleProgramExecutable.cpp: (JSC::ModuleProgramExecutable::getUnlinkedCodeBlock): * Source/JavaScriptCore/runtime/SymbolTable.cpp: (JSC::SymbolTable::cloneScopePart): * Source/JavaScriptCore/runtime/SymbolTable.h: Canonical link: https://commits.webkit.org/314723@main
1 parent a09d473 commit 6da8ead

5 files changed

Lines changed: 33 additions & 7 deletions

File tree

‎Source/JavaScriptCore/bytecode/CodeBlock.cpp‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1080,7 +1080,12 @@ Vector<unsigned> CodeBlock::setConstantRegisters(const FixedVector<WriteBarrier<
10801080
// watchpoint intact and assume the value is still the original constant.
10811081
SymbolTable* clone = globalObject->symbolTableCache().get(symbolTable);
10821082
if (!clone) {
1083-
clone = symbolTable->cloneScopePart(vm);
1083+
// For non-builtin code, link the clone's singleton watchpoint to the master
1084+
// SymbolTable held inside the UnlinkedCodeBlock so that all per-realm clones
1085+
// share one InferredValue. This avoids re-firing the singleton watchpoint
1086+
// independently in every realm (e.g. on navigation) for the same code.
1087+
auto propagateCloneInvalidationToOriginal = m_unlinkedCode->isBuiltinFunction() ? SymbolTable::PropagateCloneInvalidationToOriginal::No : SymbolTable::PropagateCloneInvalidationToOriginal::Yes;
1088+
clone = symbolTable->cloneScopePart(vm, propagateCloneInvalidationToOriginal);
10841089
globalObject->symbolTableCache().set(symbolTable, clone);
10851090
}
10861091
if (wasCompiledWithDebuggingOpcodes())

‎Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,7 +1128,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, ModuleProgramNode* moduleProgramNod
11281128
if (shouldEmitTypeProfilerHooks() || moduleProgramNode->usesAwait())
11291129
constantSymbolTable = addConstantValue(moduleEnvironmentSymbolTable);
11301130
else
1131-
constantSymbolTable = addConstantValue(moduleEnvironmentSymbolTable->cloneScopePart(m_vm));
1131+
constantSymbolTable = addConstantValue(moduleEnvironmentSymbolTable->cloneScopePart(m_vm, SymbolTable::PropagateCloneInvalidationToOriginal::No));
11321132

11331133
if (moduleProgramNode->usesAwait()) {
11341134
m_generatorFrameSymbolTable.set(m_vm, moduleEnvironmentSymbolTable);
@@ -2255,7 +2255,7 @@ void BytecodeGenerator::pushLexicalScopeInternal(VariableEnvironment& environmen
22552255
newScope = addVar();
22562256
if (!constantSymbolTable) {
22572257
ASSERT(!shouldEmitTypeProfilerHooks());
2258-
constantSymbolTable = addConstantValue(symbolTable->cloneScopePart(m_vm));
2258+
constantSymbolTable = addConstantValue(symbolTable->cloneScopePart(m_vm, SymbolTable::PropagateCloneInvalidationToOriginal::No));
22592259
symbolTableConstantIndex = constantSymbolTable->index();
22602260
}
22612261
if (constantSymbolTableResult)

‎Source/JavaScriptCore/runtime/ModuleProgramExecutable.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ UnlinkedModuleProgramCodeBlock* ModuleProgramExecutable::getUnlinkedCodeBlock(JS
6767
m_unlinkedCodeBlock.set(vm, this, unlinkedModuleProgramCode);
6868
VirtualRegister symbolTableReg = VirtualRegister(unlinkedModuleProgramCode->moduleEnvironmentSymbolTableConstantRegisterOffset());
6969
SymbolTable* symbolTable = uncheckedDowncast<SymbolTable>(unlinkedModuleProgramCode->getConstant(symbolTableReg));
70-
m_moduleEnvironmentSymbolTable.set(vm, this, symbolTable->cloneScopePart(vm));
70+
m_moduleEnvironmentSymbolTable.set(vm, this, symbolTable->cloneScopePart(vm, SymbolTable::PropagateCloneInvalidationToOriginal::Yes));
7171
RELEASE_AND_RETURN(throwScope, unlinkedModuleProgramCode);
7272
}
7373

‎Source/JavaScriptCore/runtime/SymbolTable.cpp‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ SymbolTableEntry* SymbolTable::entryFor(const ConcurrentJSLocker& locker, ScopeO
138138
return toEntryVector[offset.offset()];
139139
}
140140

141-
SymbolTable* SymbolTable::cloneScopePart(VM& vm)
141+
SymbolTable* SymbolTable::cloneScopePart(VM& vm, PropagateCloneInvalidationToOriginal propagateCloneInvalidationToOriginal)
142142
{
143143
SymbolTable* result = SymbolTable::create(vm);
144144

@@ -213,7 +213,14 @@ SymbolTable* SymbolTable::cloneScopePart(VM& vm)
213213
result->m_rareData->m_privateNames.add(name.key, name.value);
214214
}
215215
}
216+
216217
result->m_clonedFrom.set(vm, result, this);
218+
result->m_propagateCloneInvalidationToOriginal = propagateCloneInvalidationToOriginal;
219+
if (result->m_propagateCloneInvalidationToOriginal == PropagateCloneInvalidationToOriginal::Yes) {
220+
// If the original SymbolTable's singleton is already invalidated, the new clone starts out invalidated too.
221+
if (m_singleton.hasBeenInvalidated())
222+
result->m_singleton.invalidate(vm, StringFireDetail("Singleton was previously invalidated"));
223+
}
217224
return result;
218225
}
219226

‎Source/JavaScriptCore/runtime/SymbolTable.h‎

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -743,19 +743,32 @@ class SymbolTable final : public JSCell {
743743
void setScopeType(ScopeType type) { m_scopeType = type; }
744744
ScopeType scopeType() const { return static_cast<ScopeType>(m_scopeType); }
745745

746-
SymbolTable* cloneScopePart(VM&);
746+
enum class PropagateCloneInvalidationToOriginal : bool { No, Yes };
747+
SymbolTable* cloneScopePart(VM&, PropagateCloneInvalidationToOriginal);
747748

748749
void prepareForTypeProfiling(const ConcurrentJSLocker&);
749750

750751
String NODELETE inferredName();
751752
DebuggerLocation NODELETE debuggerLocation();
752753
void collectDebuggerInfo(CodeBlock*);
753-
754+
754755
InferredValue<JSScope>& singleton() LIFETIME_BOUND { return m_singleton; }
755756

756757
void notifyCreation(VM& vm, JSScope* scope, const char* reason)
757758
{
758759
m_singleton.notifyWrite(vm, this, scope, reason);
760+
if (m_singleton.hasBeenInvalidated()) {
761+
// Propagate to the original SymbolTable so future clones in other realms pre-invalidate.
762+
// This "SymbolTable can be reused multiple times for the different lexical environments" is
763+
// important feedback information from the code execution, and it is derived from the code's lexical
764+
// characteristics. Thus carrying beyond realms makes sense.
765+
if (m_propagateCloneInvalidationToOriginal == PropagateCloneInvalidationToOriginal::Yes) {
766+
if (auto* origin = m_clonedFrom.get()) {
767+
if (!origin->m_singleton.hasBeenInvalidated())
768+
origin->m_singleton.invalidate(vm, StringFireDetail("Singleton invalidated in clone"));
769+
}
770+
}
771+
}
759772
}
760773

761774
DECLARE_VISIT_CHILDREN;
@@ -802,6 +815,7 @@ class SymbolTable final : public JSCell {
802815
unsigned m_usesSloppyEval : 1;
803816
unsigned m_nestedLexicalScope : 1; // Non-function LexicalScope.
804817
unsigned m_scopeType : 3; // ScopeType
818+
PropagateCloneInvalidationToOriginal m_propagateCloneInvalidationToOriginal : 1 { PropagateCloneInvalidationToOriginal::No };
805819

806820
std::unique_ptr<SymbolTableRareData> m_rareData;
807821

0 commit comments

Comments
 (0)