Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (119843 => 119844)
--- trunk/Source/_javascript_Core/ChangeLog 2012-06-08 18:14:55 UTC (rev 119843)
+++ trunk/Source/_javascript_Core/ChangeLog 2012-06-08 18:17:16 UTC (rev 119844)
@@ -1,3 +1,85 @@
+ Don't rely on weak pointers for eager CodeBlock finalization
+ https://bugs.webkit.org/show_bug.cgi?id=88465
+
+ Reviewed by Gavin Barraclough.
+
+ This is incompatible with lazy weak pointer finalization.
+
+ I considered just making CodeBlock finalization lazy-friendly, but it
+ turns out that the heap is already way up in CodeBlock's business when
+ it comes to finalization, so I decided to finish the job and move full
+ responsibility for CodeBlock finalization into the heap.
+
+ * _javascript_Core.vcproj/_javascript_Core/_javascript_Core.def: Maybe this
+ will build.
+
+ * debugger/Debugger.cpp: Updated for rename.
+
+ * heap/Heap.cpp:
+ (JSC::Heap::deleteAllCompiledCode): Renamed for consistency. Fixed a bug
+ where we would not delete code for a code block that had been previously
+ jettisoned. I don't know if this happens in practice -- I mostly did
+ this to improve consistency with deleteUnmarkedCompiledCode.
+
+ (JSC::Heap::deleteUnmarkedCompiledCode): New function, responsible for
+ eager finalization of unmarked code blocks.
+
+ (JSC::Heap::collect): Updated for rename. Updated to call
+ deleteUnmarkedCompiledCode(), which takes care of jettisoned DFG code
+ blocks too.
+
+ (JSC::Heap::addCompiledCode): Renamed, since this points to all code
+ now, not just functions.
+
+ * heap/Heap.h:
+ (Heap): Keep track of all user code, not just functions. This is a
+ negligible additional overhead, since most code is function code.
+
+ * runtime/Executable.cpp:
+ (JSC::*::finalize): Removed these functions, since we don't rely on
+ weak pointer finalization anymore.
+
+ (JSC::FunctionExecutable::FunctionExecutable): Moved linked-list stuff
+ into base class so all executables can be in the list.
+
+ (JSC::EvalExecutable::clearCode):
+ (JSC::ProgramExecutable::clearCode):
+ (JSC::FunctionExecutable::clearCode): All we need to do is delete our
+ CodeBlock -- that will delete all of its internal data structures.
+
+ (JSC::FunctionExecutable::clearCodeIfNotCompiling): Factored out a helper
+ function to improve clarity.
+
+ * runtime/Executable.h:
+ (JSC::ExecutableBase): Moved linked-list stuff
+ into base class so all executables can be in the list.
+
+ (JSC::NativeExecutable::create):
+ (NativeExecutable):
+ (ScriptExecutable):
+ (JSC::ScriptExecutable::finishCreation):
+ (JSC::EvalExecutable::create):
+ (EvalExecutable):
+ (JSC::ProgramExecutable::create):
+ (ProgramExecutable):
+ (FunctionExecutable):
+ (JSC::FunctionExecutable::create): Don't use a finalizer -- the heap
+ will call us back to destroy our code block.
+
+ (JSC::FunctionExecutable::discardCode): Renamed to clearCodeIfNotCompiling()
+ for clarity.
+
+ (JSC::FunctionExecutable::isCompiling): New helper function, for clarity.
+
+ (JSC::ScriptExecutable::clearCodeVirtual): New helper function, since
+ the heap needs to make polymorphic calls to clear code.
+
+ * runtime/JSGlobalData.cpp:
+ (JSC::StackPreservingRecompiler::operator()):
+ * runtime/JSGlobalObject.cpp:
+ (JSC::DynamicGlobalObjectScope::DynamicGlobalObjectScope): Updated for
+ renames.
+
2012-06-07 Filip Pizlo <[email protected]>
DFG should inline prototype chain accesses, and do the right things if the
Modified: trunk/Source/_javascript_Core/_javascript_Core.vcproj/_javascript_Core/_javascript_Core.def (119843 => 119844)
--- trunk/Source/_javascript_Core/_javascript_Core.vcproj/_javascript_Core/_javascript_Core.def 2012-06-08 18:14:55 UTC (rev 119843)
+++ trunk/Source/_javascript_Core/_javascript_Core.vcproj/_javascript_Core/_javascript_Core.def 2012-06-08 18:17:16 UTC (rev 119844)
@@ -151,7 +151,7 @@
?detach@Debugger@JSC@@UAEXPAVJSGlobalObject@2@@Z
?detachThread@WTF@@YAXI@Z
?didTimeOut@TimeoutChecker@JSC@@QAE_NPAVExecState@2@@Z
- ?discardAllCompiledCode@Heap@JSC@@QAEXXZ
+ ?deleteAllCompiledCode@Heap@JSC@@QAEXXZ
?displayName@JSFunction@JSC@@QAE?BVUString@2@PAVExecState@2@@Z
?dtoa@WTF@@YAXQADNAA_NAAHAAI@Z
?dumpSampleData@JSGlobalData@JSC@@QAEXPAVExecState@2@@Z
Modified: trunk/Source/_javascript_Core/debugger/Debugger.cpp (119843 => 119844)
--- trunk/Source/_javascript_Core/debugger/Debugger.cpp 2012-06-08 18:14:55 UTC (rev 119843)
+++ trunk/Source/_javascript_Core/debugger/Debugger.cpp 2012-06-08 18:17:16 UTC (rev 119844)
@@ -79,7 +79,7 @@
return;
ExecState* exec = function->scope()->globalObject->JSGlobalObject::globalExec();
- executable->discardCode();
+ executable->clearCodeIfNotCompiling();
if (m_debugger == function->scope()->globalObject->debugger())
m_sourceProviders.add(executable->source().provider(), exec);
}
Modified: trunk/Source/_javascript_Core/heap/Heap.cpp (119843 => 119844)
--- trunk/Source/_javascript_Core/heap/Heap.cpp 2012-06-08 18:14:55 UTC (rev 119843)
+++ trunk/Source/_javascript_Core/heap/Heap.cpp 2012-06-08 18:17:16 UTC (rev 119844)
@@ -643,17 +643,40 @@
return m_objectSpace.forEachCell<RecordType>();
}
-void Heap::discardAllCompiledCode()
+void Heap::deleteAllCompiledCode()
{
- // If _javascript_ is running, it's not safe to recompile, since we'll end
- // up throwing away code that is live on the stack.
+ // If _javascript_ is running, it's not safe to delete code, since we'll end
+ // up deleting code that is live on the stack.
if (m_globalData->dynamicGlobalObject)
return;
- for (FunctionExecutable* current = m_functions.head(); current; current = current->next())
- current->discardCode();
+ for (ExecutableBase* current = m_compiledCode.head(); current; current = current->next()) {
+ if (!current->isFunctionExecutable())
+ continue;
+ static_cast<FunctionExecutable*>(current)->clearCodeIfNotCompiling();
+ }
+
+ m_dfgCodeBlocks.clearMarks();
+ m_dfgCodeBlocks.deleteUnmarkedJettisonedCodeBlocks();
}
+void Heap::deleteUnmarkedCompiledCode()
+{
+ ExecutableBase* next;
+ for (ExecutableBase* current = m_compiledCode.head(); current; current = next) {
+ next = current->next();
+ if (isMarked(current))
+ continue;
+
+ // We do this because executable memory is limited on some platforms and because
+ // CodeBlock requires eager finalization.
+ ExecutableBase::clearCodeVirtual(current);
+ m_compiledCode.remove(current);
+ }
+
+ m_dfgCodeBlocks.deleteUnmarkedJettisonedCodeBlocks();
+}
+
void Heap::collectAllGarbage()
{
if (!m_isSafeToCollect)
@@ -680,7 +703,7 @@
double lastGCStartTime = WTF::currentTime();
if (lastGCStartTime - m_lastCodeDiscardTime > minute) {
- discardAllCompiledCode();
+ deleteAllCompiledCode();
m_lastCodeDiscardTime = WTF::currentTime();
}
@@ -703,6 +726,8 @@
m_objectSpace.reapWeakSets();
}
+ _javascript_CORE_GC_MARKED();
+
{
GCPHASE(FinalizeUnconditionalFinalizers);
finalizeUnconditionalFinalizers();
@@ -713,12 +738,10 @@
m_objectSpace.sweepWeakSets();
m_globalData->smallStrings.finalizeSmallStrings();
}
-
- _javascript_CORE_GC_MARKED();
{
GCPHASE(DeleteCodeBlocks);
- m_dfgCodeBlocks.deleteUnmarkedJettisonedCodeBlocks();
+ deleteUnmarkedCompiledCode();
}
if (sweepToggle == DoSweep) {
@@ -808,14 +831,9 @@
WeakSet::deallocate(WeakImpl::asWeakImpl(slot));
}
-void Heap::addFunctionExecutable(FunctionExecutable* executable)
+void Heap::addCompiledCode(ExecutableBase* executable)
{
- m_functions.append(executable);
+ m_compiledCode.append(executable);
}
-void Heap::removeFunctionExecutable(FunctionExecutable* executable)
-{
- m_functions.remove(executable);
-}
-
} // namespace JSC
Modified: trunk/Source/_javascript_Core/heap/Heap.h (119843 => 119844)
--- trunk/Source/_javascript_Core/heap/Heap.h 2012-06-08 18:14:55 UTC (rev 119843)
+++ trunk/Source/_javascript_Core/heap/Heap.h 2012-06-08 18:17:16 UTC (rev 119844)
@@ -42,7 +42,7 @@
class CopiedSpace;
class CodeBlock;
- class FunctionExecutable;
+ class ExecutableBase;
class GCActivityCallback;
class GlobalCodeBlock;
class Heap;
@@ -116,8 +116,7 @@
typedef void (*Finalizer)(JSCell*);
JS_EXPORT_PRIVATE void addFinalizer(JSCell*, Finalizer);
- void addFunctionExecutable(FunctionExecutable*);
- void removeFunctionExecutable(FunctionExecutable*);
+ void addCompiledCode(ExecutableBase*);
void notifyIsSafeToCollect() { m_isSafeToCollect = true; }
@@ -159,7 +158,7 @@
double lastGCLength() { return m_lastGCLength; }
void increaseLastGCLength(double amount) { m_lastGCLength += amount; }
- JS_EXPORT_PRIVATE void discardAllCompiledCode();
+ JS_EXPORT_PRIVATE void deleteAllCompiledCode();
void didAllocate(size_t);
void didAbandon(size_t);
@@ -194,6 +193,7 @@
void markTempSortVectors(HeapRootVisitor&);
void harvestWeakReferences();
void finalizeUnconditionalFinalizers();
+ void deleteUnmarkedCompiledCode();
RegisterFile& registerFile();
BlockAllocator& blockAllocator();
@@ -239,7 +239,7 @@
double m_lastGCLength;
double m_lastCodeDiscardTime;
- DoublyLinkedList<FunctionExecutable> m_functions;
+ DoublyLinkedList<ExecutableBase> m_compiledCode;
};
inline bool Heap::shouldCollect()
Modified: trunk/Source/_javascript_Core/runtime/Executable.cpp (119843 => 119844)
--- trunk/Source/_javascript_Core/runtime/Executable.cpp 2012-06-08 18:14:55 UTC (rev 119843)
+++ trunk/Source/_javascript_Core/runtime/Executable.cpp 2012-06-08 18:17:16 UTC (rev 119844)
@@ -47,7 +47,7 @@
}
#endif
-inline void ExecutableBase::clearCode()
+void ExecutableBase::clearCode()
{
#if ENABLE(JIT)
m_jitCodeForCall.clear();
@@ -98,11 +98,6 @@
}
#endif
-void NativeExecutable::finalize(JSCell* cell)
-{
- jsCast<NativeExecutable*>(cell)->clearCode();
-}
-
const ClassInfo ScriptExecutable::s_info = { "ScriptExecutable", &ExecutableBase::s_info, 0, 0, CREATE_METHOD_TABLE(ScriptExecutable) };
#if ENABLE(JIT)
@@ -146,8 +141,6 @@
, m_name(name)
, m_inferredName(inferredName.isNull() ? globalData.propertyNames->emptyIdentifier : inferredName)
, m_symbolTable(0)
- , m_next(0)
- , m_prev(0)
{
}
@@ -159,8 +152,6 @@
, m_name(name)
, m_inferredName(inferredName.isNull() ? exec->globalData().propertyNames->emptyIdentifier : inferredName)
, m_symbolTable(0)
- , m_next(0)
- , m_prev(0)
{
}
@@ -292,17 +283,9 @@
#endif
}
-void EvalExecutable::finalize(JSCell* cell)
+void EvalExecutable::clearCode()
{
- jsCast<EvalExecutable*>(cell)->clearCode();
-}
-
-inline void EvalExecutable::clearCode()
-{
- if (m_evalCodeBlock) {
- m_evalCodeBlock->clearEvalCache();
- m_evalCodeBlock.clear();
- }
+ m_evalCodeBlock.clear();
Base::clearCode();
}
@@ -424,17 +407,9 @@
thisObject->m_programCodeBlock->visitAggregate(visitor);
}
-void ProgramExecutable::finalize(JSCell* cell)
+void ProgramExecutable::clearCode()
{
- jsCast<ProgramExecutable*>(cell)->clearCode();
-}
-
-inline void ProgramExecutable::clearCode()
-{
- if (m_programCodeBlock) {
- m_programCodeBlock->clearEvalCache();
- m_programCodeBlock.clear();
- }
+ m_programCodeBlock.clear();
Base::clearCode();
}
@@ -642,37 +617,17 @@
thisObject->m_codeBlockForConstruct->visitAggregate(visitor);
}
-void FunctionExecutable::discardCode()
+void FunctionExecutable::clearCodeIfNotCompiling()
{
-#if ENABLE(JIT)
- // These first two checks are to handle the rare case where
- // we are trying to evict code for a function during its
- // codegen.
- if (!m_jitCodeForCall && m_codeBlockForCall)
+ if (isCompiling())
return;
- if (!m_jitCodeForConstruct && m_codeBlockForConstruct)
- return;
-#endif
clearCode();
}
-void FunctionExecutable::finalize(JSCell* cell)
+void FunctionExecutable::clearCode()
{
- FunctionExecutable* executable = jsCast<FunctionExecutable*>(cell);
- Heap::heap(executable)->removeFunctionExecutable(executable);
- executable->clearCode();
-}
-
-inline void FunctionExecutable::clearCode()
-{
- if (m_codeBlockForCall) {
- m_codeBlockForCall->clearEvalCache();
- m_codeBlockForCall.clear();
- }
- if (m_codeBlockForConstruct) {
- m_codeBlockForConstruct->clearEvalCache();
- m_codeBlockForConstruct.clear();
- }
+ m_codeBlockForCall.clear();
+ m_codeBlockForConstruct.clear();
Base::clearCode();
}
Modified: trunk/Source/_javascript_Core/runtime/Executable.h (119843 => 119844)
--- trunk/Source/_javascript_Core/runtime/Executable.h 2012-06-08 18:14:55 UTC (rev 119843)
+++ trunk/Source/_javascript_Core/runtime/Executable.h 2012-06-08 18:17:16 UTC (rev 119844)
@@ -54,7 +54,8 @@
return false;
}
- class ExecutableBase : public JSCell {
+ class ExecutableBase : public JSCell, public DoublyLinkedListNode<ExecutableBase> {
+ friend class WTF::DoublyLinkedListNode<ExecutableBase>;
friend class JIT;
protected:
@@ -80,6 +81,11 @@
static void destroy(JSCell*);
#endif
+ bool isFunctionExecutable()
+ {
+ return structure()->typeInfo().type() == FunctionExecutableType;
+ }
+
bool isHostFunction() const
{
ASSERT((m_numParametersForCall == NUM_PARAMETERS_IS_HOST) == (m_numParametersForConstruct == NUM_PARAMETERS_IS_HOST));
@@ -88,6 +94,8 @@
static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) { return Structure::create(globalData, globalObject, proto, TypeInfo(CompoundType, StructureFlags), &s_info); }
+ void clearCode();
+
static JS_EXPORTDATA const ClassInfo s_info;
protected:
@@ -97,6 +105,8 @@
#if ENABLE(JIT)
public:
+ static void clearCodeVirtual(ExecutableBase*);
+
JITCode& generatedJITCodeForCall()
{
ASSERT(m_jitCodeForCall);
@@ -168,12 +178,14 @@
}
protected:
+ ExecutableBase* m_prev;
+ ExecutableBase* m_next;
+
JITCode m_jitCodeForCall;
JITCode m_jitCodeForConstruct;
MacroAssemblerCodePtr m_jitCodeForCallWithArityCheck;
MacroAssemblerCodePtr m_jitCodeForConstructWithArityCheck;
#endif
- void clearCode();
};
class NativeExecutable : public ExecutableBase {
@@ -194,7 +206,6 @@
executable = new (NotNull, allocateCell<NativeExecutable>(globalData.heap)) NativeExecutable(globalData, function, constructor);
executable->finishCreation(globalData, JITCode::HostFunction(callThunk), JITCode::HostFunction(constructThunk), intrinsic);
}
- globalData.heap.addFinalizer(executable, &finalize);
return executable;
}
#endif
@@ -205,7 +216,6 @@
ASSERT(!globalData.canUseJIT());
NativeExecutable* executable = new (NotNull, allocateCell<NativeExecutable>(globalData.heap)) NativeExecutable(globalData, function, constructor);
executable->finishCreation(globalData);
- globalData.heap.addFinalizer(executable, &finalize);
return executable;
}
#endif
@@ -246,8 +256,6 @@
}
#endif
- static void finalize(JSCell*);
-
private:
NativeExecutable(JSGlobalData& globalData, NativeFunction function, NativeFunction constructor)
: ExecutableBase(globalData, globalData.nativeExecutableStructure.get(), NUM_PARAMETERS_IS_HOST)
@@ -303,6 +311,8 @@
void finishCreation(JSGlobalData& globalData)
{
Base::finishCreation(globalData);
+ globalData.heap.addCompiledCode(this); // Balanced by Heap::deleteUnmarkedCompiledCode().
+
#if ENABLE(CODEBLOCK_SAMPLING)
if (SamplingTool* sampler = globalData.interpreter->sampler())
sampler->notifyOfScope(globalData, this);
@@ -358,7 +368,6 @@
{
EvalExecutable* executable = new (NotNull, allocateCell<EvalExecutable>(*exec->heap())) EvalExecutable(exec, source, isInStrictContext);
executable->finishCreation(exec->globalData());
- exec->globalData().heap.addFinalizer(executable, &finalize);
return executable;
}
@@ -377,9 +386,7 @@
void unlinkCalls();
- protected:
void clearCode();
- static void finalize(JSCell*);
private:
static const unsigned StructureFlags = OverridesVisitChildren | ScriptExecutable::StructureFlags;
@@ -400,7 +407,6 @@
{
ProgramExecutable* executable = new (NotNull, allocateCell<ProgramExecutable>(*exec->heap())) ProgramExecutable(exec, source);
executable->finishCreation(exec->globalData());
- exec->globalData().heap.addFinalizer(executable, &finalize);
return executable;
}
@@ -447,9 +453,7 @@
void unlinkCalls();
- protected:
void clearCode();
- static void finalize(JSCell*);
private:
static const unsigned StructureFlags = OverridesVisitChildren | ScriptExecutable::StructureFlags;
@@ -461,10 +465,9 @@
OwnPtr<ProgramCodeBlock> m_programCodeBlock;
};
- class FunctionExecutable : public ScriptExecutable, public DoublyLinkedListNode<FunctionExecutable> {
+ class FunctionExecutable : public ScriptExecutable {
friend class JIT;
friend class LLIntOffsetsExtractor;
- friend class WTF::DoublyLinkedListNode<FunctionExecutable>;
public:
typedef ScriptExecutable Base;
@@ -472,8 +475,6 @@
{
FunctionExecutable* executable = new (NotNull, allocateCell<FunctionExecutable>(*exec->heap())) FunctionExecutable(exec, name, inferredName, source, forceUsesArguments, parameters, isInStrictContext);
executable->finishCreation(exec->globalData(), name, firstLine, lastLine);
- exec->globalData().heap.addFunctionExecutable(executable);
- exec->globalData().heap.addFinalizer(executable, &finalize);
return executable;
}
@@ -481,8 +482,6 @@
{
FunctionExecutable* executable = new (NotNull, allocateCell<FunctionExecutable>(globalData.heap)) FunctionExecutable(globalData, name, inferredName, source, forceUsesArguments, parameters, isInStrictContext);
executable->finishCreation(globalData, name, firstLine, lastLine);
- globalData.heap.addFunctionExecutable(executable);
- globalData.heap.addFinalizer(executable, &finalize);
return executable;
}
@@ -639,7 +638,7 @@
UString paramString() const;
SharedSymbolTable* symbolTable() const { return m_symbolTable; }
- void discardCode();
+ void clearCodeIfNotCompiling();
static void visitChildren(JSCell*, SlotVisitor&);
static FunctionExecutable* fromGlobalCode(const Identifier&, ExecState*, Debugger*, const SourceCode&, JSObject** exception);
static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto)
@@ -651,10 +650,9 @@
void unlinkCalls();
- protected:
void clearCode();
- static void finalize(JSCell*);
+ protected:
void finishCreation(JSGlobalData& globalData, const Identifier& name, int firstLine, int lastLine)
{
Base::finishCreation(globalData);
@@ -677,7 +675,18 @@
ASSERT(kind == CodeForConstruct);
return m_codeBlockForConstruct;
}
-
+
+ bool isCompiling()
+ {
+#if ENABLE(JIT)
+ if (!m_jitCodeForCall && m_codeBlockForCall)
+ return true;
+ if (!m_jitCodeForConstruct && m_codeBlockForConstruct)
+ return true;
+#endif
+ return false;
+ }
+
static const unsigned StructureFlags = OverridesVisitChildren | ScriptExecutable::StructureFlags;
unsigned m_numCapturedVariables : 31;
bool m_forceUsesArguments : 1;
@@ -689,8 +698,6 @@
Identifier m_inferredName;
WriteBarrier<JSString> m_nameValue;
SharedSymbolTable* m_symbolTable;
- FunctionExecutable* m_next;
- FunctionExecutable* m_prev;
};
inline FunctionExecutable* JSFunction::jsExecutable() const
@@ -725,6 +732,20 @@
return function->nativeFunction() == nativeFunction;
}
+ inline void ExecutableBase::clearCodeVirtual(ExecutableBase* executable)
+ {
+ switch (executable->structure()->typeInfo().type()) {
+ case EvalExecutableType:
+ return jsCast<EvalExecutable*>(executable)->clearCode();
+ case ProgramExecutableType:
+ return jsCast<ProgramExecutable*>(executable)->clearCode();
+ case FunctionExecutableType:
+ return jsCast<FunctionExecutable*>(executable)->clearCode();
+ default:
+ return jsCast<NativeExecutable*>(executable)->clearCode();
+ }
+ }
+
inline void ScriptExecutable::unlinkCalls()
{
switch (structure()->typeInfo().type()) {
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalData.cpp (119843 => 119844)
--- trunk/Source/_javascript_Core/runtime/JSGlobalData.cpp 2012-06-08 18:14:55 UTC (rev 119843)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalData.cpp 2012-06-08 18:17:16 UTC (rev 119844)
@@ -419,7 +419,7 @@
FunctionExecutable* executable = jsCast<FunctionExecutable*>(cell);
if (currentlyExecutingFunctions.contains(executable))
return;
- executable->discardCode();
+ executable->clearCodeIfNotCompiling();
}
};
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (119843 => 119844)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2012-06-08 18:14:55 UTC (rev 119843)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2012-06-08 18:17:16 UTC (rev 119844)
@@ -443,7 +443,7 @@
if (!m_dynamicGlobalObjectSlot) {
#if ENABLE(ASSEMBLER)
if (ExecutableAllocator::underMemoryPressure())
- globalData.heap.discardAllCompiledCode();
+ globalData.heap.deleteAllCompiledCode();
#endif
m_dynamicGlobalObjectSlot = dynamicGlobalObject;