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

commit b5c36b9fd857674f951dcd4892d98ddd8f429d36
Author: Andy Wingo <wi...@igalia.com>
AuthorDate: Fri Oct 4 11:40:09 2024 +0200

    Explicitly support immediate values
    
    Because we have to deref edges ourselves, as part of generational
    marking, we need to ignore edges that don't point to heap objects.
---
 api/gc-config.h       |  4 ++++
 api/gc-ref.h          | 15 ++++++++++++++-
 src/field-set.h       | 12 ++++++------
 src/gc-finalizer.c    |  5 ++---
 src/mmc.c             | 28 +++++++++++++++-------------
 src/nofl-space.h      |  2 +-
 src/parallel-tracer.h |  8 ++++----
 src/pcc.c             | 10 ++++++----
 src/semi.c            |  5 ++++-
 src/serial-tracer.h   |  2 +-
 10 files changed, 57 insertions(+), 34 deletions(-)

diff --git a/api/gc-config.h b/api/gc-config.h
index 91dd555e2..ca1b38d14 100644
--- a/api/gc-config.h
+++ b/api/gc-config.h
@@ -5,6 +5,10 @@
 #define GC_DEBUG 0
 #endif
 
+#ifndef GC_HAS_IMMEDIATES
+#define GC_HAS_IMMEDIATES 1
+#endif
+
 #ifndef GC_PARALLEL
 #define GC_PARALLEL 0
 #endif
diff --git a/api/gc-ref.h b/api/gc-ref.h
index 33ac5e73b..29e1a3853 100644
--- a/api/gc-ref.h
+++ b/api/gc-ref.h
@@ -2,6 +2,7 @@
 #define GC_REF_H
 
 #include "gc-assert.h"
+#include "gc-config.h"
 
 #include <stdint.h>
 
