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

commit b082f5f50d64edf11918f3d3bcb27e1e06bc3586
Author: Andy Wingo <wi...@igalia.com>
AuthorDate: Tue Aug 16 17:54:15 2022 +0200

    Separate compilation!!!!!
---
 Makefile                               | 47 +++++++++++++++--------
 bdw-attrs.h                            | 13 ++++---
 bdw.h => bdw.c                         | 56 ++++++++++++++++------------
 conservative-roots-api.h               | 12 ++++++
 conservative-roots-embedder.h          | 21 +++++++++++
 conservative-roots-types.h             |  8 ++++
 conservative-roots.h                   | 21 -----------
 gc-api.h                               |  8 ++--
 gc-assert.h                            |  6 ++-
 gc-attrs.h                             | 39 +++++++++++++++++++
 gc-config.h                            | 12 ++++++
 gc-embedder-api.h                      |  4 ++
 gc-visibility.h                        |  7 ++++
 mt-gcbench-embedder.h                  |  2 +-
 mt-gcbench-types.h                     |  3 ++
 mt-gcbench.c                           | 16 ++++----
 parallel-tracer.h                      |  4 +-
 precise-roots.h => precise-roots-api.h | 16 +++-----
 precise-roots-embedder.h               | 31 ++++++++++++++++
 precise-roots-types.h                  | 17 +++++++++
 quads-embedder.h                       |  2 +
 quads.c                                | 11 ++++--
 semi-attrs.h                           | 11 +++---
 semi.h => semi.c                       | 60 +++++++++++++++++-------------
 serial-tracer.h                        |  4 +-
 simple-gc-embedder.h                   | 31 +++-------------
 whippet-attrs.h                        |  3 +-
 whippet.h => whippet.c                 | 68 +++++++++++++++++-----------------
 28 files changed, 344 insertions(+), 189 deletions(-)

diff --git a/Makefile b/Makefile
index 3945498c2..9244c246c 100644
--- a/Makefile
+++ b/Makefile
@@ -2,32 +2,49 @@ TESTS=quads mt-gcbench # MT_GCBench MT_GCBench2
 COLLECTORS=bdw semi whippet parallel-whippet generational-whippet 
parallel-generational-whippet
 
 CC=gcc
-CFLAGS=-Wall -O2 -g -fno-strict-aliasing -fvisibility=hidden -Wno-unused 
-DNDEBUG
+CFLAGS=-Wall -O2 -g -flto -fno-strict-aliasing -fvisibility=hidden -Wno-unused 
-DNDEBUG
 INCLUDES=-I.
-LDFLAGS=-lpthread
-COMPILE=$(CC) $(CFLAGS) $(INCLUDES) $(LDFLAGS)
+LDFLAGS=-lpthread -flto
+COMPILE=$(CC) $(CFLAGS) $(INCLUDES)
 
 ALL_TESTS=$(foreach COLLECTOR,$(COLLECTORS),$(addprefix 
$(COLLECTOR)-,$(TESTS)))
 
 all: $(ALL_TESTS)
 
-bdw-%: bdw.h conservative-roots.h %-types.h %.c
-       $(COMPILE) `pkg-config --libs --cflags bdw-gc` -DGC_BDW -o $@ $*.c
+bdw-%-gc.o: semi.c %-embedder.h %.c
+       $(COMPILE) `pkg-config --cflags bdw-gc` -include $*-embedder.h -o $@ -c 
bdw.c
+bdw-%.o: semi.c %.c
+       $(COMPILE) -include bdw-attrs.h -o $@ -c $*.c
+bdw-%: bdw-%.o bdw-%-gc.o
+       $(CC) $(LDFLAGS) `pkg-config --libs bdw-gc` -o $@ $^
 
-semi-%: semi.h precise-roots.h large-object-space.h %-types.h heap-objects.h 
%.c
-       $(COMPILE) -DGC_SEMI -o $@ $*.c
+semi-%-gc.o: semi.c %-embedder.h large-object-space.h assert.h debug.h %.c
+       $(COMPILE) -DGC_PRECISE=1 -include $*-embedder.h -o $@ -c semi.c
+semi-%.o: semi.c %.c
+       $(COMPILE) -DGC_PRECISE=1 -include semi-attrs.h -o $@ -c $*.c
 
-whippet-%: whippet.h precise-roots.h large-object-space.h serial-tracer.h 
assert.h debug.h %-types.h heap-objects.h %.c
-       $(COMPILE) -DGC_WHIPPET -o $@ $*.c
+whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h serial-tracer.h 
assert.h debug.h heap-objects.h %.c
+       $(COMPILE) -DGC_PRECISE=1 -include $*-embedder.h -o $@ -c whippet.c
+whippet-%.o: whippet.c %.c
+       $(COMPILE) -DGC_PRECISE=1 -include whippet-attrs.h -o $@ -c $*.c
 
-parallel-whippet-%: whippet.h precise-roots.h large-object-space.h 
parallel-tracer.h assert.h debug.h %-types.h heap-objects.h %.c
-       $(COMPILE) -DGC_PARALLEL_WHIPPET -o $@ $*.c
+parallel-whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h 
parallel-tracer.h assert.h debug.h heap-objects.h %.c
+       $(COMPILE) -DGC_PARALLEL=1 -DGC_PRECISE=1 -include $*-embedder.h -o $@ 
-c whippet.c
+parallel-whippet-%.o: whippet.c %.c
+       $(COMPILE) -DGC_PARALLEL=1 -DGC_PRECISE=1 -include whippet-attrs.h -o 
$@ -c $*.c
 
-generational-whippet-%: whippet.h precise-roots.h large-object-space.h 
serial-tracer.h assert.h debug.h %-types.h heap-objects.h %.c
-       $(COMPILE) -DGC_GENERATIONAL_WHIPPET -o $@ $*.c
+generational-whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h 
serial-tracer.h assert.h debug.h heap-objects.h %.c
+       $(COMPILE) -DGC_GENERATIONAL=1 -DGC_PRECISE=1 -include $*-embedder.h -o 
$@ -c whippet.c
+generational-whippet-%.o: whippet.c %.c
+       $(COMPILE) -DGC_GENERATIONAL=1 -DGC_PRECISE=1 -include whippet-attrs.h 
-o $@ -c $*.c
 
