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 @@ ...@@ -15,6 +15,10 @@
#ifndef PYSTON_GC_COLLECTOR_H #ifndef PYSTON_GC_COLLECTOR_H
#define PYSTON_GC_COLLECTOR_H #define PYSTON_GC_COLLECTOR_H
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "gc/gc.h" #include "gc/gc.h"
namespace pyston { namespace pyston {
...@@ -72,6 +76,43 @@ public: ...@@ -72,6 +76,43 @@ public:
void visitPotentialRedundant(void* p) override { visitPotential(p); } void visitPotentialRedundant(void* p) override { visitPotential(p); }
void visitPotentialRangeRedundant(void** start, void** end) override { visitPotentialRange(start, end); } 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); ...@@ -54,9 +54,10 @@ void popGCObject(gc::GCVisitable* obj);
namespace gc { 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 // 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 // to a subclass of GCVisitor. However, that requires the use of virtual functions which
...@@ -65,7 +66,11 @@ class TraceStack; ...@@ -65,7 +66,11 @@ class TraceStack;
// the virtualness property is #if'd out for the regular use case with only mark-and-sweep. // the virtualness property is #if'd out for the regular use case with only mark-and-sweep.
class GCVisitor { class GCVisitor {
private: private:
TraceStack* stack; TraversalWorklist* worklist = NULL;
protected:
// The origin object of the current visit calls.
GCAllocation* source = NULL;
#if MOVING_GC #if MOVING_GC
virtual void _visit(void** ptr_address); virtual void _visit(void** ptr_address);
...@@ -78,7 +83,8 @@ private: ...@@ -78,7 +83,8 @@ private:
virtual void _visitRangeRedundant(void** start, void** end) {} virtual void _visitRangeRedundant(void** start, void** end) {}
public: public:
GCVisitor(TraceStack* stack) : stack(stack) {} GCVisitor() {}
GCVisitor(TraversalWorklist* worklist) : worklist(worklist) {}
virtual ~GCVisitor() {} virtual ~GCVisitor() {}
#if MOVING_GC #if MOVING_GC
...@@ -122,6 +128,8 @@ public: ...@@ -122,6 +128,8 @@ public:
// change that later for performance. // change that later for performance.
void visitNonRelocatable(void* p) { visitPotential(p); } void visitNonRelocatable(void* p) { visitPotential(p); }
void visitNonRelocatableRange(void** start, void** end) { visitPotentialRange(start, end); } void visitNonRelocatableRange(void** start, void** end) { visitPotentialRange(start, end); }
void setSource(GCAllocation* al) { source = al; }
}; };
enum class GCKind : uint8_t { enum class GCKind : uint8_t {
......
...@@ -414,6 +414,53 @@ void SmallArena::assertConsistent() { ...@@ -414,6 +414,53 @@ void SmallArena::assertConsistent() {
} }
#endif #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) { void SmallArena::freeUnmarked(std::vector<Box*>& weakly_referenced) {
assertConsistent(); assertConsistent();
......
...@@ -93,6 +93,7 @@ inline void registerGCManagedBytes(size_t bytes) { ...@@ -93,6 +93,7 @@ inline void registerGCManagedBytes(size_t bytes) {
class Heap; class Heap;
class ReferenceMap;
struct HeapStatistics; struct HeapStatistics;
typedef uint8_t kindid_t; typedef uint8_t kindid_t;
...@@ -263,6 +264,8 @@ public: ...@@ -263,6 +264,8 @@ public:
} }
} }
void forEachReference(std::function<void(GCAllocation*, size_t)>);
GCAllocation* realloc(GCAllocation* alloc, size_t bytes); GCAllocation* realloc(GCAllocation* alloc, size_t bytes);
void free(GCAllocation* al); void free(GCAllocation* al);
...@@ -405,6 +408,7 @@ private: ...@@ -405,6 +408,7 @@ private:
// TODO only use thread caches if we're in GRWL mode? // TODO only use thread caches if we're in GRWL mode?
threading::PerThreadSet<ThreadBlockCache, Heap*, SmallArena*> thread_caches; threading::PerThreadSet<ThreadBlockCache, Heap*, SmallArena*> thread_caches;
void getPointersInBlockChain(std::vector<GCAllocation*>& ptrs, Block** head);
Block* _allocBlock(uint64_t size, Block** prev); Block* _allocBlock(uint64_t size, Block** prev);
GCAllocation* _allocFromBlock(Block* b); GCAllocation* _allocFromBlock(Block* b);
Block* _claimBlock(size_t rounded_size, Block** free_head); Block* _claimBlock(size_t rounded_size, Block** free_head);
...@@ -625,6 +629,9 @@ public: ...@@ -625,6 +629,9 @@ public:
return NULL; 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: // not thread safe:
void freeUnmarked(std::vector<Box*>& weakly_referenced) { void freeUnmarked(std::vector<Box*>& weakly_referenced) {
small_arena.freeUnmarked(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