@@ -19,8 +20,20 @@ static inline uintptr_t gc_ref_value(struct gc_ref ref) {
 static inline struct gc_ref gc_ref_null(void) {
   return gc_ref(0);
 }
+static inline int gc_ref_is_null(struct gc_ref ref) {
+  return ref.value == 0;
+}
+static inline int gc_ref_is_immediate(struct gc_ref ref) {
+  GC_ASSERT(!gc_ref_is_null(ref));
+  return GC_HAS_IMMEDIATES && (ref.value & (sizeof(void*) - 1));
+}
+static inline struct gc_ref gc_ref_immediate(uintptr_t val) {
+  GC_ASSERT(val & (sizeof(void*) - 1));
+  GC_ASSERT(GC_HAS_IMMEDIATES);
+  return gc_ref(val);
+}
 static inline int gc_ref_is_heap_object(struct gc_ref ref) {
-  return ref.value != 0;
+  return !gc_ref_is_immediate(ref);
 }
 static inline struct gc_ref gc_ref_from_heap_object_or_null(void *obj) {
   return gc_ref((uintptr_t) obj);
diff --git a/src/field-set.h b/src/field-set.h
index c7ddffd08..2c93232c1 100644
--- a/src/field-set.h
+++ b/src/field-set.h
@@ -162,15 +162,15 @@ gc_field_set_clear(struct gc_field_set *set,
 }
 
 static inline void
-gc_field_set_trace_edge_buffer(struct gc_field_set *set,
+gc_field_set_visit_edge_buffer(struct gc_field_set *set,
                                struct gc_edge_buffer *buf,
-                               void (*tracer_visit)(struct gc_edge,
-                                                    struct gc_heap*,
-                                                    void *data),
+                               void (*visit)(struct gc_edge,
+                                             struct gc_heap*,
+                                             void *data),
                                struct gc_heap *heap,
-                               struct gc_trace_worker *worker) {
+                               void *data) {
   for (size_t i = 0; i < buf->size; i++)
-    tracer_visit(buf->edges[i], heap, worker);
+    visit(buf->edges[i], heap, data);
 }
 
 static void
diff --git a/src/gc-finalizer.c b/src/gc-finalizer.c
index c0e5831bf..5365899fc 100644
--- a/src/gc-finalizer.c
+++ b/src/gc-finalizer.c
@@ -164,8 +164,7 @@ void gc_finalizer_init_internal(struct gc_finalizer *f,
   // value.
   if (f->state != FINALIZER_STATE_INIT)
     GC_CRASH();
-  if (gc_ref_is_heap_object(f->object))
-    GC_CRASH();
+  GC_ASSERT(gc_ref_is_null(f->object));
   f->object = object;
   f->closure = closure;
 }
@@ -179,7 +178,7 @@ void gc_finalizer_attach_internal(struct gc_finalizer_state 
*state,
   // value.
   if (f->state != FINALIZER_STATE_INIT)
     GC_CRASH();
-  if (!gc_ref_is_heap_object(f->object))
+  if (gc_ref_is_null(f->object))
     GC_CRASH();
 
   f->state = FINALIZER_STATE_ACTIVE;
diff --git a/src/mmc.c b/src/mmc.c
index b1f4238a7..e5d5a95be 100644
--- a/src/mmc.c
+++ b/src/mmc.c
@@ -121,8 +121,6 @@ gc_trace_worker_call_with_data(void (*f)(struct gc_tracer 
*tracer,
 static inline int
 do_trace(struct gc_heap *heap, struct gc_edge edge, struct gc_ref ref,
          struct gc_trace_worker_data *data) {
-  if (!gc_ref_is_heap_object(ref))
-    return 0;
   if (GC_LIKELY(nofl_space_contains(heap_nofl_space(heap), ref)))
     return nofl_space_evacuate_or_mark_object(heap_nofl_space(heap), edge, ref,
                                               &data->allocator);
@@ -137,6 +135,9 @@ static inline int
 trace_edge(struct gc_heap *heap, struct gc_edge edge,
            struct gc_trace_worker_data *data) {
   struct gc_ref ref = gc_edge_ref(edge);
+  if (gc_ref_is_null(ref) || gc_ref_is_immediate(ref))
+    return 0;
+
   int is_new = do_trace(heap, edge, ref, data);
 
   if (is_new &&
@@ -150,9 +151,10 @@ trace_edge(struct gc_heap *heap, struct gc_edge edge,
 int
 gc_visit_ephemeron_key(struct gc_edge edge, struct gc_heap *heap) {
   struct gc_ref ref = gc_edge_ref(edge);
-  if (!gc_ref_is_heap_object(ref))
-    return 0;
-
+  GC_ASSERT(!gc_ref_is_null(ref));
+  if (gc_ref_is_immediate(ref))
+    return 1;
+  GC_ASSERT(gc_ref_is_heap_object(ref));
   struct nofl_space *nofl_space = heap_nofl_space(heap);
   if (GC_LIKELY(nofl_space_contains(nofl_space, ref)))
     return nofl_space_forward_or_mark_if_traced(nofl_space, edge, ref);
@@ -271,11 +273,11 @@ static inline struct gc_ref
 trace_conservative_ref(struct gc_heap *heap, struct gc_conservative_ref ref,
                        int possibly_interior) {
   struct gc_ref ret = do_trace_conservative_ref(heap, ref, possibly_interior);
-
-  if (gc_ref_is_heap_object(ret) &&
-      GC_UNLIKELY(atomic_load_explicit(&heap->check_pending_ephemerons,
-                                       memory_order_relaxed)))
-    gc_resolve_pending_ephemerons(ret, heap);
+  if (!gc_ref_is_null(ret)) {
+    if (GC_UNLIKELY(atomic_load_explicit(&heap->check_pending_ephemerons,
+                                         memory_order_relaxed)))
+      gc_resolve_pending_ephemerons(ret, heap);
+  }
 
   return ret;
 }
@@ -286,7 +288,7 @@ tracer_trace_conservative_ref(struct gc_conservative_ref 
ref,
                               struct gc_trace_worker *worker,
                               int possibly_interior) {
   struct gc_ref resolved = trace_conservative_ref(heap, ref, 
possibly_interior);
-  if (gc_ref_is_heap_object(resolved))
+  if (!gc_ref_is_null(resolved))
     gc_trace_worker_enqueue(worker, resolved);
 }
 
@@ -367,7 +369,7 @@ trace_root(struct gc_root root, struct gc_heap *heap,
     tracer_visit(root.edge, heap, worker);
     break;
   case GC_ROOT_KIND_EDGE_BUFFER:
-    gc_field_set_trace_edge_buffer(&heap->remembered_set, root.edge_buffer,
+    gc_field_set_visit_edge_buffer(&heap->remembered_set, root.edge_buffer,
                                    tracer_visit, heap, worker);
     break;
   default:
@@ -910,7 +912,7 @@ void
 gc_write_barrier_slow(struct gc_mutator *mut, struct gc_ref obj,
                       size_t obj_size, struct gc_edge edge,
                       struct gc_ref new_val) {
-  GC_ASSERT(gc_ref_is_heap_object(new_val));
+  GC_ASSERT(!gc_ref_is_null(new_val));
   if (!GC_GENERATIONAL) return;
   if (gc_object_is_old_generation_slow(mut, new_val))
     return;
diff --git a/src/nofl-space.h b/src/nofl-space.h
index 9759b3d8e..bc5144205 100644
--- a/src/nofl-space.h
+++ b/src/nofl-space.h
@@ -1483,7 +1483,7 @@ nofl_space_evacuate(struct nofl_space *space, uint8_t 
*metadata, uint8_t byte,
     size_t object_granules = nofl_space_live_object_granules(metadata);
     struct gc_ref new_ref = nofl_evacuation_allocate(evacuate, space,
                                                      object_granules);
-    if (gc_ref_is_heap_object(new_ref)) {
+    if (!gc_ref_is_null(new_ref)) {
       // Copy object contents before committing, as we don't know what
       // part of the object (if any) will be overwritten by the
       // commit.
diff --git a/src/parallel-tracer.h b/src/parallel-tracer.h
index 20d66730f..8115c369d 100644
--- a/src/parallel-tracer.h
+++ b/src/parallel-tracer.h
@@ -207,7 +207,7 @@ trace_worker_steal_from_any(struct gc_trace_worker *worker,
   for (size_t i = 0; i < tracer->worker_count; i++) {
     LOG("tracer #%zu: stealing from #%zu\n", worker->id, worker->steal_id);
     struct gc_ref obj = tracer_steal_from_worker(tracer, worker->steal_id);
-    if (gc_ref_is_heap_object(obj)) {
+    if (!gc_ref_is_null(obj)) {
       LOG("tracer #%zu: stealing got %p\n", worker->id,
             gc_ref_heap_object(obj));
       return obj;
@@ -281,13 +281,13 @@ trace_worker_steal(struct gc_trace_worker *worker) {
   {
     LOG("tracer #%zu: trying to pop worker's own deque\n", worker->id);
     struct gc_ref obj = shared_worklist_try_pop(&worker->shared);
-    if (gc_ref_is_heap_object(obj))
+    if (!gc_ref_is_null(obj))
       return obj;
   }
 
   LOG("tracer #%zu: trying to steal\n", worker->id);
   struct gc_ref obj = trace_worker_steal_from_any(worker, tracer);
-  if (gc_ref_is_heap_object(obj))
+  if (!gc_ref_is_null(obj))
     return obj;
 
   return gc_ref_null();
@@ -337,7 +337,7 @@ trace_with_data(struct gc_tracer *tracer,
           ref = local_worklist_pop(&worker->local);
         } else {
           ref = trace_worker_steal(worker);
-          if (!gc_ref_is_heap_object(ref))
+          if (gc_ref_is_null(ref))
             break;
         }
         trace_one(ref, heap, worker);
diff --git a/src/pcc.c b/src/pcc.c
index 593f43fc6..6b8a55c03 100644
--- a/src/pcc.c
+++ b/src/pcc.c
@@ -103,8 +103,6 @@ gc_trace_worker_call_with_data(void (*f)(struct gc_tracer 
*tracer,
 static inline int do_trace(struct gc_heap *heap, struct gc_edge edge,
                            struct gc_ref ref,
                            struct gc_trace_worker_data *data) {
-  if (!gc_ref_is_heap_object(ref))
-    return 0;
   if (GC_LIKELY(copy_space_contains(heap_copy_space(heap), ref)))
     return copy_space_forward(heap_copy_space(heap), edge, ref,
                               &data->allocator);
@@ -117,6 +115,8 @@ static inline int do_trace(struct gc_heap *heap, struct 
gc_edge edge,
 static inline int trace_edge(struct gc_heap *heap, struct gc_edge edge,
                              struct gc_trace_worker *worker) {
   struct gc_ref ref = gc_edge_ref(edge);
+  if (gc_ref_is_null(ref) || gc_ref_is_immediate(ref))
+    return 0;
   struct gc_trace_worker_data *data = gc_trace_worker_data(worker);
   int is_new = do_trace(heap, edge, ref, data);
 
@@ -130,8 +130,10 @@ static inline int trace_edge(struct gc_heap *heap, struct 
gc_edge edge,
 
 int gc_visit_ephemeron_key(struct gc_edge edge, struct gc_heap *heap) {
   struct gc_ref ref = gc_edge_ref(edge);
-  if (!gc_ref_is_heap_object(ref))
-    return 0;
+  GC_ASSERT(!gc_ref_is_null(ref));
+  if (gc_ref_is_immediate(ref))
+    return 1;
+  GC_ASSERT(gc_ref_is_heap_object(ref));
   if (GC_LIKELY(copy_space_contains(heap_copy_space(heap), ref)))
     return copy_space_forward_if_traced(heap_copy_space(heap), edge, ref);
   if (large_object_space_contains(heap_large_object_space(heap), ref))
diff --git a/src/semi.c b/src/semi.c
index ca7a31607..239367999 100644
--- a/src/semi.c
+++ b/src/semi.c
@@ -233,7 +233,7 @@ static void visit_external_object(struct gc_heap *heap,
 
 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))
+  if (gc_ref_is_null(ref) || gc_ref_is_immediate(ref))
     return;
   if (semi_space_contains(heap_semi_space(heap), ref))
     visit_semi_space(heap, heap_semi_space(heap), edge, ref);
@@ -250,6 +250,9 @@ gc_heap_pending_ephemerons(struct gc_heap *heap) {
 
 int gc_visit_ephemeron_key(struct gc_edge edge, struct gc_heap *heap) {
   struct gc_ref ref = gc_edge_ref(edge);
+  GC_ASSERT(!gc_ref_is_null(ref));
+  if (gc_ref_is_immediate(ref))
+    return 1;
   GC_ASSERT(gc_ref_is_heap_object(ref));
   if (semi_space_contains(heap_semi_space(heap), ref)) {
     uintptr_t forwarded = gc_object_forwarded_nonatomic(ref);
diff --git a/src/serial-tracer.h b/src/serial-tracer.h
index b9575fddb..a3289e30c 100644
--- a/src/serial-tracer.h
+++ b/src/serial-tracer.h
@@ -65,7 +65,7 @@ tracer_trace_with_data(struct gc_tracer *tracer, struct 
gc_heap *heap,
   if (!tracer->trace_roots_only) {
     do {
       struct gc_ref obj = simple_worklist_pop(&tracer->worklist);
-      if (!gc_ref_is_heap_object(obj))
+      if (gc_ref_is_null(obj))
         break;
       trace_one(obj, heap, worker);
     } while (1);

Reply via email to