The cnode/IR/opcode compiler subsystems currently use pool allocators, and were designed to use a "poor person's garbage collector" for memory management. The pool allocators are present, but there's currently no "garbage collection."
Important: the GC must be limited to the compiler. Neither the VM nor any emitted code should use this. It's important to keep the memory use of the VM/emitted code well-defined and as deterministic as possible.
Basic description of the GC that should be written:
-
For the basic description, we'll focus on cnodes trees/forests, but the ideas should be the same for the IR and opcodes.
-
The basic gist is that cnode trees/forests are registered as roots. Nothing reachable from a root will be reclaimed.
-
Any cnode tree/forest that's a parameter to a public facing jvst_cnode function (eg: jvst_cnode_simplify or jvst_cnode_canonify) will be registered as a root until that function returns.
-
Callers can also register cnode trees/forests as roots.
-
When a cnode must be allocated, the allocator checks the current pool. If it has unallocated nodes, the top is bumped and a new cnode is returned.
-
If the current pool has allocated all of its nodes, the free list is checked. The allocator returns the first node on the free list, unless the free list is empty.
-
If the free list is empty, the allocator invokes the pool garbage collector (pgc):
- The pgc clears the marked bitfield of each pool
- The pgc then descends each root and marks all reachable nodes
- The pgc then iterates over the bitfield of each pool and sweeps unreachable nodes into the free list
- Unreachable nodes should be poisoned if ASAN is enabled.
-
After the pgc cycle, the freelist is checked. If a node is present, the allocator returns that node.
-
If the freelist is empty after a pgc cycle, the pool is expanded (a new pool chunk is allocated).
The cnode/IR/opcode compiler subsystems currently use pool allocators, and were designed to use a "poor person's garbage collector" for memory management. The pool allocators are present, but there's currently no "garbage collection."
Important: the GC must be limited to the compiler. Neither the VM nor any emitted code should use this. It's important to keep the memory use of the VM/emitted code well-defined and as deterministic as possible.
Basic description of the GC that should be written:
For the basic description, we'll focus on cnodes trees/forests, but the ideas should be the same for the IR and opcodes.
The basic gist is that cnode trees/forests are registered as roots. Nothing reachable from a root will be reclaimed.
Any cnode tree/forest that's a parameter to a public facing jvst_cnode function (eg: jvst_cnode_simplify or jvst_cnode_canonify) will be registered as a root until that function returns.
Callers can also register cnode trees/forests as roots.
When a cnode must be allocated, the allocator checks the current pool. If it has unallocated nodes, the top is bumped and a new cnode is returned.
If the current pool has allocated all of its nodes, the free list is checked. The allocator returns the first node on the free list, unless the free list is empty.
If the free list is empty, the allocator invokes the pool garbage collector (pgc):
After the pgc cycle, the freelist is checked. If a node is present, the allocator returns that node.
If the freelist is empty after a pgc cycle, the pool is expanded (a new pool chunk is allocated).