diff --git a/src/field-set.h b/src/field-set.h index f5b2e42c6..ff9a68e83 100644 --- a/src/field-set.h +++ b/src/field-set.h @@ -174,21 +174,26 @@ gc_field_set_clear(struct gc_field_set *set, static inline void gc_field_set_visit_edge_buffer(struct gc_field_set *set, struct gc_edge_buffer *buf, - void (*visit)(struct gc_edge, - struct gc_heap*, - void *data), + int (*visit)(struct gc_edge, + struct gc_heap*, + void *data), struct gc_heap *heap, void *data) GC_ALWAYS_INLINE; static inline void gc_field_set_visit_edge_buffer(struct gc_field_set *set, struct gc_edge_buffer *buf, - void (*visit)(struct gc_edge, - struct gc_heap*, - void *data), + int (*visit)(struct gc_edge, + struct gc_heap*, + void *data), struct gc_heap *heap, void *data) { - for (size_t i = 0; i < buf->size; i++) - visit(buf->edges[i], heap, data); + size_t i = 0; + while (i < buf->size) { + if (visit(buf->edges[i], heap, data)) + i++; + else + buf->edges[i] = buf->edges[--buf->size]; + } gc_field_set_release_buffer(set, buf); } diff --git a/src/large-object-space.h b/src/large-object-space.h index f3470e0e4..285664d03 100644 --- a/src/large-object-space.h +++ b/src/large-object-space.h @@ -249,6 +249,16 @@ large_object_space_remember_edge(struct large_object_space *space, return remembered; } +static void +large_object_space_forget_edge(struct large_object_space *space, + struct gc_edge edge) { + uintptr_t edge_addr = gc_edge_address(edge); + pthread_mutex_lock(&space->lock); + GC_ASSERT(address_set_contains(&space->remembered_edges, edge_addr)); + address_set_remove(&space->remembered_edges, edge_addr); + pthread_mutex_unlock(&space->lock); +} + static void large_object_space_clear_remembered_edges(struct large_object_space *space) { address_set_clear(&space->remembered_edges); diff --git a/src/mmc.c b/src/mmc.c index 0af725138..445bda8ec 100644 --- a/src/mmc.c +++ b/src/mmc.c @@ -264,6 +264,13 @@ tracer_visit(struct gc_edge edge, struct gc_heap *heap, void *trace_data) { gc_trace_worker_enqueue(worker, gc_edge_ref(edge)); } +static inline int +trace_remembered_edge(struct gc_edge edge, struct gc_heap *heap, void *trace_data) { + tracer_visit(edge, heap, trace_data); + // Keep the edge in the remembered set; we clear these in bulk later. + return 1; +} + static inline struct gc_ref do_trace_conservative_ref(struct gc_heap *heap, struct gc_conservative_ref ref, int possibly_interior) { @@ -380,7 +387,7 @@ trace_root(struct gc_root root, struct gc_heap *heap, break; case GC_ROOT_KIND_EDGE_BUFFER: gc_field_set_visit_edge_buffer(&heap->remembered_set, root.edge_buffer, - tracer_visit, heap, worker); + trace_remembered_edge, heap, worker); break; default: GC_CRASH(); diff --git a/src/pcc.c b/src/pcc.c index 422276afd..2f7178f00 100644 --- a/src/pcc.c +++ b/src/pcc.c @@ -544,6 +544,26 @@ tracer_visit(struct gc_edge edge, struct gc_heap *heap, void *trace_data) { gc_trace_worker_enqueue(worker, gc_edge_ref(edge)); } +static inline int +trace_remembered_edge(struct gc_edge edge, struct gc_heap *heap, + void *trace_data) { + GC_ASSERT(is_minor_collection(heap)); + tracer_visit(edge, heap, trace_data); + + // Return 1 if the edge should be kept in the remset, which is the + // case only for new objects that survive the minor GC, and only the + // nursery copy space has survivors. + if (new_space_contains(heap, gc_edge_ref(edge))) + return 1; // Keep edge in remset. + // Otherwise remove field-logging bit and return 0 to indicate that + // the remembered field set should remove this edge. + if (copy_space_contains_edge(heap_old_space(heap), edge)) + copy_space_forget_edge(heap_old_space(heap), edge); + else + large_object_space_forget_edge(heap_large_object_space(heap), edge); + return 0; +} + static inline void trace_one(struct gc_ref ref, struct gc_heap *heap, struct gc_trace_worker *worker) { #ifdef DEBUG @@ -582,7 +602,7 @@ static inline void trace_root(struct gc_root root, struct gc_heap *heap, break; case GC_ROOT_KIND_EDGE_BUFFER: gc_field_set_visit_edge_buffer(heap_remembered_set(heap), root.edge_buffer, - tracer_visit, heap, worker); + trace_remembered_edge, heap, worker); break; default: GC_CRASH();