Commit 138c2ea1 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #924 from kmod/gc_fixes

Gc fix
parents 8beb9b52 c9598857
...@@ -84,6 +84,9 @@ uint8_t* ICSlotRewrite::getSlotStart() { ...@@ -84,6 +84,9 @@ uint8_t* ICSlotRewrite::getSlotStart() {
return (uint8_t*)ic->start_addr + ic_entry->idx * ic->getSlotSize(); return (uint8_t*)ic->start_addr + ic_entry->idx * ic->getSlotSize();
} }
// Map of gc pointers -> number of ics that point tot hem.
static llvm::DenseMap<void*, int> ic_gc_references;
void ICSlotRewrite::commit(CommitHook* hook, std::vector<void*> gc_references) { void ICSlotRewrite::commit(CommitHook* hook, std::vector<void*> gc_references) {
bool still_valid = true; bool still_valid = true;
for (int i = 0; i < dependencies.size(); i++) { for (int i = 0; i < dependencies.size(); i++) {
...@@ -120,7 +123,16 @@ void ICSlotRewrite::commit(CommitHook* hook, std::vector<void*> gc_references) { ...@@ -120,7 +123,16 @@ void ICSlotRewrite::commit(CommitHook* hook, std::vector<void*> gc_references) {
// if (VERBOSITY()) printf("Commiting to %p-%p\n", start, start + ic->slot_size); // if (VERBOSITY()) printf("Commiting to %p-%p\n", start, start + ic->slot_size);
memcpy(slot_start, buf, ic->getSlotSize()); memcpy(slot_start, buf, ic->getSlotSize());
for (auto p : ic_entry->gc_references) {
int& i = ic_gc_references[p];
if (i == 1)
ic_gc_references.erase(p);
else
--i;
}
ic_entry->gc_references = std::move(gc_references); ic_entry->gc_references = std::move(gc_references);
for (auto p : ic_entry->gc_references)
ic_gc_references[p]++;
ic->times_rewritten++; ic->times_rewritten++;
...@@ -344,10 +356,15 @@ bool ICInfo::isMegamorphic() { ...@@ -344,10 +356,15 @@ bool ICInfo::isMegamorphic() {
} }
void ICInfo::visitGCReferences(gc::GCVisitor* v) { void ICInfo::visitGCReferences(gc::GCVisitor* v) {
for (auto&& p : ic_gc_references) {
v->visitNonRelocatable(p.first);
}
#if MOVING_GC
for (const auto& p : ics_list) { for (const auto& p : ics_list) {
for (auto& slot : p->slots) { for (auto& slot : p->slots) {
v->visitNonRelocatableRange(&slot.gc_references[0], &slot.gc_references[slot.gc_references.size()]); v->visitNonRelocatableRange(&slot.gc_references[0], &slot.gc_references[slot.gc_references.size()]);
} }
} }
#endif
} }
} }
...@@ -716,6 +716,11 @@ void Rewriter::_trap() { ...@@ -716,6 +716,11 @@ void Rewriter::_trap() {
assembler->trap(); assembler->trap();
} }
void Rewriter::addGCReference(void* obj) {
assert(gc::isValidGCObject(obj));
gc_references.push_back(obj);
}
RewriterVar* Rewriter::loadConst(int64_t val, Location dest) { RewriterVar* Rewriter::loadConst(int64_t val, Location dest) {
STAT_TIMER(t0, "us_timer_rewriter", 10); STAT_TIMER(t0, "us_timer_rewriter", 10);
......
...@@ -554,6 +554,11 @@ public: ...@@ -554,6 +554,11 @@ public:
const char* debugName() { return rewrite->debugName(); } const char* debugName() { return rewrite->debugName(); }
// Register that this rewrite will embed a reference to a particular gc object.
// TODO: come up with an implementation that is performant enough that we can automatically
// infer these from loadConst calls.
void addGCReference(void* obj);
void trap(); void trap();
RewriterVar* loadConst(int64_t val, Location loc = Location::any()); RewriterVar* loadConst(int64_t val, Location loc = Location::any());
// has_side_effects: whether this call could have "side effects". the exact side effects we've // has_side_effects: whether this call could have "side effects". the exact side effects we've
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <cassert> #include <cassert>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <llvm/ADT/DenseSet.h>
#include "asm_writing/icinfo.h" #include "asm_writing/icinfo.h"
#include "codegen/ast_interpreter.h" #include "codegen/ast_interpreter.h"
...@@ -178,7 +179,7 @@ public: ...@@ -178,7 +179,7 @@ public:
} }
void addWork(void* p) { void addWork(void* p) {
GC_TRACE_LOG("Pushing %p\n", p); GC_TRACE_LOG("Pushing (%d) %p\n", visit_type, p);
GCAllocation* al = GCAllocation::fromUserData(p); GCAllocation* al = GCAllocation::fromUserData(p);
switch (visit_type) { switch (visit_type) {
...@@ -213,8 +214,10 @@ public: ...@@ -213,8 +214,10 @@ public:
// http://pypy.readthedocs.org/en/latest/discussion/finalizer-order.html // http://pypy.readthedocs.org/en/latest/discussion/finalizer-order.html
case TraversalType::FinalizationOrderingFindReachable: case TraversalType::FinalizationOrderingFindReachable:
if (orderingState(al) == FinalizationState::UNREACHABLE) { if (orderingState(al) == FinalizationState::UNREACHABLE) {
GC_TRACE_LOG("%p is now TEMPORARY\n", al->user_data);
setOrderingState(al, FinalizationState::TEMPORARY); setOrderingState(al, FinalizationState::TEMPORARY);
} else if (orderingState(al) == FinalizationState::REACHABLE_FROM_FINALIZER) { } else if (orderingState(al) == FinalizationState::REACHABLE_FROM_FINALIZER) {
GC_TRACE_LOG("%p is now ALIVE\n", al->user_data);
setOrderingState(al, FinalizationState::ALIVE); setOrderingState(al, FinalizationState::ALIVE);
} else { } else {
return; return;
...@@ -222,6 +225,7 @@ public: ...@@ -222,6 +225,7 @@ public:
break; break;
case TraversalType::FinalizationOrderingRemoveTemporaries: case TraversalType::FinalizationOrderingRemoveTemporaries:
if (orderingState(al) == FinalizationState::TEMPORARY) { if (orderingState(al) == FinalizationState::TEMPORARY) {
GC_TRACE_LOG("%p is now REACHABLE_FROM_FINALIZER\n", al->user_data);
setOrderingState(al, FinalizationState::REACHABLE_FROM_FINALIZER); setOrderingState(al, FinalizationState::REACHABLE_FROM_FINALIZER);
} else { } else {
return; return;
...@@ -366,6 +370,7 @@ void registerPythonObject(Box* b) { ...@@ -366,6 +370,7 @@ void registerPythonObject(Box* b) {
assert(b->cls); assert(b->cls);
if (hasOrderedFinalizer(b->cls)) { if (hasOrderedFinalizer(b->cls)) {
GC_TRACE_LOG("%p is registered as having an ordered finalizer\n", b);
objects_with_ordered_finalizers.push_back(b); objects_with_ordered_finalizers.push_back(b);
} }
} }
...@@ -380,6 +385,7 @@ void invalidateOrderedFinalizerList() { ...@@ -380,6 +385,7 @@ void invalidateOrderedFinalizerList() {
if (!hasOrderedFinalizer(box->cls) || hasFinalized(al)) { if (!hasOrderedFinalizer(box->cls) || hasFinalized(al)) {
// Cleanup. // Cleanup.
GC_TRACE_LOG("Removing %p from objects_with_ordered_finalizers\n", box);
iter = objects_with_ordered_finalizers.erase(iter); iter = objects_with_ordered_finalizers.erase(iter);
} else { } else {
++iter; ++iter;
...@@ -540,8 +546,8 @@ static void visitRoots(GCVisitor& visitor) { ...@@ -540,8 +546,8 @@ static void visitRoots(GCVisitor& visitor) {
} }
GC_TRACE_LOG("Looking at generated code pointers\n"); GC_TRACE_LOG("Looking at generated code pointers\n");
#if MOVING_GC
ICInfo::visitGCReferences(&visitor); ICInfo::visitGCReferences(&visitor);
#if MOVING_GC
CompiledFunction::visitAllCompiledFunctions(&visitor); CompiledFunction::visitAllCompiledFunctions(&visitor);
#endif #endif
} }
...@@ -554,8 +560,10 @@ static void finalizationOrderingFindReachable(Box* obj) { ...@@ -554,8 +560,10 @@ static void finalizationOrderingFindReachable(Box* obj) {
TraversalWorklist worklist(TraversalType::FinalizationOrderingFindReachable); TraversalWorklist worklist(TraversalType::FinalizationOrderingFindReachable);
GCVisitor visitor(&worklist); GCVisitor visitor(&worklist);
GC_TRACE_LOG("findReachable %p\n", obj);
worklist.addWork(obj); worklist.addWork(obj);
while (void* p = worklist.next()) { while (void* p = worklist.next()) {
GC_TRACE_LOG("findReachable, looking at %p\n", p);
sc_marked_objs.log(); sc_marked_objs.log();
visitByGCKind(p, visitor); visitByGCKind(p, visitor);
...@@ -572,8 +580,10 @@ static void finalizationOrderingRemoveTemporaries(Box* obj) { ...@@ -572,8 +580,10 @@ static void finalizationOrderingRemoveTemporaries(Box* obj) {
TraversalWorklist worklist(TraversalType::FinalizationOrderingRemoveTemporaries); TraversalWorklist worklist(TraversalType::FinalizationOrderingRemoveTemporaries);
GCVisitor visitor(&worklist); GCVisitor visitor(&worklist);
GC_TRACE_LOG("removeTemporaries %p\n", obj);
worklist.addWork(obj); worklist.addWork(obj);
while (void* p = worklist.next()) { while (void* p = worklist.next()) {
GC_TRACE_LOG("removeTemporaries, looking at %p\n", p);
GCAllocation* al = GCAllocation::fromUserData(p); GCAllocation* al = GCAllocation::fromUserData(p);
assert(orderingState(al) != FinalizationState::UNREACHABLE); assert(orderingState(al) != FinalizationState::UNREACHABLE);
visitByGCKind(p, visitor); visitByGCKind(p, visitor);
...@@ -611,6 +621,7 @@ static void orderFinalizers() { ...@@ -611,6 +621,7 @@ static void orderFinalizers() {
assert(state == FinalizationState::REACHABLE_FROM_FINALIZER || state == FinalizationState::ALIVE); assert(state == FinalizationState::REACHABLE_FROM_FINALIZER || state == FinalizationState::ALIVE);
if (state == FinalizationState::REACHABLE_FROM_FINALIZER) { if (state == FinalizationState::REACHABLE_FROM_FINALIZER) {
GC_TRACE_LOG("%p is now pending finalization\n", marked);
pending_finalization_list.push_back(marked); pending_finalization_list.push_back(marked);
} }
} }
...@@ -667,6 +678,8 @@ static void callPendingFinalizers() { ...@@ -667,6 +678,8 @@ static void callPendingFinalizers() {
Box* box = pending_finalization_list.front(); Box* box = pending_finalization_list.front();
pending_finalization_list.pop_front(); pending_finalization_list.pop_front();
GC_TRACE_LOG("Running finalizer for %p\n", box);
ASSERT(isValidGCObject(box), "objects to be finalized should still be alive"); ASSERT(isValidGCObject(box), "objects to be finalized should still be alive");
if (isWeaklyReferenced(box)) { if (isWeaklyReferenced(box)) {
...@@ -741,7 +754,7 @@ static void prepareWeakrefCallbacks(Box* box) { ...@@ -741,7 +754,7 @@ static void prepareWeakrefCallbacks(Box* box) {
} }
} }
static void markPhase() { void markPhase() {
static StatCounter sc_us("us_gc_mark_phase"); static StatCounter sc_us("us_gc_mark_phase");
Timer _t("markPhase", /*min_usec=*/10000); Timer _t("markPhase", /*min_usec=*/10000);
...@@ -775,13 +788,13 @@ static void markPhase() { ...@@ -775,13 +788,13 @@ static void markPhase() {
sc_us.log(us); sc_us.log(us);
} }
static void sweepPhase(std::vector<Box*>& weakly_referenced) { static void sweepPhase(std::vector<Box*>& weakly_referenced, std::vector<BoxedClass*>& classes_to_free) {
static StatCounter sc_us("us_gc_sweep_phase"); static StatCounter sc_us("us_gc_sweep_phase");
Timer _t("sweepPhase", /*min_usec=*/10000); Timer _t("sweepPhase", /*min_usec=*/10000);
// we need to use the allocator here because these objects are referenced only here, and calling the weakref // we need to use the allocator here because these objects are referenced only here, and calling the weakref
// callbacks could start another gc // callbacks could start another gc
global_heap.freeUnmarked(weakly_referenced); global_heap.freeUnmarked(weakly_referenced, classes_to_free);
long us = _t.end(); long us = _t.end();
sc_us.log(us); sc_us.log(us);
...@@ -901,6 +914,24 @@ void endGCUnexpectedRegion() { ...@@ -901,6 +914,24 @@ void endGCUnexpectedRegion() {
should_not_reenter_gc = false; should_not_reenter_gc = false;
} }
#if TRACE_GC_MARKING
static void openTraceFp(bool is_pre) {
if (trace_fp)
fclose(trace_fp);
char tracefn_buf[80];
snprintf(tracefn_buf, sizeof(tracefn_buf), "gc_trace_%d.%03d%s.txt", getpid(), ncollections + is_pre,
is_pre ? "_pre" : "");
trace_fp = fopen(tracefn_buf, "w");
}
static int _dummy() {
openTraceFp(true);
return 0;
}
static int _initializer = _dummy();
#endif
void runCollection() { void runCollection() {
static StatCounter sc_us("us_gc_collections"); static StatCounter sc_us("us_gc_collections");
static StatCounter sc("gc_collections"); static StatCounter sc("gc_collections");
...@@ -925,13 +956,7 @@ void runCollection() { ...@@ -925,13 +956,7 @@ void runCollection() {
Timer _t("collecting", /*min_usec=*/10000); Timer _t("collecting", /*min_usec=*/10000);
#if TRACE_GC_MARKING #if TRACE_GC_MARKING
#if 1 // separate log file per collection openTraceFp(false);
char tracefn_buf[80];
snprintf(tracefn_buf, sizeof(tracefn_buf), "gc_trace_%d.%03d.txt", getpid(), ncollections);
trace_fp = fopen(tracefn_buf, "w");
#else // overwrite previous log file with each collection
trace_fp = fopen("gc_trace.txt", "w");
#endif
#endif #endif
global_heap.prepareForCollection(); global_heap.prepareForCollection();
...@@ -949,7 +974,15 @@ void runCollection() { ...@@ -949,7 +974,15 @@ void runCollection() {
// since the deallocation of other objects (namely, the weakref objects themselves) can affect // since the deallocation of other objects (namely, the weakref objects themselves) can affect
// those lists, and we want to see the final versions. // those lists, and we want to see the final versions.
std::vector<Box*> weakly_referenced; std::vector<Box*> weakly_referenced;
sweepPhase(weakly_referenced);
// Separately keep track of classes that we will be freeing in this collection.
// We want to make sure that any instances get freed before the class itself gets freed,
// since the freeing logic can look at the class object.
// So instead of directly freeing the classes, we stuff them into this vector and then
// free them at the end.
std::vector<BoxedClass*> classes_to_free;
sweepPhase(weakly_referenced, classes_to_free);
// Handle weakrefs in two passes: // Handle weakrefs in two passes:
// - first, find all of the weakref objects whose callbacks we need to call. we need to iterate // - first, find all of the weakref objects whose callbacks we need to call. we need to iterate
...@@ -958,8 +991,27 @@ void runCollection() { ...@@ -958,8 +991,27 @@ void runCollection() {
// - the callbacks are called later, along with the finalizers // - the callbacks are called later, along with the finalizers
for (auto o : weakly_referenced) { for (auto o : weakly_referenced) {
assert(isValidGCObject(o)); assert(isValidGCObject(o));
GC_TRACE_LOG("%p is weakly referenced\n", o);
prepareWeakrefCallbacks(o); prepareWeakrefCallbacks(o);
global_heap.free(GCAllocation::fromUserData(o));
if (PyType_Check(o))
classes_to_free.push_back(static_cast<BoxedClass*>(o));
else
global_heap.free(GCAllocation::fromUserData(o));
}
// We want to make sure that classes get freed before their metaclasses.
// Use a simple approach of only freeing one level of the hierarchy and then
// letting the next collection do the next one.
llvm::DenseSet<BoxedClass*> classes_to_not_free;
for (auto b : classes_to_free) {
classes_to_not_free.insert(b->cls);
}
for (auto b : classes_to_free) {
if (classes_to_not_free.count(b))
continue;
global_heap._setFree(GCAllocation::fromUserData(b));
} }
global_heap.cleanupAfterCollection(); global_heap.cleanupAfterCollection();
...@@ -969,8 +1021,7 @@ void runCollection() { ...@@ -969,8 +1021,7 @@ void runCollection() {
#endif #endif
#if TRACE_GC_MARKING #if TRACE_GC_MARKING
fclose(trace_fp); openTraceFp(true);
trace_fp = NULL;
#endif #endif
should_not_reenter_gc = false; // end non-reentrant section should_not_reenter_gc = false; // end non-reentrant section
......
...@@ -112,6 +112,8 @@ extern "C" inline void* gc_alloc(size_t bytes, GCKind kind_id) { ...@@ -112,6 +112,8 @@ extern "C" inline void* gc_alloc(size_t bytes, GCKind kind_id) {
// if (VERBOSITY()) printf("Allocated %ld bytes at [%p, %p)\n", bytes, r, (char*)r + bytes); // if (VERBOSITY()) printf("Allocated %ld bytes at [%p, %p)\n", bytes, r, (char*)r + bytes);
#endif #endif
GC_TRACE_LOG("Allocated %p\n", r);
#if STAT_ALLOCATIONS #if STAT_ALLOCATIONS
gc_alloc_bytes.log(alloc_bytes); gc_alloc_bytes.log(alloc_bytes);
gc_alloc_bytes_typed[(int)kind_id].log(alloc_bytes); gc_alloc_bytes_typed[(int)kind_id].log(alloc_bytes);
......
...@@ -45,7 +45,7 @@ template <> void return_temporary_buffer<pyston::Box*>(pyston::Box** p) { ...@@ -45,7 +45,7 @@ template <> void return_temporary_buffer<pyston::Box*>(pyston::Box** p) {
namespace pyston { namespace pyston {
namespace gc { namespace gc {
bool _doFree(GCAllocation* al, std::vector<Box*>* weakly_referenced); bool _doFree(GCAllocation* al, std::vector<Box*>* weakly_referenced, std::vector<BoxedClass*>* classes_to_free);
// lots of linked lists around here, so let's just use template functions for operations on them. // lots of linked lists around here, so let's just use template functions for operations on them.
template <class ListT> inline void nullNextPrev(ListT* node) { template <class ListT> inline void nullNextPrev(ListT* node) {
...@@ -88,7 +88,8 @@ template <class ListT, typename Func> inline void forEach(ListT* list, Func func ...@@ -88,7 +88,8 @@ template <class ListT, typename Func> inline void forEach(ListT* list, Func func
} }
template <class ListT, typename Free> template <class ListT, typename Free>
inline void sweepList(ListT* head, std::vector<Box*>& weakly_referenced, Free free_func) { inline void sweepList(ListT* head, std::vector<Box*>& weakly_referenced, std::vector<BoxedClass*>& classes_to_free,
Free free_func) {
auto cur = head; auto cur = head;
while (cur) { while (cur) {
GCAllocation* al = cur->data; GCAllocation* al = cur->data;
...@@ -97,7 +98,7 @@ inline void sweepList(ListT* head, std::vector<Box*>& weakly_referenced, Free fr ...@@ -97,7 +98,7 @@ inline void sweepList(ListT* head, std::vector<Box*>& weakly_referenced, Free fr
clearMark(al); clearMark(al);
cur = cur->next; cur = cur->next;
} else { } else {
if (_doFree(al, &weakly_referenced)) { if (_doFree(al, &weakly_referenced, &classes_to_free)) {
removeFromLL(cur); removeFromLL(cur);
auto to_free = cur; auto to_free = cur;
...@@ -128,12 +129,7 @@ void _bytesAllocatedTripped() { ...@@ -128,12 +129,7 @@ void _bytesAllocatedTripped() {
/// Finalizers /// Finalizers
bool hasOrderedFinalizer(BoxedClass* cls) { bool hasOrderedFinalizer(BoxedClass* cls) {
if (PyType_FastSubclass(cls, Py_TPFLAGS_TYPE_SUBCLASS)) { if (cls->has_safe_tp_dealloc) {
// Class objects need to be kept alive for at least as long as any objects that point
// to them, even if those objects are garbage (or involved in finalizer chains).
// Marking class objects as having finalizers should get us this behavior.
return true;
} else if (cls->has_safe_tp_dealloc) {
ASSERT(!cls->tp_del, "class \"%s\" with safe tp_dealloc also has tp_del?", cls->tp_name); ASSERT(!cls->tp_del, "class \"%s\" with safe tp_dealloc also has tp_del?", cls->tp_name);
return false; return false;
} else if (cls->hasNonDefaultTpDealloc()) { } else if (cls->hasNonDefaultTpDealloc()) {
...@@ -164,9 +160,10 @@ __attribute__((always_inline)) bool isWeaklyReferenced(Box* b) { ...@@ -164,9 +160,10 @@ __attribute__((always_inline)) bool isWeaklyReferenced(Box* b) {
Heap global_heap; Heap global_heap;
__attribute__((always_inline)) bool _doFree(GCAllocation* al, std::vector<Box*>* weakly_referenced) { static StatCounter gc_safe_destructors("gc_safe_destructor_calls");
static StatCounter gc_safe_destructors("gc_safe_destructor_calls");
__attribute__((always_inline)) bool _doFree(GCAllocation* al, std::vector<Box*>* weakly_referenced,
std::vector<BoxedClass*>* classes_to_free) {
#ifndef NVALGRIND #ifndef NVALGRIND
VALGRIND_DISABLE_ERROR_REPORTING; VALGRIND_DISABLE_ERROR_REPORTING;
#endif #endif
...@@ -175,6 +172,7 @@ __attribute__((always_inline)) bool _doFree(GCAllocation* al, std::vector<Box*>* ...@@ -175,6 +172,7 @@ __attribute__((always_inline)) bool _doFree(GCAllocation* al, std::vector<Box*>*
VALGRIND_ENABLE_ERROR_REPORTING; VALGRIND_ENABLE_ERROR_REPORTING;
#endif #endif
GC_TRACE_LOG("doFree %p\n", al->user_data);
if (alloc_kind == GCKind::PYTHON) { if (alloc_kind == GCKind::PYTHON) {
#ifndef NVALGRIND #ifndef NVALGRIND
VALGRIND_DISABLE_ERROR_REPORTING; VALGRIND_DISABLE_ERROR_REPORTING;
...@@ -185,15 +183,24 @@ __attribute__((always_inline)) bool _doFree(GCAllocation* al, std::vector<Box*>* ...@@ -185,15 +183,24 @@ __attribute__((always_inline)) bool _doFree(GCAllocation* al, std::vector<Box*>*
#endif #endif
assert(b->cls); assert(b->cls);
if (isWeaklyReferenced(b)) { if (unlikely(isWeaklyReferenced(b))) {
assert(weakly_referenced && "attempting to free a weakly referenced object manually"); assert(weakly_referenced && "attempting to free a weakly referenced object manually");
weakly_referenced->push_back(b); weakly_referenced->push_back(b);
GC_TRACE_LOG("%p is weakly referenced\n", al->user_data);
return false; return false;
} }
ASSERT(!hasOrderedFinalizer(b->cls) || hasFinalized(al), "%s", getTypeName(b)); ASSERT(!hasOrderedFinalizer(b->cls) || hasFinalized(al), "%s", getTypeName(b));
// Note: do this check after the weakrefs check.
if (unlikely(PyType_Check(b))) {
assert(classes_to_free);
classes_to_free->push_back(static_cast<BoxedClass*>(b));
return false;
}
if (b->cls->tp_dealloc != dealloc_null && b->cls->has_safe_tp_dealloc) { if (b->cls->tp_dealloc != dealloc_null && b->cls->has_safe_tp_dealloc) {
GC_TRACE_LOG("running safe destructor for %p\n", b);
gc_safe_destructors.log(); gc_safe_destructors.log();
GCAllocation* al = GCAllocation::fromUserData(b); GCAllocation* al = GCAllocation::fromUserData(b);
...@@ -208,7 +215,7 @@ __attribute__((always_inline)) bool _doFree(GCAllocation* al, std::vector<Box*>* ...@@ -208,7 +215,7 @@ __attribute__((always_inline)) bool _doFree(GCAllocation* al, std::vector<Box*>*
} }
void Heap::destructContents(GCAllocation* al) { void Heap::destructContents(GCAllocation* al) {
_doFree(al, NULL); _doFree(al, NULL, NULL);
} }
struct HeapStatistics { struct HeapStatistics {
...@@ -481,11 +488,15 @@ void SmallArena::forEachReference(std::function<void(GCAllocation*, size_t)> f) ...@@ -481,11 +488,15 @@ void SmallArena::forEachReference(std::function<void(GCAllocation*, size_t)> f)
} }
} }
void SmallArena::freeUnmarked(std::vector<Box*>& weakly_referenced) { void SmallArena::freeUnmarked(std::vector<Box*>& weakly_referenced, std::vector<BoxedClass*>& classes_to_free) {
assertConsistent(); assertConsistent();
thread_caches.forEachValue([this, &weakly_referenced](ThreadBlockCache* cache) { thread_caches.forEachValue([this, &weakly_referenced, &classes_to_free](ThreadBlockCache* cache) {
for (int bidx = 0; bidx < NUM_BUCKETS; bidx++) { // Iterate over the buckets from largest to smallest. I don't think it really matters, but
// doing it in this order means that we will tend to free types early in the sweep (since they
// are fairly large), and we are more likely to detect if other code depended on that type
// being alive.
for (int bidx = NUM_BUCKETS - 1; bidx >= 0; bidx--) {
Block* h = cache->cache_free_heads[bidx]; Block* h = cache->cache_free_heads[bidx];
// Try to limit the amount of unused memory a thread can hold onto; // Try to limit the amount of unused memory a thread can hold onto;
// currently pretty dumb, just limit the number of blocks in the free-list // currently pretty dumb, just limit the number of blocks in the free-list
...@@ -504,8 +515,8 @@ void SmallArena::freeUnmarked(std::vector<Box*>& weakly_referenced) { ...@@ -504,8 +515,8 @@ void SmallArena::freeUnmarked(std::vector<Box*>& weakly_referenced) {
insertIntoLL(&heads[bidx], h); insertIntoLL(&heads[bidx], h);
} }
Block** chain_end = _freeChain(&cache->cache_free_heads[bidx], weakly_referenced); Block** chain_end = _freeChain(&cache->cache_free_heads[bidx], weakly_referenced, classes_to_free);
_freeChain(&cache->cache_full_heads[bidx], weakly_referenced); _freeChain(&cache->cache_full_heads[bidx], weakly_referenced, classes_to_free);
while (Block* b = cache->cache_full_heads[bidx]) { while (Block* b = cache->cache_full_heads[bidx]) {
removeFromLLAndNull(b); removeFromLLAndNull(b);
...@@ -514,9 +525,9 @@ void SmallArena::freeUnmarked(std::vector<Box*>& weakly_referenced) { ...@@ -514,9 +525,9 @@ void SmallArena::freeUnmarked(std::vector<Box*>& weakly_referenced) {
} }
}); });
for (int bidx = 0; bidx < NUM_BUCKETS; bidx++) { for (int bidx = NUM_BUCKETS - 1; bidx >= 0; bidx--) {
Block** chain_end = _freeChain(&heads[bidx], weakly_referenced); Block** chain_end = _freeChain(&heads[bidx], weakly_referenced, classes_to_free);
_freeChain(&full_heads[bidx], weakly_referenced); _freeChain(&full_heads[bidx], weakly_referenced, classes_to_free);
while (Block* b = full_heads[bidx]) { while (Block* b = full_heads[bidx]) {
removeFromLLAndNull(b); removeFromLLAndNull(b);
...@@ -543,7 +554,8 @@ void SmallArena::getStatistics(HeapStatistics* stats) { ...@@ -543,7 +554,8 @@ void SmallArena::getStatistics(HeapStatistics* stats) {
} }
SmallArena::Block** SmallArena::_freeChain(Block** head, std::vector<Box*>& weakly_referenced) { SmallArena::Block** SmallArena::_freeChain(Block** head, std::vector<Box*>& weakly_referenced,
std::vector<BoxedClass*>& classes_to_free) {
while (Block* b = *head) { while (Block* b = *head) {
int num_objects = b->numObjects(); int num_objects = b->numObjects();
int first_obj = b->minObjIndex(); int first_obj = b->minObjIndex();
...@@ -568,7 +580,7 @@ SmallArena::Block** SmallArena::_freeChain(Block** head, std::vector<Box*>& weak ...@@ -568,7 +580,7 @@ SmallArena::Block** SmallArena::_freeChain(Block** head, std::vector<Box*>& weak
if (isMarked(al)) { if (isMarked(al)) {
clearMark(al); clearMark(al);
} else { } else {
if (_doFree(al, &weakly_referenced)) { if (_doFree(al, &weakly_referenced, &classes_to_free)) {
// GC_TRACE_LOG("freeing %p\n", al->user_data); // GC_TRACE_LOG("freeing %p\n", al->user_data);
b->isfree.set(atom_idx); b->isfree.set(atom_idx);
#ifndef NDEBUG #ifndef NDEBUG
...@@ -807,8 +819,8 @@ void LargeArena::cleanupAfterCollection() { ...@@ -807,8 +819,8 @@ void LargeArena::cleanupAfterCollection() {
lookup.clear(); lookup.clear();
} }
void LargeArena::freeUnmarked(std::vector<Box*>& weakly_referenced) { void LargeArena::freeUnmarked(std::vector<Box*>& weakly_referenced, std::vector<BoxedClass*>& classes_to_free) {
sweepList(head, weakly_referenced, [this](LargeObj* ptr) { _freeLargeObj(ptr); }); sweepList(head, weakly_referenced, classes_to_free, [this](LargeObj* ptr) { _freeLargeObj(ptr); });
} }
void LargeArena::getStatistics(HeapStatistics* stats) { void LargeArena::getStatistics(HeapStatistics* stats) {
...@@ -1016,8 +1028,8 @@ void HugeArena::cleanupAfterCollection() { ...@@ -1016,8 +1028,8 @@ void HugeArena::cleanupAfterCollection() {
lookup.clear(); lookup.clear();
} }
void HugeArena::freeUnmarked(std::vector<Box*>& weakly_referenced) { void HugeArena::freeUnmarked(std::vector<Box*>& weakly_referenced, std::vector<BoxedClass*>& classes_to_free) {
sweepList(head, weakly_referenced, [this](HugeObj* ptr) { _freeHugeObj(ptr); }); sweepList(head, weakly_referenced, classes_to_free, [this](HugeObj* ptr) { _freeHugeObj(ptr); });
} }
void HugeArena::getStatistics(HeapStatistics* stats) { void HugeArena::getStatistics(HeapStatistics* stats) {
......
...@@ -272,7 +272,7 @@ public: ...@@ -272,7 +272,7 @@ public:
void free(GCAllocation* al); void free(GCAllocation* al);
GCAllocation* allocationFrom(void* ptr); GCAllocation* allocationFrom(void* ptr);
void freeUnmarked(std::vector<Box*>& weakly_referenced); void freeUnmarked(std::vector<Box*>& weakly_referenced, std::vector<BoxedClass*>& classes_to_free);
void getStatistics(HeapStatistics* stats); void getStatistics(HeapStatistics* stats);
...@@ -414,7 +414,7 @@ private: ...@@ -414,7 +414,7 @@ private:
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);
Block** _freeChain(Block** head, std::vector<Box*>& weakly_referenced); Block** _freeChain(Block** head, std::vector<Box*>& weakly_referenced, std::vector<BoxedClass*>& classes_to_free);
void _getChainStatistics(HeapStatistics* stats, Block** head); void _getChainStatistics(HeapStatistics* stats, Block** head);
GCAllocation* _alloc(size_t bytes, int bucket_idx); GCAllocation* _alloc(size_t bytes, int bucket_idx);
...@@ -496,7 +496,7 @@ public: ...@@ -496,7 +496,7 @@ public:
void free(GCAllocation* alloc); void free(GCAllocation* alloc);
GCAllocation* allocationFrom(void* ptr); GCAllocation* allocationFrom(void* ptr);
void freeUnmarked(std::vector<Box*>& weakly_referenced); void freeUnmarked(std::vector<Box*>& weakly_referenced, std::vector<BoxedClass*>& classes_to_free);
void getStatistics(HeapStatistics* stats); void getStatistics(HeapStatistics* stats);
...@@ -517,7 +517,7 @@ public: ...@@ -517,7 +517,7 @@ public:
void free(GCAllocation* alloc); void free(GCAllocation* alloc);
GCAllocation* allocationFrom(void* ptr); GCAllocation* allocationFrom(void* ptr);
void freeUnmarked(std::vector<Box*>& weakly_referenced); void freeUnmarked(std::vector<Box*>& weakly_referenced, std::vector<BoxedClass*>& classes_to_free);
void getStatistics(HeapStatistics* stats); void getStatistics(HeapStatistics* stats);
...@@ -623,18 +623,7 @@ public: ...@@ -623,18 +623,7 @@ public:
void free(GCAllocation* alloc) { void free(GCAllocation* alloc) {
destructContents(alloc); destructContents(alloc);
if (large_arena.contains(alloc)) { _setFree(alloc);
large_arena.free(alloc);
return;
}
if (huge_arena.contains(alloc)) {
huge_arena.free(alloc);
return;
}
assert(small_arena.contains(alloc));
small_arena.free(alloc);
} }
// not thread safe: // not thread safe:
...@@ -654,10 +643,10 @@ public: ...@@ -654,10 +643,10 @@ public:
void forEachSmallArenaReference(std::function<void(GCAllocation*, size_t)> f) { small_arena.forEachReference(f); } 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, std::vector<BoxedClass*>& classes_to_free) {
small_arena.freeUnmarked(weakly_referenced); small_arena.freeUnmarked(weakly_referenced, classes_to_free);
large_arena.freeUnmarked(weakly_referenced); large_arena.freeUnmarked(weakly_referenced, classes_to_free);
huge_arena.freeUnmarked(weakly_referenced); huge_arena.freeUnmarked(weakly_referenced, classes_to_free);
} }
void prepareForCollection() { void prepareForCollection() {
...@@ -673,6 +662,27 @@ public: ...@@ -673,6 +662,27 @@ public:
} }
void dumpHeapStatistics(int level); void dumpHeapStatistics(int level);
private:
// Internal function that just marks the allocation as being freed, without doing any
// Python-semantics on it.
void _setFree(GCAllocation* alloc) {
if (large_arena.contains(alloc)) {
large_arena.free(alloc);
return;
}
if (huge_arena.contains(alloc)) {
huge_arena.free(alloc);
return;
}
assert(small_arena.contains(alloc));
small_arena.free(alloc);
}
friend void markPhase();
friend void runCollection();
}; };
extern Heap global_heap; extern Heap global_heap;
......
...@@ -532,8 +532,15 @@ Box* getattrFuncInternal(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ...@@ -532,8 +532,15 @@ Box* getattrFuncInternal(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args,
// value is fixed. // value is fixed.
if (!PyString_CheckExact(_str) && !PyUnicode_CheckExact(_str)) if (!PyString_CheckExact(_str) && !PyUnicode_CheckExact(_str))
rewrite_args = NULL; rewrite_args = NULL;
else else {
if (PyString_CheckExact(_str) && PyString_CHECK_INTERNED(_str) == SSTATE_INTERNED_IMMORTAL) {
// can avoid keeping the extra gc reference
} else {
rewrite_args->rewriter->addGCReference(_str);
}
rewrite_args->arg2->addGuard((intptr_t)arg2); rewrite_args->arg2->addGuard((intptr_t)arg2);
}
} }
try { try {
......
import gc
l = []
for i in xrange(5100):
class C(object):
pass
C.l = [C() for _ in xrange(17)]
if i % 10 == 0:
print i
# gc.collect()
# for i in xrange(100):
# l.append('=' * i)
# Regression test: make sure we guard / invalidate our getattr() rewrites:
class C(object):
pass
c = C()
for i in xrange(100):
setattr(c, "attr%d" % i, i)
def f():
for j in xrange(2, 10):
t = 0
for i in xrange(2000):
for k in xrange(j):
t += getattr(c, "attr%d" % k)
print t
f()
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