Commit e96bf9a9 authored by Rudi Chen's avatar Rudi Chen

Basic infrastructure for implementing a prototype moving gc.

- Refactor the stack logic into a separate data structure ChunkedStack,
  keep the push function that deals with marking as functionality specific
  to a subclass of ChunkedStack (no virtual, just reuse code).
- Add an additional (disabled) phase that maps out the reference graph
  reachable objects after the sweep phase. Prepare moving all objects in
  the small heap.

Also, rename TraceStack to TraversalWorklist because the fact
that we're using a stack is an implementation detail.
parent 3451880d
This diff is collapsed.
......@@ -15,6 +15,10 @@
#ifndef PYSTON_GC_COLLECTOR_H
#define PYSTON_GC_COLLECTOR_H
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "gc/gc.h"
namespace pyston {
......@@ -72,6 +76,43 @@ public:
void visitPotentialRedundant(void* p) override { visitPotential(p); }
void visitPotentialRangeRedundant(void** start, void** end) override { visitPotentialRange(start, end); }
};
//
// Code to prototype a moving GC.
//
class ReferenceMapWorklist;
#if MOVING_GC
#define MOVING_OVERRIDE override
#else
#define MOVING_OVERRIDE
#endif
// Bulds the reference map, and also determine which objects cannot be moved.
class GCVisitorPinning : public GCVisitorNoRedundancy {
private:
ReferenceMapWorklist* worklist;
void _visit(void** ptr_address) MOVING_OVERRIDE;
public:
GCVisitorPinning(ReferenceMapWorklist* worklist) : worklist(worklist) {}
virtual ~GCVisitorPinning() {}
void visitPotential(void* p) MOVING_OVERRIDE;
};
class GCAllocation;
class ReferenceMap {
public:
// Pinned objects are objects that should not be moved (their pointer value should
// never change).
std::unordered_set<GCAllocation*> pinned;
// Map from objects O to all objects that contain a reference to O.
std::unordered_map<GCAllocation*, std::vector<GCAllocation*>> references;
};
}
}
......
......@@ -54,9 +54,10 @@ void popGCObject(gc::GCVisitable* obj);
namespace gc {
class TraceStack;
class GCAllocation;
class TraversalWorklist;
// The base version of the GC visitor is used for marking, in conjuction with a TraceStack.
// The base version of the GC visitor is used for marking, in conjuction with a TraversalWorklist.
//
// Conceptually, GCVisitor should be abstract and the 'marking' behavior should be specific
// to a subclass of GCVisitor. However, that requires the use of virtual functions which
......@@ -65,7 +66,11 @@ class TraceStack;
// the virtualness property is #if'd out for the regular use case with only mark-and-sweep.
class GCVisitor {
private:
TraceStack* stack;
TraversalWorklist* worklist = NULL;
protected:
// The origin object of the current visit calls.
GCAllocation* source = NULL;
#if MOVING_GC
virtual void _visit(void** ptr_address);
......@@ -78,7 +83,8 @@ private:
virtual void _visitRangeRedundant(void** start, void** end) {}
public:
GCVisitor(TraceStack* stack) : stack(stack) {}
GCVisitor() {}
GCVisitor(TraversalWorklist* worklist) : worklist(worklist) {}
virtual ~GCVisitor() {}
#if MOVING_GC
......@@ -122,6 +128,8 @@ public:
// change that later for performance.
void visitNonRelocatable(void* p) { visitPotential(p); }
void visitNonRelocatableRange(void** start, void** end) { visitPotentialRange(start, end); }
void setSource(GCAllocation* al) { source = al; }
};
enum class GCKind : uint8_t {
......
......@@ -414,6 +414,53 @@ void SmallArena::assertConsistent() {
}
#endif
void SmallArena::getPointersInBlockChain(std::vector<GCAllocation*>& ptrs, Block** head) {
while (Block* b = *head) {
int num_objects = b->numObjects();
int first_obj = b->minObjIndex();
int atoms_per_obj = b->atomsPerObj();
for (int atom_idx = first_obj * atoms_per_obj; atom_idx < num_objects * atoms_per_obj;
atom_idx += atoms_per_obj) {
if (b->isfree.isSet(atom_idx))
continue;
void* p = &b->atoms[atom_idx];
GCAllocation* al = reinterpret_cast<GCAllocation*>(p);
ptrs.push_back(al);
}
head = &b->next;
}
}
void SmallArena::forEachReference(std::function<void(GCAllocation*, size_t)> f) {
thread_caches.forEachValue([this, &f](ThreadBlockCache* cache) {
for (int bidx = 0; bidx < NUM_BUCKETS; bidx++) {
Block* h = cache->cache_free_heads[bidx];
std::vector<GCAllocation*> ptrs;
getPointersInBlockChain(ptrs, &cache->cache_free_heads[bidx]);
getPointersInBlockChain(ptrs, &cache->cache_full_heads[bidx]);
for (GCAllocation* al : ptrs) {
f(al, sizes[bidx]);
}
}
});
for (int bidx = 0; bidx < NUM_BUCKETS; bidx++) {
std::vector<GCAllocation*> ptrs;
getPointersInBlockChain(ptrs, &heads[bidx]);
getPointersInBlockChain(ptrs, &full_heads[bidx]);
for (GCAllocation* al : ptrs) {
f(al, sizes[bidx]);
}
}
}
void SmallArena::freeUnmarked(std::vector<Box*>& weakly_referenced) {
assertConsistent();
......
......@@ -93,6 +93,7 @@ inline void registerGCManagedBytes(size_t bytes) {
class Heap;
class ReferenceMap;
struct HeapStatistics;
typedef uint8_t kindid_t;
......@@ -263,6 +264,8 @@ public:
}
}
void forEachReference(std::function<void(GCAllocation*, size_t)>);
GCAllocation* realloc(GCAllocation* alloc, size_t bytes);
void free(GCAllocation* al);
......@@ -405,6 +408,7 @@ private:
// TODO only use thread caches if we're in GRWL mode?
threading::PerThreadSet<ThreadBlockCache, Heap*, SmallArena*> thread_caches;
void getPointersInBlockChain(std::vector<GCAllocation*>& ptrs, Block** head);
Block* _allocBlock(uint64_t size, Block** prev);
GCAllocation* _allocFromBlock(Block* b);
Block* _claimBlock(size_t rounded_size, Block** free_head);
......@@ -625,6 +629,9 @@ public:
return NULL;
}
// Calls the function for every object in the small heap.
void forEachSmallArenaReference(std::function<void(GCAllocation*, size_t)> f) { small_arena.forEachReference(f); }
// not thread safe:
void freeUnmarked(std::vector<Box*>& weakly_referenced) {
small_arena.freeUnmarked(weakly_referenced);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment