diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status index 3573d455af8..12b565ada88 100644 --- a/runtime/tests/vm/vm.status +++ b/runtime/tests/vm/vm.status @@ -6,3 +6,6 @@ prefix vm # When a spawned isolate throws an uncaught exception, we terminate the vm. RunLoop_Exception: Fail + +# Partially implemented old generation GC. +OldGC: CRASH diff --git a/runtime/vm/gc_marker.cc b/runtime/vm/gc_marker.cc new file mode 100644 index 00000000000..48012921a9a --- /dev/null +++ b/runtime/vm/gc_marker.cc @@ -0,0 +1,207 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/gc_marker.h" + +#include "vm/allocation.h" +#include "vm/isolate.h" +#include "vm/pages.h" +#include "vm/raw_object.h" +#include "vm/stack_frame.h" +#include "vm/visitor.h" + +namespace dart { + +// A simple chunked marking stack. +class MarkingStack : public ValueObject { + public: + MarkingStack() + : head_(new MarkingStackChunk()), + empty_chunks_(NULL), + marking_stack_(NULL), + top_(0) { + marking_stack_ = head_->MarkingStackChunkMemory(); + } + + ~MarkingStack() { + // TODO(iposva): Consider caching a couple emtpy marking stack chunks. + ASSERT(IsEmpty()); + delete head_; + MarkingStackChunk* next; + while (empty_chunks_ != NULL) { + next = empty_chunks_->next(); + delete empty_chunks_; + empty_chunks_ = next; + } + } + + bool IsEmpty() const { + return IsMarkingStackChunkEmpty() && (head_->next() == NULL); + } + + void Push(RawObject* value) { + ASSERT(!IsMarkingStackChunkFull()); + marking_stack_[top_] = value; + top_++; + if (IsMarkingStackChunkFull()) { + MarkingStackChunk* new_chunk; + if (empty_chunks_ == NULL) { + new_chunk = new MarkingStackChunk(); + } else { + new_chunk = empty_chunks_; + empty_chunks_ = new_chunk->next(); + } + new_chunk->set_next(head_); + head_ = new_chunk; + marking_stack_ = head_->MarkingStackChunkMemory(); + top_ = 0; + } + } + + RawObject* Pop() { + ASSERT(head_ != NULL); + ASSERT(!IsEmpty()); + if (IsMarkingStackChunkEmpty()) { + MarkingStackChunk* empty_chunk = head_; + head_ = head_->next(); + empty_chunk->set_next(empty_chunks_); + empty_chunks_ = empty_chunk; + marking_stack_ = head_->MarkingStackChunkMemory(); + top_ = MarkingStackChunk::kMarkingStackChunkSize; + } + top_--; + return marking_stack_[top_]; + } + + private: + class MarkingStackChunk { + public: + MarkingStackChunk() : next_(NULL) {} + ~MarkingStackChunk() {} + + RawObject** MarkingStackChunkMemory() { + return &memory_[0]; + } + + MarkingStackChunk* next() const { return next_; } + void set_next(MarkingStackChunk* value) { next_ = value; } + + static const uint32_t kMarkingStackChunkSize = 1024; + + private: + RawObject* memory_[kMarkingStackChunkSize]; + MarkingStackChunk* next_; + + DISALLOW_COPY_AND_ASSIGN(MarkingStackChunk); + }; + + bool IsMarkingStackChunkFull() const { + return top_ == MarkingStackChunk::kMarkingStackChunkSize; + } + + bool IsMarkingStackChunkEmpty() const { + return top_ == 0; + } + + MarkingStackChunk* head_; + MarkingStackChunk* empty_chunks_; + RawObject** marking_stack_; + uint32_t top_; + + DISALLOW_COPY_AND_ASSIGN(MarkingStack); +}; + + +class MarkingVisitor : public ObjectPointerVisitor { + public: + MarkingVisitor(Heap* heap, PageSpace* page_space, MarkingStack* marking_stack) + : heap_(heap), + vm_heap_(Dart::vm_isolate()->heap()), + page_space_(page_space), + marking_stack_(marking_stack) {} + + MarkingStack* marking_stack() const { return marking_stack_; } + + void VisitPointers(RawObject** first, RawObject** last) { + for (RawObject** current = first; current <= last; current++) { + MarkObject(*current); + } + } + + private: + void MarkAndPush(RawObject* raw_obj) { + ASSERT(raw_obj->IsHeapObject()); + + // Mark the object and push it on the marking stack. + ASSERT(!raw_obj->IsMarked()); + RawClass* raw_class = raw_obj->ptr()->class_; + raw_obj->SetMarkBit(); + marking_stack_->Push(raw_obj); + + // TODO(iposva): Should we mark the classes early? + MarkObject(raw_class); + } + + void MarkObject(RawObject* raw_obj) { + // Fast exit if the raw object is a Smi. + if (!raw_obj->IsHeapObject()) return; + + // Fast exit if the raw object is marked. + if (raw_obj->IsMarked()) return; + + // Skip over new objects, but verify consistency of heap while at it. + if (raw_obj->IsNewObject()) { + // TODO(iposva): Add consistency check. + return; + } + + uword raw_addr = RawObject::ToAddr(raw_obj); + // TODO(iposva): Premark vm_isolate objects, to avoid this extra check here. + if (vm_heap_->Contains(raw_addr)) { + return; + } + // TODO(iposva): merge old and code spaces. + // ASSERT(page_space_->Contains(raw_addr)); + + MarkAndPush(raw_obj); + } + + Heap* heap_; + Heap* vm_heap_; + PageSpace* page_space_; + MarkingStack* marking_stack_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(MarkingVisitor); +}; + + +void GCMarker::Prologue(Isolate* isolate) { + // Nothing to do at the moment. +} + + +void GCMarker::IterateRoots(Isolate* isolate, MarkingVisitor* visitor) { + isolate->VisitObjectPointers(visitor, + StackFrameIterator::kDontValidateFrames); + heap_->IterateNewPointers(visitor); +} + + +void GCMarker::DrainMarkingStack(Isolate* isolate, MarkingVisitor* visitor) { + while (!visitor->marking_stack()->IsEmpty()) { + RawObject* raw_obj = visitor->marking_stack()->Pop(); + raw_obj->VisitPointers(visitor); + } +} + + +void GCMarker::MarkObjects(Isolate* isolate, PageSpace* page_space) { + MarkingStack marking_stack; + Prologue(isolate); + MarkingVisitor mark(heap_, page_space, &marking_stack); + IterateRoots(isolate, &mark); + DrainMarkingStack(isolate, &mark); +} + +} // namespace dart diff --git a/runtime/vm/gc_marker.h b/runtime/vm/gc_marker.h new file mode 100644 index 00000000000..4d46fd52eb7 --- /dev/null +++ b/runtime/vm/gc_marker.h @@ -0,0 +1,39 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef VM_GC_MARKER_H_ +#define VM_GC_MARKER_H_ + +#include "vm/allocation.h" + +namespace dart { + +// Forward declarations. +class Heap; +class Isolate; +class MarkingVisitor; +class PageSpace; + +// The class GCMarker is used to mark reachable old generation objects as part +// of the mark-sweep collection. The marking bit used is defined in RawObject. +class GCMarker : public ValueObject { + public: + explicit GCMarker(Heap* heap) : heap_(heap) { } + ~GCMarker() { } + + void MarkObjects(Isolate* isolate, PageSpace* page_space); + + private: + void Prologue(Isolate* isolate); + void IterateRoots(Isolate* isolate, MarkingVisitor* visitor); + void DrainMarkingStack(Isolate* isolate, MarkingVisitor* visitor); + + Heap* heap_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(GCMarker); +}; + +} // namespace dart + +#endif // VM_GC_MARKER_H_ diff --git a/runtime/vm/heap.cc b/runtime/vm/heap.cc index ba779b7cf12..2b39d1689e0 100644 --- a/runtime/vm/heap.cc +++ b/runtime/vm/heap.cc @@ -103,6 +103,53 @@ bool Heap::CodeContains(uword addr) const { } +void Heap::IterateNewPointers(ObjectPointerVisitor* visitor) { + new_space_->VisitObjectPointers(visitor); +} + + +void Heap::IterateOldPointers(ObjectPointerVisitor* visitor) { + old_space_->VisitObjectPointers(visitor); + code_space_->VisitObjectPointers(visitor); +} + + +void Heap::CollectGarbage(Space space) { + switch (space) { + case kNew: + new_space_->Scavenge(); + break; + case kOld: + old_space_->MarkSweep(); + break; + case kExecutable: + UNIMPLEMENTED(); + code_space_->MarkSweep(); + break; + default: + UNREACHABLE(); + } +} + + +void Heap::CollectAllGarbage() { + new_space_->Scavenge(); + old_space_->MarkSweep(); + // TODO(iposva): Merge old and code space. + // code_space_->MarkSweep(); +} + + +uword Heap::TopAddress() { + return reinterpret_cast(new_space_->TopAddress()); +} + + +uword Heap::EndAddress() { + return reinterpret_cast(new_space_->EndAddress()); +} + + void Heap::Init(Isolate* isolate) { ASSERT(isolate->heap() == NULL); Heap* heap = new Heap(); @@ -120,22 +167,6 @@ bool Heap::Verify() const { } -void Heap::IterateOldPointers(ObjectPointerVisitor* visitor) { - old_space_->VisitObjectPointers(visitor); - code_space_->VisitObjectPointers(visitor); -} - - -uword Heap::TopAddress() { - return reinterpret_cast(new_space_->TopAddress()); -} - - -uword Heap::EndAddress() { - return reinterpret_cast(new_space_->EndAddress()); -} - - #if defined(DEBUG) NoGCScope::NoGCScope() : StackResource(Isolate::Current()) { isolate()->IncrementNoGCScopeDepth(); diff --git a/runtime/vm/heap.h b/runtime/vm/heap.h index 796cdd8c8fa..467da2f5556 100644 --- a/runtime/vm/heap.h +++ b/runtime/vm/heap.h @@ -53,19 +53,24 @@ class Heap { bool Contains(uword addr) const; bool CodeContains(uword addr) const; - // Initialize the heap and register it with the isolate. - static void Init(Isolate* isolate); - - // Verify that all pointers in the heap point to the heap. - bool Verify() const; - + // Visit all pointers in the space. + void IterateNewPointers(ObjectPointerVisitor* visitor); void IterateOldPointers(ObjectPointerVisitor* visitor); + void CollectGarbage(Space space); + void CollectAllGarbage(); + // Accessors for inlined allocation in generated code. uword TopAddress(); uword EndAddress(); static intptr_t new_space_offset() { return OFFSET_OF(Heap, new_space_); } + // Initialize the heap and register it with the isolate. + static void Init(Isolate* isolate); + + // Verify that all pointers in the heap point to the heap. + bool Verify() const; + private: Heap(); diff --git a/runtime/vm/heap_test.cc b/runtime/vm/heap_test.cc index cba6e43f61c..5fb3a9628bc 100644 --- a/runtime/vm/heap_test.cc +++ b/runtime/vm/heap_test.cc @@ -2,4 +2,31 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -// TODO(iposva): Intentionally left blank for now. +#include "vm/assert.h" +#include "vm/globals.h" +#include "vm/heap.h" +#include "vm/unit_test.h" + +namespace dart { + +#if defined(TARGET_ARCH_IA32) +TEST_CASE(OldGC) { + const char* kScriptChars = + "class HeapTester {\n" + " static void main() {\n" + " return [1, 2, 3];\n" + " }\n" + "}\n"; + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); + Dart_Handle result = Dart_InvokeStatic(lib, + Dart_NewString("HeapTester"), + Dart_NewString("main"), + 0, NULL); + + EXPECT_VALID(result); + Isolate* isolate = Isolate::Current(); + Heap* heap = isolate->heap(); + heap->CollectGarbage(Heap::kOld); +} +#endif // TARGET_ARCH_IA32 +} diff --git a/runtime/vm/pages.cc b/runtime/vm/pages.cc index 62894a76d55..24c8da5a307 100644 --- a/runtime/vm/pages.cc +++ b/runtime/vm/pages.cc @@ -5,6 +5,7 @@ #include "vm/pages.h" #include "vm/assert.h" +#include "vm/gc_marker.h" #include "vm/object.h" #include "vm/virtual_memory.h" @@ -55,7 +56,9 @@ PageSpace::PageSpace(Heap* heap, intptr_t max_capacity, bool is_executable) max_capacity_(max_capacity), capacity_(0), in_use_(0), - is_executable_(is_executable) { } + count_(0), + is_executable_(is_executable), + sweeping_(false) { } PageSpace::~PageSpace() { @@ -174,4 +177,31 @@ void PageSpace::VisitObjectPointers(ObjectPointerVisitor* visitor) const { } } + +void PageSpace::MarkSweep() { + // MarkSweep is not reentrant. Make sure that is the case. + ASSERT(!sweeping_); + sweeping_ = true; + Isolate* isolate = Isolate::Current(); + NoHandleScope no_handles(isolate); + + Timer timer(FLAG_verbose_gc, "MarkSweep"); + timer.Start(); + + // Mark all reachable old-gen objects. + GCMarker marker(heap_); + marker.MarkObjects(isolate, this); + + UNIMPLEMENTED(); + timer.Stop(); + if (FLAG_verbose_gc) { + OS::PrintErr("Mark-Sweep[%d]: %dus\n", count_, timer.TotalElapsedTime()); + } + + count_++; + // Done, reset the marker. + ASSERT(sweeping_); + sweeping_ = false; +} + } // namespace dart diff --git a/runtime/vm/pages.h b/runtime/vm/pages.h index 6e47a98811e..18e4b604df8 100644 --- a/runtime/vm/pages.h +++ b/runtime/vm/pages.h @@ -85,6 +85,9 @@ class PageSpace { void VisitObjectPointers(ObjectPointerVisitor* visitor) const; + // Collect the garbage in the page space using mark-sweep. + void MarkSweep(); + private: static const intptr_t kAllocatablePageSize = kPageSize - sizeof(HeapPage); @@ -105,8 +108,14 @@ class PageSpace { intptr_t capacity_; intptr_t in_use_; + // Old-gen GC cycle count. + int count_; + bool is_executable_; + // Keep track whether a MarkSweep is currently running. + bool sweeping_; + DISALLOW_IMPLICIT_CONSTRUCTORS(PageSpace); }; diff --git a/runtime/vm/vm_sources.gypi b/runtime/vm/vm_sources.gypi index 3c31ef814fe..b163ae2557a 100644 --- a/runtime/vm/vm_sources.gypi +++ b/runtime/vm/vm_sources.gypi @@ -103,6 +103,8 @@ 'flags.cc', 'flags.h', 'flags_test.cc', + 'gc_marker.cc', + 'gc_marker.h', 'gdbjit_linux.cc', 'gdbjit_linux.h', 'globals.h',