2023-03-27 10:09:54 +02:00
# ifndef __LAMA_GC__
# define __LAMA_GC__
2023-05-23 13:40:46 +02:00
# include "runtime_common.h"
2023-04-26 14:22:14 +02:00
2023-05-31 11:01:11 +02:00
# define GET_MARK_BIT(x) (((int)(x)) & 1)
# define SET_MARK_BIT(x) (x = (((int)(x)) | 1))
# define IS_ENQUEUED(x) (((int)(x)) & 2)
# define MAKE_ENQUEUED(x) (x = (((int)(x)) | 2))
# define MAKE_DEQUEUED(x) (x = (((int)(x)) & (~2)))
# define RESET_MARK_BIT(x) (x = (((int)(x)) & (~1)))
# define GET_FORWARD_ADDRESS(x) \
( ( ( size_t ) ( x ) ) \
& ( ~ 3 ) ) // since last 2 bits are used for mark-bit and enqueued-bit and due to correct alignment we can expect that last 2 bits don't influence address (they should always be zero)
# define SET_FORWARD_ADDRESS(x, addr) \
( x = ( ( x & 3 ) | ( ( int ) ( addr ) ) ) ) // take the last two bits as they are and make all others zero
# define EXTRA_ROOM_HEAP_COEFFICIENT 2 // TODO: tune this parameter
2023-04-26 14:22:14 +02:00
# ifdef DEBUG_VERSION
2023-05-31 11:01:11 +02:00
# define MINIMUM_HEAP_CAPACITY (8)
2023-04-26 14:22:14 +02:00
# else
2023-06-25 22:51:40 +02:00
# define MINIMUM_HEAP_CAPACITY (1 << 5)
2023-04-26 14:22:14 +02:00
# endif
2023-03-27 10:09:54 +02:00
# include <stdbool.h>
2023-05-31 11:01:11 +02:00
# include <stddef.h>
2023-03-27 10:09:54 +02:00
typedef enum { ARRAY , CLOSURE , STRING , SEXP } lama_type ;
typedef struct {
2023-05-31 11:01:11 +02:00
size_t * current ;
2023-03-27 10:09:54 +02:00
} heap_iterator ;
typedef struct {
2023-05-31 11:01:11 +02:00
// holds type of object, which fields we are iterating over
lama_type type ;
// here a pointer to the object header is stored
void * obj_ptr ;
void * cur_field ;
2023-03-27 10:09:54 +02:00
} obj_field_iterator ;
typedef struct {
2023-05-31 11:01:11 +02:00
size_t * begin ;
size_t * end ;
size_t * current ;
size_t size ;
2023-03-27 10:09:54 +02:00
} memory_chunk ;
/* GC extra roots */
2023-05-31 11:01:11 +02:00
# define MAX_EXTRA_ROOTS_NUMBER 32
2023-03-27 10:09:54 +02:00
typedef struct {
2023-05-31 11:01:11 +02:00
int current_free ;
void * * roots [ MAX_EXTRA_ROOTS_NUMBER ] ;
2023-03-27 10:09:54 +02:00
} extra_roots_pool ;
// the only GC-related function that should be exposed, others are useful for tests and internal implementation
// allocates object of the given size on the heap
2023-05-31 11:01:11 +02:00
void * alloc ( size_t ) ;
2023-03-27 10:09:54 +02:00
// takes number of words as a parameter
2023-05-31 11:01:11 +02:00
void * gc_alloc ( size_t ) ;
2023-03-27 10:09:54 +02:00
// takes number of words as a parameter
void * gc_alloc_on_existing_heap ( size_t ) ;
2023-04-26 14:22:14 +02:00
// specific for mark-and-compact_phase gc
2023-05-31 11:01:11 +02:00
void mark ( void * obj ) ;
void mark_phase ( void ) ;
2023-04-26 14:22:14 +02:00
// written in ASM, scans stack for pointers to the heap and starts marking process
2023-05-31 11:01:11 +02:00
extern void
__gc_root_scan_stack ( void ) ; // TODO: write without ASM, since it is absolutely not necessary
2023-04-26 14:22:14 +02:00
// marks each pointer from extra roots
2023-05-31 11:01:11 +02:00
void scan_extra_roots ( void ) ;
2023-04-26 14:22:14 +02:00
# ifndef DEBUG_VERSION
// marks each valid pointer from global area
2023-05-31 11:01:11 +02:00
void scan_global_area ( void ) ;
2023-04-26 14:22:14 +02:00
# endif
2023-03-27 10:09:54 +02:00
// takes number of words that are required to be allocated somewhere on the heap
2023-05-31 11:01:11 +02:00
void compact_phase ( size_t additional_size ) ;
2023-03-27 10:09:54 +02:00
// specific for Lisp-2 algorithm
2023-05-31 11:01:11 +02:00
size_t compute_locations ( ) ;
void update_references ( memory_chunk * ) ;
void physically_relocate ( memory_chunk * ) ;
2023-03-27 10:09:54 +02:00
// written in ASM
2023-05-31 11:01:11 +02:00
extern void __gc_init (
void ) ; // MANDATORY TO CALL BEFORE ANY INTERACTION WITH GC (apart from cases where we are working with virtual stack as happens in tests)
extern void __init (
void ) ; // should be called before interaction with GC in case of using in tests with virtual stack, otherwise it is automatically invoked by __gc_init
extern void __shutdown (
void ) ; // mostly useful for tests but basically you want to call this in case you want to deallocate all object allocated via GC
2023-04-26 14:22:14 +02:00
// written in ASM
2023-05-31 11:01:11 +02:00
extern void __pre_gc ( void ) ;
2023-04-26 14:22:14 +02:00
// written in ASM
2023-05-31 11:01:11 +02:00
extern void __post_gc ( void ) ;
2023-03-27 10:09:54 +02:00
// invoked from ASM
2023-05-31 11:01:11 +02:00
extern void gc_test_and_mark_root ( size_t * * root ) ;
inline bool is_valid_heap_pointer ( const size_t * ) ;
inline bool is_valid_pointer ( const size_t * ) ;
2023-03-27 10:09:54 +02:00
void clear_extra_roots ( void ) ;
2023-05-31 11:01:11 +02:00
void push_extra_root ( void * * p ) ;
2023-03-27 10:09:54 +02:00
2023-05-31 11:01:11 +02:00
void pop_extra_root ( void * * p ) ;
2023-03-27 10:09:54 +02:00
/* Functions for tests */
# ifdef DEBUG_VERSION
2023-04-26 14:22:14 +02:00
// makes a snapshot of current objects in heap (both alive and dead), writes these ids to object_ids_buf,
// returns number of ids dumped
// object_ids_buf is pointer to area preallocated by user for dumping ids of objects in heap
// object_ids_buf_size is in WORDS, NOT BYTES
2023-05-31 11:01:11 +02:00
size_t objects_snapshot ( int * object_ids_buf , size_t object_ids_buf_size ) ;
2023-03-27 10:09:54 +02:00
// essential function to mock program stack
2023-05-31 11:01:11 +02:00
void set_stack ( size_t stack_top , size_t stack_bottom ) ;
2023-03-27 10:09:54 +02:00
// function to mock extra roots (Lama specific)
2023-05-31 11:01:11 +02:00
void set_extra_roots ( size_t extra_roots_size , void * * extra_roots_ptr ) ;
2023-03-27 10:09:54 +02:00
# endif
/* Utility functions */
2023-04-26 14:22:14 +02:00
// accepts pointer to the start of the region and to the end of the region
// scans it and if it meets a pointer, it should be modified in according to forward address
2023-05-31 11:01:11 +02:00
void scan_and_fix_region ( memory_chunk * old_heap , void * start , void * end ) ;
2023-04-26 14:22:14 +02:00
2023-03-27 10:09:54 +02:00
// takes a pointer to an object content as an argument, returns forwarding address
2023-05-31 11:01:11 +02:00
size_t get_forward_address ( void * obj ) ;
2023-03-27 10:09:54 +02:00
// takes a pointer to an object content as an argument, sets forwarding address to value 'addr'
2023-05-31 11:01:11 +02:00
void set_forward_address ( void * obj , size_t addr ) ;
2023-03-27 10:09:54 +02:00
// takes a pointer to an object content as an argument, returns whether this object was marked as live
2023-05-31 11:01:11 +02:00
bool is_marked ( void * obj ) ;
2023-03-27 10:09:54 +02:00
// takes a pointer to an object content as an argument, marks the object as live
2023-05-31 11:01:11 +02:00
void mark_object ( void * obj ) ;
2023-03-27 10:09:54 +02:00
// takes a pointer to an object content as an argument, marks the object as dead
2023-05-31 11:01:11 +02:00
void unmark_object ( void * obj ) ;
2023-03-27 10:09:54 +02:00
2023-05-31 01:06:53 +02:00
// takes a pointer to an object content as an argument, returns whether this object was enqueued to the queue (which is used in mark phase)
2023-05-31 11:01:11 +02:00
bool is_enqueued ( void * obj ) ;
2023-05-31 01:06:53 +02:00
// takes a pointer to an object content as an argument, marks object as enqueued
2023-05-31 11:01:11 +02:00
void make_enqueued ( void * obj ) ;
2023-05-31 01:06:53 +02:00
// takes a pointer to an object content as an argument, unmarks object as enqueued
2023-05-31 11:01:11 +02:00
void make_dequeued ( void * obj ) ;
2023-05-31 01:06:53 +02:00
2023-03-27 10:09:54 +02:00
// returns iterator to an object with the lowest address
2023-05-31 11:01:11 +02:00
heap_iterator heap_begin_iterator ( ) ;
void heap_next_obj_iterator ( heap_iterator * it ) ;
bool heap_is_done_iterator ( heap_iterator * it ) ;
2023-03-27 10:09:54 +02:00
// returns correct type when pointer to actual data is passed (header is excluded)
2023-05-31 11:01:11 +02:00
lama_type get_type_row_ptr ( void * ptr ) ;
2023-03-27 10:09:54 +02:00
// returns correct type when pointer to an object header is passed
2023-05-31 11:01:11 +02:00
lama_type get_type_header_ptr ( void * ptr ) ;
2023-03-27 10:09:54 +02:00
// returns correct object size (together with header) of an object, ptr is pointer to an actual data is passed (header is excluded)
2023-05-31 11:01:11 +02:00
size_t obj_size_row_ptr ( void * ptr ) ;
2023-03-27 10:09:54 +02:00
// returns correct object size (together with header) of an object, ptr is pointer to an object header
2023-05-31 11:01:11 +02:00
size_t obj_size_header_ptr ( void * ptr ) ;
2023-03-27 10:09:54 +02:00
// returns total padding size that we need to store given object type
2023-05-31 11:01:11 +02:00
size_t get_header_size ( lama_type type ) ;
2023-03-27 10:09:54 +02:00
// returns number of bytes that are required to allocate array with 'sz' elements (header included)
2023-05-31 11:01:11 +02:00
size_t array_size ( size_t sz ) ;
2023-03-27 10:09:54 +02:00
// returns number of bytes that are required to allocate string of length 'l' (header included)
2023-05-31 11:01:11 +02:00
size_t string_size ( size_t len ) ;
2023-03-27 10:09:54 +02:00
// TODO: ask if it is actually so? number of captured elements is actually sz-1 and 1 extra word is code ptr?
// returns number of bytes that are required to allocate closure with 'sz-1' captured values (header included)
2023-05-31 11:01:11 +02:00
size_t closure_size ( size_t sz ) ;
2023-04-26 14:22:14 +02:00
// returns number of bytes that are required to allocate s-expression with 'members' fields (header included)
2023-05-31 11:01:11 +02:00
size_t sexp_size ( size_t members ) ;
2023-03-27 10:09:54 +02:00
// returns an iterator over object fields, obj is ptr to object header
// (in case of s-exp, it is mandatory that obj ptr is very beginning of the object,
// considering that now we store two versions of header in there)
2023-05-31 11:01:11 +02:00
obj_field_iterator field_begin_iterator ( void * obj ) ;
2023-03-27 10:09:54 +02:00
// returns an iterator over object fields which are actual pointers, obj is ptr to object header
// (in case of s-exp, it is mandatory that obj ptr is very beginning of the object,
// considering that now we store two versions of header in there)
2023-05-31 11:01:11 +02:00
obj_field_iterator ptr_field_begin_iterator ( void * obj ) ;
2023-03-27 10:09:54 +02:00
// moves the iterator to next object field
2023-05-31 11:01:11 +02:00
void obj_next_field_iterator ( obj_field_iterator * it ) ;
2023-03-27 10:09:54 +02:00
// moves the iterator to the next object field which is an actual pointer
2023-05-31 11:01:11 +02:00
void obj_next_ptr_field_iterator ( obj_field_iterator * it ) ;
2023-03-27 10:09:54 +02:00
// returns if we are done iterating over fields of the object
2023-05-31 11:01:11 +02:00
bool field_is_done_iterator ( obj_field_iterator * it ) ;
2023-03-27 10:09:54 +02:00
// ptr is pointer to the actual object content, returns pointer to the very beginning of the object (header)
2023-06-20 16:22:35 +02:00
void * get_obj_header_ptr ( void * ptr ) ;
2023-05-31 11:01:11 +02:00
void * get_object_content_ptr ( void * header_ptr ) ;
void * get_end_of_obj ( void * header_ptr ) ;
2023-03-27 10:09:54 +02:00
2023-05-31 11:01:11 +02:00
void * alloc_string ( int len ) ;
void * alloc_array ( int len ) ;
void * alloc_sexp ( int members ) ;
void * alloc_closure ( int captured ) ;
2023-04-26 14:22:14 +02:00
2023-05-31 11:01:11 +02:00
# endif