summaryrefslogtreecommitdiff
path: root/libguile/whippet/api
diff options
context:
space:
mode:
authorAndy Wingo <wingo@pobox.com>2025-04-11 14:10:41 +0200
committerAndy Wingo <wingo@pobox.com>2025-04-11 14:10:41 +0200
commitdb181e67ff482ab08cbdb0c1a88997b3a886f900 (patch)
tree6d16fbca515ae3cf506a9d16dbc89c472446e5d9 /libguile/whippet/api
parentaf96820e072d18c49ac03e80c6f3466d568dc77d (diff)
parentf909438596f63ecd44c5b9a28385260714857361 (diff)
downloadguile-db181e67ff482ab08cbdb0c1a88997b3a886f900.tar.gz
Merged Whippet into libguile/whippet
Diffstat (limited to 'libguile/whippet/api')
-rw-r--r--libguile/whippet/api/bdw-attrs.h91
-rw-r--r--libguile/whippet/api/gc-allocation-kind.h19
-rw-r--r--libguile/whippet/api/gc-api.h301
-rw-r--r--libguile/whippet/api/gc-assert.h21
-rw-r--r--libguile/whippet/api/gc-attrs.h69
-rw-r--r--libguile/whippet/api/gc-basic-stats.h177
-rw-r--r--libguile/whippet/api/gc-collection-kind.h11
-rw-r--r--libguile/whippet/api/gc-config.h40
-rw-r--r--libguile/whippet/api/gc-conservative-ref.h17
-rw-r--r--libguile/whippet/api/gc-edge.h26
-rw-r--r--libguile/whippet/api/gc-embedder-api.h67
-rw-r--r--libguile/whippet/api/gc-ephemeron.h42
-rw-r--r--libguile/whippet/api/gc-event-listener-chain.h145
-rw-r--r--libguile/whippet/api/gc-event-listener.h29
-rw-r--r--libguile/whippet/api/gc-finalizer.h81
-rw-r--r--libguile/whippet/api/gc-forwarding.h20
-rw-r--r--libguile/whippet/api/gc-histogram.h82
-rw-r--r--libguile/whippet/api/gc-inline.h7
-rw-r--r--libguile/whippet/api/gc-lttng.h100
-rw-r--r--libguile/whippet/api/gc-null-event-listener.h50
-rw-r--r--libguile/whippet/api/gc-options.h39
-rw-r--r--libguile/whippet/api/gc-ref.h50
-rw-r--r--libguile/whippet/api/gc-tracepoint.h17
-rw-r--r--libguile/whippet/api/gc-visibility.h12
-rw-r--r--libguile/whippet/api/mmc-attrs.h121
-rw-r--r--libguile/whippet/api/pcc-attrs.h92
-rw-r--r--libguile/whippet/api/semi-attrs.h80
27 files changed, 1806 insertions, 0 deletions
diff --git a/libguile/whippet/api/bdw-attrs.h b/libguile/whippet/api/bdw-attrs.h
new file mode 100644
index 000000000..7f8000b3f
--- /dev/null
+++ b/libguile/whippet/api/bdw-attrs.h
@@ -0,0 +1,91 @@
+#ifndef BDW_ATTRS_H
+#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;
+}
+static inline size_t gc_allocator_small_granule_size(void) {
+ return 2 * sizeof(void *);
+}
+static inline size_t gc_allocator_large_threshold(void) {
+ return 256;
+}
+
+static inline size_t gc_allocator_allocation_pointer_offset(void) {
+ GC_CRASH();
+}
+static inline size_t gc_allocator_allocation_limit_offset(void) {
+ GC_CRASH();
+}
+
+static inline size_t gc_allocator_freelist_offset(size_t size,
+ enum gc_allocation_kind kind) {
+ GC_ASSERT(size);
+ size_t base;
+ switch (kind) {
+ case GC_ALLOCATION_TAGGED:
+ case GC_ALLOCATION_UNTAGGED_CONSERVATIVE:
+ base = 0;
+ break;
+ case GC_ALLOCATION_UNTAGGED_POINTERLESS:
+ case GC_ALLOCATION_TAGGED_POINTERLESS:
+ base = (sizeof(void*) * gc_allocator_large_threshold() /
+ gc_allocator_small_granule_size());
+ break;
+ }
+ size_t bucket = (size - 1) / gc_allocator_small_granule_size();
+ return base + sizeof(void*) * bucket;
+}
+
+static inline size_t gc_allocator_alloc_table_alignment(void) {
+ return 0;
+}
+static inline uint8_t gc_allocator_alloc_table_begin_pattern(enum gc_allocation_kind) {
+ GC_CRASH();
+}
+static inline uint8_t gc_allocator_alloc_table_end_pattern(void) {
+ GC_CRASH();
+}
+
+static inline enum gc_old_generation_check_kind gc_old_generation_check_kind(size_t) {
+ return GC_OLD_GENERATION_CHECK_NONE;
+}
+static inline uint8_t gc_old_generation_check_alloc_table_tag_mask(void) {
+ GC_CRASH();
+}
+static inline uint8_t gc_old_generation_check_alloc_table_young_tag(void) {
+ GC_CRASH();
+}
+
+static inline enum gc_write_barrier_kind gc_write_barrier_kind(size_t) {
+ return GC_WRITE_BARRIER_NONE;
+}
+static inline size_t gc_write_barrier_field_table_alignment(void) {
+ GC_CRASH();
+}
+static inline ptrdiff_t gc_write_barrier_field_table_offset(void) {
+ GC_CRASH();
+}
+static inline size_t gc_write_barrier_field_fields_per_byte(void) {
+ GC_CRASH();
+}
+static inline uint8_t gc_write_barrier_field_first_bit_pattern(void) {
+ GC_CRASH();
+}
+
+static inline enum gc_safepoint_mechanism gc_safepoint_mechanism(void) {
+ return GC_SAFEPOINT_MECHANISM_SIGNAL;
+}
+
+static inline enum gc_cooperative_safepoint_kind gc_cooperative_safepoint_kind(void) {
+ return GC_COOPERATIVE_SAFEPOINT_NONE;
+}
+
+static inline int gc_can_pin_objects(void) {
+ return 1;
+}
+
+#endif // BDW_ATTRS_H
diff --git a/libguile/whippet/api/gc-allocation-kind.h b/libguile/whippet/api/gc-allocation-kind.h
new file mode 100644
index 000000000..72de3b6be
--- /dev/null
+++ b/libguile/whippet/api/gc-allocation-kind.h
@@ -0,0 +1,19 @@
+#ifndef GC_ALLOCATION_KIND_H
+#define GC_ALLOCATION_KIND_H
+
+enum gc_allocation_kind {
+ // An object whose type can be inspected at run-time based on its contents,
+ // and whose fields be traced via the gc_trace_object procedure.
+ GC_ALLOCATION_TAGGED,
+ // Like GC_ALLOCATION_TAGGED, but not containing any fields that reference
+ // GC-managed objects. The GC may choose to handle these specially.
+ GC_ALLOCATION_TAGGED_POINTERLESS,
+ // A raw allocation whose type cannot be inspected at trace-time, and whose
+ // fields should be traced conservatively.
+ GC_ALLOCATION_UNTAGGED_CONSERVATIVE,
+ // A raw allocation whose type cannot be inspected at trace-time, but
+ // containing no fields that reference GC-managed objects.
+ GC_ALLOCATION_UNTAGGED_POINTERLESS
+};
+
+#endif // GC_ALLOCATION_KIND_H
diff --git a/libguile/whippet/api/gc-api.h b/libguile/whippet/api/gc-api.h
new file mode 100644
index 000000000..78d8b2bdb
--- /dev/null
+++ b/libguile/whippet/api/gc-api.h
@@ -0,0 +1,301 @@
+#ifndef GC_API_H_
+#define GC_API_H_
+
+#include "gc-config.h"
+#include "gc-allocation-kind.h"
+#include "gc-assert.h"
+#include "gc-attrs.h"
+#include "gc-collection-kind.h"
+#include "gc-edge.h"
+#include "gc-event-listener.h"
+#include "gc-inline.h"
+#include "gc-options.h"
+#include "gc-ref.h"
+#include "gc-visibility.h"
+
+#include <stdatomic.h>
+#include <stdint.h>
+#include <string.h>
+
+struct gc_heap;
+struct gc_mutator;
+
+struct gc_stack_addr;
+GC_API_ void* gc_call_with_stack_addr(void* (*f)(struct gc_stack_addr *,
+ void *),
+ void *data) GC_NEVER_INLINE;
+
+GC_API_ int gc_init(const struct gc_options *options,
+ struct gc_stack_addr *base, struct gc_heap **heap,
+ struct gc_mutator **mutator,
+ struct gc_event_listener event_listener,
+ void *event_listener_data);
+
+GC_API_ uint64_t gc_allocation_counter(struct gc_heap *heap);
+
+GC_API_ struct gc_heap* gc_mutator_heap(struct gc_mutator *mut);
+
+GC_API_ uintptr_t gc_small_object_nursery_low_address(struct gc_heap *heap);
+GC_API_ uintptr_t gc_small_object_nursery_high_address(struct gc_heap *heap);
+
+struct gc_mutator_roots;
+GC_API_ void gc_mutator_set_roots(struct gc_mutator *mut,
+ struct gc_mutator_roots *roots);
+
+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);
+GC_API_ void* gc_call_without_gc(struct gc_mutator *mut, void* (*f)(void*),
+ void *data) GC_NEVER_INLINE;
+
+GC_API_ void gc_collect(struct gc_mutator *mut,
+ enum gc_collection_kind requested_kind);
+
+static inline void gc_update_alloc_table(struct gc_ref obj, size_t size,
+ enum gc_allocation_kind kind) GC_ALWAYS_INLINE;
+static inline void gc_update_alloc_table(struct gc_ref obj, size_t size,
+ enum gc_allocation_kind kind) {
+ size_t alignment = gc_allocator_alloc_table_alignment();
+ if (!alignment) return;
+
+ uintptr_t addr = gc_ref_value(obj);
+ uintptr_t base = addr & ~(alignment - 1);
+ size_t granule_size = gc_allocator_small_granule_size();
+ uintptr_t granule = (addr & (alignment - 1)) / granule_size;
+ uint8_t *alloc = (uint8_t*)(base + granule);
+
+ uint8_t begin_pattern = gc_allocator_alloc_table_begin_pattern(kind);
+ uint8_t end_pattern = gc_allocator_alloc_table_end_pattern();
+ if (end_pattern) {
+ size_t granules = size / granule_size;
+ if (granules == 1) {
+ alloc[0] = begin_pattern | end_pattern;
+ } else {
+ alloc[0] = begin_pattern;
+ if (granules > 2)
+ memset(alloc + 1, 0, granules - 2);
+ alloc[granules - 1] = end_pattern;
+ }
+ } else {
+ alloc[0] = begin_pattern;
+ }
+}
+
+GC_API_ void* gc_allocate_slow(struct gc_mutator *mut, size_t bytes,
+ enum gc_allocation_kind kind) GC_NEVER_INLINE;
+
+static inline void*
+gc_allocate_small_fast_bump_pointer(struct gc_mutator *mut, size_t size,
+ enum gc_allocation_kind kind) GC_ALWAYS_INLINE;
+static inline void* gc_allocate_small_fast_bump_pointer(struct gc_mutator *mut,
+ size_t size,
+ enum gc_allocation_kind kind) {
+ GC_ASSERT(size <= gc_allocator_large_threshold());
+
+ size_t granule_size = gc_allocator_small_granule_size();
+ size_t hp_offset = gc_allocator_allocation_pointer_offset();
+ size_t limit_offset = gc_allocator_allocation_limit_offset();
+
+ uintptr_t base_addr = (uintptr_t)mut;
+ uintptr_t *hp_loc = (uintptr_t*)(base_addr + hp_offset);
+ uintptr_t *limit_loc = (uintptr_t*)(base_addr + limit_offset);
+
+ size = (size + granule_size - 1) & ~(granule_size - 1);
+ uintptr_t hp = *hp_loc;
+ uintptr_t limit = *limit_loc;
+ uintptr_t new_hp = hp + size;
+
+ if (GC_UNLIKELY (new_hp > limit))
+ return NULL;
+
+ *hp_loc = new_hp;
+
+ gc_update_alloc_table(gc_ref(hp), size, kind);
+
+ return (void*)hp;
+}
+
+static inline void* gc_allocate_small_fast_freelist(struct gc_mutator *mut,
+ size_t size,
+ enum gc_allocation_kind kind) GC_ALWAYS_INLINE;
+static inline void* gc_allocate_small_fast_freelist(struct gc_mutator *mut,
+ size_t size,
+ enum gc_allocation_kind kind) {
+ GC_ASSERT(size <= gc_allocator_large_threshold());
+
+ size_t freelist_offset = gc_allocator_freelist_offset(size, kind);
+ uintptr_t base_addr = (uintptr_t)mut;
+ void **freelist_loc = (void**)(base_addr + freelist_offset);
+
+ void *head = *freelist_loc;
+ if (GC_UNLIKELY(!head))
+ return NULL;
+
+ *freelist_loc = *(void**)head;
+
+ gc_update_alloc_table(gc_ref_from_heap_object(head), size, kind);
+
+ return head;
+}
+
+static inline void* gc_allocate_small_fast(struct gc_mutator *mut, size_t size,
+ enum gc_allocation_kind kind) GC_ALWAYS_INLINE;
+static inline void* gc_allocate_small_fast(struct gc_mutator *mut, size_t size,
+ enum gc_allocation_kind kind) {
+ GC_ASSERT(size != 0);
+ GC_ASSERT(size <= gc_allocator_large_threshold());
+
+ switch (gc_allocator_kind()) {
+ case GC_ALLOCATOR_INLINE_BUMP_POINTER:
+ return gc_allocate_small_fast_bump_pointer(mut, size, kind);
+ case GC_ALLOCATOR_INLINE_FREELIST:
+ return gc_allocate_small_fast_freelist(mut, size, kind);
+ case GC_ALLOCATOR_INLINE_NONE:
+ return NULL;
+ default:
+ GC_CRASH();
+ }
+}
+
+static inline void* gc_allocate_fast(struct gc_mutator *mut, size_t size,
+ enum gc_allocation_kind kind) GC_ALWAYS_INLINE;
+static inline void* gc_allocate_fast(struct gc_mutator *mut, size_t size,
+ enum gc_allocation_kind kind) {
+ GC_ASSERT(size != 0);
+ if (size > gc_allocator_large_threshold())
+ return NULL;
+
+ return gc_allocate_small_fast(mut, size, kind);
+}
+
+static inline void* gc_allocate(struct gc_mutator *mut, size_t size,
+ enum gc_allocation_kind kind) GC_ALWAYS_INLINE;
+static inline void* gc_allocate(struct gc_mutator *mut, size_t size,
+ enum gc_allocation_kind kind) {
+ void *ret = gc_allocate_fast(mut, size, kind);
+ if (GC_LIKELY(ret != NULL))
+ return ret;
+
+ return gc_allocate_slow(mut, size, kind);
+}
+
+GC_API_ int gc_object_is_old_generation_slow(struct gc_mutator *mut,
+ struct gc_ref obj) GC_NEVER_INLINE;
+
+static inline int gc_object_is_old_generation(struct gc_mutator *mut,
+ struct gc_ref obj,
+ size_t obj_size) GC_ALWAYS_INLINE;
+static inline int gc_object_is_old_generation(struct gc_mutator *mut,
+ struct gc_ref obj,
+ size_t obj_size) {
+ switch (gc_old_generation_check_kind(obj_size)) {
+ case GC_OLD_GENERATION_CHECK_ALLOC_TABLE: {
+ size_t alignment = gc_allocator_alloc_table_alignment();
+ GC_ASSERT(alignment);
+ uintptr_t addr = gc_ref_value(obj);
+ uintptr_t base = addr & ~(alignment - 1);
+ size_t granule_size = gc_allocator_small_granule_size();
+ uintptr_t granule = (addr & (alignment - 1)) / granule_size;
+ uint8_t *byte_loc = (uint8_t*)(base + granule);
+ uint8_t byte = atomic_load_explicit(byte_loc, memory_order_relaxed);
+ uint8_t mask = gc_old_generation_check_alloc_table_tag_mask();
+ uint8_t young = gc_old_generation_check_alloc_table_young_tag();
+ return (byte & mask) != young;
+ }
+ case GC_OLD_GENERATION_CHECK_SMALL_OBJECT_NURSERY: {
+ struct gc_heap *heap = gc_mutator_heap(mut);
+ // Note that these addresses are fixed and that the embedder might
+ // want to store them somewhere or inline them into the output of
+ // JIT-generated code. They may also be power-of-two aligned.
+ uintptr_t low_addr = gc_small_object_nursery_low_address(heap);
+ uintptr_t high_addr = gc_small_object_nursery_high_address(heap);
+ uintptr_t size = high_addr - low_addr;
+ uintptr_t addr = gc_ref_value(obj);
+ return addr - low_addr >= size;
+ }
+ case GC_OLD_GENERATION_CHECK_SLOW:
+ return gc_object_is_old_generation_slow(mut, obj);
+ default:
+ GC_CRASH();
+ }
+}
+
+GC_API_ 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_NEVER_INLINE;
+
+static inline int gc_write_barrier_fast(struct gc_mutator *mut, struct gc_ref obj,
+ size_t obj_size, struct gc_edge edge,
+ struct gc_ref new_val) GC_ALWAYS_INLINE;
+static inline int gc_write_barrier_fast(struct gc_mutator *mut, struct gc_ref obj,
+ size_t obj_size, struct gc_edge edge,
+ struct gc_ref new_val) {
+ switch (gc_write_barrier_kind(obj_size)) {
+ case GC_WRITE_BARRIER_NONE:
+ return 0;
+ case GC_WRITE_BARRIER_FIELD: {
+ if (!gc_object_is_old_generation(mut, obj, obj_size))
+ return 0;
+
+ size_t field_table_alignment = gc_write_barrier_field_table_alignment();
+ size_t fields_per_byte = gc_write_barrier_field_fields_per_byte();
+ uint8_t first_bit_pattern = gc_write_barrier_field_first_bit_pattern();
+ ssize_t table_offset = gc_write_barrier_field_table_offset();
+
+ uintptr_t addr = gc_edge_address(edge);
+ uintptr_t base = addr & ~(field_table_alignment - 1);
+ uintptr_t field = (addr & (field_table_alignment - 1)) / sizeof(uintptr_t);
+ uintptr_t log_byte = field / fields_per_byte;
+ uint8_t log_bit = first_bit_pattern << (field % fields_per_byte);
+ uint8_t *byte_loc = (uint8_t*)(base + table_offset + log_byte);
+ uint8_t byte = atomic_load_explicit(byte_loc, memory_order_relaxed);
+ return !(byte & log_bit);
+ }
+ case GC_WRITE_BARRIER_SLOW:
+ return 1;
+ default:
+ GC_CRASH();
+ }
+}
+
+static inline void gc_write_barrier(struct gc_mutator *mut, struct gc_ref obj,
+ size_t obj_size, struct gc_edge edge,
+ struct gc_ref new_val) GC_ALWAYS_INLINE;
+static inline void gc_write_barrier(struct gc_mutator *mut, struct gc_ref obj,
+ size_t obj_size, struct gc_edge edge,
+ struct gc_ref new_val) {
+ if (GC_UNLIKELY(gc_write_barrier_fast(mut, obj, obj_size, edge, new_val)))
+ gc_write_barrier_slow(mut, obj, obj_size, edge, new_val);
+}
+
+GC_API_ void gc_pin_object(struct gc_mutator *mut, struct gc_ref obj);
+
+GC_API_ void gc_safepoint_slow(struct gc_mutator *mut) GC_NEVER_INLINE;
+GC_API_ int* gc_safepoint_flag_loc(struct gc_mutator *mut);
+static inline int gc_should_stop_for_safepoint(struct gc_mutator *mut) {
+ switch (gc_cooperative_safepoint_kind()) {
+ case GC_COOPERATIVE_SAFEPOINT_NONE:
+ return 0;
+ case GC_COOPERATIVE_SAFEPOINT_MUTATOR_FLAG:
+ case GC_COOPERATIVE_SAFEPOINT_HEAP_FLAG: {
+ return atomic_load_explicit(gc_safepoint_flag_loc(mut),
+ memory_order_relaxed);
+ }
+ default:
+ GC_CRASH();
+ }
+}
+static inline void gc_safepoint(struct gc_mutator *mut) {
+ if (GC_UNLIKELY(gc_should_stop_for_safepoint(mut)))
+ gc_safepoint_slow(mut);
+}
+
+#endif // GC_API_H_
diff --git a/libguile/whippet/api/gc-assert.h b/libguile/whippet/api/gc-assert.h
new file mode 100644
index 000000000..c3fa6b749
--- /dev/null
+++ b/libguile/whippet/api/gc-assert.h
@@ -0,0 +1,21 @@
+#ifndef GC_ASSERT_H
+#define GC_ASSERT_H
+
+#include "gc-config.h"
+
+#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))) 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))
+
+#endif // GC_ASSERT_H
diff --git a/libguile/whippet/api/gc-attrs.h b/libguile/whippet/api/gc-attrs.h
new file mode 100644
index 000000000..44d5d47e6
--- /dev/null
+++ b/libguile/whippet/api/gc-attrs.h
@@ -0,0 +1,69 @@
+#ifndef GC_ATTRS_H
+#define GC_ATTRS_H
+
+#include "gc-inline.h"
+#include "gc-allocation-kind.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,
+ enum gc_allocation_kind kind) 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(enum gc_allocation_kind kind) GC_ALWAYS_INLINE;
+static inline uint8_t gc_allocator_alloc_table_end_pattern(void) GC_ALWAYS_INLINE;
+
+enum gc_old_generation_check_kind {
+ GC_OLD_GENERATION_CHECK_NONE,
+ GC_OLD_GENERATION_CHECK_ALLOC_TABLE,
+ GC_OLD_GENERATION_CHECK_SMALL_OBJECT_NURSERY,
+ GC_OLD_GENERATION_CHECK_SLOW
+};
+
+static inline enum gc_old_generation_check_kind gc_old_generation_check_kind(size_t obj_size) GC_ALWAYS_INLINE;
+
+static inline uint8_t gc_old_generation_check_alloc_table_tag_mask(void) GC_ALWAYS_INLINE;
+static inline uint8_t gc_old_generation_check_alloc_table_young_tag(void) GC_ALWAYS_INLINE;
+
+enum gc_write_barrier_kind {
+ GC_WRITE_BARRIER_NONE,
+ GC_WRITE_BARRIER_FIELD,
+ GC_WRITE_BARRIER_SLOW
+};
+
+static inline enum gc_write_barrier_kind gc_write_barrier_kind(size_t obj_size) GC_ALWAYS_INLINE;
+static inline size_t gc_write_barrier_field_table_alignment(void) GC_ALWAYS_INLINE;
+static inline ptrdiff_t gc_write_barrier_field_table_offset(void) GC_ALWAYS_INLINE;
+static inline size_t gc_write_barrier_field_fields_per_byte(void) GC_ALWAYS_INLINE;
+static inline uint8_t gc_write_barrier_field_first_bit_pattern(void) GC_ALWAYS_INLINE;
+
+enum gc_safepoint_mechanism {
+ GC_SAFEPOINT_MECHANISM_COOPERATIVE,
+ GC_SAFEPOINT_MECHANISM_SIGNAL,
+};
+static inline enum gc_safepoint_mechanism gc_safepoint_mechanism(void) GC_ALWAYS_INLINE;
+
+enum gc_cooperative_safepoint_kind {
+ GC_COOPERATIVE_SAFEPOINT_NONE,
+ GC_COOPERATIVE_SAFEPOINT_MUTATOR_FLAG,
+ GC_COOPERATIVE_SAFEPOINT_HEAP_FLAG,
+};
+static inline enum gc_cooperative_safepoint_kind gc_cooperative_safepoint_kind(void) GC_ALWAYS_INLINE;
+
+static inline int gc_can_pin_objects(void) GC_ALWAYS_INLINE;
+
+#endif // GC_ATTRS_H
diff --git a/libguile/whippet/api/gc-basic-stats.h b/libguile/whippet/api/gc-basic-stats.h
new file mode 100644
index 000000000..055340817
--- /dev/null
+++ b/libguile/whippet/api/gc-basic-stats.h
@@ -0,0 +1,177 @@
+#ifndef GC_BASIC_STATS_H
+#define GC_BASIC_STATS_H
+
+#include "gc-event-listener.h"
+#include "gc-histogram.h"
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+GC_DEFINE_HISTOGRAM(gc_latency, 25, 4);
+
+struct gc_basic_stats {
+ uint64_t major_collection_count;
+ uint64_t minor_collection_count;
+ uint64_t last_time_usec;
+ uint64_t last_cpu_time_usec;
+ uint64_t elapsed_mutator_usec;
+ uint64_t elapsed_collector_usec;
+ uint64_t cpu_mutator_usec;
+ uint64_t cpu_collector_usec;
+ size_t heap_size;
+ size_t max_heap_size;
+ size_t max_live_data_size;
+ struct gc_latency pause_times;
+};
+
+static inline uint64_t gc_basic_stats_now(void) {
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) != 0) GC_CRASH();
+ uint64_t ret = tv.tv_sec;
+ ret *= 1000 * 1000;
+ ret += tv.tv_usec;
+ return ret;
+}
+
+static inline uint64_t gc_basic_stats_cpu_time(void) {
+ struct timespec ts;
+ clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &ts);
+ uint64_t ret = ts.tv_sec;
+ ret *= 1000 * 1000;
+ ret += ts.tv_nsec / 1000;
+ return ret;
+}
+
+static inline void gc_basic_stats_init(void *data, size_t heap_size) {
+ struct gc_basic_stats *stats = data;
+ memset(stats, 0, sizeof(*stats));
+ stats->last_time_usec = gc_basic_stats_now();
+ stats->last_cpu_time_usec = gc_basic_stats_cpu_time();
+ stats->heap_size = stats->max_heap_size = heap_size;
+}
+
+static inline void gc_basic_stats_requesting_stop(void *data) {
+ struct gc_basic_stats *stats = data;
+ uint64_t now = gc_basic_stats_now();
+ uint64_t cpu_time = gc_basic_stats_cpu_time();
+ stats->elapsed_mutator_usec += now - stats->last_time_usec;
+ stats->cpu_mutator_usec += cpu_time - stats->last_cpu_time_usec;
+ stats->last_time_usec = now;
+ stats->last_cpu_time_usec = cpu_time;
+}
+static inline void gc_basic_stats_waiting_for_stop(void *data) {}
+static inline void gc_basic_stats_mutators_stopped(void *data) {}
+
+static inline void gc_basic_stats_prepare_gc(void *data,
+ enum gc_collection_kind kind) {
+ struct gc_basic_stats *stats = data;
+ if (kind == GC_COLLECTION_MINOR)
+ stats->minor_collection_count++;
+ else
+ stats->major_collection_count++;
+}
+
+static inline void gc_basic_stats_roots_traced(void *data) {}
+static inline void gc_basic_stats_heap_traced(void *data) {}
+static inline void gc_basic_stats_ephemerons_traced(void *data) {}
+static inline void gc_basic_stats_finalizers_traced(void *data) {}
+
+static inline void gc_basic_stats_restarting_mutators(void *data) {
+ struct gc_basic_stats *stats = data;
+ uint64_t now = gc_basic_stats_now();
+ uint64_t cpu_time = gc_basic_stats_cpu_time();
+ uint64_t pause_time = now - stats->last_time_usec;
+ uint64_t pause_cpu_time = cpu_time - stats->last_cpu_time_usec;
+ stats->elapsed_collector_usec += pause_time;
+ stats->cpu_collector_usec += pause_cpu_time;
+ gc_latency_record(&stats->pause_times, pause_time);
+ stats->last_time_usec = now;
+ stats->last_cpu_time_usec = cpu_time;
+}
+
+static inline void* gc_basic_stats_mutator_added(void *data) {
+ return NULL;
+}
+static inline void gc_basic_stats_mutator_cause_gc(void *mutator_data) {}
+static inline void gc_basic_stats_mutator_stopping(void *mutator_data) {}
+static inline void gc_basic_stats_mutator_stopped(void *mutator_data) {}
+static inline void gc_basic_stats_mutator_restarted(void *mutator_data) {}
+static inline void gc_basic_stats_mutator_removed(void *mutator_data) {}
+
+static inline void gc_basic_stats_heap_resized(void *data, size_t size) {
+ struct gc_basic_stats *stats = data;
+ stats->heap_size = size;
+ if (size > stats->max_heap_size)
+ stats->max_heap_size = size;
+}
+
+static inline void gc_basic_stats_live_data_size(void *data, size_t size) {
+ struct gc_basic_stats *stats = data;
+ if (size > stats->max_live_data_size)
+ stats->max_live_data_size = size;
+}
+
+#define GC_BASIC_STATS \
+ ((struct gc_event_listener) { \
+ gc_basic_stats_init, \
+ gc_basic_stats_requesting_stop, \
+ gc_basic_stats_waiting_for_stop, \
+ gc_basic_stats_mutators_stopped, \
+ gc_basic_stats_prepare_gc, \
+ gc_basic_stats_roots_traced, \
+ gc_basic_stats_heap_traced, \
+ gc_basic_stats_ephemerons_traced, \
+ gc_basic_stats_finalizers_traced, \
+ gc_basic_stats_restarting_mutators, \
+ gc_basic_stats_mutator_added, \
+ gc_basic_stats_mutator_cause_gc, \
+ gc_basic_stats_mutator_stopping, \
+ gc_basic_stats_mutator_stopped, \
+ gc_basic_stats_mutator_restarted, \
+ gc_basic_stats_mutator_removed, \
+ gc_basic_stats_heap_resized, \
+ gc_basic_stats_live_data_size, \
+ })
+
+static inline void gc_basic_stats_finish(struct gc_basic_stats *stats) {
+ uint64_t now = gc_basic_stats_now();
+ uint64_t cpu_time = gc_basic_stats_cpu_time();
+ stats->elapsed_mutator_usec += now - stats->last_time_usec;
+ stats->cpu_mutator_usec += cpu_time - stats->last_cpu_time_usec;
+ stats->last_time_usec = now;
+ stats->last_cpu_time_usec = cpu_time;
+}
+
+static inline void gc_basic_stats_print(struct gc_basic_stats *stats, FILE *f) {
+ fprintf(f, "Completed %" PRIu64 " major collections (%" PRIu64 " minor).\n",
+ stats->major_collection_count, stats->minor_collection_count);
+ uint64_t stopped = stats->elapsed_collector_usec;
+ uint64_t elapsed = stats->elapsed_mutator_usec + stopped;
+ uint64_t cpu_stopped = stats->cpu_collector_usec;
+ uint64_t cpu_total = stats->cpu_mutator_usec + cpu_stopped;
+ uint64_t ms = 1000; // per usec
+ fprintf(f, "%" PRIu64 ".%.3" PRIu64 " ms total time "
+ "(%" PRIu64 ".%.3" PRIu64 " stopped); "
+ "%" PRIu64 ".%.3" PRIu64 " ms CPU time "
+ "(%" PRIu64 ".%.3" PRIu64 " stopped).\n",
+ elapsed / ms, elapsed % ms, stopped / ms, stopped % ms,
+ cpu_total / ms, cpu_total % ms, cpu_stopped / ms, cpu_stopped % ms);
+ uint64_t pause_median = gc_latency_median(&stats->pause_times);
+ uint64_t pause_p95 = gc_latency_percentile(&stats->pause_times, 0.95);
+ uint64_t pause_max = gc_latency_max(&stats->pause_times);
+ fprintf(f, "%" PRIu64 ".%.3" PRIu64 " ms median pause time, "
+ "%" PRIu64 ".%.3" PRIu64 " p95, "
+ "%" PRIu64 ".%.3" PRIu64 " max.\n",
+ pause_median / ms, pause_median % ms, pause_p95 / ms, pause_p95 % ms,
+ pause_max / ms, pause_max % ms);
+ double MB = 1e6;
+ fprintf(f, "Heap size is %.3f MB (max %.3f MB); peak live data %.3f MB.\n",
+ stats->heap_size / MB, stats->max_heap_size / MB,
+ stats->max_live_data_size / MB);
+}
+
+#endif // GC_BASIC_STATS_H_
diff --git a/libguile/whippet/api/gc-collection-kind.h b/libguile/whippet/api/gc-collection-kind.h
new file mode 100644
index 000000000..11cfc276a
--- /dev/null
+++ b/libguile/whippet/api/gc-collection-kind.h
@@ -0,0 +1,11 @@
+#ifndef GC_COLLECTION_KIND_H
+#define GC_COLLECTION_KIND_H
+
+enum gc_collection_kind {
+ GC_COLLECTION_ANY,
+ GC_COLLECTION_MINOR,
+ GC_COLLECTION_MAJOR,
+ GC_COLLECTION_COMPACTING,
+};
+
+#endif // GC_COLLECTION_KIND_H
diff --git a/libguile/whippet/api/gc-config.h b/libguile/whippet/api/gc-config.h
new file mode 100644
index 000000000..867af63d2
--- /dev/null
+++ b/libguile/whippet/api/gc-config.h
@@ -0,0 +1,40 @@
+#ifndef GC_CONFIG_H
+#define GC_CONFIG_H
+
+#ifndef GC_DEBUG
+#define GC_DEBUG 0
+#endif
+
+#ifndef GC_HAS_IMMEDIATES
+#define GC_HAS_IMMEDIATES 1
+#endif
+
+#ifndef GC_PARALLEL
+#define GC_PARALLEL 0
+#endif
+
+#ifndef GC_GENERATIONAL
+#define GC_GENERATIONAL 0
+#endif
+
+// Though you normally wouldn't configure things this way, it's possible
+// to have both precise and conservative roots. However we have to
+// either have precise or conservative tracing; not a mix.
+
+#ifndef GC_PRECISE_ROOTS
+#define GC_PRECISE_ROOTS 0
+#endif
+
+#ifndef GC_CONSERVATIVE_ROOTS
+#define GC_CONSERVATIVE_ROOTS 0
+#endif
+
+#ifndef GC_CONSERVATIVE_TRACE
+#define GC_CONSERVATIVE_TRACE 0
+#endif
+
+#ifndef GC_CONCURRENT_TRACE
+#define GC_CONCURRENT_TRACE 0
+#endif
+
+#endif // GC_CONFIG_H
diff --git a/libguile/whippet/api/gc-conservative-ref.h b/libguile/whippet/api/gc-conservative-ref.h
new file mode 100644
index 000000000..a2b260384
--- /dev/null
+++ b/libguile/whippet/api/gc-conservative-ref.h
@@ -0,0 +1,17 @@
+#ifndef GC_CONSERVATIVE_REF_H
+#define GC_CONSERVATIVE_REF_H
+
+#include <stdint.h>
+
+struct gc_conservative_ref {
+ uintptr_t value;
+};
+
+static inline struct gc_conservative_ref gc_conservative_ref(uintptr_t value) {
+ return (struct gc_conservative_ref){value};
+}
+static inline uintptr_t gc_conservative_ref_value(struct gc_conservative_ref ref) {
+ return ref.value;
+}
+
+#endif // GC_CONSERVATIVE_REF_H
diff --git a/libguile/whippet/api/gc-edge.h b/libguile/whippet/api/gc-edge.h
new file mode 100644
index 000000000..ec487df9d
--- /dev/null
+++ b/libguile/whippet/api/gc-edge.h
@@ -0,0 +1,26 @@
+#ifndef GC_EDGE_H
+#define GC_EDGE_H
+
+#include "gc-ref.h"
+
+struct gc_edge {
+ struct gc_ref *dst;
+};
+
+static inline struct gc_edge gc_edge(void* addr) {
+ return (struct gc_edge){addr};
+}
+static inline struct gc_ref gc_edge_ref(struct gc_edge edge) {
+ return *edge.dst;
+}
+static inline struct gc_ref* gc_edge_loc(struct gc_edge edge) {
+ return edge.dst;
+}
+static inline uintptr_t gc_edge_address(struct gc_edge edge) {
+ return (uintptr_t)gc_edge_loc(edge);
+}
+static inline void gc_edge_update(struct gc_edge edge, struct gc_ref ref) {
+ *edge.dst = ref;
+}
+
+#endif // GC_EDGE_H
diff --git a/libguile/whippet/api/gc-embedder-api.h b/libguile/whippet/api/gc-embedder-api.h
new file mode 100644
index 000000000..c1b272a51
--- /dev/null
+++ b/libguile/whippet/api/gc-embedder-api.h
@@ -0,0 +1,67 @@
+#ifndef GC_EMBEDDER_API_H
+#define GC_EMBEDDER_API_H
+
+#include <stddef.h>
+
+#include "gc-config.h"
+#include "gc-edge.h"
+#include "gc-inline.h"
+#include "gc-forwarding.h"
+
+#ifndef GC_EMBEDDER_API
+#define GC_EMBEDDER_API static
+#endif
+
+struct gc_mutator_roots;
+struct gc_heap_roots;
+struct gc_atomic_forward;
+struct gc_heap;
+struct gc_extern_space;
+
+GC_EMBEDDER_API inline int gc_is_valid_conservative_ref_displacement(uintptr_t displacement);
+GC_EMBEDDER_API inline size_t gc_finalizer_priority_count(void);
+
+GC_EMBEDDER_API inline int gc_extern_space_visit(struct gc_extern_space *space,
+ struct gc_edge edge,
+ 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,
+ void *visit_data),
+ struct gc_heap *heap,
+ void *trace_data,
+ size_t *size) GC_ALWAYS_INLINE;
+
+GC_EMBEDDER_API inline void gc_trace_mutator_roots(struct gc_mutator_roots *roots,
+ void (*trace_edge)(struct gc_edge edge,
+ struct gc_heap *heap,
+ void *trace_data),
+ struct gc_heap *heap,
+ void *trace_data);
+GC_EMBEDDER_API inline void gc_trace_heap_roots(struct gc_heap_roots *roots,
+ void (*trace_edge)(struct gc_edge edge,
+ struct gc_heap *heap,
+ void *trace_data),
+ struct gc_heap *heap,
+ void *trace_data);
+
+GC_EMBEDDER_API inline uintptr_t gc_object_forwarded_nonatomic(struct gc_ref ref);
+GC_EMBEDDER_API inline void gc_object_forward_nonatomic(struct gc_ref ref,
+ struct gc_ref new_ref);
+
+GC_EMBEDDER_API inline struct gc_atomic_forward gc_atomic_forward_begin(struct gc_ref ref);
+GC_EMBEDDER_API inline void gc_atomic_forward_acquire(struct gc_atomic_forward *);
+GC_EMBEDDER_API inline int gc_atomic_forward_retry_busy(struct gc_atomic_forward *);
+GC_EMBEDDER_API inline void gc_atomic_forward_abort(struct gc_atomic_forward *);
+GC_EMBEDDER_API inline size_t gc_atomic_forward_object_size(struct gc_atomic_forward *);
+GC_EMBEDDER_API inline void gc_atomic_forward_commit(struct gc_atomic_forward *,
+ struct gc_ref new_ref);
+GC_EMBEDDER_API inline uintptr_t gc_atomic_forward_address(struct gc_atomic_forward *);
+
+
+#endif // GC_EMBEDDER_API_H
diff --git a/libguile/whippet/api/gc-ephemeron.h b/libguile/whippet/api/gc-ephemeron.h
new file mode 100644
index 000000000..1d9e59b55
--- /dev/null
+++ b/libguile/whippet/api/gc-ephemeron.h
@@ -0,0 +1,42 @@
+#ifndef GC_EPHEMERON_H_
+#define GC_EPHEMERON_H_
+
+#include "gc-edge.h"
+#include "gc-ref.h"
+#include "gc-visibility.h"
+
+// Ephemerons establish an association between a "key" object and a
+// "value" object. If the ephemeron and the key are live, then the
+// value is live, and can be retrieved from the ephemeron. Ephemerons
+// can be chained together, which allows them to function as links in a
+// buckets-and-chains hash table.
+//
+// This file defines the user-facing API for ephemerons.
+
+struct gc_heap;
+struct gc_mutator;
+struct gc_ephemeron;
+
+GC_API_ size_t gc_ephemeron_size(void);
+GC_API_ struct gc_ephemeron* gc_allocate_ephemeron(struct gc_mutator *mut);
+GC_API_ void gc_ephemeron_init(struct gc_mutator *mut,
+ struct gc_ephemeron *ephemeron,
+ struct gc_ref key, struct gc_ref value);
+
+GC_API_ struct gc_ref gc_ephemeron_key(struct gc_ephemeron *ephemeron);
+GC_API_ struct gc_ref gc_ephemeron_value(struct gc_ephemeron *ephemeron);
+
+GC_API_ struct gc_ephemeron* gc_ephemeron_chain_head(struct gc_ephemeron **loc);
+GC_API_ void gc_ephemeron_chain_push(struct gc_ephemeron **loc,
+ struct gc_ephemeron *ephemeron);
+GC_API_ struct gc_ephemeron* gc_ephemeron_chain_next(struct gc_ephemeron *ephemeron);
+GC_API_ void gc_ephemeron_mark_dead(struct gc_ephemeron *ephemeron);
+
+GC_API_ void gc_trace_ephemeron(struct gc_ephemeron *ephemeron,
+ void (*visit)(struct gc_edge edge,
+ struct gc_heap *heap,
+ void *visit_data),
+ struct gc_heap *heap,
+ void *trace_data);
+
+#endif // GC_EPHEMERON_H_
diff --git a/libguile/whippet/api/gc-event-listener-chain.h b/libguile/whippet/api/gc-event-listener-chain.h
new file mode 100644
index 000000000..27b56d5c6
--- /dev/null
+++ b/libguile/whippet/api/gc-event-listener-chain.h
@@ -0,0 +1,145 @@
+#ifndef GC_EVENT_LISTENER_CHAIN_H
+#define GC_EVENT_LISTENER_CHAIN_H
+
+#include "gc-event-listener.h"
+
+struct gc_event_listener_chain {
+ struct gc_event_listener head; void *head_data;
+ struct gc_event_listener tail; void *tail_data;
+};
+
+struct gc_event_listener_chain_mutator {
+ struct gc_event_listener_chain *chain;
+ void *head_mutator_data;
+ void *tail_mutator_data;
+};
+
+static inline void gc_event_listener_chain_init(void *data, size_t heap_size) {
+ struct gc_event_listener_chain *chain = data;
+ chain->head.init(chain->head_data, heap_size);
+ chain->tail.init(chain->tail_data, heap_size);
+}
+
+static inline void gc_event_listener_chain_requesting_stop(void *data) {
+ struct gc_event_listener_chain *chain = data;
+ chain->head.requesting_stop(chain->head_data);
+ chain->tail.requesting_stop(chain->tail_data);
+}
+static inline void gc_event_listener_chain_waiting_for_stop(void *data) {
+ struct gc_event_listener_chain *chain = data;
+ chain->head.waiting_for_stop(chain->head_data);
+ chain->tail.waiting_for_stop(chain->tail_data);
+}
+static inline void gc_event_listener_chain_mutators_stopped(void *data) {
+ struct gc_event_listener_chain *chain = data;
+ chain->head.mutators_stopped(chain->head_data);
+ chain->tail.mutators_stopped(chain->tail_data);
+}
+static inline void
+gc_event_listener_chain_prepare_gc(void *data, enum gc_collection_kind kind) {
+ struct gc_event_listener_chain *chain = data;
+ chain->head.prepare_gc(chain->head_data, kind);
+ chain->tail.prepare_gc(chain->tail_data, kind);
+}
+static inline void gc_event_listener_chain_roots_traced(void *data) {
+ struct gc_event_listener_chain *chain = data;
+ chain->head.roots_traced(chain->head_data);
+ chain->tail.roots_traced(chain->tail_data);
+}
+static inline void gc_event_listener_chain_heap_traced(void *data) {
+ struct gc_event_listener_chain *chain = data;
+ chain->head.heap_traced(chain->head_data);
+ chain->tail.heap_traced(chain->tail_data);
+}
+static inline void gc_event_listener_chain_ephemerons_traced(void *data) {
+ struct gc_event_listener_chain *chain = data;
+ chain->head.ephemerons_traced(chain->head_data);
+ chain->tail.ephemerons_traced(chain->tail_data);
+}
+static inline void gc_event_listener_chain_finalizers_traced(void *data) {
+ struct gc_event_listener_chain *chain = data;
+ chain->head.finalizers_traced(chain->head_data);
+ chain->tail.finalizers_traced(chain->tail_data);
+}
+
+static inline void gc_event_listener_chain_restarting_mutators(void *data) {
+ struct gc_event_listener_chain *chain = data;
+ chain->head.restarting_mutators(chain->head_data);
+ chain->tail.restarting_mutators(chain->tail_data);
+}
+
+static inline void* gc_event_listener_chain_mutator_added(void *data) {
+ struct gc_event_listener_chain *chain = data;
+ struct gc_event_listener_chain_mutator *mutator = malloc(sizeof(*mutator));;
+ if (!mutator) abort();
+ mutator->chain = chain;
+ mutator->head_mutator_data = chain->head.mutator_added(chain->head_data);
+ mutator->tail_mutator_data = chain->tail.mutator_added(chain->tail_data);
+ return mutator;
+}
+
+static inline void gc_event_listener_chain_mutator_cause_gc(void *mutator_data) {
+ struct gc_event_listener_chain_mutator *mutator = mutator_data;
+ mutator->chain->head.restarting_mutators(mutator->head_data);
+ mutator->chain->tail.restarting_mutators(mutator->tail_data);
+}
+static inline void gc_event_listener_chain_mutator_stopping(void *mutator_data) {
+ struct gc_event_listener_chain_mutator *mutator = mutator_data;
+ mutator->chain->head.mutator_stopping(mutator->head_data);
+ mutator->chain->tail.mutator_stopping(mutator->tail_data);
+}
+static inline void gc_event_listener_chain_mutator_stopped(void *mutator_data) {
+ struct gc_event_listener_chain_mutator *mutator = mutator_data;
+ mutator->chain->head.mutator_stopped(mutator->head_data);
+ mutator->chain->tail.mutator_stopped(mutator->tail_data);
+}
+static inline void gc_event_listener_chain_mutator_restarted(void *mutator_data) {
+ struct gc_event_listener_chain_mutator *mutator = mutator_data;
+ mutator->chain->head.mutator_restarted(mutator->head_data);
+ mutator->chain->tail.mutator_restarted(mutator->tail_data);
+}
+static inline void gc_event_listener_chain_mutator_removed(void *mutator_data) {
+ struct gc_event_listener_chain_mutator *mutator = mutator_data;
+ mutator->chain->head.mutator_removed(mutator->head_data);
+ mutator->chain->tail.mutator_removed(mutator->tail_data);
+ free(mutator);
+}
+
+static inline void gc_event_listener_chain_heap_resized(void *data, size_t size) {
+ struct gc_event_listener_chain *chain = data;
+ chain->head.heap_resized(chain->head_data, size);
+ chain->tail.heap_resized(chain->tail_data, size);
+}
+
+static inline void gc_event_listener_chain_live_data_size(void *data, size_t size) {
+ struct gc_event_listener_chain *chain = data;
+ chain->head.live_data_size(chain->head_data, size);
+ chain->tail.live_data_size(chain->tail_data, size);
+}
+
+#define GC_EVENT_LISTENER_CHAIN \
+ ((struct gc_event_listener) { \
+ gc_event_listener_chain_init, \
+ gc_event_listener_chain_requesting_stop, \
+ gc_event_listener_chain_waiting_for_stop, \
+ gc_event_listener_chain_mutators_stopped, \
+ gc_event_listener_chain_prepare_gc, \
+ gc_event_listener_chain_roots_traced, \
+ gc_event_listener_chain_heap_traced, \
+ gc_event_listener_chain_ephemerons_traced, \
+ gc_event_listener_chain_finalizers_traced, \
+ gc_event_listener_chain_restarting_mutators, \
+ gc_event_listener_chain_mutator_added, \
+ gc_event_listener_chain_mutator_cause_gc, \
+ gc_event_listener_chain_mutator_stopping, \
+ gc_event_listener_chain_mutator_stopped, \
+ gc_event_listener_chain_mutator_restarted, \
+ gc_event_listener_chain_mutator_removed, \
+ gc_event_listener_chain_heap_resized, \
+ gc_event_listener_chain_live_data_size, \
+ })
+
+#define GC_EVENT_LISTENER_CHAIN_DATA(head, head_data, tail, tail_data) \
+ ((struct gc_event_listener_chain_data){head, head_data, tail, tail_data})
+
+#endif // GC_EVENT_LISTENER_CHAIN_H
diff --git a/libguile/whippet/api/gc-event-listener.h b/libguile/whippet/api/gc-event-listener.h
new file mode 100644
index 000000000..66801a52c
--- /dev/null
+++ b/libguile/whippet/api/gc-event-listener.h
@@ -0,0 +1,29 @@
+#ifndef GC_EVENT_LISTENER_H
+#define GC_EVENT_LISTENER_H
+
+#include "gc-collection-kind.h"
+
+struct gc_event_listener {
+ void (*init)(void *data, size_t heap_size);
+ void (*requesting_stop)(void *data);
+ void (*waiting_for_stop)(void *data);
+ void (*mutators_stopped)(void *data);
+ void (*prepare_gc)(void *data, enum gc_collection_kind kind);
+ void (*roots_traced)(void *data);
+ void (*heap_traced)(void *data);
+ void (*ephemerons_traced)(void *data);
+ void (*finalizers_traced)(void *data);
+ void (*restarting_mutators)(void *data);
+
+ void* (*mutator_added)(void *data);
+ void (*mutator_cause_gc)(void *mutator_data);
+ void (*mutator_stopping)(void *mutator_data);
+ void (*mutator_stopped)(void *mutator_data);
+ void (*mutator_restarted)(void *mutator_data);
+ void (*mutator_removed)(void *mutator_data);
+
+ void (*heap_resized)(void *data, size_t size);
+ void (*live_data_size)(void *data, size_t size);
+};
+
+#endif // GC_EVENT_LISTENER_H
diff --git a/libguile/whippet/api/gc-finalizer.h b/libguile/whippet/api/gc-finalizer.h
new file mode 100644
index 000000000..1dcb0fb2f
--- /dev/null
+++ b/libguile/whippet/api/gc-finalizer.h
@@ -0,0 +1,81 @@
+#ifndef GC_FINALIZER_H_
+#define GC_FINALIZER_H_
+
+#include "gc-edge.h"
+#include "gc-ref.h"
+#include "gc-visibility.h"
+
+// A finalizer allows the embedder to be notified when an object becomes
+// unreachable.
+//
+// A finalizer has a priority. When the heap is created, the embedder
+// should declare how many priorities there are. Lower-numbered
+// priorities take precedence; if an object has a priority-0 finalizer
+// outstanding, that will prevent any finalizer at level 1 (or 2, ...)
+// from firing until no priority-0 finalizer remains.
+//
+// Call gc_attach_finalizer to attach a finalizer to an object.
+//
+// A finalizer also references an associated GC-managed closure object.
+// A finalizer's reference to the closure object is strong: if a
+// finalizer's closure closure references its finalizable object,
+// directly or indirectly, the finalizer will never fire.
+//
+// When an object with a finalizer becomes unreachable, it is added to a
+// queue. The embedder can call gc_pop_finalizable to get the next
+// finalizable object and its associated closure. At that point the
+// embedder can do anything with the object, including keeping it alive.
+// Ephemeron associations will still be present while the finalizable
+// object is live. Note however that any objects referenced by the
+// finalizable object may themselves be already finalized; finalizers
+// are enqueued for objects when they become unreachable, which can
+// concern whole subgraphs of objects at once.
+//
+// The usual way for an embedder to know when the queue of finalizable
+// object is non-empty is to call gc_set_finalizer_callback to
+// provide a function that will be invoked when there are pending
+// finalizers.
+//
+// Arranging to call gc_pop_finalizable and doing something with the
+// finalizable object and closure is the responsibility of the embedder.
+// The embedder's finalization action can end up invoking arbitrary
+// code, so unless the embedder imposes some kind of restriction on what
+// finalizers can do, generally speaking finalizers should be run in a
+// dedicated thread instead of recursively from within whatever mutator
+// thread caused GC. Setting up such a thread is the responsibility of
+// the mutator. gc_pop_finalizable is thread-safe, allowing multiple
+// finalization threads if that is appropriate.
+//
+// gc_allocate_finalizer returns a finalizer, which is a fresh
+// GC-managed heap object. The mutator should then directly attach it
+// to an object using gc_finalizer_attach. When the finalizer is fired,
+// it becomes available to the mutator via gc_pop_finalizable.
+
+struct gc_heap;
+struct gc_mutator;
+struct gc_finalizer;
+
+GC_API_ size_t gc_finalizer_size(void);
+GC_API_ struct gc_finalizer* gc_allocate_finalizer(struct gc_mutator *mut);
+GC_API_ void gc_finalizer_attach(struct gc_mutator *mut,
+ struct gc_finalizer *finalizer,
+ unsigned priority,
+ struct gc_ref object, struct gc_ref closure);
+
+GC_API_ struct gc_ref gc_finalizer_object(struct gc_finalizer *finalizer);
+GC_API_ struct gc_ref gc_finalizer_closure(struct gc_finalizer *finalizer);
+
+GC_API_ struct gc_finalizer* gc_pop_finalizable(struct gc_mutator *mut);
+
+typedef void (*gc_finalizer_callback)(struct gc_heap *heap, size_t count);
+GC_API_ void gc_set_finalizer_callback(struct gc_heap *heap,
+ gc_finalizer_callback callback);
+
+GC_API_ void gc_trace_finalizer(struct gc_finalizer *finalizer,
+ void (*visit)(struct gc_edge edge,
+ struct gc_heap *heap,
+ void *visit_data),
+ struct gc_heap *heap,
+ void *trace_data);
+
+#endif // GC_FINALIZER_H_
diff --git a/libguile/whippet/api/gc-forwarding.h b/libguile/whippet/api/gc-forwarding.h
new file mode 100644
index 000000000..25aca3011
--- /dev/null
+++ b/libguile/whippet/api/gc-forwarding.h
@@ -0,0 +1,20 @@
+#ifndef GC_FORWARDING_H
+#define GC_FORWARDING_H
+
+#include <stdint.h>
+#include "gc-ref.h"
+
+enum gc_forwarding_state {
+ GC_FORWARDING_STATE_FORWARDED,
+ GC_FORWARDING_STATE_BUSY,
+ GC_FORWARDING_STATE_ACQUIRED,
+ GC_FORWARDING_STATE_NOT_FORWARDED
+};
+
+struct gc_atomic_forward {
+ struct gc_ref ref;
+ uintptr_t data;
+ enum gc_forwarding_state state;
+};
+
+#endif // GC_FORWARDING_H
diff --git a/libguile/whippet/api/gc-histogram.h b/libguile/whippet/api/gc-histogram.h
new file mode 100644
index 000000000..0761a630f
--- /dev/null
+++ b/libguile/whippet/api/gc-histogram.h
@@ -0,0 +1,82 @@
+#ifndef GC_HISTOGRAM_H
+#define GC_HISTOGRAM_H
+
+#include "gc-assert.h"
+
+#include <stdint.h>
+
+static inline size_t gc_histogram_bucket(uint64_t max_value_bits,
+ uint64_t precision,
+ uint64_t val) {
+ uint64_t major = val < (1ULL << precision)
+ ? 0ULL
+ : 64ULL - __builtin_clzl(val) - precision;
+ uint64_t minor = val < (1 << precision)
+ ? val
+ : (val >> (major - 1ULL)) & ((1ULL << precision) - 1ULL);
+ uint64_t idx = (major << precision) | minor;
+ if (idx >= (max_value_bits << precision))
+ idx = max_value_bits << precision;
+ return idx;
+}
+
+static inline uint64_t gc_histogram_bucket_min_val(uint64_t precision,
+ size_t idx) {
+ uint64_t major = idx >> precision;
+ uint64_t minor = idx & ((1ULL << precision) - 1ULL);
+ uint64_t min_val = major
+ ? ((1ULL << precision) | minor) << (major - 1ULL)
+ : minor;
+ return min_val;
+}
+
+#define GC_DEFINE_HISTOGRAM(name, max_value_bits, precision) \
+ struct name { uint32_t buckets[((max_value_bits) << (precision)) + 1]; }; \
+ static inline size_t name##_size(void) { \
+ return ((max_value_bits) << (precision)) + 1; \
+ } \
+ static inline uint64_t name##_bucket_min_val(size_t idx) { \
+ GC_ASSERT(idx < name##_size()); \
+ return gc_histogram_bucket_min_val((precision), idx); \
+ } \
+ static inline struct name make_##name(void) { \
+ return (struct name) { { 0, }}; \
+ } \
+ static inline void name##_record(struct name *h, uint64_t val) { \
+ h->buckets[gc_histogram_bucket((max_value_bits), (precision), val)]++; \
+ } \
+ static inline uint64_t name##_ref(struct name *h, size_t idx) { \
+ GC_ASSERT(idx < name##_size()); \
+ return h->buckets[idx]; \
+ } \
+ static inline uint64_t name##_min(struct name *h) { \
+ for (size_t bucket = 0; bucket < name##_size(); bucket++) \
+ if (h->buckets[bucket]) return name##_bucket_min_val(bucket); \
+ return -1; \
+ } \
+ static inline uint64_t name##_max(struct name *h) { \
+ if (h->buckets[name##_size()-1]) return -1LL; \
+ for (ssize_t bucket = name##_size() - 1; bucket >= 0; bucket--) \
+ if (h->buckets[bucket]) return name##_bucket_min_val(bucket+1); \
+ return 0; \
+ } \
+ static inline uint64_t name##_count(struct name *h) { \
+ uint64_t sum = 0; \
+ for (size_t bucket = 0; bucket < name##_size(); bucket++) \
+ sum += h->buckets[bucket]; \
+ return sum; \
+ } \
+ static inline uint64_t name##_percentile(struct name *h, double p) { \
+ uint64_t n = name##_count(h) * p; \
+ uint64_t sum = 0; \
+ for (size_t bucket = 0; bucket + 1 < name##_size(); bucket++) { \
+ sum += h->buckets[bucket]; \
+ if (sum >= n) return name##_bucket_min_val(bucket+1); \
+ } \
+ return -1ULL; \
+ } \
+ static inline uint64_t name##_median(struct name *h) { \
+ return name##_percentile(h, 0.5); \
+ }
+
+#endif // GC_HISTOGRAM_H
diff --git a/libguile/whippet/api/gc-inline.h b/libguile/whippet/api/gc-inline.h
new file mode 100644
index 000000000..30eac54f3
--- /dev/null
+++ b/libguile/whippet/api/gc-inline.h
@@ -0,0 +1,7 @@
+#ifndef GC_INLINE_H_
+#define GC_INLINE_H_
+
+#define GC_ALWAYS_INLINE __attribute__((always_inline))
+#define GC_NEVER_INLINE __attribute__((noinline))
+
+#endif // GC_INLINE_H_
diff --git a/libguile/whippet/api/gc-lttng.h b/libguile/whippet/api/gc-lttng.h
new file mode 100644
index 000000000..d192be4ed
--- /dev/null
+++ b/libguile/whippet/api/gc-lttng.h
@@ -0,0 +1,100 @@
+#define LTTNG_UST_TRACEPOINT_PROVIDER whippet
+
+#undef LTTNG_UST_TRACEPOINT_INCLUDE
+#define LTTNG_UST_TRACEPOINT_INCLUDE "gc-lttng.h"
+
+#if !defined(_TP_H) || defined(LTTNG_UST_TRACEPOINT_HEADER_MULTI_READ)
+#define _TP_H
+
+#include <lttng/tracepoint.h>
+
+LTTNG_UST_TRACEPOINT_ENUM(
+ whippet, gc_kind,
+ LTTNG_UST_TP_ENUM_VALUES
+ (lttng_ust_field_enum_value("MINOR", 1)
+ lttng_ust_field_enum_value("MAJOR", 2)
+ lttng_ust_field_enum_value("COMPACTING", 3)))
+
+LTTNG_UST_TRACEPOINT_EVENT_CLASS(
+ whippet, tracepoint,
+ LTTNG_UST_TP_ARGS(),
+ LTTNG_UST_TP_FIELDS())
+
+LTTNG_UST_TRACEPOINT_EVENT_CLASS(
+ whippet, size_tracepoint,
+ LTTNG_UST_TP_ARGS(size_t, size),
+ LTTNG_UST_TP_FIELDS(lttng_ust_field_integer(size_t, size, size)))
+
+
+/* The tracepoint instances */
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, size_tracepoint, whippet, init,
+ LTTNG_UST_TP_ARGS(size_t, size))
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, size_tracepoint, whippet, heap_resized,
+ LTTNG_UST_TP_ARGS(size_t, size))
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, size_tracepoint, whippet, live_data_size,
+ LTTNG_UST_TP_ARGS(size_t, size))
+
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, requesting_stop, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, waiting_for_stop, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, mutators_stopped, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT(
+ whippet, prepare_gc,
+ LTTNG_UST_TP_ARGS(int, gc_kind),
+ LTTNG_UST_TP_FIELDS(
+ lttng_ust_field_enum(whippet, gc_kind, int, gc_kind, gc_kind)))
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, roots_traced, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, heap_traced, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, ephemerons_traced, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, finalizers_traced, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, restarting_mutators, LTTNG_UST_TP_ARGS())
+
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, mutator_added, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, mutator_cause_gc, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, mutator_stopping, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, mutator_stopped, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, mutator_restarted, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, mutator_removed, LTTNG_UST_TP_ARGS())
+
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, trace_unpark_all, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, trace_share, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, trace_check_termination_begin, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, trace_check_termination_end, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, trace_steal, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, trace_roots_begin, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, trace_roots_end, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, trace_objects_begin, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, trace_objects_end, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, trace_worker_begin, LTTNG_UST_TP_ARGS())
+LTTNG_UST_TRACEPOINT_EVENT_INSTANCE(
+ whippet, tracepoint, whippet, trace_worker_end, LTTNG_UST_TP_ARGS())
+
+#endif /* _TP_H */
+
+#include <lttng/tracepoint-event.h>
diff --git a/libguile/whippet/api/gc-null-event-listener.h b/libguile/whippet/api/gc-null-event-listener.h
new file mode 100644
index 000000000..9c032ffc2
--- /dev/null
+++ b/libguile/whippet/api/gc-null-event-listener.h
@@ -0,0 +1,50 @@
+#ifndef GC_NULL_EVENT_LISTENER_H
+#define GC_NULL_EVENT_LISTENER_H
+
+#include "gc-event-listener.h"
+
+static inline void gc_null_event_listener_init(void *data, size_t size) {}
+static inline void gc_null_event_listener_requesting_stop(void *data) {}
+static inline void gc_null_event_listener_waiting_for_stop(void *data) {}
+static inline void gc_null_event_listener_mutators_stopped(void *data) {}
+static inline void gc_null_event_listener_prepare_gc(void *data,
+ enum gc_collection_kind) {}
+static inline void gc_null_event_listener_roots_traced(void *data) {}
+static inline void gc_null_event_listener_heap_traced(void *data) {}
+static inline void gc_null_event_listener_ephemerons_traced(void *data) {}
+static inline void gc_null_event_listener_finalizers_traced(void *data) {}
+static inline void gc_null_event_listener_restarting_mutators(void *data) {}
+
+static inline void* gc_null_event_listener_mutator_added(void *data) {}
+static inline void gc_null_event_listener_mutator_cause_gc(void *mutator_data) {}
+static inline void gc_null_event_listener_mutator_stopping(void *mutator_data) {}
+static inline void gc_null_event_listener_mutator_stopped(void *mutator_data) {}
+static inline void gc_null_event_listener_mutator_restarted(void *mutator_data) {}
+static inline void gc_null_event_listener_mutator_removed(void *mutator_data) {}
+
+static inline void gc_null_event_listener_heap_resized(void *, size_t) {}
+static inline void gc_null_event_listener_live_data_size(void *, size_t) {}
+
+#define GC_NULL_EVENT_LISTENER \
+ ((struct gc_event_listener) { \
+ gc_null_event_listener_init, \
+ gc_null_event_listener_requesting_stop, \
+ gc_null_event_listener_waiting_for_stop, \
+ gc_null_event_listener_mutators_stopped, \
+ gc_null_event_listener_prepare_gc, \
+ gc_null_event_listener_roots_traced, \
+ gc_null_event_listener_heap_traced, \
+ gc_null_event_listener_ephemerons_traced, \
+ gc_null_event_listener_finalizers_traced, \
+ gc_null_event_listener_restarting_mutators, \
+ gc_null_event_listener_mutator_added, \
+ gc_null_event_listener_mutator_cause_gc, \
+ gc_null_event_listener_mutator_stopping, \
+ gc_null_event_listener_mutator_stopped, \
+ gc_null_event_listener_mutator_restarted, \
+ gc_null_event_listener_mutator_removed, \
+ gc_null_event_listener_heap_resized, \
+ gc_null_event_listener_live_data_size, \
+ })
+
+#endif // GC_NULL_EVENT_LISTENER_H_
diff --git a/libguile/whippet/api/gc-options.h b/libguile/whippet/api/gc-options.h
new file mode 100644
index 000000000..2f3f7f792
--- /dev/null
+++ b/libguile/whippet/api/gc-options.h
@@ -0,0 +1,39 @@
+#ifndef GC_OPTIONS_H
+#define GC_OPTIONS_H
+
+#include "gc-visibility.h"
+
+enum gc_heap_size_policy {
+ GC_HEAP_SIZE_FIXED,
+ GC_HEAP_SIZE_GROWABLE,
+ GC_HEAP_SIZE_ADAPTIVE,
+};
+
+enum {
+ GC_OPTION_HEAP_SIZE_POLICY,
+ GC_OPTION_HEAP_SIZE,
+ GC_OPTION_MAXIMUM_HEAP_SIZE,
+ GC_OPTION_HEAP_SIZE_MULTIPLIER,
+ GC_OPTION_HEAP_EXPANSIVENESS,
+ GC_OPTION_PARALLELISM
+};
+
+struct gc_options;
+
+GC_API_ int gc_option_from_string(const char *str);
+
+GC_API_ struct gc_options* gc_allocate_options(void);
+
+GC_API_ int gc_options_set_int(struct gc_options *options, int option,
+ int value);
+GC_API_ int gc_options_set_size(struct gc_options *options, int option,
+ size_t value);
+GC_API_ int gc_options_set_double(struct gc_options *options, int option,
+ double value);
+
+GC_API_ int gc_options_parse_and_set(struct gc_options *options,
+ int option, const char *value);
+GC_API_ int gc_options_parse_and_set_many(struct gc_options *options,
+ const char *str);
+
+#endif // GC_OPTIONS_H
diff --git a/libguile/whippet/api/gc-ref.h b/libguile/whippet/api/gc-ref.h
new file mode 100644
index 000000000..29e1a3853
--- /dev/null
+++ b/libguile/whippet/api/gc-ref.h
@@ -0,0 +1,50 @@
+#ifndef GC_REF_H
+#define GC_REF_H
+
+#include "gc-assert.h"
+#include "gc-config.h"
+
+#include <stdint.h>
+
+struct gc_ref {
+ uintptr_t value;
+};
+
+static inline struct gc_ref gc_ref(uintptr_t value) {
+ return (struct gc_ref){value};
+}
+static inline uintptr_t gc_ref_value(struct gc_ref ref) {
+ return ref.value;
+}
+
+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 !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);
+}
+static inline struct gc_ref gc_ref_from_heap_object(void *obj) {
+ GC_ASSERT(obj);
+ return gc_ref_from_heap_object_or_null(obj);
+}
+static inline void* gc_ref_heap_object(struct gc_ref ref) {
+ GC_ASSERT(gc_ref_is_heap_object(ref));
+ return (void *) gc_ref_value(ref);
+}
+
+#endif // GC_REF_H
diff --git a/libguile/whippet/api/gc-tracepoint.h b/libguile/whippet/api/gc-tracepoint.h
new file mode 100644
index 000000000..598d0bc44
--- /dev/null
+++ b/libguile/whippet/api/gc-tracepoint.h
@@ -0,0 +1,17 @@
+#ifndef GC_TRACEPOINT_H
+#define GC_TRACEPOINT_H
+
+#ifdef GC_TRACEPOINT_LTTNG
+
+#include "gc-lttng.h"
+
+#define GC_TRACEPOINT(...) \
+ lttng_ust_tracepoint(whippet, __VA_ARGS__)
+
+#else // GC_TRACEPOINT_LTTNG
+
+#define GC_TRACEPOINT(...) do {} while (0)
+
+#endif // GC_TRACEPOINT_LTTNG
+
+#endif // GC_TRACEPOINT_H
diff --git a/libguile/whippet/api/gc-visibility.h b/libguile/whippet/api/gc-visibility.h
new file mode 100644
index 000000000..b7e1995df
--- /dev/null
+++ b/libguile/whippet/api/gc-visibility.h
@@ -0,0 +1,12 @@
+#ifndef GC_VISIBILITY_H_
+#define GC_VISIBILITY_H_
+
+#define GC_INTERNAL __attribute__((visibility("hidden")))
+#define GC_PUBLIC __attribute__((visibility("default")))
+
+// FIXME: Conflict with bdw-gc GC_API. Switch prefix?
+#ifndef GC_API_
+#define GC_API_ GC_INTERNAL
+#endif
+
+#endif // GC_VISIBILITY_H
diff --git a/libguile/whippet/api/mmc-attrs.h b/libguile/whippet/api/mmc-attrs.h
new file mode 100644
index 000000000..9371f8abe
--- /dev/null
+++ b/libguile/whippet/api/mmc-attrs.h
@@ -0,0 +1,121 @@
+#ifndef MMC_ATTRS_H
+#define MMC_ATTRS_H
+
+#include "gc-config.h"
+#include "gc-assert.h"
+#include "gc-attrs.h"
+
+static inline enum gc_allocator_kind gc_allocator_kind(void) {
+ return GC_ALLOCATOR_INLINE_BUMP_POINTER;
+}
+static inline size_t gc_allocator_small_granule_size(void) {
+ return 16;
+}
+static inline size_t gc_allocator_large_threshold(void) {
+ return 8192;
+}
+
+static inline size_t gc_allocator_allocation_pointer_offset(void) {
+ return sizeof(uintptr_t) * 0;
+}
+static inline size_t gc_allocator_allocation_limit_offset(void) {
+ return sizeof(uintptr_t) * 1;
+}
+
+static inline size_t gc_allocator_freelist_offset(size_t size,
+ enum gc_allocation_kind kind) {
+ GC_CRASH();
+}
+
+static inline size_t gc_allocator_alloc_table_alignment(void) {
+ return 4 * 1024 * 1024;
+}
+static inline uint8_t gc_allocator_alloc_table_begin_pattern(enum gc_allocation_kind kind) {
+ uint8_t young = 1;
+ uint8_t trace_precisely = 0;
+ uint8_t trace_none = 8;
+ uint8_t trace_conservatively = 16;
+ uint8_t pinned = 16;
+ if (GC_CONSERVATIVE_TRACE) {
+ switch (kind) {
+ case GC_ALLOCATION_TAGGED:
+ case GC_ALLOCATION_UNTAGGED_CONSERVATIVE:
+ return young | trace_conservatively;
+ case GC_ALLOCATION_TAGGED_POINTERLESS:
+ return young | trace_none;
+ case GC_ALLOCATION_UNTAGGED_POINTERLESS:
+ return young | trace_none;
+ default:
+ GC_CRASH();
+ };
+ } else {
+ switch (kind) {
+ case GC_ALLOCATION_TAGGED:
+ return young | trace_precisely;
+ case GC_ALLOCATION_TAGGED_POINTERLESS:
+ return young | trace_none;
+ case GC_ALLOCATION_UNTAGGED_POINTERLESS:
+ return young | trace_none | pinned;
+ case GC_ALLOCATION_UNTAGGED_CONSERVATIVE:
+ default:
+ GC_CRASH();
+ };
+ }
+}
+static inline uint8_t gc_allocator_alloc_table_end_pattern(void) {
+ return 32;
+}
+
+static inline enum gc_old_generation_check_kind gc_old_generation_check_kind(size_t obj_size) {
+ if (GC_GENERATIONAL) {
+ if (obj_size <= gc_allocator_large_threshold())
+ return GC_OLD_GENERATION_CHECK_ALLOC_TABLE;
+ return GC_OLD_GENERATION_CHECK_SLOW;
+ }
+ return GC_OLD_GENERATION_CHECK_NONE;
+}
+static inline uint8_t gc_old_generation_check_alloc_table_tag_mask(void) {
+ return 7;
+}
+static inline uint8_t gc_old_generation_check_alloc_table_young_tag(void) {
+ return 1;
+}
+
+static inline enum gc_write_barrier_kind gc_write_barrier_kind(size_t obj_size) {
+ if (GC_GENERATIONAL) {
+ if (obj_size <= gc_allocator_large_threshold())
+ return GC_WRITE_BARRIER_FIELD;
+ return GC_WRITE_BARRIER_SLOW;
+ }
+ return GC_WRITE_BARRIER_NONE;
+}
+static inline size_t gc_write_barrier_field_table_alignment(void) {
+ GC_ASSERT(GC_GENERATIONAL);
+ return gc_allocator_alloc_table_alignment();
+}
+static inline ptrdiff_t gc_write_barrier_field_table_offset(void) {
+ GC_ASSERT(GC_GENERATIONAL);
+ return 0;
+}
+static inline size_t gc_write_barrier_field_fields_per_byte(void) {
+ GC_ASSERT(GC_GENERATIONAL);
+ return 2;
+}
+static inline uint8_t gc_write_barrier_field_first_bit_pattern(void) {
+ GC_ASSERT(GC_GENERATIONAL);
+ return 64; // NOFL_METADATA_BYTE_LOGGED_0
+}
+
+static inline enum gc_safepoint_mechanism gc_safepoint_mechanism(void) {
+ return GC_SAFEPOINT_MECHANISM_COOPERATIVE;
+}
+
+static inline enum gc_cooperative_safepoint_kind gc_cooperative_safepoint_kind(void) {
+ return GC_COOPERATIVE_SAFEPOINT_HEAP_FLAG;
+}
+
+static inline int gc_can_pin_objects(void) {
+ return 1;
+}
+
+#endif // MMC_ATTRS_H
diff --git a/libguile/whippet/api/pcc-attrs.h b/libguile/whippet/api/pcc-attrs.h
new file mode 100644
index 000000000..12a555a5d
--- /dev/null
+++ b/libguile/whippet/api/pcc-attrs.h
@@ -0,0 +1,92 @@
+#ifndef PCC_ATTRS_H
+#define PCC_ATTRS_H
+
+#include "gc-config.h"
+#include "gc-assert.h"
+#include "gc-attrs.h"
+
+static const uintptr_t GC_ALIGNMENT = 8;
+static const size_t GC_LARGE_OBJECT_THRESHOLD = 8192;
+
+static inline enum gc_allocator_kind gc_allocator_kind(void) {
+ return GC_ALLOCATOR_INLINE_BUMP_POINTER;
+}
+static inline size_t gc_allocator_small_granule_size(void) {
+ return GC_ALIGNMENT;
+}
+static inline size_t gc_allocator_large_threshold(void) {
+ return GC_LARGE_OBJECT_THRESHOLD;
+}
+
+static inline size_t gc_allocator_allocation_pointer_offset(void) {
+ return sizeof(uintptr_t) * 0;
+}
+static inline size_t gc_allocator_allocation_limit_offset(void) {
+ return sizeof(uintptr_t) * 1;
+}
+
+static inline size_t gc_allocator_freelist_offset(size_t size, enum gc_allocation_kind kind) {
+ GC_CRASH();
+}
+
+static inline size_t gc_allocator_alloc_table_alignment(void) {
+ return 0;
+}
+static inline uint8_t gc_allocator_alloc_table_begin_pattern(enum gc_allocation_kind kind) {
+ GC_CRASH();
+}
+static inline uint8_t gc_allocator_alloc_table_end_pattern(void) {
+ GC_CRASH();
+}
+
+static inline enum gc_old_generation_check_kind gc_old_generation_check_kind(size_t size) {
+ if (!GC_GENERATIONAL)
+ return GC_OLD_GENERATION_CHECK_NONE;
+ if (size <= gc_allocator_large_threshold())
+ return GC_OLD_GENERATION_CHECK_SMALL_OBJECT_NURSERY;
+ return GC_OLD_GENERATION_CHECK_SLOW;
+}
+static inline uint8_t gc_old_generation_check_alloc_table_tag_mask(void) {
+ GC_CRASH();
+}
+static inline uint8_t gc_old_generation_check_alloc_table_young_tag(void) {
+ GC_CRASH();
+}
+
+static inline enum gc_write_barrier_kind gc_write_barrier_kind(size_t obj_size) {
+ if (!GC_GENERATIONAL)
+ return GC_WRITE_BARRIER_NONE;
+ if (obj_size <= gc_allocator_large_threshold())
+ return GC_WRITE_BARRIER_FIELD;
+ return GC_WRITE_BARRIER_SLOW;
+}
+static inline size_t gc_write_barrier_field_table_alignment(void) {
+ GC_ASSERT(GC_GENERATIONAL);
+ return 64 * 1024 * 1024;
+}
+static inline ptrdiff_t gc_write_barrier_field_table_offset(void) {
+ GC_ASSERT(GC_GENERATIONAL);
+ return 128 * 1024;
+}
+static inline size_t gc_write_barrier_field_fields_per_byte(void) {
+ GC_ASSERT(GC_GENERATIONAL);
+ return 8;
+}
+static inline uint8_t gc_write_barrier_field_first_bit_pattern(void) {
+ GC_ASSERT(GC_GENERATIONAL);
+ return 1;
+}
+
+static inline enum gc_safepoint_mechanism gc_safepoint_mechanism(void) {
+ return GC_SAFEPOINT_MECHANISM_COOPERATIVE;
+}
+
+static inline enum gc_cooperative_safepoint_kind gc_cooperative_safepoint_kind(void) {
+ return GC_COOPERATIVE_SAFEPOINT_HEAP_FLAG;
+}
+
+static inline int gc_can_pin_objects(void) {
+ return 0;
+}
+
+#endif // PCC_ATTRS_H
diff --git a/libguile/whippet/api/semi-attrs.h b/libguile/whippet/api/semi-attrs.h
new file mode 100644
index 000000000..f2efbd831
--- /dev/null
+++ b/libguile/whippet/api/semi-attrs.h
@@ -0,0 +1,80 @@
+#ifndef SEMI_ATTRS_H
+#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;
+
+static inline enum gc_allocator_kind gc_allocator_kind(void) {
+ return GC_ALLOCATOR_INLINE_BUMP_POINTER;
+}
+static inline size_t gc_allocator_small_granule_size(void) {
+ return GC_ALIGNMENT;
+}
+static inline size_t gc_allocator_large_threshold(void) {
+ return GC_LARGE_OBJECT_THRESHOLD;
+}
+
+static inline size_t gc_allocator_allocation_pointer_offset(void) {
+ return sizeof(uintptr_t) * 0;
+}
+static inline size_t gc_allocator_allocation_limit_offset(void) {
+ return sizeof(uintptr_t) * 1;
+}
+
+static inline size_t gc_allocator_freelist_offset(size_t size,
+ enum gc_allocation_kind kind) {
+ GC_CRASH();
+}
+
+static inline size_t gc_allocator_alloc_table_alignment(void) {
+ return 0;
+}
+static inline uint8_t gc_allocator_alloc_table_begin_pattern(enum gc_allocation_kind kind) {
+ GC_CRASH();
+}
+static inline uint8_t gc_allocator_alloc_table_end_pattern(void) {
+ GC_CRASH();
+}
+
+static inline enum gc_old_generation_check_kind gc_old_generation_check_kind(size_t) {
+ return GC_OLD_GENERATION_CHECK_NONE;
+}
+static inline uint8_t gc_old_generation_check_alloc_table_tag_mask(void) {
+ GC_CRASH();
+}
+static inline uint8_t gc_old_generation_check_alloc_table_young_tag(void) {
+ GC_CRASH();
+}
+
+static inline enum gc_write_barrier_kind gc_write_barrier_kind(size_t) {
+ return GC_WRITE_BARRIER_NONE;
+}
+static inline size_t gc_write_barrier_field_table_alignment(void) {
+ GC_CRASH();
+}
+static inline ptrdiff_t gc_write_barrier_field_table_offset(void) {
+ GC_CRASH();
+}
+static inline size_t gc_write_barrier_field_fields_per_byte(void) {
+ GC_CRASH();
+}
+static inline uint8_t gc_write_barrier_field_first_bit_pattern(void) {
+ GC_CRASH();
+}
+
+static inline enum gc_safepoint_mechanism gc_safepoint_mechanism(void) {
+ return GC_SAFEPOINT_MECHANISM_COOPERATIVE;
+}
+
+static inline enum gc_cooperative_safepoint_kind gc_cooperative_safepoint_kind(void) {
+ return GC_COOPERATIVE_SAFEPOINT_NONE;
+}
+
+static inline int gc_can_pin_objects(void) {
+ return 0;
+}
+
+#endif // SEMI_ATTRS_H