Commit d342e879 authored by mjs@apple.com's avatar mjs@apple.com

JavaScriptCore:

        Reviewed by Darin and Geoff.

        - Fixed "Stack overflow crash in JavaScript garbage collector mark pass"
        http://bugs.webkit.org/show_bug.cgi?id=12216
        
        Implement mark stack. This version is not suitable for prime time because it makes a
        huge allocation on every collect, and potentially makes marking of detached subtrees
        slow. But it is an 0.4% SunSpider speedup even without much tweaking.
        
        The basic approach is to replace mark() methods with
        markChildren(MarkStack&) methods. Reachable references are pushed
        onto a mark stack (which encapsulates ignoring already-marked
        references). 
        
        Objects are no longer responsible for actually setting their own
        mark bits, the collector does that. This means that for objects on
        the number heap we don't have to call markChildren() at all since
        we know there aren't any.
        
        The mark phase of collect pushes roots onto the mark stack
        and drains it as often as possible.
        
        To make this approach viable requires a constant-size mark stack
        and a slow fallback approach for when the stack size is exceeded,
        plus optimizations to make the required stack small in common
        cases. This should be doable.

        * JavaScriptCore.exp: Export new symbols.
        * JavaScriptCore.xcodeproj/project.pbxproj: Add new file.
        * kjs/collector.cpp:
        (KJS::Collector::heapAllocate):
        (KJS::drainMarkStack): Helper for all of the below.
        (KJS::Collector::markStackObjectsConservatively): Use mark stack.
        (KJS::Collector::markCurrentThreadConservatively): ditto
        (KJS::Collector::markOtherThreadConservatively): ditto
        (KJS::Collector::markProtectedObjects): ditto
        (KJS::Collector::markMainThreadOnlyObjects): ditto
        (KJS::Collector::collect): ditto
        * kjs/collector.h:
        (KJS::Collector::cellMayHaveRefs): Helper for MarkStack.

        * kjs/MarkStack.h: Added. The actual mark stack implementation.
        (KJS::MarkStack::push):
        (KJS::MarkStack::pushAtom):
        (KJS::MarkStack::pop):
        (KJS::MarkStack::isEmpty):
        (KJS::MarkStack::reserveCapacity):

        Changed mark() methods to markChildren() methods:
        
        * kjs/ExecState.cpp:
        (KJS::ExecState::markChildren):
        * kjs/ExecState.h:
        * kjs/JSWrapperObject.cpp:
        (KJS::JSWrapperObject::markChildren):
        * kjs/JSWrapperObject.h:
        * kjs/array_instance.cpp:
        (KJS::ArrayInstance::markChildren):
        * kjs/array_instance.h:
        * kjs/bool_object.cpp:
        (BooleanInstance::markChildren):
        * kjs/bool_object.h:
        * kjs/error_object.cpp:
        * kjs/error_object.h:
        * kjs/function.cpp:
        (KJS::FunctionImp::markChildren):
        (KJS::Arguments::Arguments):
        (KJS::Arguments::markChildren):
        (KJS::ActivationImp::markChildren):
        * kjs/function.h:
        * kjs/internal.cpp:
        (KJS::GetterSetterImp::markChildren):
        * kjs/interpreter.cpp:
        (KJS::Interpreter::markRoots):
        * kjs/interpreter.h:
        * kjs/list.cpp:
        (KJS::List::markProtectedListsSlowCase):
        * kjs/list.h:
        (KJS::List::markProtectedLists):
        * kjs/object.cpp:
        (KJS::JSObject::markChildren):
        * kjs/object.h:
        (KJS::ScopeChain::markChildren):
        * kjs/property_map.cpp:
        (KJS::PropertyMap::markChildren):
        * kjs/property_map.h:
        * kjs/scope_chain.h:
        * kjs/string_object.cpp:
        (KJS::StringInstance::markChildren):
        * kjs/string_object.h:

JavaScriptGlue:

        Reviewed by Darin and Geoff.

        Fixups for JavaScriptCore mark stack.

        * JSObject.cpp:
        (JSUserObject::Mark):
        * JSObject.h:
        * JSValueWrapper.cpp:
        (JSValueWrapper::JSObjectMark):
        * JSValueWrapper.h:
        * UserObjectImp.cpp:
        * UserObjectImp.h:

