1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-04-30 03:40:34 +02:00

Trim remembered-set during minor GC

When visiting remembered-set roots, if the target is no longer in
newspace, forget the edge.
This commit is contained in:
Andy Wingo 2025-01-15 22:31:48 +01:00
parent cc68a9a610
commit 685c63ab3a
4 changed files with 52 additions and 10 deletions

View file

@ -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);
}

View file

@ -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);

View file

@ -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();

View file

@ -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();