1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-08 02:40:17 +02:00

Reorganize source tree and document

This commit is contained in:
Andy Wingo 2023-08-06 12:04:33 +02:00
parent 5fbd21a7c3
commit e6de2fd633
63 changed files with 225 additions and 235 deletions

126
Makefile
View file

@ -29,7 +29,7 @@ BUILD_CFLAGS=$(BUILD_CFLAGS_$(or $(BUILD),$(DEFAULT_BUILD)))
CC=gcc CC=gcc
CFLAGS=-Wall -flto -fno-strict-aliasing -fvisibility=hidden -Wno-unused $(BUILD_CFLAGS) CFLAGS=-Wall -flto -fno-strict-aliasing -fvisibility=hidden -Wno-unused $(BUILD_CFLAGS)
INCLUDES=-I. INCLUDES=-Iapi
LDFLAGS=-lpthread -flto LDFLAGS=-lpthread -flto
COMPILE=$(CC) $(CFLAGS) $(INCLUDES) COMPILE=$(CC) $(CFLAGS) $(INCLUDES)
PLATFORM=gnu-linux PLATFORM=gnu-linux
@ -38,113 +38,113 @@ ALL_TESTS=$(foreach COLLECTOR,$(COLLECTORS),$(addprefix $(COLLECTOR)-,$(TESTS)))
all: $(ALL_TESTS) all: $(ALL_TESTS)
gc-platform.o: gc-platform.h gc-platform-$(PLATFORM).c gc-visibility.h gc-platform.o: src/gc-platform.h src/gc-platform-$(PLATFORM).c api/gc-visibility.h
$(COMPILE) -o $@ -c gc-platform-$(PLATFORM).c $(COMPILE) -o $@ -c src/gc-platform-$(PLATFORM).c
gc-stack.o: gc-stack.c gc-stack.o: src/gc-stack.c
$(COMPILE) -o $@ -c $< $(COMPILE) -o $@ -c $<
gc-options.o: gc-options.c gc-options.h gc-options-internal.h gc-options.o: src/gc-options.c api/gc-options.h src/gc-options-internal.h
$(COMPILE) -o $@ -c $< $(COMPILE) -o $@ -c $<
gc-ephemeron-%.o: gc-ephemeron.c gc-ephemeron.h gc-ephemeron-internal.h %-embedder.h gc-ephemeron-%.o: src/gc-ephemeron.c api/gc-ephemeron.h src/gc-ephemeron-internal.h benchmarks/%-embedder.h
$(COMPILE) -include $*-embedder.h -o $@ -c $< $(COMPILE) -include benchmarks/$*-embedder.h -o $@ -c $<
bdw-%-gc.o: bdw.c %-embedder.h %.c bdw-%-gc.o: src/bdw.c benchmarks/%-embedder.h benchmarks/%.c
$(COMPILE) -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 `pkg-config --cflags bdw-gc` -include $*-embedder.h -o $@ -c bdw.c $(COMPILE) -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 `pkg-config --cflags bdw-gc` -include benchmarks/$*-embedder.h -o $@ -c src/bdw.c
bdw-%.o: bdw.c %.c bdw-%.o: src/bdw.c benchmarks/%.c
$(COMPILE) -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include bdw-attrs.h -o $@ -c $*.c $(COMPILE) -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include api/bdw-attrs.h -o $@ -c benchmarks/$*.c
bdw-%: bdw-%.o bdw-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o bdw-%: bdw-%.o bdw-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o
$(CC) $(LDFLAGS) `pkg-config --libs bdw-gc` -o $@ $^ $(CC) $(LDFLAGS) `pkg-config --libs bdw-gc` -o $@ $^
semi-%-gc.o: semi.c %-embedder.h large-object-space.h assert.h debug.h %.c semi-%-gc.o: src/semi.c benchmarks/%-embedder.h
$(COMPILE) -DGC_PRECISE_ROOTS=1 -include $*-embedder.h -o $@ -c semi.c $(COMPILE) -DGC_PRECISE_ROOTS=1 -include benchmarks/$*-embedder.h -o $@ -c src/semi.c
semi-%.o: semi.c %.c semi-%.o: src/semi.c benchmarks/%.c
$(COMPILE) -DGC_PRECISE_ROOTS=1 -include semi-attrs.h -o $@ -c $*.c $(COMPILE) -DGC_PRECISE_ROOTS=1 -include api/semi-attrs.h -o $@ -c benchmarks/$*.c
semi-%: semi-%.o semi-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o semi-%: semi-%.o semi-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o
$(CC) $(LDFLAGS) -o $@ $^ $(CC) $(LDFLAGS) -o $@ $^
whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h serial-tracer.h assert.h debug.h heap-objects.h %.c whippet-%-gc.o: src/whippet.c benchmarks/%-embedder.h
$(COMPILE) -DGC_PRECISE_ROOTS=1 -include $*-embedder.h -o $@ -c whippet.c $(COMPILE) -DGC_PRECISE_ROOTS=1 -include benchmarks/$*-embedder.h -o $@ -c src/whippet.c
whippet-%.o: whippet.c %.c whippet-%.o: src/whippet.c benchmarks/%.c
$(COMPILE) -DGC_PRECISE_ROOTS=1 -include whippet-attrs.h -o $@ -c $*.c $(COMPILE) -DGC_PRECISE_ROOTS=1 -include api/whippet-attrs.h -o $@ -c benchmarks/$*.c
whippet-%: whippet-%.o whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o whippet-%: whippet-%.o whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o
$(CC) $(LDFLAGS) -o $@ $^ $(CC) $(LDFLAGS) -o $@ $^
stack-conservative-whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h serial-tracer.h assert.h debug.h heap-objects.h %.c stack-conservative-whippet-%-gc.o: src/whippet.c benchmarks/%-embedder.h
$(COMPILE) -DGC_CONSERVATIVE_ROOTS=1 -include $*-embedder.h -o $@ -c whippet.c $(COMPILE) -DGC_CONSERVATIVE_ROOTS=1 -include benchmarks/$*-embedder.h -o $@ -c src/whippet.c
stack-conservative-whippet-%.o: whippet.c %.c stack-conservative-whippet-%.o: src/whippet.c benchmarks/%.c
$(COMPILE) -DGC_CONSERVATIVE_ROOTS=1 -include whippet-attrs.h -o $@ -c $*.c $(COMPILE) -DGC_CONSERVATIVE_ROOTS=1 -include api/whippet-attrs.h -o $@ -c benchmarks/$*.c
stack-conservative-whippet-%: stack-conservative-whippet-%.o stack-conservative-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o stack-conservative-whippet-%: stack-conservative-whippet-%.o stack-conservative-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o
$(CC) $(LDFLAGS) -o $@ $^ $(CC) $(LDFLAGS) -o $@ $^
heap-conservative-whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h serial-tracer.h assert.h debug.h heap-objects.h %.c heap-conservative-whippet-%-gc.o: src/whippet.c benchmarks/%-embedder.h
$(COMPILE) -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include $*-embedder.h -o $@ -c whippet.c $(COMPILE) -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include benchmarks/$*-embedder.h -o $@ -c src/whippet.c
heap-conservative-whippet-%.o: whippet.c %.c heap-conservative-whippet-%.o: src/whippet.c benchmarks/%.c
$(COMPILE) -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include whippet-attrs.h -o $@ -c $*.c $(COMPILE) -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include api/whippet-attrs.h -o $@ -c benchmarks/$*.c
heap-conservative-whippet-%: heap-conservative-whippet-%.o heap-conservative-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o heap-conservative-whippet-%: heap-conservative-whippet-%.o heap-conservative-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o
$(CC) $(LDFLAGS) -o $@ $^ $(CC) $(LDFLAGS) -o $@ $^
parallel-whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h parallel-tracer.h assert.h debug.h heap-objects.h %.c parallel-whippet-%-gc.o: src/whippet.c benchmarks/%-embedder.h
$(COMPILE) -DGC_PARALLEL=1 -DGC_PRECISE_ROOTS=1 -include $*-embedder.h -o $@ -c whippet.c $(COMPILE) -DGC_PARALLEL=1 -DGC_PRECISE_ROOTS=1 -include benchmarks/$*-embedder.h -o $@ -c src/whippet.c
parallel-whippet-%.o: whippet.c %.c parallel-whippet-%.o: src/whippet.c benchmarks/%.c
$(COMPILE) -DGC_PARALLEL=1 -DGC_PRECISE_ROOTS=1 -include whippet-attrs.h -o $@ -c $*.c $(COMPILE) -DGC_PARALLEL=1 -DGC_PRECISE_ROOTS=1 -include api/whippet-attrs.h -o $@ -c benchmarks/$*.c
parallel-whippet-%: parallel-whippet-%.o parallel-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o parallel-whippet-%: parallel-whippet-%.o parallel-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o
$(CC) $(LDFLAGS) -o $@ $^ $(CC) $(LDFLAGS) -o $@ $^
stack-conservative-parallel-whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h serial-tracer.h assert.h debug.h heap-objects.h %.c stack-conservative-parallel-whippet-%-gc.o: src/whippet.c benchmarks/%-embedder.h
$(COMPILE) -DGC_PARALLEL=1 -DGC_CONSERVATIVE_ROOTS=1 -include $*-embedder.h -o $@ -c whippet.c $(COMPILE) -DGC_PARALLEL=1 -DGC_CONSERVATIVE_ROOTS=1 -include benchmarks/$*-embedder.h -o $@ -c src/whippet.c
stack-conservative-parallel-whippet-%.o: whippet.c %.c stack-conservative-parallel-whippet-%.o: src/whippet.c benchmarks/%.c
$(COMPILE) -DGC_PARALLEL=1 -DGC_CONSERVATIVE_ROOTS=1 -include whippet-attrs.h -o $@ -c $*.c $(COMPILE) -DGC_PARALLEL=1 -DGC_CONSERVATIVE_ROOTS=1 -include api/whippet-attrs.h -o $@ -c benchmarks/$*.c
stack-conservative-parallel-whippet-%: stack-conservative-parallel-whippet-%.o stack-conservative-parallel-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o stack-conservative-parallel-whippet-%: stack-conservative-parallel-whippet-%.o stack-conservative-parallel-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o
$(CC) $(LDFLAGS) -o $@ $^ $(CC) $(LDFLAGS) -o $@ $^
heap-conservative-parallel-whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h serial-tracer.h assert.h debug.h heap-objects.h %.c heap-conservative-parallel-whippet-%-gc.o: src/whippet.c benchmarks/%-embedder.h
$(COMPILE) -DGC_PARALLEL=1 -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include $*-embedder.h -o $@ -c whippet.c $(COMPILE) -DGC_PARALLEL=1 -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include benchmarks/$*-embedder.h -o $@ -c src/whippet.c
heap-conservative-parallel-whippet-%.o: whippet.c %.c heap-conservative-parallel-whippet-%.o: src/whippet.c benchmarks/%.c
$(COMPILE) -DGC_PARALLEL=1 -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -DGC_FULLY_CONSERVATIVE=1 -include whippet-attrs.h -o $@ -c $*.c $(COMPILE) -DGC_PARALLEL=1 -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -DGC_FULLY_CONSERVATIVE=1 -include api/whippet-attrs.h -o $@ -c benchmarks/$*.c
heap-conservative-parallel-whippet-%: heap-conservative-parallel-whippet-%.o heap-conservative-parallel-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o heap-conservative-parallel-whippet-%: heap-conservative-parallel-whippet-%.o heap-conservative-parallel-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o
$(CC) $(LDFLAGS) -o $@ $^ $(CC) $(LDFLAGS) -o $@ $^
generational-whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h serial-tracer.h assert.h debug.h heap-objects.h %.c generational-whippet-%-gc.o: src/whippet.c benchmarks/%-embedder.h
$(COMPILE) -DGC_GENERATIONAL=1 -DGC_PRECISE_ROOTS=1 -include $*-embedder.h -o $@ -c whippet.c $(COMPILE) -DGC_GENERATIONAL=1 -DGC_PRECISE_ROOTS=1 -include benchmarks/$*-embedder.h -o $@ -c src/whippet.c
generational-whippet-%.o: whippet.c %.c generational-whippet-%.o: src/whippet.c benchmarks/%.c
$(COMPILE) -DGC_GENERATIONAL=1 -DGC_PRECISE_ROOTS=1 -include whippet-attrs.h -o $@ -c $*.c $(COMPILE) -DGC_GENERATIONAL=1 -DGC_PRECISE_ROOTS=1 -include api/whippet-attrs.h -o $@ -c benchmarks/$*.c
generational-whippet-%: generational-whippet-%.o generational-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o generational-whippet-%: generational-whippet-%.o generational-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o
$(CC) $(LDFLAGS) -o $@ $^ $(CC) $(LDFLAGS) -o $@ $^
stack-conservative-generational-whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h serial-tracer.h assert.h debug.h heap-objects.h %.c stack-conservative-generational-whippet-%-gc.o: src/whippet.c benchmarks/%-embedder.h
$(COMPILE) -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -include $*-embedder.h -o $@ -c whippet.c $(COMPILE) -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -include benchmarks/$*-embedder.h -o $@ -c src/whippet.c
stack-conservative-generational-whippet-%.o: whippet.c %.c stack-conservative-generational-whippet-%.o: src/whippet.c benchmarks/%.c
$(COMPILE) -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -include whippet-attrs.h -o $@ -c $*.c $(COMPILE) -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -include api/whippet-attrs.h -o $@ -c benchmarks/$*.c
stack-conservative-generational-whippet-%: stack-conservative-generational-whippet-%.o stack-conservative-generational-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o stack-conservative-generational-whippet-%: stack-conservative-generational-whippet-%.o stack-conservative-generational-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o
$(CC) $(LDFLAGS) -o $@ $^ $(CC) $(LDFLAGS) -o $@ $^
heap-conservative-generational-whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h serial-tracer.h assert.h debug.h heap-objects.h %.c heap-conservative-generational-whippet-%-gc.o: src/whippet.c benchmarks/%-embedder.h
$(COMPILE) -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include $*-embedder.h -o $@ -c whippet.c $(COMPILE) -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include benchmarks/$*-embedder.h -o $@ -c src/whippet.c
heap-conservative-generational-whippet-%.o: whippet.c %.c heap-conservative-generational-whippet-%.o: src/whippet.c benchmarks/%.c
$(COMPILE) -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include whippet-attrs.h -o $@ -c $*.c $(COMPILE) -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include api/whippet-attrs.h -o $@ -c benchmarks/$*.c
heap-conservative-generational-whippet-%: heap-conservative-generational-whippet-%.o heap-conservative-generational-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o heap-conservative-generational-whippet-%: heap-conservative-generational-whippet-%.o heap-conservative-generational-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o
$(CC) $(LDFLAGS) -o $@ $^ $(CC) $(LDFLAGS) -o $@ $^
parallel-generational-whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h parallel-tracer.h assert.h debug.h heap-objects.h %.c parallel-generational-whippet-%-gc.o: src/whippet.c benchmarks/%-embedder.h
$(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_PRECISE_ROOTS=1 -include $*-embedder.h -o $@ -c whippet.c $(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_PRECISE_ROOTS=1 -include benchmarks/$*-embedder.h -o $@ -c src/whippet.c
parallel-generational-whippet-%.o: whippet.c %.c parallel-generational-whippet-%.o: src/whippet.c benchmarks/%.c
$(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_PRECISE_ROOTS=1 -include whippet-attrs.h -o $@ -c $*.c $(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_PRECISE_ROOTS=1 -include api/whippet-attrs.h -o $@ -c benchmarks/$*.c
parallel-generational-whippet-%: parallel-generational-whippet-%.o parallel-generational-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o parallel-generational-whippet-%: parallel-generational-whippet-%.o parallel-generational-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o
$(CC) $(LDFLAGS) -o $@ $^ $(CC) $(LDFLAGS) -o $@ $^
stack-conservative-parallel-generational-whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h parallel-tracer.h assert.h debug.h heap-objects.h %.c stack-conservative-parallel-generational-whippet-%-gc.o: src/whippet.c benchmarks/%-embedder.h
$(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -include $*-embedder.h -o $@ -c whippet.c $(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -include benchmarks/$*-embedder.h -o $@ -c src/whippet.c
stack-conservative-parallel-generational-whippet-%.o: whippet.c %.c stack-conservative-parallel-generational-whippet-%.o: src/whippet.c benchmarks/%.c
$(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -include whippet-attrs.h -o $@ -c $*.c $(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -include api/whippet-attrs.h -o $@ -c benchmarks/$*.c
stack-conservative-parallel-generational-whippet-%: stack-conservative-parallel-generational-whippet-%.o stack-conservative-parallel-generational-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o stack-conservative-parallel-generational-whippet-%: stack-conservative-parallel-generational-whippet-%.o stack-conservative-parallel-generational-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o
$(CC) $(LDFLAGS) -o $@ $^ $(CC) $(LDFLAGS) -o $@ $^
heap-conservative-parallel-generational-whippet-%-gc.o: whippet.c %-embedder.h large-object-space.h parallel-tracer.h assert.h debug.h heap-objects.h %.c heap-conservative-parallel-generational-whippet-%-gc.o: src/whippet.c benchmarks/%-embedder.h
$(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include $*-embedder.h -o $@ -c whippet.c $(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include benchmarks/$*-embedder.h -o $@ -c src/whippet.c
heap-conservative-parallel-generational-whippet-%.o: whippet.c %.c heap-conservative-parallel-generational-whippet-%.o: src/whippet.c benchmarks/%.c
$(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include whippet-attrs.h -o $@ -c $*.c $(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1 -include api/whippet-attrs.h -o $@ -c benchmarks/$*.c
heap-conservative-parallel-generational-whippet-%: heap-conservative-parallel-generational-whippet-%.o heap-conservative-parallel-generational-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o heap-conservative-parallel-generational-whippet-%: heap-conservative-parallel-generational-whippet-%.o heap-conservative-parallel-generational-whippet-%-gc.o gc-stack.o gc-options.o gc-platform.o gc-ephemeron-%.o
$(CC) $(LDFLAGS) -o $@ $^ $(CC) $(LDFLAGS) -o $@ $^

165
README.md
View file

@ -4,139 +4,37 @@ This repository is for development of Whippet, a new garbage collector
implementation, eventually for use in [Guile implementation, eventually for use in [Guile
Scheme](https://gnu.org/s/guile). Scheme](https://gnu.org/s/guile).
## Design Whippet is an embed-only C library, designed to be copied into a
program's source tree. It exposes an abstract C API for managed memory
allocation, and provides a number of implementations of that API.
Whippet is mainly a mark-region collector, like One of the implementations is also called "whippet", and is the
[Immix](http://users.cecs.anu.edu.au/~steveb/pubs/papers/immix-pldi-2008.pdf). motivation for creating this library. For a detailed introduction, see
See also the lovely detailed [Rust [Whippet: Towards a new local
implementation](http://users.cecs.anu.edu.au/~steveb/pubs/papers/rust-ismm-2016.pdf). maximum](https://wingolog.org/archives/2023/02/07/whippet-towards-a-new-local-maximum),
a talk given at FOSDEM 2023.
To a first approximation, Whippet is a whole-heap Immix collector with a ## Documentation
large object space on the side. See the Immix paper for full details,
but basically Immix divides the heap into 32kB blocks, and then divides
those blocks into 128B lines. An Immix allocation never spans blocks;
allocations larger than 8kB go into a separate large object space.
Mutators request blocks from the global store and allocate into those
blocks using bump-pointer allocation. When all blocks are consumed,
Immix stops the world and traces the object graph, marking objects but
also the lines that objects are on. After marking, blocks contain some
lines with live objects and others that are completely free. Spans of
free lines are called holes. When a mutator gets a recycled block from
the global block store, it allocates into those holes. Also, sometimes
Immix can choose to evacuate rather than mark. Bump-pointer-into-holes
allocation is quite compatible with conservative roots, so it's an
interesting option for Guile, which has a lot of legacy C API users.
The essential difference of Whippet from Immix stems from a simple * [Design](./doc/design.md): What's the general idea?
observation: Immix needs a side table of line mark bytes and also a mark * [Manual](./doc/manual.md): How do you get your program to use
bit or bits in each object (or in a side table). But if instead you Whippet? What is the API?
choose to store mark bytes instead of bits (for concurrency reasons) in * [Guile](./doc/guile.md): Some notes on a potential rebase of Guile on
a side table, with one mark byte per granule (unit of allocation, top of Whippet.
perhaps 16 bytes), then you effectively have a line mark table where the
granule size is the line size. You can bump-pointer allocate into holes
in the mark byte table.
You might think this is a bad tradeoff, and perhaps it is: I don't know ## Source repository structure
yet. If your granule size is two pointers, then one mark byte per
granule is 6.25% overhead on 64-bit, or 12.5% on 32-bit. Especially on
32-bit, it's a lot! On the other hand, instead of the worst case of one
survivor object wasting a line (or two, in the case of conservative line
marking), granule-size-is-line-size instead wastes nothing. Also, you
don't need GC bits in the object itself, and you can use the mark byte
array to record the object end, so that finding holes in a block can
just read the mark table and can avoid looking at object memory.
Other ideas in Whippet: * [api/](./api/): The user-facing API. Also, the "embedder API"; see
the [manual](./doc/manual.md) for more.
* Minimize stop-the-world phase via parallel marking and punting all * [doc/](./doc/): Documentation, such as it is.
sweeping to mutators * [src/](./src/): The actual GC implementation. The specific
implementations of the Whippet API are [`semi.c`](./src/semi.c), a
* Enable mutator parallelism via lock-free block acquisition and lazy semi-space collector; [`bdw.c`](./src/bdw.c), the third-party
statistics collation [BDW-GC](https://github.com/ivmai/bdwgc) conservative parallel
stop-the-world mark-sweep segregated-fits collector with lazy
* Allocate block space using aligned 4 MB slabs, with embedded metadata sweeping; and [`whippet.c`](./src/whippet.c), the whippet collector.
to allow metadata bytes, slab headers, and block metadata to be * [benchmarks/](./benchmarks/): Benchmarks. A work in progress.
located via address arithmetic * [test/](./test/): A dusty attic of minimal testing.
* Facilitate conservative collection via mark byte array, oracle for
"does this address start an object"
* Enable in-place generational collection via card table with one entry
per 256B or so
* Enable concurrent marking by having three mark bit states (dead,
survivor, marked) that rotate at each collection, and sweeping a
block clears metadata for dead objects; but concurrent marking and
associated SATB barrier not yet implemented
## What's there
This repository is a workspace for Whippet implementation. As such, it
has files implementing Whippet itself. It also has some benchmarks to
use in optimizing Whippet:
- [`mt-gcbench.c`](./mt-gcbench.c): The multi-threaded [GCBench
benchmark](https://hboehm.info/gc/gc_bench.html). An old but
standard benchmark that allocates different sizes of binary trees.
As parameters it takes a heap multiplier and a number of mutator
threads. We analytically compute the peak amount of live data and
then size the GC heap as a multiplier of that size. It has a peak
heap consumption of 10 MB or so per mutator thread: not very large.
At a 2x heap multiplier, it causes about 30 collections for the
whippet collector, and runs somewhere around 200-400 milliseconds in
single-threaded mode, on the machines I have in 2022. For low thread
counts, the GCBench benchmark is small; but then again many Guile
processes also are quite short-lived, so perhaps it is useful to
ensure that small heaps remain lightweight.
To stress Whippet's handling of fragmentation, we modified this
benchmark to intersperse pseudorandomly-sized holes between tree
nodes.
- [`quads.c`](./quads.c): A synthetic benchmark that allocates quad
trees. The mutator begins by allocating one long-lived tree of depth
N, and then allocates 13% of the heap in depth-3 trees, 20 times,
simulating a fixed working set and otherwise an allocation-heavy
workload. By observing the times to allocate 13% of the heap in
garbage we can infer mutator overheads, and also note the variance
for the cycles in which GC hits.
The repository has two other collector implementations, to appropriately
situate Whippet's performance in context:
- `bdw.h`: The external BDW-GC conservative parallel stop-the-world
mark-sweep segregated-fits collector with lazy sweeping.
- `semi.h`: Semispace copying collector.
- `whippet.h`: The whippet collector. Two different marking
implementations: single-threaded and parallel. Generational and
non-generational variants, also.
## Guile
If the Whippet collector works out, it could replace Guile's garbage
collector. Guile currently uses BDW-GC. Guile has a widely used C API
and implements part of its run-time in C. For this reason it may be
infeasible to require precise enumeration of GC roots -- we may need to
allow GC roots to be conservatively identified from data sections and
from stacks. Such conservative roots would be pinned, but other objects
can be moved by the collector if it chooses to do so. We assume that
object references within a heap object can be precisely identified.
(However, Guile currently uses BDW-GC in its default configuration,
which scans for references conservatively even on the heap.)
The existing C API allows direct access to mutable object fields,
without the mediation of read or write barriers. Therefore it may be
impossible to switch to collector strategies that need barriers, such as
generational or concurrent collectors. However, we shouldn't write off
this possibility entirely; an ideal replacement for Guile's GC will
offer the possibility of migration to other GC designs without imposing
new requirements on C API users in the initial phase.
In this regard, the Whippet experiment also has the goal of identifying
a smallish GC abstraction in Guile, so that we might consider evolving
GC implementation in the future without too much pain. If we switch
away from BDW-GC, we should be able to evaluate that it's a win for a
large majority of use cases.
## To do ## To do
@ -166,14 +64,8 @@ size. It would be nice if whippet-gc turns out to have this property.
## License ## License
gcbench.c, MT_GCBench.c, and MT_GCBench2.c are from
https://hboehm.info/gc/gc_bench/ and have a somewhat unclear license. I
have modified GCBench significantly so that I can slot in different GC
implementations. The GC implementations themselves are available under
a MIT-style license, the text of which follows:
``` ```
Copyright (c) 2022 Andy Wingo Copyright (c) 2022-2023 Andy Wingo
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the copy of this software and associated documentation files (the
@ -194,3 +86,6 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
``` ```
Note that some benchmarks have other licenses; see
[`benchmarks/README.md`](./benchmarks/README.md) for more.

35
benchmarks/README.md Normal file
View file

@ -0,0 +1,35 @@
# Benchmarks
- [`mt-gcbench.c`](./mt-gcbench.c): The multi-threaded [GCBench
benchmark](https://hboehm.info/gc/gc_bench.html). An old but
standard benchmark that allocates different sizes of binary trees.
As parameters it takes a heap multiplier and a number of mutator
threads. We analytically compute the peak amount of live data and
then size the GC heap as a multiplier of that size. It has a peak
heap consumption of 10 MB or so per mutator thread: not very large.
At a 2x heap multiplier, it causes about 30 collections for the
whippet collector, and runs somewhere around 200-400 milliseconds in
single-threaded mode, on the machines I have in 2022. For low thread
counts, the GCBench benchmark is small; but then again many Guile
processes also are quite short-lived, so perhaps it is useful to
ensure that small heaps remain lightweight.
To stress Whippet's handling of fragmentation, we modified this
benchmark to intersperse pseudorandomly-sized holes between tree
nodes.
- [`quads.c`](./quads.c): A synthetic benchmark that allocates quad
trees. The mutator begins by allocating one long-lived tree of depth
N, and then allocates 13% of the heap in depth-3 trees, 20 times,
simulating a fixed working set and otherwise an allocation-heavy
workload. By observing the times to allocate 13% of the heap in
garbage we can infer mutator overheads, and also note the variance
for the cycles in which GC hits.
## License
mt-gcbench.c was originally from https://hboehm.info/gc/gc_bench/, which
has a somewhat unclear license. I have modified GCBench significantly
so that I can slot in different GC implementations. Other files are
distributed under the Whippet license; see the top-level
[README.md](../README.md) for more.

64
doc/design.md Normal file
View file

@ -0,0 +1,64 @@
# Design
Whippet is mainly a mark-region collector, like
[Immix](http://users.cecs.anu.edu.au/~steveb/pubs/papers/immix-pldi-2008.pdf).
See also the lovely detailed [Rust
implementation](http://users.cecs.anu.edu.au/~steveb/pubs/papers/rust-ismm-2016.pdf).
To a first approximation, Whippet is a whole-heap Immix collector with a
large object space on the side. See the Immix paper for full details,
but basically Immix divides the heap into 32kB blocks, and then divides
those blocks into 128B lines. An Immix allocation never spans blocks;
allocations larger than 8kB go into a separate large object space.
Mutators request blocks from the global store and allocate into those
blocks using bump-pointer allocation. When all blocks are consumed,
Immix stops the world and traces the object graph, marking objects but
also the lines that objects are on. After marking, blocks contain some
lines with live objects and others that are completely free. Spans of
free lines are called holes. When a mutator gets a recycled block from
the global block store, it allocates into those holes. Also, sometimes
Immix can choose to evacuate rather than mark. Bump-pointer-into-holes
allocation is quite compatible with conservative roots, so it's an
interesting option for Guile, which has a lot of legacy C API users.
The essential difference of Whippet from Immix stems from a simple
observation: Immix needs a side table of line mark bytes and also a mark
bit or bits in each object (or in a side table). But if instead you
choose to store mark bytes instead of bits (for concurrency reasons) in
a side table, with one mark byte per granule (unit of allocation,
perhaps 16 bytes), then you effectively have a line mark table where the
granule size is the line size. You can bump-pointer allocate into holes
in the mark byte table.
You might think this is a bad tradeoff, and perhaps it is: I don't know
yet. If your granule size is two pointers, then one mark byte per
granule is 6.25% overhead on 64-bit, or 12.5% on 32-bit. Especially on
32-bit, it's a lot! On the other hand, instead of the worst case of one
survivor object wasting a line (or two, in the case of conservative line
marking), granule-size-is-line-size instead wastes nothing. Also, you
don't need GC bits in the object itself, and you can use the mark byte
array to record the object end, so that finding holes in a block can
just read the mark table and can avoid looking at object memory.
Other ideas in Whippet:
* Minimize stop-the-world phase via parallel marking and punting all
sweeping to mutators
* Enable mutator parallelism via lock-free block acquisition and lazy
statistics collation
* Allocate block space using aligned 4 MB slabs, with embedded metadata
to allow metadata bytes, slab headers, and block metadata to be
located via address arithmetic
* Facilitate conservative collection via mark byte array, oracle for
"does this address start an object"
* Enable in-place generational collection via card table with one entry
per 256B or so
* Enable concurrent marking by having three mark bit states (dead,
survivor, marked) that rotate at each collection, and sweeping a
block clears metadata for dead objects; but concurrent marking and
associated SATB barrier not yet implemented

26
doc/guile.md Normal file
View file

@ -0,0 +1,26 @@
# Whippet and Guile
If the Whippet collector works out, it could replace Guile's garbage
collector. Guile currently uses BDW-GC. Guile has a widely used C API
and implements part of its run-time in C. For this reason it may be
infeasible to require precise enumeration of GC roots -- we may need to
allow GC roots to be conservatively identified from data sections and
from stacks. Such conservative roots would be pinned, but other objects
can be moved by the collector if it chooses to do so. We assume that
object references within a heap object can be precisely identified.
(However, Guile currently uses BDW-GC in its default configuration,
which scans for references conservatively even on the heap.)
The existing C API allows direct access to mutable object fields,
without the mediation of read or write barriers. Therefore it may be
impossible to switch to collector strategies that need barriers, such as
generational or concurrent collectors. However, we shouldn't write off
this possibility entirely; an ideal replacement for Guile's GC will
offer the possibility of migration to other GC designs without imposing
new requirements on C API users in the initial phase.
In this regard, the Whippet experiment also has the goal of identifying
a smallish GC abstraction in Guile, so that we might consider evolving
GC implementation in the future without too much pain. If we switch
away from BDW-GC, we should be able to evaluate that it's a win for a
large majority of use cases.

View file

@ -51,7 +51,7 @@ itself. This is the *embedder API*, and this document describes what
Whippet requires from a program. Whippet requires from a program.
A program should provide a header file implementing the API in A program should provide a header file implementing the API in
[`gc-embedder-api.h`](./gc-embedder-api.h). This header should only be [`gc-embedder-api.h`](../api/gc-embedder-api.h). This header should only be
included when compiling Whippet itself; it is not part of the API that included when compiling Whippet itself; it is not part of the API that
Whippet exposes to the program. Whippet exposes to the program.
@ -83,7 +83,7 @@ Most kinds of GC-managed object are defined by the program, but the GC
itself has support for a specific object kind: ephemerons. If the itself has support for a specific object kind: ephemerons. If the
program allocates ephemerons, it should trace them in the program allocates ephemerons, it should trace them in the
`gc_trace_object` function by calling `gc_trace_ephemeron` from `gc_trace_object` function by calling `gc_trace_ephemeron` from
[`gc-ephemerons.h`](./gc-ephemerons.h). [`gc-ephemerons.h`](../api/gc-ephemerons.h).
### Remembered-set bits ### Remembered-set bits
@ -91,7 +91,7 @@ When built to support generational garbage collection, Whippet requires
that all "large" or potentially large objects have a flag bit reserved that all "large" or potentially large objects have a flag bit reserved
for use of the garbage collector. A large object is one whose size for use of the garbage collector. A large object is one whose size
exceeds the `gc_allocator_large_threshold()` (see exceeds the `gc_allocator_large_threshold()` (see
[`gc-attrs.h`](./gc-attrs.h)), which is a collector-specific value. [`gc-attrs.h`](../api/gc-attrs.h)), which is a collector-specific value.
Currently the only generational collector is the in-place Whippet Currently the only generational collector is the in-place Whippet
collector, whose large object threshold is 4096 bytes. The collector, whose large object threshold is 4096 bytes. The
`gc_object_set_remembered`, `gc_object_is_remembered_nonatomic`, and `gc_object_set_remembered`, `gc_object_is_remembered_nonatomic`, and
@ -116,7 +116,7 @@ The atomic API is gnarly. It is used by parallel collectors, in which
multiple collector threads can race to evacuate an object. multiple collector threads can race to evacuate an object.
There is a state machine associated with the `gc_atomic_forward` There is a state machine associated with the `gc_atomic_forward`
structure from [`gc-forwarding.h`](./gc-forwarding.h); the embedder API structure from [`gc-forwarding.h`](../api/gc-forwarding.h); the embedder API
implements the state changes. The collector calls implements the state changes. The collector calls
`gc_atomic_forward_begin` on an object to begin a forwarding attempt, `gc_atomic_forward_begin` on an object to begin a forwarding attempt,
and the resulting `gc_atomic_forward` can be in the `NOT_FORWARDED`, and the resulting `gc_atomic_forward` can be in the `NOT_FORWARDED`,
@ -379,7 +379,7 @@ program? No, because your program isn't written yet? Well this section
is for you: we describe the user-facing API of Whippet, where "user" in is for you: we describe the user-facing API of Whippet, where "user" in
this case denotes the embedding program. this case denotes the embedding program.
What is the API, you ask? It is in [`gc-api.h`](./gc-api.h). What is the API, you ask? It is in [`gc-api.h`](../api/gc-api.h).
### Heaps and mutators ### Heaps and mutators
@ -442,7 +442,7 @@ defined for all collectors:
processors, with a maximum of 8. processors, with a maximum of 8.
You can set these options via `gc_option_set_int` and so on; see You can set these options via `gc_option_set_int` and so on; see
[`gc-options.h`](./gc-options.h). Or, you can parse options from [`gc-options.h`](../api/gc-options.h). Or, you can parse options from
strings: `heap-size-policy`, `heap-size`, `maximum-heap-size`, and so strings: `heap-size-policy`, `heap-size`, `maximum-heap-size`, and so
on. Use `gc_option_from_string` to determine if a string is really an on. Use `gc_option_from_string` to determine if a string is really an
option. Use `gc_option_parse_and_set` to parse a value for an option. option. Use `gc_option_parse_and_set` to parse a value for an option.
@ -519,7 +519,7 @@ Whippet supports ephemerons, first-class objects that weakly associate
keys with values. If the an ephemeron's key ever becomes unreachable, keys with values. If the an ephemeron's key ever becomes unreachable,
the ephemeron becomes dead and loses its value. the ephemeron becomes dead and loses its value.
The user-facing API is in [`gc-ephemeron.h`](./gc-ephemeron.h). To The user-facing API is in [`gc-ephemeron.h`](../api/gc-ephemeron.h). To
allocate an ephemeron, call `gc_allocate_ephemeron`, then initialize its allocate an ephemeron, call `gc_allocate_ephemeron`, then initialize its
key and value via `gc_ephemeron_init`. Get the key and value via key and value via `gc_ephemeron_init`. Get the key and value via
`gc_ephemeron_key` and `gc_ephemeron_value`, respectively. `gc_ephemeron_key` and `gc_ephemeron_value`, respectively.

30
gc.h
View file

@ -1,30 +0,0 @@
#ifndef GC_H_
#define GC_H_
#include "gc-api.h"
#if defined(GC_BDW)
#include "bdw.h"
#elif defined(GC_SEMI)
#include "semi.h"
#elif defined(GC_WHIPPET)
#define GC_PARALLEL_TRACE 0
#define GC_GENERATIONAL 0
#include "whippet.h"
#elif defined(GC_PARALLEL_WHIPPET)
#define GC_PARALLEL_TRACE 1
#define GC_GENERATIONAL 0
#include "whippet.h"
#elif defined(GC_GENERATIONAL_WHIPPET)
#define GC_PARALLEL_TRACE 0
#define GC_GENERATIONAL 1
#include "whippet.h"
#elif defined(GC_PARALLEL_GENERATIONAL_WHIPPET)
#define GC_PARALLEL_TRACE 1
#define GC_GENERATIONAL 1
#include "whippet.h"
#else
#error unknown gc
#endif
#endif // GC_H_

View file

View file

View file