Skip to content

Commit

Permalink
[cpp] Remove RET_VAL_ROOTING, RootSet, etc.
Browse files Browse the repository at this point in the history
Still need to remove some annotations

The GC works now with local var rooting + manual collection points!
  • Loading branch information
Andy C committed Dec 17, 2022
1 parent e987dc3 commit dda5592
Show file tree
Hide file tree
Showing 6 changed files with 7 additions and 421 deletions.
8 changes: 0 additions & 8 deletions asdl/gc_test.cc
Expand Up @@ -62,34 +62,26 @@ TEST hnode_test() {

rec = Alloc<hnode__Record>();
rec->node_type = StrFromC("dummy_node");
#ifndef RET_VAL_ROOTING
ASSERT_EQ_FMT(10, gHeap.Collect(), "%d");
#endif

h = rec; // base type
array->children->append(h);

format::PrintTree(h, ast_f);
printf("\n");
#ifndef RET_VAL_ROOTING
ASSERT_EQ_FMT(11, gHeap.Collect(), "%d");
#endif

h = Alloc<hnode__Leaf>(StrFromC("zz"), color_e::TypeName);
array->children->append(h);

format::PrintTree(h, ast_f);
printf("\n");
#ifndef RET_VAL_ROOTING
ASSERT_EQ_FMT(13, gHeap.Collect(), "%d");
#endif

h = array;
format::PrintTree(h, ast_f);
printf("\n");
#ifndef RET_VAL_ROOTING
ASSERT_EQ_FMT(13, gHeap.Collect(), "%d");
#endif

PASS();
}
Expand Down
3 changes: 0 additions & 3 deletions mycpp/gc_builtins_test.cc
Expand Up @@ -760,7 +760,6 @@ TEST exceptions_test() {
throw Alloc<IndexError>();
} catch (IndexError* e) {
log("e %p", e);
gHeap.RootInCurrentFrame(e);
other = e;
caught = true;
}
Expand All @@ -772,7 +771,6 @@ TEST exceptions_test() {
try {
throw Alloc<OSError>(99);
} catch (IOError_OSError* e) {
gHeap.RootInCurrentFrame(e);
caught = true;
}
ASSERT(caught);
Expand All @@ -789,7 +787,6 @@ TEST exceptions_test() {
throw r;

} catch (RuntimeError* e) {
gHeap.RootInCurrentFrame(e);
caught = true;

log("RuntimeError %s", e->message->data());
Expand Down
9 changes: 2 additions & 7 deletions mycpp/gc_heap_test.cc
Expand Up @@ -3,13 +3,8 @@
#include "mycpp/runtime.h"
#include "vendor/greatest.h"

// disable for now
#ifdef RET_VAL_ROOTING
#define ASSERT_NUM_LIVE_OBJS(x) ;
#else
#define ASSERT_NUM_LIVE_OBJS(x) \
ASSERT_EQ_FMT((x), static_cast<int>(gHeap.num_live_), "%d");
#endif
#define ASSERT_NUM_LIVE_OBJS(x) \
ASSERT_EQ_FMT((x), static_cast<int>(gHeap.num_live_), "%d");

// Hm we're getting a warning because these aren't plain old data?
// https://stackoverflow.com/questions/1129894/why-cant-you-use-offsetof-on-non-pod-structures-in-c
Expand Down
36 changes: 2 additions & 34 deletions mycpp/mark_sweep_heap.cc
Expand Up @@ -2,19 +2,6 @@

#include "mycpp/runtime.h"

// Start of garbage collection. We have a circular dependency here because I
// don't want some kind of STL iterator.
void RootSet::MarkRoots(MarkSweepHeap* heap) {
for (int i = 0; i < num_frames_; ++i) {
const std::vector<Obj*>& frame = stack_[i];
int n = frame.size();
for (int j = 0; j < n; ++j) {
// TODO: would be nice to do non-recursive marking
heap->MarkObjects(frame[j]);
}
}
}

void MarkSweepHeap::Init() {
Init(1000); // collect at 1000 objects in tests
}
Expand Down Expand Up @@ -104,13 +91,6 @@ void* MarkSweepHeap::Allocate(size_t num_bytes) {
num_allocated_++;
bytes_allocated_ += num_bytes;

// Allocate() is special: we use RootInCurrentFrame because it's a LEAF, and
// this function doesn't have RootsFrame to do PushFrame/PopFrame
#if RET_VAL_ROOTING
gHeap.RootInCurrentFrame(static_cast<Obj*>(result));
static_cast<Obj*>(result)->heap_tag_ = Tag::Opaque; // it is opaque to start!
#endif

return result;
}

Expand Down Expand Up @@ -214,22 +194,13 @@ int MarkSweepHeap::Collect() {
}
#endif

#if RET_VAL_ROOTING

#ifdef GC_VERBOSE
log("Collect with %d roots and %d frames", root_set_.NumRoots(),
root_set_.NumFrames());
#endif

root_set_.MarkRoots(this);
#else
int num_roots = roots_.size();
int num_globals = global_roots_.size();

#ifdef GC_VERBOSE
#ifdef GC_VERBOSE
log("%2d. GC with %d roots (%d global) and %d live objects", num_collections_,
num_roots + num_globals, num_globals, num_live_);
#endif
#endif

// Note: Can we get rid of double pointers?

Expand All @@ -246,7 +217,6 @@ int MarkSweepHeap::Collect() {
MarkObjects(root);
}
}
#endif

Sweep();
#ifdef GC_VERBOSE
Expand Down Expand Up @@ -282,15 +252,13 @@ void MarkSweepHeap::DoProcessExit(bool fast_exit) {
if (fast_exit) {
// don't collect by default; OIL_GC_ON_EXIT=1 overrides
if (e && strcmp(e, "1") == 0) {
root_set_.PopFrame();
Collect();
}
} else {
// collect by default; OIL_GC_ON_EXIT=0 overrides
if (e && strcmp(e, "0") == 0) {
;
} else {
root_set_.PopFrame();
Collect();
}
}
Expand Down
141 changes: 3 additions & 138 deletions mycpp/mark_sweep_heap.h
Expand Up @@ -4,122 +4,15 @@
#include <unordered_set>
#include <vector>

class MarkSweepHeap; // forward decl for circular dep

// The set of objects where the mark and sweep algorithm starts. Terminology:
// to "root" an object means "add it to the root set".
class RootSet {
public:
// start with 1 live frame and e.g. 32 reserved ones
explicit RootSet(int num_reserved) : num_frames_(1) {
assert(num_reserved > 1);
stack_.reserve(num_reserved);
for (int i = 0; i < num_reserved; ++i) {
stack_.emplace_back(); // Construct std::vector frame IN PLACE.
stack_.back().reserve(16); // Reserve 16 rooted variables per frame.
}
}

// Called on function entry
void PushFrame() {
// Construct more std::vector frames if necessary. We reuse vectors to
// avoid constructing one on every function call.
int num_constructed = stack_.size();
if (num_frames_ >= num_constructed) {
stack_.emplace_back();
stack_.back().reserve(16);
#if 0
num_constructed = roots_.size();
log("num_frames_ %d, num_constructed %d", num_frames_, num_constructed);
assert(num_frames_ + 1 == num_constructed);
#endif
}

num_frames_++;
// log("PushFrame -> %d", num_frames_);
}

// Called on function exit
void PopFrame() {
// Remove all roots owned by the top frame. We're REUSING frames, so not
// calling vector<>::pop().
stack_[num_frames_ - 1].clear();
num_frames_--;
// log("PopFrame -> %d", num_frames_);
}

// Called when returning a value (except in trivial passthrough case)
void RootOnReturn(Obj* root) {
// log("RootOnReturn %p %d", root, num_frames_);

if (root == nullptr) { // No reason to add it
return;
}
// We should have 2 frames because we start with 1 for main(), and main()
// itself can't return GC objects.
assert(num_frames_ > 1);

// Owned by the frame BELOW
stack_[num_frames_ - 2].push_back(root);
}

// Called in 2 situations:
// - the "leaf" Allocate(), which does not have a RootsFrame
// - when catching exceptions:
// catch (IOError e) { gHeap.RootInCurrentFrame(e); }
void RootInCurrentFrame(Obj* root) {
if (root == nullptr) { // No reason to add it
return;
}
assert(num_frames_ > 0);
stack_[num_frames_ - 1].push_back(root);
}

void RootGlobalVar(Obj* root) {
if (root == nullptr) { // No reason to add it
return;
}
assert(num_frames_ > 0);
stack_[0].push_back(root);
}

// For testing
int NumFrames() {
return num_frames_;
}

// Calculate size of root set, for unit tests only.
int NumRoots() {
int result = 0;
for (int i = 0; i < num_frames_; ++i) {
result += stack_[i].size();
}
return result;
}

void MarkRoots(MarkSweepHeap* heap);

// A stack of frames that's updated in parallel the call stack.
// This representation is appropriate since multiple stack frames are "in
// play" at once. That is, RootOnReturn() may mutate root_set_[1] while
// root_set_[2] is being pushed/popped/modified.
std::vector<std::vector<Obj*>> stack_;
int num_frames_ = 0; // frames 0 to N-1 are valid
};

class MarkSweepHeap {
public:
// reserve 32 frames to start
MarkSweepHeap() : root_set_(32) {
MarkSweepHeap() {
}

void Init(); // use default threshold
void Init(int gc_threshold);

//
// OLD Local Var Rooting
//

void PushRoot(Obj** p) {
roots_.push_back(p);
}
Expand All @@ -128,24 +21,10 @@ class MarkSweepHeap {
roots_.pop_back();
}

//
// NEW Return Value Rooting
//

void RootOnReturn(Obj* root) {
root_set_.RootOnReturn(root);
}

void RootInCurrentFrame(Obj* root) {
root_set_.RootInCurrentFrame(root);
}

// TODO: change to void* to avoid implicit static_cast<>, which changes the
// address when there's a vtable
void RootGlobalVar(Obj* root) {
#ifdef RET_VAL_ROOTING
root_set_.RootGlobalVar(root);
#else
global_roots_.push_back(root);
#endif
}

void* Allocate(size_t num_bytes);
Expand Down Expand Up @@ -182,13 +61,9 @@ class MarkSweepHeap {
double max_gc_millis_ = 0.0;
double total_gc_millis_ = 0.0;

// OLD rooting
std::vector<Obj**> roots_;
std::vector<Obj*> global_roots_;

// NEW rooting
RootSet root_set_;

std::vector<void*> live_objs_;
std::unordered_set<void*> marked_;

Expand Down Expand Up @@ -220,22 +95,12 @@ class RootsFrame {
public:
#ifdef COLLECT_COVERAGE
explicit RootsFrame(const char* description) {
log(">>> %s", description);
#ifndef BUMP_LEAK
gHeap.root_set_.PushFrame();
#endif
}
#endif

RootsFrame() {
#ifndef BUMP_LEAK
gHeap.root_set_.PushFrame();
#endif
}
~RootsFrame() {
#ifndef BUMP_LEAK
gHeap.root_set_.PopFrame();
#endif
}
};

Expand Down

0 comments on commit dda5592

Please sign in to comment.