wingo pushed a commit to branch wip-whippet
in repository guile.

commit fbe49598f583ae4f861528e54c0efeb0368cbbfd
Author: Andy Wingo <wi...@igalia.com>
AuthorDate: Tue Aug 15 11:34:29 2023 +0200

    Add "extern space"
    
    This is mostly for static data.
---
 api/gc-api.h                    |  4 ++++
 api/gc-embedder-api.h           |  8 ++++++++
 benchmarks/simple-gc-embedder.h | 12 ++++++++++++
 doc/manual.md                   | 13 +++++++++++++
 src/bdw.c                       |  3 +++
 src/semi.c                      | 27 +++++++++++++++++++++++++--
 src/whippet.c                   | 13 ++++++++++++-
 7 files changed, 77 insertions(+), 3 deletions(-)

diff --git a/api/gc-api.h b/api/gc-api.h
index 6cf783703..821891bca 100644
--- a/api/gc-api.h
+++ b/api/gc-api.h
@@ -34,6 +34,10 @@ struct gc_heap_roots;
 GC_API_ void gc_heap_set_roots(struct gc_heap *heap,
                                struct gc_heap_roots *roots);
 
+struct gc_extern_space;
+GC_API_ void gc_heap_set_extern_space(struct gc_heap *heap,
+                                      struct gc_extern_space *space);
+
 GC_API_ struct gc_mutator* gc_init_for_thread(struct gc_stack_addr *base,
                                               struct gc_heap *heap);
 GC_API_ void gc_finish_for_thread(struct gc_mutator *mut);
diff --git a/api/gc-embedder-api.h b/api/gc-embedder-api.h
index 6e39f05ea..31793316f 100644
--- a/api/gc-embedder-api.h
+++ b/api/gc-embedder-api.h
@@ -17,9 +17,17 @@ struct gc_heap_roots;
 struct gc_atomic_forward;
 struct gc_heap;
 struct gc_ephemeron;
+struct gc_extern_space;
 
 GC_EMBEDDER_API inline int gc_is_valid_conservative_ref_displacement(uintptr_t 
displacement);
 