WebCore:

        Reviewed by Darin and Geoff.

        Implement mark stack. This version is not suitable for prime time because it makes a
        huge allocation on every collect, and potentially makes marking of detached subtrees
        slow. But it is a .2% - .4% speedup even without much tweaking.

        I replaced mark() methods with markChildren() as usual. One
        optimization that is lost is avoiding walking detached DOM
        subtrees more than once to mark them; since marking is not
        recursive there's no obvious way to bracket operation on the tree
        any more.

        * bindings/js/JSDocumentCustom.cpp:
        (WebCore::JSDocument::markChildren):
        * bindings/js/JSNodeCustom.cpp:
        (WebCore::JSNode::markChildren):
        * bindings/js/JSNodeFilterCondition.cpp:
        * bindings/js/JSNodeFilterCondition.h:
        * bindings/js/JSNodeFilterCustom.cpp:
        (WebCore::JSNodeFilter::markChildren):
        * bindings/js/JSNodeIteratorCustom.cpp:
        (WebCore::JSNodeIterator::markChildren):
        * bindings/js/JSTreeWalkerCustom.cpp:
        (WebCore::JSTreeWalker::markChildren):
        * bindings/js/JSXMLHttpRequest.cpp:
        (KJS::JSXMLHttpRequest::markChildren):
        * bindings/js/JSXMLHttpRequest.h:
        * bindings/js/kjs_binding.cpp:
        (KJS::ScriptInterpreter::markDOMNodesForDocument):
        * bindings/js/kjs_binding.h:
        * bindings/js/kjs_events.cpp:
        (WebCore::JSUnprotectedEventListener::markChildren):
        * bindings/js/kjs_events.h:
        * bindings/js/kjs_window.cpp:
        (KJS::Window::markChildren):
        * bindings/js/kjs_window.h:
        * bindings/scripts/CodeGeneratorJS.pm:
        * dom/Node.cpp:
        (WebCore::Node::Node):
        * dom/Node.h:
        * dom/NodeFilter.h:
        * dom/NodeFilterCondition.h:

LayoutTests:

        Not reviewed.
        
        - Test cases for "Stack overflow crash in JavaScript garbage collector mark pass"
        http://bugs.webkit.org/show_bug.cgi?id=12216

        I have fixed this with the mark stack work.
        
        * fast/js/gc-breadth-2-expected.txt: Added.
        * fast/js/gc-breadth-2.html: Added.
        * fast/js/gc-breadth-expected.txt: Added.
        * fast/js/gc-breadth.html: Added.
        * fast/js/gc-depth-expected.txt: Added.
        * fast/js/gc-depth.html: Added.
        * fast/js/resources/gc-breadth-2.js: Added.
        * fast/js/resources/gc-breadth.js: Added.
        * fast/js/resources/gc-depth.js: Added.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@28106 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 61a95a0d
