diff options
author | Andy Wingo <wingo@pobox.com> | 2025-04-11 14:10:41 +0200 |
---|---|---|
committer | Andy Wingo <wingo@pobox.com> | 2025-04-11 14:10:41 +0200 |
commit | db181e67ff482ab08cbdb0c1a88997b3a886f900 (patch) | |
tree | 6d16fbca515ae3cf506a9d16dbc89c472446e5d9 /libguile/whippet/api | |
parent | af96820e072d18c49ac03e80c6f3466d568dc77d (diff) | |
parent | f909438596f63ecd44c5b9a28385260714857361 (diff) | |
download | guile-db181e67ff482ab08cbdb0c1a88997b3a886f900.tar.gz |
Merged Whippet into libguile/whippet
Diffstat (limited to 'libguile/whippet/api')
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 |