+GC_EMBEDDER_API inline int gc_extern_space_mark(struct gc_extern_space *space,
+                                                struct gc_ref ref) 
GC_ALWAYS_INLINE;
+GC_EMBEDDER_API inline void gc_extern_space_start_gc(struct gc_extern_space 
*space,
+                                                     int is_minor_gc);
+GC_EMBEDDER_API inline void gc_extern_space_finish_gc(struct gc_extern_space 
*space,
+                                                      int is_minor_gc);
+
 GC_EMBEDDER_API inline void gc_trace_object(struct gc_ref ref,
                                             void (*visit)(struct gc_edge edge,
                                                           struct gc_heap *heap,
diff --git a/benchmarks/simple-gc-embedder.h b/benchmarks/simple-gc-embedder.h
index 14fb142e7..d4276192f 100644
--- a/benchmarks/simple-gc-embedder.h
+++ b/benchmarks/simple-gc-embedder.h
@@ -18,6 +18,18 @@ gc_is_valid_conservative_ref_displacement(uintptr_t 
displacement) {
 #endif
 }
 
+// No external objects in simple benchmarks.
+static inline int gc_extern_space_mark(struct gc_extern_space *space,
+                                       struct gc_ref ref) {
+  GC_CRASH();
+}
+static inline void gc_extern_space_start_gc(struct gc_extern_space *space,
+                                            int is_minor_gc) {
+}
+static inline void gc_extern_space_finish_gc(struct gc_extern_space *space,
+                                             int is_minor_gc) {
+}
+
 static inline void gc_trace_object(struct gc_ref ref,
                                    void (*trace_edge)(struct gc_edge edge,
                                                       struct gc_heap *heap,
diff --git a/doc/manual.md b/doc/manual.md
index 41ff83d91..e94095635 100644
--- a/doc/manual.md
+++ b/doc/manual.md
@@ -163,6 +163,19 @@ embedder should return 1 only if the displacement is 0, 
but if the
 program allows low-bit tagged pointers, then it should also return 1 for
 those pointer tags.
 
+### External objects
+
+Sometimes a system will allocate objects outside the GC, for example on
+the stack or in static data sections.  To support this use case, Whippet
+allows the embedder to provide a `struct gc_extern_space`
+implementation.  Whippet will call `gc_extern_space_start_gc` at the
+start of each collection, and `gc_extern_space_finish_gc` at the end.
+External objects will be visited by `gc_extern_space_mark`, which should
+return nonzero if the object hasn't been seen before and needs to be
+traced via `gc_trace_object` (coloring the object grey).  Note,
+`gc_extern_space_mark` may be called concurrently from many threads; be
+prepared!
+
 ## Configuration, compilation, and linking
 
 To the user, Whippet presents an abstract API that does not encode the
diff --git a/src/bdw.c b/src/bdw.c
index cf17f19e7..3b01e9dcb 100644
--- a/src/bdw.c
+++ b/src/bdw.c
@@ -325,6 +325,9 @@ void gc_mutator_set_roots(struct gc_mutator *mut,
 }
 void gc_heap_set_roots(struct gc_heap *heap, struct gc_heap_roots *roots) {
 }
+void gc_heap_set_extern_space(struct gc_heap *heap,
+                              struct gc_extern_space *space) {
+}
 
 void gc_print_stats(struct gc_heap *heap) {
   printf("Completed %ld collections\n", (long)GC_get_gc_no());
diff --git a/src/semi.c b/src/semi.c
index 11b74ec5f..151bafb83 100644
--- a/src/semi.c
+++ b/src/semi.c
@@ -37,12 +37,14 @@ struct gc_heap {
   struct semi_space semi_space;
   struct large_object_space large_object_space;
   struct gc_pending_ephemerons *pending_ephemerons;
+  struct gc_extern_space *extern_space;
   double pending_ephemerons_size_factor;
   double pending_ephemerons_size_slop;
   size_t size;
   long count;
   int check_pending_ephemerons;
   const struct gc_options *options;
+  struct gc_heap_roots *roots;
 };
 // One mutator per space, can just store the heap in the mutator.
 struct gc_mutator {
@@ -195,6 +197,17 @@ static int semi_space_contains(struct semi_space *space, 
struct gc_ref ref) {
   return region_contains(&space->from_space, addr);
 }
 
+static void visit_external_object(struct gc_heap *heap,
+                                  struct gc_extern_space *space,
+                                  struct gc_ref ref) {
+  if (gc_extern_space_mark(space, ref)) {
+    if (GC_UNLIKELY(heap->check_pending_ephemerons))
+      gc_resolve_pending_ephemerons(ref, heap);
+
+    gc_trace_object(ref, trace, heap, NULL, NULL);
+  }
+}
+
 static void visit(struct gc_edge edge, struct gc_heap *heap) {
   struct gc_ref ref = gc_edge_ref(edge);
   if (!gc_ref_is_heap_object(ref))
@@ -204,7 +217,7 @@ static void visit(struct gc_edge edge, struct gc_heap 
*heap) {
   else if (large_object_space_contains(heap_large_object_space(heap), ref))
     visit_large_object_space(heap, heap_large_object_space(heap), ref);
   else
-    GC_CRASH();
+    visit_external_object(heap, heap->extern_space, ref);
 }
 
 struct gc_pending_ephemerons *
@@ -329,10 +342,13 @@ static void collect(struct gc_mutator *mut, size_t 
for_alloc) {
   struct large_object_space *large = heap_large_object_space(heap);
   // fprintf(stderr, "start collect #%ld:\n", space->count);
   large_object_space_start_gc(large, 0);
+  gc_extern_space_start_gc(heap->extern_space, 0);
   flip(semi);
   heap->count++;
   heap->check_pending_ephemerons = 0;
   uintptr_t grey = semi->hp;
+  if (heap->roots)
+    gc_trace_heap_roots(heap->roots, trace, heap, NULL);
   if (mut->roots)
     gc_trace_mutator_roots(mut->roots, trace, heap, NULL);
   // fprintf(stderr, "pushed %zd bytes in roots\n", space->hp - grey);
@@ -344,6 +360,7 @@ static void collect(struct gc_mutator *mut, size_t 
for_alloc) {
     while(grey < semi->hp)
       grey = scan(heap, gc_ref(grey));
   large_object_space_finish_gc(large, 0);
+  gc_extern_space_finish_gc(heap->extern_space, 0);
   semi_space_finish_gc(semi, large->live_pages_at_last_collection);
   gc_sweep_pending_ephemerons(heap->pending_ephemerons, 0, 1);
   adjust_heap_size_and_limits(heap, for_alloc);
@@ -485,11 +502,13 @@ unsigned gc_heap_ephemeron_trace_epoch(struct gc_heap 
*heap) {
 }
 
 static int heap_init(struct gc_heap *heap, const struct gc_options *options) {
+  heap->extern_space = NULL;
   heap->pending_ephemerons_size_factor = 0.01;
   heap->pending_ephemerons_size_slop = 0.5;
   heap->count = 0;
   heap->options = options;
   heap->size = options->common.heap_size;
+  heap->roots = NULL;
 
   return heap_prepare_pending_ephemerons(heap);
 }
@@ -559,7 +578,11 @@ void gc_mutator_set_roots(struct gc_mutator *mut,
   mut->roots = roots;
 }
 void gc_heap_set_roots(struct gc_heap *heap, struct gc_heap_roots *roots) {
-  GC_CRASH();
+  heap->roots = roots;
+}
+void gc_heap_set_extern_space(struct gc_heap *heap,
+                              struct gc_extern_space *space) {
+  heap->extern_space = space;
 }
 
 struct gc_mutator* gc_init_for_thread(struct gc_stack_addr *base,
diff --git a/src/whippet.c b/src/whippet.c
index 4771e37e9..ae247482b 100644
--- a/src/whippet.c
+++ b/src/whippet.c
@@ -300,6 +300,7 @@ enum gc_kind {
 struct gc_heap {
   struct mark_space mark_space;
   struct large_object_space large_object_space;
+  struct gc_extern_space *extern_space;
   size_t large_object_pages;
   pthread_mutex_t lock;
   pthread_cond_t collector_cond;
@@ -360,6 +361,9 @@ static inline struct mark_space* heap_mark_space(struct 
gc_heap *heap) {
 static inline struct large_object_space* heap_large_object_space(struct 
gc_heap *heap) {
   return &heap->large_object_space;
 }
+static inline struct gc_extern_space* heap_extern_space(struct gc_heap *heap) {
+  return heap->extern_space;
+}
 static inline struct gc_heap* mutator_heap(struct gc_mutator *mutator) {
   return mutator->heap;
 }
@@ -667,7 +671,7 @@ static inline int do_trace(struct gc_heap *heap, struct 
gc_edge edge,
     return large_object_space_mark_object(heap_large_object_space(heap),
                                           ref);
   else
-    GC_CRASH();
+    return gc_extern_space_mark(heap_extern_space(heap), ref);
 }
 
 static inline int trace_edge(struct gc_heap *heap, struct gc_edge edge) {
@@ -1078,6 +1082,10 @@ void gc_mutator_set_roots(struct gc_mutator *mut,
 void gc_heap_set_roots(struct gc_heap *heap, struct gc_heap_roots *roots) {
   heap->roots = roots;
 }
+void gc_heap_set_extern_space(struct gc_heap *heap,
+                              struct gc_extern_space *space) {
+  heap->extern_space = space;
+}
 
 static void trace_and_enqueue_locally(struct gc_edge edge,
                                       struct gc_heap *heap,
@@ -1803,6 +1811,7 @@ static void collect(struct gc_mutator *mut) {
   struct gc_heap *heap = mutator_heap(mut);
   struct mark_space *space = heap_mark_space(heap);
   struct large_object_space *lospace = heap_large_object_space(heap);
+  struct gc_extern_space *exspace = heap_extern_space(heap);
   if (maybe_grow_heap(heap)) {
     DEBUG("grew heap instead of collecting #%ld:\n", heap->count);
     return;
@@ -1811,6 +1820,7 @@ static void collect(struct gc_mutator *mut) {
   enum gc_kind gc_kind = determine_collection_kind(heap);
   update_mark_patterns(space, !(gc_kind & GC_KIND_FLAG_MINOR));
   large_object_space_start_gc(lospace, gc_kind & GC_KIND_FLAG_MINOR);
+  gc_extern_space_start_gc(exspace, gc_kind & GC_KIND_FLAG_MINOR);
   resolve_ephemerons_lazily(heap);
   tracer_prepare(heap);
   request_mutators_to_stop(heap);
@@ -1832,6 +1842,7 @@ static void collect(struct gc_mutator *mut) {
   tracer_release(heap);
   mark_space_finish_gc(space, gc_kind);
   large_object_space_finish_gc(lospace, gc_kind & GC_KIND_FLAG_MINOR);
+  gc_extern_space_finish_gc(exspace, gc_kind & GC_KIND_FLAG_MINOR);
   heap->count++;
   heap->last_collection_was_minor = gc_kind & GC_KIND_FLAG_MINOR;
   if (heap->last_collection_was_minor)

Reply via email to