-parallel-generational-whippet-%: whippet.h precise-roots.h 
large-object-space.h parallel-tracer.h assert.h debug.h %-types.h 
heap-objects.h %.c
-       $(COMPILE) -DGC_PARALLEL_GENERATIONAL_WHIPPET -o $@ $*.c
+parallel-generational-whippet-%-gc.o: whippet.c %-embedder.h 
large-object-space.h parallel-tracer.h assert.h debug.h heap-objects.h %.c
+       $(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_PRECISE=1 -include 
$*-embedder.h -o $@ -c whippet.c
+parallel-generational-whippet-%.o: whippet.c %.c
+       $(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_PRECISE=1 -include 
whippet-attrs.h -o $@ -c $*.c
+
+%: %.o %-gc.o
+       $(CC) $(LDFLAGS) $($*_LDFLAGS) -o $@ $^
 
 check: $(addprefix test-$(TARGET),$(TARGETS))
 
diff --git a/bdw-attrs.h b/bdw-attrs.h
index 5743cd3a2..960e543b0 100644
--- a/bdw-attrs.h
+++ b/bdw-attrs.h
@@ -2,6 +2,7 @@
 #define BDW_ATTRS_H
 
 #include "gc-attrs.h"
+#include "gc-assert.h"
 
 static inline enum gc_allocator_kind gc_allocator_kind(void) {
   return GC_ALLOCATOR_INLINE_FREELIST;
@@ -14,10 +15,10 @@ static inline size_t gc_allocator_large_threshold(void) {
 }
 
 static inline size_t gc_allocator_allocation_pointer_offset(void) {
-  abort();
+  GC_CRASH();
 }
 static inline size_t gc_allocator_allocation_limit_offset(void) {
-  abort();
+  GC_CRASH();
 }
 
 static inline size_t gc_allocator_freelist_offset(size_t size) {
@@ -29,10 +30,10 @@ static inline size_t 
gc_allocator_alloc_table_alignment(void) {
   return 0;
 }
 static inline uint8_t gc_allocator_alloc_table_begin_pattern(void) {
-  abort();
+  GC_CRASH();
 }
 static inline uint8_t gc_allocator_alloc_table_end_pattern(void) {
-  abort();
+  GC_CRASH();
 }
 
 static inline int gc_allocator_needs_clear(void) {
@@ -43,10 +44,10 @@ static inline enum gc_write_barrier_kind 
gc_small_write_barrier_kind(void) {
   return GC_WRITE_BARRIER_NONE;
 }
 static inline size_t gc_small_write_barrier_card_table_alignment(void) {
-  abort();
+  GC_CRASH();
 }
 static inline size_t gc_small_write_barrier_card_size(void) {
-  abort();
+  GC_CRASH();
 }
 
 #endif // BDW_ATTRS_H
diff --git a/bdw.h b/bdw.c
similarity index 84%
rename from bdw.h
rename to bdw.c
index 0af332090..7f3ec186b 100644
--- a/bdw.h
+++ b/bdw.c
@@ -1,8 +1,18 @@
 #include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 
+#define GC_API_ 
+#include "gc-api.h"
+
 #include "bdw-attrs.h"
-#include "conservative-roots.h"
+
+#if GC_PRECISE
+#error bdw-gc is a conservative collector
+#else
+#include "conservative-roots-embedder.h"
+#endif
 
 // When pthreads are used, let `libgc' know about it and redirect
 // allocation calls such as `GC_MALLOC ()' to (contention-free, faster)
@@ -61,10 +71,10 @@ static void* allocate_small_slow(void **freelist, size_t 
idx,
   size_t bytes = gc_inline_freelist_object_size(idx);
   GC_generic_malloc_many(bytes, kind, freelist);
   void *head = *freelist;
-  if (UNLIKELY (!head)) {
+  if (GC_UNLIKELY (!head)) {
     fprintf(stderr, "ran out of space, heap size %zu\n",
             GC_get_heap_size());
-    abort();
+    GC_CRASH();
   }
   *freelist = *(void **)(head);
   return head;
@@ -74,25 +84,25 @@ static inline void *
 allocate_small(void **freelist, size_t idx, enum gc_inline_kind kind) {
   void *head = *freelist;
 
-  if (UNLIKELY (!head))
+  if (GC_UNLIKELY (!head))
     return allocate_small_slow(freelist, idx, kind);
 
   *freelist = *(void **)(head);
   return head;
 }
 
-static void* gc_allocate_large(struct mutator *mut, size_t size) {
+void* gc_allocate_large(struct mutator *mut, size_t size) {
   return GC_malloc(size);
 }
 
-static void* gc_allocate_small(struct mutator *mut, size_t size) {
+void* gc_allocate_small(struct mutator *mut, size_t size) {
   GC_ASSERT(size != 0);
   GC_ASSERT(size <= gc_allocator_large_threshold());
   size_t idx = gc_inline_bytes_to_freelist_index(size);
   return allocate_small(&mut->freelists[idx], idx, GC_INLINE_KIND_NORMAL);
 }
 
-static inline void* gc_allocate_pointerless(struct mutator *mut,
+void* gc_allocate_pointerless(struct mutator *mut,
                                             size_t size) {
   // Because the BDW API requires us to implement a custom marker so
   // that the pointerless freelist gets traced, even though it's in a
@@ -126,7 +136,7 @@ static void dump_available_gc_options(void) {
   fprintf(stderr, "\n");
 }
 
-static int gc_option_from_string(const char *str) {
+int gc_option_from_string(const char *str) {
 #define PARSE_OPTION(option, name) if (strcmp(str, name) == 0) return option;
   FOR_EACH_GC_OPTION(PARSE_OPTION)
 #undef PARSE_OPTION
@@ -145,8 +155,8 @@ struct options {
 };
 
 static size_t parse_size_t(double value) {
-  ASSERT(value >= 0);
-  ASSERT(value <= (size_t) -1);
+  GC_ASSERT(value >= 0);
+  GC_ASSERT(value <= (size_t) -1);
   return value;
 }
 
@@ -163,7 +173,7 @@ static int parse_options(int argc, struct gc_option argv[],
       options->parallelism = parse_size_t(argv[i].value);
       break;
     default:
-      abort();
+      GC_CRASH();
     }
   }
 
@@ -177,8 +187,8 @@ static int parse_options(int argc, struct gc_option argv[],
   return 1;
 }
 
-static int gc_init(int argc, struct gc_option argv[],
-                   struct heap **heap, struct mutator **mutator) {
+int gc_init(int argc, struct gc_option argv[],
+            struct heap **heap, struct mutator **mutator) {
   GC_ASSERT_EQ(gc_allocator_small_granule_size(), GC_INLINE_GRANULE_BYTES);
   GC_ASSERT_EQ(gc_allocator_large_threshold(),
                GC_INLINE_FREELIST_COUNT * GC_INLINE_GRANULE_BYTES);
@@ -208,8 +218,8 @@ static int gc_init(int argc, struct gc_option argv[],
   return 1;
 }
 
-static struct mutator* gc_init_for_thread(uintptr_t *stack_base,
-                                          struct heap *heap) {
+struct mutator* gc_init_for_thread(uintptr_t *stack_base,
+                                   struct heap *heap) {
   pthread_mutex_lock(&heap->lock);
   if (!heap->multithreaded) {
     GC_allow_register_threads();
@@ -221,23 +231,23 @@ static struct mutator* gc_init_for_thread(uintptr_t 
*stack_base,
   GC_register_my_thread(&base);
   return add_mutator(heap);
 }
-static void gc_finish_for_thread(struct mutator *mut) {
+void gc_finish_for_thread(struct mutator *mut) {
   GC_unregister_my_thread();
 }
 
-static void* gc_call_without_gc(struct mutator *mut,
-                                void* (*f)(void*),
-                                void *data) {
+void* gc_call_without_gc(struct mutator *mut,
+                         void* (*f)(void*),
+                         void *data) {
   return GC_do_blocking(f, data);
 }
 
-static void gc_mutator_set_roots(struct mutator *mut,
-                                 struct gc_mutator_roots *roots) {
+void gc_mutator_set_roots(struct mutator *mut,
+                          struct gc_mutator_roots *roots) {
 }
-static void gc_heap_set_roots(struct heap *heap, struct gc_heap_roots *roots) {
+void gc_heap_set_roots(struct heap *heap, struct gc_heap_roots *roots) {
 }
 
-static void gc_print_stats(struct heap *heap) {
+void gc_print_stats(struct heap *heap) {
   printf("Completed %ld collections\n", (long)GC_get_gc_no());
   printf("Heap size is %ld\n", (long)GC_get_heap_size());
 }
diff --git a/conservative-roots-api.h b/conservative-roots-api.h
new file mode 100644
index 000000000..1619cf640
--- /dev/null
+++ b/conservative-roots-api.h
@@ -0,0 +1,12 @@
+#ifndef CONSERVATIVE_ROOTS_API_H
+#define CONSERVATIVE_ROOTS_API_H
+
+#include "conservative-roots-types.h"
+
+#define HANDLE_TO(T) union { T* v; struct handle handle; }
+#define HANDLE_REF(h) h.v
+#define HANDLE_SET(h,val) do { h.v = val; } while (0)
+#define PUSH_HANDLE(cx, h) do { (void) &h; } while (0)
+#define POP_HANDLE(cx) do { } while (0)
+
+#endif // CONSERVATIVE_ROOTS_API_H
diff --git a/conservative-roots-embedder.h b/conservative-roots-embedder.h
new file mode 100644
index 000000000..ae7120010
--- /dev/null
+++ b/conservative-roots-embedder.h
@@ -0,0 +1,21 @@
+#ifndef CONSERVATIVE_ROOTS_EMBEDDER_H
+#define CONSERVATIVE_ROOTS_EMBEDDER_H
+
+#include "gc-assert.h"
+#include "conservative-roots-types.h"
+
+static inline void gc_trace_mutator_roots(struct gc_mutator_roots *roots,
+                                          void (*trace_edge)(struct gc_edge 
edge,
+                                                             void *trace_data),
+                                          void *trace_data) {
+  GC_CRASH();
+}
+
+static inline void gc_trace_heap_roots(struct gc_heap_roots *roots,
+                                       void (*trace_edge)(struct gc_edge edge,
+                                                          void *trace_data),
+                                       void *trace_data) {
+  GC_CRASH();
+}
+
+#endif // CONSERVATIVE_ROOTS_EMBEDDER_H
diff --git a/conservative-roots-types.h b/conservative-roots-types.h
new file mode 100644
index 000000000..4744d746e
--- /dev/null
+++ b/conservative-roots-types.h
@@ -0,0 +1,8 @@
+#ifndef CONSERVATIVE_ROOTS_TYPES_H
+#define CONSERVATIVE_ROOTS_TYPES_H
+
+struct handle { void *unused; };
+struct gc_heap_roots { void *unused; };
+struct gc_mutator_roots { void *unused; };
+
+#endif // CONSERVATIVE_ROOTS_TYPES_H
diff --git a/conservative-roots.h b/conservative-roots.h
deleted file mode 100644
index 23fc38de7..000000000
--- a/conservative-roots.h
+++ /dev/null
@@ -1,21 +0,0 @@
-struct handle { void *unused; };
-
-#define HANDLE_TO(T) union { T* v; struct handle handle; }
-#define HANDLE_REF(h) h.v
-#define HANDLE_SET(h,val) do { h.v = val; } while (0)
-#define PUSH_HANDLE(cx, h) do { (void) &h; } while (0)
-#define POP_HANDLE(cx) do { } while (0)
-
-static inline void visit_thread_roots(void *thread_roots,
-                                      void (*trace_edge)(struct gc_edge edge,
-                                                         void *trace_data),
-                                      void *trace_data) {
-  abort();
-}
-
-static inline void visit_roots(struct handle *roots,
-                               void (*trace_edge)(struct gc_edge edge,
-                                                  void *trace_data),
-                               void *trace_data) {
-  GC_ASSERT(!roots);
-}
diff --git a/gc-api.h b/gc-api.h
index c7d332176..c00881f4f 100644
--- a/gc-api.h
+++ b/gc-api.h
@@ -32,7 +32,7 @@ struct gc_mutator {
 
 // FIXME: Conflict with bdw-gc GC_API.  Switch prefix?
 #ifndef GC_API_
-#define GC_API_ static
+#define GC_API_ __attribute__((visibility("hidden")))
 #endif
 
 GC_API_ int gc_option_from_string(const char *str);
@@ -159,12 +159,12 @@ static inline void* gc_allocate(struct mutator *mut, 
size_t size) {
   case GC_ALLOCATOR_INLINE_NONE:
     return gc_allocate_small(mut, size);
   default:
-    abort();
+    GC_CRASH();
   }
 }
 
 // FIXME: remove :P
-static inline void* gc_allocate_pointerless(struct mutator *mut, size_t bytes);
+GC_API_ void* gc_allocate_pointerless(struct mutator *mut, size_t bytes);
 
 static inline void gc_small_write_barrier(struct gc_ref obj, struct gc_edge 
edge,
                                           struct gc_ref new_val) 
GC_ALWAYS_INLINE;
@@ -183,7 +183,7 @@ static inline void gc_small_write_barrier(struct gc_ref 
obj, struct gc_edge edge
     return;
   }
   default:
-    abort();
+    GC_CRASH();
   }
 }
 
diff --git a/gc-assert.h b/gc-assert.h
index dc39bee25..c3fa6b749 100644
--- a/gc-assert.h
+++ b/gc-assert.h
@@ -6,10 +6,14 @@
 #define GC_UNLIKELY(e) __builtin_expect(e, 0)
 #define GC_LIKELY(e) __builtin_expect(e, 1)
 
+#define GC_CRASH() __builtin_trap()
+
 #if GC_DEBUG
-#define GC_ASSERT(x) do { if (GC_UNLIKELY(!(x))) __builtin_trap(); } while (0)
+#define GC_ASSERT(x) do { if (GC_UNLIKELY(!(x))) GC_CRASH(); } while (0)
+#define GC_UNREACHABLE() GC_CRASH()
 #else
 #define GC_ASSERT(x) do { } while (0)
+#define GC_UNREACHABLE() __builtin_unreachable()
 #endif
 
 #define GC_ASSERT_EQ(a, b) GC_ASSERT((a) == (b))
diff --git a/gc-attrs.h b/gc-attrs.h
new file mode 100644
index 000000000..17ff2add5
--- /dev/null
+++ b/gc-attrs.h
@@ -0,0 +1,39 @@
+#ifndef GC_ATTRS_H
+#define GC_ATTRS_H
+
+#include "gc-inline.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+enum gc_allocator_kind {
+  GC_ALLOCATOR_INLINE_BUMP_POINTER,
+  GC_ALLOCATOR_INLINE_FREELIST,
+  GC_ALLOCATOR_INLINE_NONE
+};
+
+static inline enum gc_allocator_kind gc_allocator_kind(void) GC_ALWAYS_INLINE;
+static inline size_t gc_allocator_large_threshold(void) GC_ALWAYS_INLINE;
+static inline size_t gc_allocator_small_granule_size(void) GC_ALWAYS_INLINE;
+
+static inline size_t gc_allocator_allocation_pointer_offset(void) 
GC_ALWAYS_INLINE;
+static inline size_t gc_allocator_allocation_limit_offset(void) 
GC_ALWAYS_INLINE;
+
+static inline size_t gc_allocator_freelist_offset(size_t size) 
GC_ALWAYS_INLINE;
+
+static inline size_t gc_allocator_alloc_table_alignment(void) GC_ALWAYS_INLINE;
+static inline uint8_t gc_allocator_alloc_table_begin_pattern(void) 
GC_ALWAYS_INLINE;
+static inline uint8_t gc_allocator_alloc_table_end_pattern(void) 
GC_ALWAYS_INLINE;
+
+static inline int gc_allocator_needs_clear(void) GC_ALWAYS_INLINE;
+
+enum gc_write_barrier_kind {
+  GC_WRITE_BARRIER_NONE,
+  GC_WRITE_BARRIER_CARD
+};
+
+static inline enum gc_write_barrier_kind gc_small_write_barrier_kind(void) 
GC_ALWAYS_INLINE;
+static inline size_t gc_small_write_barrier_card_table_alignment(void) 
GC_ALWAYS_INLINE;
+static inline size_t gc_small_write_barrier_card_size(void) GC_ALWAYS_INLINE;
+
+#endif // GC_ATTRS_H
diff --git a/gc-config.h b/gc-config.h
index cd78e23d5..5fc27b7e5 100644
--- a/gc-config.h
+++ b/gc-config.h
@@ -5,4 +5,16 @@
 #define GC_DEBUG 0
 #endif
 
+#ifndef GC_PARALLEL
+#define GC_PARALLEL 0
+#endif
+
+#ifndef GC_GENERATIONAL
+#define GC_GENERATIONAL 0
+#endif
+
+#ifndef GC_PRECISE
+#define GC_PRECISE 0
+#endif
+
 #endif // GC_CONFIG_H
diff --git a/gc-embedder-api.h b/gc-embedder-api.h
index b74bd0486..26929b72e 100644
--- a/gc-embedder-api.h
+++ b/gc-embedder-api.h
@@ -8,6 +8,10 @@
 #define GC_EMBEDDER_API static
 #endif
 
+struct gc_mutator_roots;
+struct gc_heap_roots;
+struct gc_atomic_forward;
+
 GC_EMBEDDER_API inline void gc_trace_object(void *object,
                                             void (*trace_edge)(struct gc_edge 
edge,
                                                                void 
*trace_data),
diff --git a/gc-visibility.h b/gc-visibility.h
new file mode 100644
index 000000000..7360915a0
--- /dev/null
+++ b/gc-visibility.h
@@ -0,0 +1,7 @@
+#ifndef GC_VISIBILITY_H_
+#define GC_VISIBILITY_H_
+
+#define GC_INTERNAL __attribute__((visibility("hidden")))
+#define GC_PUBLIC __attribute__((visibility("default")))
+
+#endif // GC_VISIBILITY_H
diff --git a/mt-gcbench-embedder.h b/mt-gcbench-embedder.h
index 63076f474..3aec4808b 100644
--- a/mt-gcbench-embedder.h
+++ b/mt-gcbench-embedder.h
@@ -36,7 +36,7 @@ static inline void
 visit_hole_fields(Hole *obj,
                   void (*visit)(struct gc_edge edge, void *visit_data),
                   void *visit_data) {
-  abort();
+  GC_CRASH();
 }
 
 #include "simple-gc-embedder.h"
diff --git a/mt-gcbench-types.h b/mt-gcbench-types.h
index 32471d025..60bddc489 100644
--- a/mt-gcbench-types.h
+++ b/mt-gcbench-types.h
@@ -1,6 +1,9 @@
 #ifndef GCBENCH_TYPES_H
 #define GCBENCH_TYPES_H
 
+#include <stddef.h>
+#include <stdint.h>
+
 #define FOR_EACH_HEAP_OBJECT_KIND(M) \
   M(node, Node, NODE) \
   M(double_array, DoubleArray, DOUBLE_ARRAY) \
diff --git a/mt-gcbench.c b/mt-gcbench.c
index 4d304c636..2d2012ae1 100644
--- a/mt-gcbench.c
+++ b/mt-gcbench.c
@@ -44,16 +44,16 @@
 #include <stdlib.h>
 #include <sys/time.h>
 
-#include "mt-gcbench-types.h"
-
 #include "assert.h"
-#include "simple-allocator.h"
 #include "gc-api.h"
-
-#include "mt-gcbench-embedder.h"
-#include "gc.h"
-
-#include "gc-inline.h"
+#include "mt-gcbench-types.h"
+#if GC_PRECISE
+#include "precise-roots-api.h"
+#else
+#include "conservative-roots-api.h"
+#endif
+#include "mt-gcbench-types.h"
+#include "simple-allocator.h"
 
 #define MAX_THREAD_COUNT 256
 
diff --git a/parallel-tracer.h b/parallel-tracer.h
index 4ee90de70..07561e215 100644
--- a/parallel-tracer.h
+++ b/parallel-tracer.h
@@ -149,7 +149,7 @@ static int
 trace_deque_grow(struct trace_deque *q, int cur, size_t b, size_t t) {
   if (!trace_buf_grow(&q->bufs[cur], &q->bufs[cur + 1], b, t)) {
     fprintf(stderr, "failed to grow deque!!\n");
-    abort();
+    GC_CRASH();
   }
 
   cur++;
@@ -359,7 +359,7 @@ trace_worker_thread(void *data) {
       pthread_mutex_unlock(&worker->lock);
       return NULL;
     default:
-      abort();
+      GC_CRASH();
     }
   }
 }
diff --git a/precise-roots.h b/precise-roots-api.h
similarity index 54%
rename from precise-roots.h
rename to precise-roots-api.h
index 2eedb60ed..ced560d15 100644
--- a/precise-roots.h
+++ b/precise-roots-api.h
@@ -1,7 +1,7 @@
-struct handle {
-  void *v;
-  struct handle *next;
-};
+#ifndef PRECISE_ROOTS_API_H
+#define PRECISE_ROOTS_API_H
+
+#include "precise-roots-types.h"
 
 #define HANDLE_TO(T) union { T* v; struct handle handle; }
 #define HANDLE_REF(h) h.v
@@ -18,10 +18,4 @@ static inline void pop_handle(struct handle **roots) {
   *roots = (*roots)->next;
 }
 
-static inline void visit_roots(struct handle *roots,
-                               void (*trace_edge)(struct gc_edge edge,
-                                                  void *trace_data),
-                               void *trace_data) {
-  for (struct handle *h = roots; h; h = h->next)
-    trace_edge(gc_edge(&h->v), trace_data);
-}
+#endif // PRECISE_ROOTS_API_H
diff --git a/precise-roots-embedder.h b/precise-roots-embedder.h
new file mode 100644
index 000000000..f37b38e1a
--- /dev/null
+++ b/precise-roots-embedder.h
@@ -0,0 +1,31 @@
+#ifndef PRECISE_ROOTS_EMBEDDER_H
+#define PRECISE_ROOTS_EMBEDDER_H
+
+#include "gc-edge.h"
+#include "precise-roots-types.h"
+
+static inline void visit_roots(struct handle *roots,
+                               void (*trace_edge)(struct gc_edge edge,
+                                                  void *trace_data),
+                               void *trace_data) {
+  for (struct handle *h = roots; h; h = h->next)
+    trace_edge(gc_edge(&h->v), trace_data);
+}
+
+static inline void gc_trace_mutator_roots(struct gc_mutator_roots *roots,
+                                          void (*trace_edge)(struct gc_edge 
edge,
+                                                             void *trace_data),
+                                          void *trace_data) {
+  if (roots)
+    visit_roots(roots->roots, trace_edge, trace_data);
+}
+
+static inline void gc_trace_heap_roots(struct gc_heap_roots *roots,
+                                       void (*trace_edge)(struct gc_edge edge,
+                                                          void *trace_data),
+                                       void *trace_data) {
+  if (roots)
+    visit_roots(roots->roots, trace_edge, trace_data);
+}
+
+#endif // PRECISE_ROOTS_EMBEDDER_H
diff --git a/precise-roots-types.h b/precise-roots-types.h
new file mode 100644
index 000000000..d2dc96491
--- /dev/null
+++ b/precise-roots-types.h
@@ -0,0 +1,17 @@
+#ifndef PRECISE_ROOTS_TYPES_H
+#define PRECISE_ROOTS_TYPES_H
+
+struct handle {
+  void *v;
+  struct handle *next;
+};
+
+struct gc_heap_roots {
+  struct handle *roots;
+};
+
+struct gc_mutator_roots {
+  struct handle *roots;
+};
+
+#endif // PRECISE_ROOTS_TYPES_H
diff --git a/quads-embedder.h b/quads-embedder.h
index 18047607c..714415dd0 100644
--- a/quads-embedder.h
+++ b/quads-embedder.h
@@ -1,6 +1,8 @@
 #ifndef QUADS_EMBEDDER_H
 #define QUADS_EMBEDDER_H
 
+#include <stddef.h>
+
 #include "quads-types.h"
 
 #define DEFINE_METHODS(name, Name, NAME) \
diff --git a/quads.c b/quads.c
index 7797bbd42..7620ff693 100644
--- a/quads.c
+++ b/quads.c
@@ -1,14 +1,17 @@
 #include <stdio.h>
+#include <stddef.h>
 #include <stdlib.h>
 #include <sys/time.h>
 
 #include "assert.h"
+#include "gc-api.h"
+#if GC_PRECISE
+#include "precise-roots-api.h"
+#else
+#include "conservative-roots-api.h"
+#endif
 #include "quads-types.h"
 #include "simple-allocator.h"
-#include "gc-api.h"
-
-#include "quads-embedder.h"
-#include "gc.h"
 
 typedef HANDLE_TO(Quad) QuadHandle;
 
diff --git a/semi-attrs.h b/semi-attrs.h
index 55691f047..e6b429178 100644
--- a/semi-attrs.h
+++ b/semi-attrs.h
@@ -2,6 +2,7 @@
 #define SEMI_ATTRS_H
 
 #include "gc-attrs.h"
+#include "gc-assert.h"
 
 static const uintptr_t GC_ALIGNMENT = 8;
 static const size_t GC_LARGE_OBJECT_THRESHOLD = 8192;
@@ -24,7 +25,7 @@ static inline size_t 
gc_allocator_allocation_limit_offset(void) {
 }
 
 static inline size_t gc_allocator_freelist_offset(size_t size) {
-  abort();
+  GC_CRASH();
 }
 
 static inline int gc_allocator_needs_clear(void) {
@@ -35,20 +36,20 @@ static inline size_t 
gc_allocator_alloc_table_alignment(void) {
   return 0;
 }
 static inline uint8_t gc_allocator_alloc_table_begin_pattern(void) {
-  abort();
+  GC_CRASH();
 }
 static inline uint8_t gc_allocator_alloc_table_end_pattern(void) {
-  abort();
+  GC_CRASH();
 }
 
 static inline enum gc_write_barrier_kind gc_small_write_barrier_kind(void) {
   return GC_WRITE_BARRIER_NONE;
 }
 static inline size_t gc_small_write_barrier_card_table_alignment(void) {
-  abort();
+  GC_CRASH();
 }
 static inline size_t gc_small_write_barrier_card_size(void) {
-  abort();
+  GC_CRASH();
 }
 
 #endif // SEMI_ATTRS_H
diff --git a/semi.h b/semi.c
similarity index 89%
rename from semi.h
rename to semi.c
index bc32eecb2..64a9ee652 100644
--- a/semi.h
+++ b/semi.c
@@ -5,9 +5,17 @@
 #include <sys/mman.h>
 #include <unistd.h>
 
+#define GC_API_ 
+#include "gc-api.h"
+
 #include "semi-attrs.h"
 #include "large-object-space.h"
-#include "precise-roots.h"
+
+#if GC_PRECISE
+#include "precise-roots-embedder.h"
+#else
+#error semi is a precise collector
+#endif
 
 struct semi_space {
   uintptr_t hp;
@@ -141,7 +149,7 @@ static void visit(struct gc_edge edge, void *visit_data) {
   else if (large_object_space_contains(heap_large_object_space(heap), obj))
     visit_large_object_space(heap, heap_large_object_space(heap), obj);
   else
-    abort();
+    GC_CRASH();
 }
 
 static void collect(struct mutator *mut) {
@@ -167,11 +175,11 @@ static void collect_for_alloc(struct mutator *mut, size_t 
bytes) {
   struct semi_space *space = mutator_semi_space(mut);
   if (space->limit - space->hp < bytes) {
     fprintf(stderr, "ran out of space, heap size %zu\n", space->size);
-    abort();
+    GC_CRASH();
   }
 }
 
-static void* gc_allocate_large(struct mutator *mut, size_t size) {
+void* gc_allocate_large(struct mutator *mut, size_t size) {
   struct heap *heap = mutator_heap(mut);
   struct large_object_space *space = heap_large_object_space(heap);
   struct semi_space *semi_space = heap_semi_space(heap);
@@ -181,7 +189,7 @@ static void* gc_allocate_large(struct mutator *mut, size_t 
size) {
     collect(mut);
     if (!semi_space_steal_pages(semi_space, npages)) {
       fprintf(stderr, "ran out of space, heap size %zu\n", semi_space->size);
-      abort();
+      GC_CRASH();
     }
   }
 
@@ -191,13 +199,13 @@ static void* gc_allocate_large(struct mutator *mut, 
size_t size) {
 
   if (!ret) {
     perror("weird: we have the space but mmap didn't work");
-    abort();
+    GC_CRASH();
   }
 
   return ret;
 }
 
-static void* gc_allocate_small(struct mutator *mut, size_t size) {
+void* gc_allocate_small(struct mutator *mut, size_t size) {
   struct semi_space *space = mutator_semi_space(mut);
   while (1) {
     uintptr_t addr = space->hp;
@@ -212,7 +220,7 @@ static void* gc_allocate_small(struct mutator *mut, size_t 
size) {
     return (void *)addr;
   }
 }
-static inline void* gc_allocate_pointerless(struct mutator *mut, size_t size) {
+void* gc_allocate_pointerless(struct mutator *mut, size_t size) {
   return gc_allocate(mut, size);
 }
 
@@ -250,7 +258,7 @@ static void dump_available_gc_options(void) {
   fprintf(stderr, "\n");
 }
 
-static int gc_option_from_string(const char *str) {
+int gc_option_from_string(const char *str) {
 #define PARSE_OPTION(option, name) if (strcmp(str, name) == 0) return option;
   FOR_EACH_GC_OPTION(PARSE_OPTION)
 #undef PARSE_OPTION
@@ -269,8 +277,8 @@ struct options {
 };
 
 static size_t parse_size_t(double value) {
-  ASSERT(value >= 0);
-  ASSERT(value <= (size_t) -1);
+  GC_ASSERT(value >= 0);
+  GC_ASSERT(value <= (size_t) -1);
   return value;
 }
 
@@ -286,7 +294,7 @@ static int parse_options(int argc, struct gc_option argv[],
       options->parallelism = parse_size_t(argv[i].value);
       break;
     default:
-      abort();
+      GC_CRASH();
     }
   }
 
@@ -302,8 +310,8 @@ static int parse_options(int argc, struct gc_option argv[],
   return 1;
 }
 
-static int gc_init(int argc, struct gc_option argv[],
-                   struct heap **heap, struct mutator **mut) {
+int gc_init(int argc, struct gc_option argv[],
+            struct heap **heap, struct mutator **mut) {
   GC_ASSERT_EQ(gc_allocator_allocation_pointer_offset(),
                offsetof(struct semi_space, hp));
   GC_ASSERT_EQ(gc_allocator_allocation_limit_offset(),
@@ -314,7 +322,7 @@ static int gc_init(int argc, struct gc_option argv[],
     return 0;
 
   *mut = calloc(1, sizeof(struct mutator));
-  if (!*mut) abort();
+  if (!*mut) GC_CRASH();
   *heap = mutator_heap(*mut);
 
   struct semi_space *space = mutator_semi_space(*mut);
@@ -328,30 +336,30 @@ static int gc_init(int argc, struct gc_option argv[],
   return 1;
 }
 
-static void gc_mutator_set_roots(struct mutator *mut,
-                                 struct gc_mutator_roots *roots) {
+void gc_mutator_set_roots(struct mutator *mut,
+                          struct gc_mutator_roots *roots) {
   mut->roots = roots;
 }
-static void gc_heap_set_roots(struct heap *heap, struct gc_heap_roots *roots) {
-  abort();
+void gc_heap_set_roots(struct heap *heap, struct gc_heap_roots *roots) {
+  GC_CRASH();
 }
 
-static struct mutator* gc_init_for_thread(uintptr_t *stack_base,
-                                          struct heap *heap) {
+struct mutator* gc_init_for_thread(uintptr_t *stack_base,
+                                   struct heap *heap) {
   fprintf(stderr,
           "Semispace copying collector not appropriate for multithreaded 
use.\n");
-  exit(1);
+  GC_CRASH();
 }
-static void gc_finish_for_thread(struct mutator *space) {
+void gc_finish_for_thread(struct mutator *space) {
 }
 
-static void* gc_call_without_gc(struct mutator *mut, void* (*f)(void*),
-                                void *data) {
+void* gc_call_without_gc(struct mutator *mut, void* (*f)(void*),
+                         void *data) {
   // Can't be threads, then there won't be collection.
   return f(data);
 }
 
-static void gc_print_stats(struct heap *heap) {
+void gc_print_stats(struct heap *heap) {
   struct semi_space *space = heap_semi_space(heap);
   printf("Completed %ld collections\n", space->count);
   printf("Heap size is %zd\n", space->size);
diff --git a/serial-tracer.h b/serial-tracer.h
index 68c4d489f..27ff36882 100644
--- a/serial-tracer.h
+++ b/serial-tracer.h
@@ -85,7 +85,7 @@ static inline void
 trace_queue_push(struct trace_queue *q, struct gcobj *p) {
   if (UNLIKELY(q->write - q->read == q->size)) {
     if (!trace_queue_grow(q))
-      abort();
+      GC_CRASH();
   }
   trace_queue_put(q, q->write++, p);
 }
@@ -94,7 +94,7 @@ static inline void
 trace_queue_push_many(struct trace_queue *q, struct gcobj **pv, size_t count) {
   while (q->size - (q->write - q->read) < count) {
     if (!trace_queue_grow(q))
-      abort();
+      GC_CRASH();
   }
   for (size_t i = 0; i < count; i++)
     trace_queue_put(q, q->write++, pv[i]);
diff --git a/simple-gc-embedder.h b/simple-gc-embedder.h
index 5a8adf717..42e04485e 100644
--- a/simple-gc-embedder.h
+++ b/simple-gc-embedder.h
@@ -19,34 +19,15 @@ static inline void gc_trace_object(void *object,
     FOR_EACH_HEAP_OBJECT_KIND(SCAN_OBJECT)
 #undef SCAN_OBJECT
   default:
-    abort ();
+    GC_CRASH();
   }
 }
 
-struct handle;
-struct gc_heap_roots { struct handle *roots; };
-struct gc_mutator_roots { struct handle *roots; };
-
-static inline void visit_roots(struct handle *roots,
-                               void (*trace_edge)(struct gc_edge edge,
-                                                  void *trace_data),
-                               void *trace_data);
-
-static inline void gc_trace_mutator_roots(struct gc_mutator_roots *roots,
-                                          void (*trace_edge)(struct gc_edge 
edge,
-                                                             void *trace_data),
-                                          void *trace_data) {
-  if (roots)
-    visit_roots(roots->roots, trace_edge, trace_data);
-}
-
-static inline void gc_trace_heap_roots(struct gc_heap_roots *roots,
-                                       void (*trace_edge)(struct gc_edge edge,
-                                                          void *trace_data),
-                                       void *trace_data) {
-  if (roots)
-    visit_roots(roots->roots, trace_edge, trace_data);
-}
+#if GC_PRECISE
+#include "precise-roots-embedder.h"
+#else
+#include "conservative-roots-embedder.h"
+#endif
 
 static inline uintptr_t gc_object_forwarded_nonatomic(void *object) {
   uintptr_t tag = *tag_word(object);
diff --git a/whippet-attrs.h b/whippet-attrs.h
index 72915b1a1..bfecc44db 100644
--- a/whippet-attrs.h
+++ b/whippet-attrs.h
@@ -2,6 +2,7 @@
 #define WHIPPET_ATTRS_H
 
 #include "gc-config.h"
+#include "gc-assert.h"
 #include "gc-attrs.h"
 
 static inline enum gc_allocator_kind gc_allocator_kind(void) {
@@ -22,7 +23,7 @@ static inline size_t 
gc_allocator_allocation_limit_offset(void) {
 }
 
 static inline size_t gc_allocator_freelist_offset(size_t size) {
-  abort();
+  GC_CRASH();
 }
 
 static inline size_t gc_allocator_alloc_table_alignment(void) {
diff --git a/whippet.h b/whippet.c
similarity index 98%
rename from whippet.h
rename to whippet.c
index 654897b5c..2fdafa1c7 100644
--- a/whippet.h
+++ b/whippet.c
@@ -1,11 +1,3 @@
-#ifndef GC_PARALLEL_TRACE
-#error define GC_PARALLEL_TRACE to 1 or 0
-#endif
-
-#ifndef GC_GENERATIONAL
-#error define GC_GENERATIONAL to 1 or 0
-#endif
-
 #include <pthread.h>
 #include <stdatomic.h>
 #include <stdint.h>
@@ -15,11 +7,13 @@
 #include <string.h>
 #include <unistd.h>
 
+#define GC_API_ 
+#include "gc-api.h"
+
 #include "debug.h"
 #include "gc-inline.h"
 #include "large-object-space.h"
-#include "precise-roots.h"
-#if GC_PARALLEL_TRACE
+#if GC_PARALLEL
 #include "parallel-tracer.h"
 #else
 #include "serial-tracer.h"
@@ -27,6 +21,12 @@
 #include "spin.h"
 #include "whippet-attrs.h"
 
+#if GC_PRECISE
+#include "precise-roots-embedder.h"
+#else
+#error whippet only currently implements precise collection
+#endif
+
 #define GRANULE_SIZE 16
 #define GRANULE_SIZE_LOG_2 4
 #define MEDIUM_OBJECT_THRESHOLD 256
@@ -560,7 +560,7 @@ static inline int mark_space_evacuate_or_mark_object(struct 
mark_space *space,
     case GC_FORWARDING_STATE_NOT_FORWARDED:
     case GC_FORWARDING_STATE_ABORTED:
       // Impossible.
-      abort();
+      GC_CRASH();
     case GC_FORWARDING_STATE_ACQUIRED: {
       // We claimed the object successfully; evacuating is up to us.
       size_t object_granules = mark_space_live_object_granules(metadata);
@@ -641,7 +641,7 @@ static inline int trace_edge(struct heap *heap, struct 
gc_edge edge) {
     return large_object_space_mark_object(heap_large_object_space(heap),
                                           obj);
   else
-    abort();
+    GC_CRASH();
 }
 
 static inline void trace_one(struct gcobj *obj, void *mark_data) {
@@ -836,7 +836,7 @@ static void mutator_mark_buf_grow(struct mutator_mark_buf 
*buf) {
                    MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
   if (mem == MAP_FAILED) {
     perror("allocating mutator mark buffer failed");
-    abort();
+    GC_CRASH();
   }
   if (old_bytes) {
     memcpy(mem, buf->objects, old_bytes);
@@ -905,11 +905,11 @@ static int mutator_should_mark_while_stopping(struct 
mutator *mut) {
   return heap_should_mark_while_stopping(mutator_heap(mut));
 }
 
-static void gc_mutator_set_roots(struct mutator *mut,
-                                 struct gc_mutator_roots *roots) {
+void gc_mutator_set_roots(struct mutator *mut,
+                          struct gc_mutator_roots *roots) {
   mut->roots = roots;
 }
-static void gc_heap_set_roots(struct heap *heap, struct gc_heap_roots *roots) {
+void gc_heap_set_roots(struct heap *heap, struct gc_heap_roots *roots) {
   heap->roots = roots;
 }
 
@@ -1213,7 +1213,7 @@ static void detect_out_of_memory(struct heap *heap) {
   // be able to yield more space: out of memory.
   fprintf(stderr, "ran out of space, heap size %zu (%zu slabs)\n",
           heap->size, mark_space->nslabs);
-  abort();
+  GC_CRASH();
 }
 
 static double clamp_major_gc_yield_threshold(struct heap *heap,
@@ -1762,7 +1762,7 @@ static void trigger_collection(struct mutator *mut) {
   heap_unlock(heap);
 }
 
-static void* gc_allocate_large(struct mutator *mut, size_t size) {
+void* gc_allocate_large(struct mutator *mut, size_t size) {
   struct heap *heap = mutator_heap(mut);
   struct large_object_space *space = heap_large_object_space(heap);
 
@@ -1781,13 +1781,13 @@ static void* gc_allocate_large(struct mutator *mut, 
size_t size) {
 
   if (!ret) {
     perror("weird: we have the space but mmap didn't work");
-    abort();
+    GC_CRASH();
   }
 
   return ret;
 }
 
-static void* gc_allocate_small(struct mutator *mut, size_t size) {
+void* gc_allocate_small(struct mutator *mut, size_t size) {
   GC_ASSERT(size > 0); // allocating 0 bytes would be silly
   GC_ASSERT(size <= gc_allocator_large_threshold());
   size = align_up(size, GRANULE_SIZE);
@@ -1816,7 +1816,7 @@ static void* gc_allocate_small(struct mutator *mut, 
size_t size) {
   return obj;
 }
 
-static inline void* gc_allocate_pointerless(struct mutator *mut, size_t size) {
+void* gc_allocate_pointerless(struct mutator *mut, size_t size) {
   return gc_allocate(mut, size);
 }
 
@@ -1832,7 +1832,7 @@ static void dump_available_gc_options(void) {
   fprintf(stderr, "\n");
 }
 
-static int gc_option_from_string(const char *str) {
+int gc_option_from_string(const char *str) {
 #define PARSE_OPTION(option, name) if (strcmp(str, name) == 0) return option;
   FOR_EACH_GC_OPTION(PARSE_OPTION)
 #undef PARSE_OPTION
@@ -1869,7 +1869,7 @@ static int parse_options(int argc, struct gc_option 
argv[],
       options->parallelism = parse_size_t(argv[i].value);
       break;
     default:
-      abort();
+      GC_CRASH();
     }
   }
 
@@ -1916,7 +1916,7 @@ static int heap_init(struct heap *heap, struct options 
*options) {
   heap->size = options->fixed_heap_size;
 
   if (!tracer_init(heap, options->parallelism))
-    abort();
+    GC_CRASH();
 
   heap->fragmentation_low_threshold = 0.05;
   heap->fragmentation_high_threshold = 0.10;
@@ -1960,7 +1960,7 @@ static int mark_space_init(struct mark_space *space, 
struct heap *heap) {
   return 1;
 }
 
-static int gc_init(int argc, struct gc_option argv[],
+int gc_init(int argc, struct gc_option argv[],
                    struct heap **heap, struct mutator **mut) {
   GC_ASSERT_EQ(gc_allocator_small_granule_size(), GRANULE_SIZE);
   GC_ASSERT_EQ(gc_allocator_large_threshold(), LARGE_OBJECT_THRESHOLD);
@@ -1982,10 +1982,10 @@ static int gc_init(int argc, struct gc_option argv[],
     return 0;
 
   *heap = calloc(1, sizeof(struct heap));
-  if (!*heap) abort();
+  if (!*heap) GC_CRASH();
 
   if (!heap_init(*heap, &options))
-    abort();
+    GC_CRASH();
 
   struct mark_space *space = heap_mark_space(*heap);
   if (!mark_space_init(space, *heap)) {
@@ -1995,24 +1995,24 @@ static int gc_init(int argc, struct gc_option argv[],
   }
   
   if (!large_object_space_init(heap_large_object_space(*heap), *heap))
-    abort();
+    GC_CRASH();
 
   *mut = calloc(1, sizeof(struct mutator));
-  if (!*mut) abort();
+  if (!*mut) GC_CRASH();
   add_mutator(*heap, *mut);
   return 1;
 }
 
-static struct mutator* gc_init_for_thread(uintptr_t *stack_base,
+struct mutator* gc_init_for_thread(uintptr_t *stack_base,
                                           struct heap *heap) {
   struct mutator *ret = calloc(1, sizeof(struct mutator));
   if (!ret)
-    abort();
+    GC_CRASH();
   add_mutator(heap, ret);
   return ret;
 }
 
-static void gc_finish_for_thread(struct mutator *mut) {
+void gc_finish_for_thread(struct mutator *mut) {
   remove_mutator(mutator_heap(mut), mut);
   mutator_mark_buf_destroy(&mut->mark_buf);
   free(mut);
@@ -2042,7 +2042,7 @@ static void reactivate_mutator(struct heap *heap, struct 
mutator *mut) {
   heap_unlock(heap);
 }
 
-static void* gc_call_without_gc(struct mutator *mut,
+void* gc_call_without_gc(struct mutator *mut,
                                 void* (*f)(void*),
                                 void *data) {
   struct heap *heap = mutator_heap(mut);
@@ -2052,7 +2052,7 @@ static void* gc_call_without_gc(struct mutator *mut,
   return ret;
 }
 
-static void gc_print_stats(struct heap *heap) {
+void gc_print_stats(struct heap *heap) {
   printf("Completed %ld collections (%ld major)\n",
          heap->count, heap->count - heap->minor_count);
   printf("Heap size with overhead is %zd (%zu slabs)\n",


Reply via email to