2007-11-28 Maciej Stachowiak <mjs@apple.com>
Reviewed by Darin and Geoff.
- Fixed "Stack overflow crash in JavaScript garbage collector mark pass"
http://bugs.webkit.org/show_bug.cgi?id=12216
Implement mark stack. This version is not suitable for prime time because it makes a
huge allocation on every collect, and potentially makes marking of detached subtrees
slow. But it is an 0.4% SunSpider speedup even without much tweaking.
The basic approach is to replace mark() methods with
markChildren(MarkStack&) methods. Reachable references are pushed
onto a mark stack (which encapsulates ignoring already-marked
references).
Objects are no longer responsible for actually setting their own
mark bits, the collector does that. This means that for objects on
the number heap we don't have to call markChildren() at all since
we know there aren't any.
The mark phase of collect pushes roots onto the mark stack
and drains it as often as possible.
To make this approach viable requires a constant-size mark stack
and a slow fallback approach for when the stack size is exceeded,
plus optimizations to make the required stack small in common
cases. This should be doable.
* JavaScriptCore.exp: Export new symbols.
* JavaScriptCore.xcodeproj/project.pbxproj: Add new file.
* kjs/collector.cpp:
(KJS::Collector::heapAllocate):
(KJS::drainMarkStack): Helper for all of the below.
(KJS::Collector::markStackObjectsConservatively): Use mark stack.
(KJS::Collector::markCurrentThreadConservatively): ditto
(KJS::Collector::markOtherThreadConservatively): ditto
(KJS::Collector::markProtectedObjects): ditto
(KJS::Collector::markMainThreadOnlyObjects): ditto
(KJS::Collector::collect): ditto
* kjs/collector.h:
(KJS::Collector::cellMayHaveRefs): Helper for MarkStack.
* kjs/MarkStack.h: Added. The actual mark stack implementation.
(KJS::MarkStack::push):
(KJS::MarkStack::pushAtom):
(KJS::MarkStack::pop):
(KJS::MarkStack::isEmpty):
(KJS::MarkStack::reserveCapacity):
Changed mark() methods to markChildren() methods:
* kjs/ExecState.cpp:
(KJS::ExecState::markChildren):
* kjs/ExecState.h:
* kjs/JSWrapperObject.cpp:
(KJS::JSWrapperObject::markChildren):
* kjs/JSWrapperObject.h:
* kjs/array_instance.cpp:
(KJS::ArrayInstance::markChildren):
* kjs/array_instance.h:
* kjs/bool_object.cpp:
(BooleanInstance::markChildren):
* kjs/bool_object.h:
* kjs/error_object.cpp:
* kjs/error_object.h:
* kjs/function.cpp:
(KJS::FunctionImp::markChildren):
(KJS::Arguments::Arguments):
(KJS::Arguments::markChildren):
(KJS::ActivationImp::markChildren):
* kjs/function.h:
* kjs/internal.cpp:
(KJS::GetterSetterImp::markChildren):
* kjs/interpreter.cpp:
(KJS::Interpreter::markRoots):
* kjs/interpreter.h:
* kjs/list.cpp:
(KJS::List::markProtectedListsSlowCase):
* kjs/list.h:
(KJS::List::markProtectedLists):
* kjs/object.cpp:
(KJS::JSObject::markChildren):
* kjs/object.h:
(KJS::ScopeChain::markChildren):
* kjs/property_map.cpp:
(KJS::PropertyMap::markChildren):
* kjs/property_map.h:
* kjs/scope_chain.h:
* kjs/string_object.cpp:
(KJS::StringInstance::markChildren):
* kjs/string_object.h:
2007-11-27 Alp Toker <alp@atoker.com>
Reviewed by Mark Rowe.
......
......@@ -122,10 +122,10 @@ __ZN3KJS11Interpreter17startTimeoutCheckEv
__ZN3KJS11Interpreter21shouldPrintExceptionsEv
__ZN3KJS11Interpreter24setShouldPrintExceptionsEb
__ZN3KJS11Interpreter27resetGlobalObjectPropertiesEv
__ZN3KJS11Interpreter4markEv
__ZN3KJS11Interpreter6s_hookE
__ZN3KJS11Interpreter8evaluateERKNS_7UStringEiPKNS_5UCharEiPNS_7JSValueE
__ZN3KJS11Interpreter8evaluateERKNS_7UStringEiS3_PNS_7JSValueE
__ZN3KJS11Interpreter9markRootsERNS_9MarkStackE
__ZN3KJS11InterpreterC1Ev
__ZN3KJS11InterpreterC2Ev
__ZN3KJS11InterpreterD1Ev
......@@ -144,6 +144,7 @@ __ZN3KJS13ArrayInstance4infoE
__ZN3KJS13SavedBuiltinsC1Ev
__ZN3KJS13SavedBuiltinsD1Ev
__ZN3KJS13jsOwnedStringERKNS_7UStringE
__ZN3KJS14StringInstance12markChildrenERNS_9MarkStackE
__ZN3KJS14StringInstance14deletePropertyEPNS_9ExecStateERKNS_10IdentifierE
__ZN3KJS14StringInstance16getPropertyNamesEPNS_9ExecStateERNS_17PropertyNameArrayE
__ZN3KJS14StringInstance18getOwnPropertySlotEPNS_9ExecStateERKNS_10IdentifierERNS_12PropertySlotE
......@@ -152,7 +153,6 @@ __ZN3KJS14StringInstance3putEPNS_9ExecStateERKNS_10IdentifierEPNS_7JSValueEi
__ZN3KJS14StringInstance4infoE
__ZN3KJS14StringInstanceC1EPNS_8JSObjectERKNS_7UStringE
__ZN3KJS14StringInstanceC2EPNS_8JSObjectERKNS_7UStringE
__ZN3KJS15JSWrapperObject4markEv
__ZN3KJS15SavedPropertiesC1Ev
__ZN3KJS15SavedPropertiesD1Ev
__ZN3KJS16RuntimeObjectImp4infoE
......@@ -201,6 +201,7 @@ __ZN3KJS8Debugger9exceptionEPNS_9ExecStateEiiPNS_7JSValueE
__ZN3KJS8DebuggerC2Ev
__ZN3KJS8DebuggerD2Ev
__ZN3KJS8JSObject11hasInstanceEPNS_9ExecStateEPNS_7JSValueE
__ZN3KJS8JSObject12markChildrenERNS_9MarkStackE
__ZN3KJS8JSObject12removeDirectERKNS_10IdentifierE
__ZN3KJS8JSObject14callAsFunctionEPNS_9ExecStateEPS0_RKNS_4ListE
__ZN3KJS8JSObject14deletePropertyEPNS_9ExecStateERKNS_10IdentifierE
......@@ -212,7 +213,6 @@ __ZN3KJS8JSObject22fillGetterPropertySlotERNS_12PropertySlotEPPNS_7JSValueE
__ZN3KJS8JSObject3putEPNS_9ExecStateERKNS_10IdentifierEPNS_7JSValueEi
__ZN3KJS8JSObject3putEPNS_9ExecStateEjPNS_7JSValueEi
__ZN3KJS8JSObject4callEPNS_9ExecStateEPS0_RKNS_4ListE
__ZN3KJS8JSObject4markEv
__ZN3KJS8JSObject9constructEPNS_9ExecStateERKNS_4ListE
__ZN3KJS8JSObject9constructEPNS_9ExecStateERKNS_4ListERKNS_10IdentifierERKNS_7UStringEi
__ZN3KJS8JSObject9putDirectERKNS_10IdentifierEPNS_7JSValueEi
......
......@@ -99,6 +99,7 @@
6592C318098B7DE10003D4F6 /* Vector.h in Headers */ = {isa = PBXBuildFile; fileRef = 6592C316098B7DE10003D4F6 /* Vector.h */; settings = {ATTRIBUTES = (Private, ); }; };
6592C319098B7DE10003D4F6 /* VectorTraits.h in Headers */ = {isa = PBXBuildFile; fileRef = 6592C317098B7DE10003D4F6 /* VectorTraits.h */; settings = {ATTRIBUTES = (Private, ); }; };
65A7A5E00CD1D50E00061F8E /* LabelStack.h in Headers */ = {isa = PBXBuildFile; fileRef = 65B813A80CD1D01900DF59D6 /* LabelStack.h */; settings = {ATTRIBUTES = (Private, ); }; };
65A8B8DB0CF408F400DC7C27 /* MarkStack.h in Headers */ = {isa = PBXBuildFile; fileRef = 65A8B8D80CF408E900DC7C27 /* MarkStack.h */; settings = {ATTRIBUTES = (Private, ); }; };
65B1749A09D0FEB700820339 /* array_object.lut.h in Headers */ = {isa = PBXBuildFile; fileRef = 65B1749909D0FEB700820339 /* array_object.lut.h */; };
65B174F509D100FA00820339 /* math_object.lut.h in Headers */ = {isa = PBXBuildFile; fileRef = 65B174F109D100FA00820339 /* math_object.lut.h */; };
65B174F609D100FA00820339 /* number_object.lut.h in Headers */ = {isa = PBXBuildFile; fileRef = 65B174F209D100FA00820339 /* number_object.lut.h */; };
......@@ -510,6 +511,7 @@
659126BC0BDD1728001921FB /* AllInOneFile.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = AllInOneFile.cpp; sourceTree = "<group>"; };
6592C316098B7DE10003D4F6 /* Vector.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Vector.h; sourceTree = "<group>"; };
6592C317098B7DE10003D4F6 /* VectorTraits.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = VectorTraits.h; sourceTree = "<group>"; };
65A8B8D80CF408E900DC7C27 /* MarkStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MarkStack.h; sourceTree = "<group>"; };
65B1749909D0FEB700820339 /* array_object.lut.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = file; name = array_object.lut.h; path = ../../../../../symroots/Debug/DerivedSources/JavaScriptCore/array_object.lut.h; sourceTree = "<group>"; };
65B174BE09D1000200820339 /* chartables.c */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.c; fileEncoding = 30; path = chartables.c; sourceTree = "<group>"; };
65B174F109D100FA00820339 /* math_object.lut.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = file; name = math_object.lut.h; path = ../../../../../symroots/Debug/DerivedSources/JavaScriptCore/math_object.lut.h; sourceTree = "<group>"; };
......@@ -1008,6 +1010,7 @@
F692A8680255597D01FF60F7 /* lookup.cpp */,
F692A8690255597D01FF60F7 /* lookup.h */,
F692A86A0255597D01FF60F7 /* math_object.cpp */,
65A8B8D80CF408E900DC7C27 /* MarkStack.h */,
F692A86B0255597D01FF60F7 /* math_object.h */,
F692A86D0255597D01FF60F7 /* nodes.cpp */,
F692A86E0255597D01FF60F7 /* nodes.h */,
......@@ -1170,6 +1173,7 @@
148A1627095D16BB00666D0D /* ListRefPtr.h in Headers */,
65F340940CD6C1C000C0CA8B /* LocalStorage.h in Headers */,
5DBD18B00C5401A700C15EAE /* MallocZoneSupport.h in Headers */,
65A8B8DB0CF408F400DC7C27 /* MarkStack.h in Headers */,
BCF655590A2049710038A194 /* MathExtras.h in Headers */,
932F5B840822A1C700736975 /* NP_jsobject.h in Headers */,
9303F56A0991190000AD71B8 /* Noncopyable.h in Headers */,
......
......@@ -88,10 +88,10 @@ ExecState::~ExecState()
m_interpreter->setCurrentExec(m_savedExecState);
}
void ExecState::mark()
void ExecState::markChildren(MarkStack& stack)
{
for (ExecState* exec = this; exec; exec = exec->m_callingExecState)
exec->m_scopeChain.mark();
exec->m_scopeChain.markChildren(stack);
}
void ExecState::setGlobalObject(JSGlobalObject* globalObject)
......
......@@ -100,7 +100,7 @@ namespace KJS {
void setGlobalObject(JSGlobalObject*);
void mark();
void markChildren(MarkStack&);
// This is a workaround to avoid accessing the global variables for these identifiers in
// important property lookup functions, to avoid taking PIC branches in Mach-O binaries
......
......@@ -24,11 +24,10 @@
namespace KJS {
void JSWrapperObject::mark()
void JSWrapperObject::markChildren(MarkStack& stack)
{
JSObject::mark();
if (m_internalValue && !m_internalValue->marked())
m_internalValue->mark();
JSObject::markChildren(stack);
stack.pushAtom(m_internalValue);
}
} // namespace KJS
......@@ -56,7 +56,7 @@ namespace KJS {
*/
void setInternalValue(JSValue* v);
virtual void mark();
virtual void markChildren(MarkStack& stack);
private:
JSValue* m_internalValue;
......@@ -64,7 +64,7 @@ namespace KJS {
inline JSWrapperObject::JSWrapperObject(JSValue* proto)
: JSObject(proto)
, m_internalValue(0)
, m_internalValue(jsNull())
{
}
......
......@@ -402,26 +402,23 @@ void ArrayInstance::setLength(unsigned newLength)
m_length = newLength;
}
void ArrayInstance::mark()
void ArrayInstance::markChildren(MarkStack& stack)
{
JSObject::mark();
JSObject::markChildren(stack);
ArrayStorage* storage = m_storage;
unsigned usedVectorLength = min(m_length, m_vectorLength);
for (unsigned i = 0; i < usedVectorLength; ++i) {
JSValue* value = storage->m_vector[i];
if (value && !value->marked())
value->mark();
if (value)
stack.push(value);
}
if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
SparseArrayValueMap::iterator end = map->end();
for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
JSValue* value = it->second;
if (!value->marked())
value->mark();
}
for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
stack.push(it->second);
}
}
......
......@@ -42,7 +42,7 @@ namespace KJS {
virtual bool deleteProperty(ExecState *, unsigned propertyName);
virtual void getPropertyNames(ExecState*, PropertyNameArray&);
virtual void mark();
virtual void markChildren(MarkStack&);
virtual const ClassInfo* classInfo() const { return &info; }
static const ClassInfo info;
......
......@@ -38,6 +38,12 @@ BooleanInstance::BooleanInstance(JSObject *proto)
{
}
void BooleanInstance::markChildren(MarkStack& stack)
{
JSObject::markChildren(stack);
ASSERT(JSImmediate::isImmediate(internalValue()));
}
// ------------------------------ BooleanPrototype --------------------------
// ECMA 15.6.4
......
......@@ -32,6 +32,7 @@ namespace KJS {
BooleanInstance(JSObject *proto);
virtual const ClassInfo *classInfo() const { return &info; }
virtual void markChildren(MarkStack& stack);
static const ClassInfo info;
};
......
......@@ -26,6 +26,7 @@
#include "ExecState.h"
#include "internal.h"
#include "list.h"
#include "MarkStack.h"
#include "value.h"
#include <algorithm>
#include <setjmp.h>
......@@ -277,6 +278,8 @@ collect:
targetBlock = (Block*)allocateBlock();
targetBlock->freeList = targetBlock->cells;
if (heapType == PrimaryHeap)
targetBlock->mayHaveRefs = 1;
targetBlockUsedCells = 0;
heap.blocks[usedBlocks] = (CollectorBlock*)targetBlock;
heap.usedBlocks = usedBlocks + 1;
......@@ -479,7 +482,14 @@ void Collector::registerThread()
// cell size needs to be a power of two for this to be valid
#define IS_HALF_CELL_ALIGNED(p) (((intptr_t)(p) & (CELL_MASK >> 1)) == 0)
void Collector::markStackObjectsConservatively(void *start, void *end)
static inline void drainMarkStack(MarkStack& stack)
{
while (!stack.isEmpty())
stack.pop()->markChildren(stack);
}
void Collector::markStackObjectsConservatively(MarkStack& stack, void *start, void *end)
{
if (start > end) {
void* tmp = start;
......@@ -521,8 +531,8 @@ void Collector::markStackObjectsConservatively(void *start, void *end)
if ((primaryBlocks[block] == blockAddr) & (offset <= lastCellOffset)) {
if (((CollectorCell*)xAsBits)->u.freeCell.zeroIfFree != 0) {
JSCell* imp = reinterpret_cast<JSCell*>(xAsBits);
if (!imp->marked())
imp->mark();
stack.push(imp);
drainMarkStack(stack);
}
break;
}
......@@ -533,7 +543,7 @@ void Collector::markStackObjectsConservatively(void *start, void *end)
}
}
void Collector::markCurrentThreadConservatively()
void Collector::markCurrentThreadConservatively(MarkStack& stack)
{
// setjmp forces volatile registers onto the stack
jmp_buf registers;
......@@ -550,7 +560,7 @@ void Collector::markCurrentThreadConservatively()
void* stackPointer = &dummy;
void* stackBase = currentThreadStackBase();
markStackObjectsConservatively(stackPointer, stackBase);
markStackObjectsConservatively(stack, stackPointer, stackBase);
}
#if USE(MULTIPLE_THREADS)
......@@ -693,7 +703,7 @@ static inline void* otherThreadStackBase(const PlatformThreadRegisters& regs, Co
#endif
}
void Collector::markOtherThreadConservatively(Thread* thread)
void Collector::markOtherThreadConservatively(MarkStack& stack, Thread* thread)
{
suspendThread(thread->platformThread);
......@@ -701,25 +711,25 @@ void Collector::markOtherThreadConservatively(Thread* thread)
size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs);
// mark the thread's registers
markStackObjectsConservatively((void*)&regs, (void*)((char*)&regs + regSize));
markStackObjectsConservatively(stack, (void*)&regs, (void*)((char*)&regs + regSize));
void* stackPointer = otherThreadStackPointer(regs);
void* stackBase = otherThreadStackBase(regs, thread);
markStackObjectsConservatively(stackPointer, stackBase);
markStackObjectsConservatively(stack, stackPointer, stackBase);
resumeThread(thread->platformThread);
}
#endif
void Collector::markStackObjectsConservatively()
void Collector::markStackObjectsConservatively(MarkStack& stack)
{
markCurrentThreadConservatively();
markCurrentThreadConservatively(stack);
#if USE(MULTIPLE_THREADS)
for (Thread *thread = registeredThreads; thread != NULL; thread = thread->next) {
if (!pthread_equal(thread->posixThread, pthread_self())) {
markOtherThreadConservatively(thread);
markOtherThreadConservatively(stack, thread);
}
}
#endif
......@@ -771,18 +781,17 @@ void Collector::collectOnMainThreadOnly(JSValue* value)
++mainThreadOnlyObjectCount;
}
void Collector::markProtectedObjects()
void Collector::markProtectedObjects(MarkStack& stack)
{
ProtectCountSet& protectedValues = KJS::protectedValues();
ProtectCountSet::iterator end = protectedValues.end();
for (ProtectCountSet::iterator it = protectedValues.begin(); it != end; ++it) {
JSCell *val = it->first;
if (!val->marked())
val->mark();
stack.push(it->first);
drainMarkStack(stack);
}
}
void Collector::markMainThreadOnlyObjects()
void Collector::markMainThreadOnlyObjects(MarkStack& stack)
{
#if USE(MULTIPLE_THREADS)
ASSERT(!onMainThread());
......@@ -814,7 +823,8 @@ void Collector::markMainThreadOnlyObjects()
if (curBlock->collectOnMainThreadOnly.get(i)) {
if (!curBlock->marked.get(i)) {
JSCell* imp = reinterpret_cast<JSCell*>(cell);
imp->mark();
stack.push(imp);
drainMarkStack(stack);
}
if (++count == mainThreadOnlyObjectCount)
return;
......@@ -950,9 +960,14 @@ bool Collector::collect()
// MARK: first mark all referenced objects recursively starting out from the set of root objects
size_t originalLiveObjects = primaryHeap.numLiveObjects + numberHeap.numLiveObjects;
MarkStack stack;
stack.reserveCapacity(primaryHeap.numLiveObjects);
#ifndef NDEBUG
// Forbid malloc during the mark phase. Marking a thread suspends it, so
// a malloc inside mark() would risk a deadlock with a thread that had been
// a malloc inside markChildren() would risk a deadlock with a thread that had been
// suspended while holding the malloc lock.
fastMallocForbid();
#endif
......@@ -960,24 +975,25 @@ bool Collector::collect()
if (Interpreter::s_hook) {
Interpreter* scr = Interpreter::s_hook;
do {
scr->mark();
scr->markRoots(stack);
drainMarkStack(stack);
scr = scr->next;
} while (scr != Interpreter::s_hook);
}
markStackObjectsConservatively();
markProtectedObjects();
List::markProtectedLists();
markStackObjectsConservatively(stack);
markProtectedObjects(stack);
List::markProtectedLists(stack);
drainMarkStack(stack);
#if USE(MULTIPLE_THREADS)
if (!currentThreadIsMainThread)
markMainThreadOnlyObjects();
markMainThreadOnlyObjects(stack);
#endif
#ifndef NDEBUG
fastMallocAllow();
#endif
size_t originalLiveObjects = primaryHeap.numLiveObjects + numberHeap.numLiveObjects;
size_t numLiveObjects = sweep<PrimaryHeap>(currentThreadIsMainThread);
numLiveObjects += sweep<NumberHeap>(currentThreadIsMainThread);
......
......@@ -31,9 +31,10 @@
namespace KJS {
class CollectorBlock;
class JSCell;
class JSValue;
class CollectorBlock;
class MarkStack;
class Collector {
public:
......@@ -65,6 +66,7 @@ namespace KJS {
static bool isCellMarked(const JSCell*);
static void markCell(JSCell*);
static bool cellMayHaveRefs(const JSCell*);
enum HeapType { PrimaryHeap, NumberHeap };
......@@ -78,12 +80,12 @@ namespace KJS {
Collector();
static void recordExtraCost(size_t);
static void markProtectedObjects();
static void markMainThreadOnlyObjects();
static void markCurrentThreadConservatively();
static void markOtherThreadConservatively(Thread*);
static void markStackObjectsConservatively();
static void markStackObjectsConservatively(void* start, void* end);
static void markProtectedObjects(MarkStack&);
static void markMainThreadOnlyObjects(MarkStack&);
static void markCurrentThreadConservatively(MarkStack&);
static void markOtherThreadConservatively(MarkStack&, Thread*);
static void markStackObjectsConservatively(MarkStack&);
static void markStackObjectsConservatively(MarkStack&, void* start, void* end);
static size_t mainThreadOnlyObjectCount;
static bool memoryFull;
......@@ -107,7 +109,7 @@ namespace KJS {
const size_t SMALL_CELL_SIZE = CELL_SIZE / 2;
const size_t CELL_MASK = CELL_SIZE - 1;
const size_t CELL_ALIGN_MASK = ~CELL_MASK;
const size_t CELLS_PER_BLOCK = (BLOCK_SIZE * 8 - sizeof(uint32_t) * 8 - sizeof(void *) * 8 - 2 * (7 + 3 * 8)) / (CELL_SIZE * 8 + 2);
const size_t CELLS_PER_BLOCK = (BLOCK_SIZE * 8 - sizeof(uint32_t) * 8 - sizeof(uint32_t) * 8 - sizeof(void *) * 8 - 2 * (7 + 3 * 8)) / (CELL_SIZE * 8 + 2);
const size_t SMALL_CELLS_PER_BLOCK = 2 * CELLS_PER_BLOCK;
const size_t BITMAP_SIZE = (CELLS_PER_BLOCK + 7) / 8;
const size_t BITMAP_WORDS = (BITMAP_SIZE + 3) / sizeof(uint32_t);
......@@ -145,6 +147,7 @@ namespace KJS {
CollectorCell cells[CELLS_PER_BLOCK];
uint32_t usedCells;
CollectorCell* freeList;
uint32_t mayHaveRefs;
CollectorBitmap marked;
CollectorBitmap collectOnMainThreadOnly;
};
......@@ -154,6 +157,7 @@ namespace KJS {
SmallCollectorCell cells[SMALL_CELLS_PER_BLOCK];
uint32_t usedCells;
SmallCollectorCell* freeList;
uint32_t mayHaveRefs;
CollectorBitmap marked;
CollectorBitmap collectOnMainThreadOnly;
};
......@@ -183,6 +187,11 @@ namespace KJS {
cellBlock(cell)->marked.set(cellOffset(cell));
}
inline bool Collector::cellMayHaveRefs(const JSCell* cell)
{
return cellBlock(cell)->mayHaveRefs;
}
inline void Collector::reportExtraMemoryCost(size_t cost)
{
if (cost > minExtraCostSize)
......
......@@ -156,9 +156,3 @@ JSValue* NativeErrorImp::callAsFunction(ExecState* exec, JSObject*, const List&
return construct(exec, args);
}
void NativeErrorImp::mark()
{
JSObject::mark();
if (proto && !proto->marked())
proto->mark();
}
......@@ -75,8 +75,6 @@ namespace KJS {
virtual JSObject *construct(ExecState *exec, const List &args);
virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args);
virtual void mark();
virtual const ClassInfo *classInfo() const { return &info; }
static const ClassInfo info;
private:
......
......@@ -61,10 +61,10 @@ FunctionImp::FunctionImp(ExecState* exec, const Identifier& name, FunctionBodyNo
{
}
void FunctionImp::mark()
void FunctionImp::markChildren(MarkStack& stack)
{
InternalFunctionImp::mark();
_scope.mark();
InternalFunctionImp::markChildren(stack);
_scope.markChildren(stack);
}
JSValue* FunctionImp::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
......@@ -331,9 +331,9 @@ const ClassInfo Arguments::info = { "Arguments", 0, 0 };
// ECMA 10.1.8
Arguments::Arguments(ExecState* exec, FunctionImp* func, const List& args, ActivationImp* act)
: JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()),
_activationObject(act),
indexToNameMap(func, args)
: JSObject(exec->lexicalInterpreter()->builtinObjectPrototype())
, _activationObject(act)
, indexToNameMap(func, args)
{
putDirect(exec->propertyNames().callee, func, DontEnum);
putDirect(exec->propertyNames().length, args.size(), DontEnum);
......@@ -347,11 +347,10 @@ indexToNameMap(func, args)
}
}
void Arguments::mark()
void Arguments::markChildren(MarkStack& stack)
{
JSObject::mark();
if (_activationObject && !_activationObject->marked())
_activationObject->mark();
JSObject::markChildren(stack);
stack.push(_activationObject);
}
JSValue* Arguments::mappedIndexGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
......@@ -484,23 +483,17 @@ void ActivationImp::put(ExecState*, const Identifier& propertyName, JSValue* val
_prop.put(propertyName, value, attr, (attr == None || attr == DontDelete));