mirror of
https://github.com/ProgramSnail/Lama.git
synced 2025-12-06 06:48:48 +00:00
add clang-format; reformat files
This commit is contained in:
parent
f20d351dd0
commit
ccd04c2159
10 changed files with 1885 additions and 1837 deletions
144
runtime/.clang-format
Normal file
144
runtime/.clang-format
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
# Common settings
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
TabWidth: 2
|
||||||
|
IndentWidth: 2
|
||||||
|
UseTab: Never
|
||||||
|
ColumnLimit: 100
|
||||||
|
IndentCaseLabels: true
|
||||||
|
|
||||||
|
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
|
||||||
|
DisableFormat: false
|
||||||
|
Standard: Cpp11
|
||||||
|
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AlignAfterOpenBracket: true
|
||||||
|
AlignConsecutiveAssignments: true
|
||||||
|
AlignConsecutiveDeclarations: true
|
||||||
|
AlignEscapedNewlines: Right
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: false
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
AllowShortBlocksOnASingleLine: true
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
AllowShortFunctionsOnASingleLine: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: false
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: true
|
||||||
|
BitFieldColonSpacing: Both
|
||||||
|
|
||||||
|
|
||||||
|
# Configure each individual brace in BraceWrapping
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
# Control of individual brace wrapping cases
|
||||||
|
BraceWrapping: {
|
||||||
|
AfterClass: 'true'
|
||||||
|
AfterControlStatement: 'true'
|
||||||
|
AfterEnum : 'true'
|
||||||
|
AfterFunction : 'true'
|
||||||
|
AfterNamespace : 'true'
|
||||||
|
AfterStruct : 'true'
|
||||||
|
AfterUnion : 'true'
|
||||||
|
BeforeCatch : 'true'
|
||||||
|
BeforeElse : 'true'
|
||||||
|
IndentBraces : 'false'
|
||||||
|
AfterExternBlock : 'true'
|
||||||
|
SplitEmptyFunction : 'false'
|
||||||
|
SplitEmptyRecord : 'false'
|
||||||
|
SplitEmptyNamespace : 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
BreakAfterJavaFieldAnnotations: true
|
||||||
|
BreakBeforeInheritanceComma: false
|
||||||
|
BreakArrays: false
|
||||||
|
BreakBeforeBinaryOperators: NonAssignment
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializersBeforeComma: true
|
||||||
|
BreakStringLiterals: true
|
||||||
|
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||||
|
IndentCaseLabels: true
|
||||||
|
FixNamespaceComments: true
|
||||||
|
IndentWrappedFunctionNames: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
JavaScriptQuotes: Double
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBlockIndentWidth: 4
|
||||||
|
ObjCSpaceAfterProperty: true
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 19
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
PointerAlignment: Right
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeParens: Custom
|
||||||
|
SpaceBeforeParensOptions: {
|
||||||
|
AfterControlStatements: 'true'
|
||||||
|
AfterForeachMacros: 'true'
|
||||||
|
AfterFunctionDeclarationName: 'true'
|
||||||
|
AfterFunctionDefinitionName: 'true'
|
||||||
|
AfterIfMacros: 'true'
|
||||||
|
AfterOverloadedOperator: 'true'
|
||||||
|
AfterRequiresInClause: 'true'
|
||||||
|
AfterRequiresInExpression: 'true'
|
||||||
|
BeforeNonEmptyParentheses: 'false'
|
||||||
|
}
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: false
|
||||||
|
SpaceInEmptyBlock: true
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 3
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInConditionalStatement: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
VerilogBreakBetweenInstancePorts: true
|
||||||
|
|
||||||
|
SortUsingDeclarations: true
|
||||||
|
SortIncludes: CaseInsensitive
|
||||||
|
|
||||||
|
IndentGotoLabels: false
|
||||||
|
InsertBraces: false
|
||||||
|
InsertNewlineAtEOF: true
|
||||||
|
|
||||||
|
# Comments are for developers, they should arrange them
|
||||||
|
ReflowComments: false
|
||||||
|
|
||||||
|
IncludeBlocks: Regroup
|
||||||
|
IndentPPDirectives: AfterHash
|
||||||
|
SeparateDefinitionBlocks: Always
|
||||||
|
|
||||||
|
IntegerLiteralSeparator:
|
||||||
|
Binary: 4
|
||||||
|
Decimal: 0
|
||||||
|
Hex: 0
|
||||||
|
|
||||||
|
---
|
||||||
953
runtime/gc.c
953
runtime/gc.c
File diff suppressed because it is too large
Load diff
177
runtime/gc.h
177
runtime/gc.h
|
|
@ -3,99 +3,105 @@
|
||||||
|
|
||||||
#include "runtime_common.h"
|
#include "runtime_common.h"
|
||||||
|
|
||||||
# define GET_MARK_BIT(x) (((int) (x)) & 1)
|
#define GET_MARK_BIT(x) (((int)(x)) & 1)
|
||||||
# define SET_MARK_BIT(x) (x = (((int) (x)) | 1))
|
#define SET_MARK_BIT(x) (x = (((int)(x)) | 1))
|
||||||
# define IS_ENQUEUED(x) (((int) (x)) & 2)
|
#define IS_ENQUEUED(x) (((int)(x)) & 2)
|
||||||
# define MAKE_ENQUEUED(x) (x = (((int) (x)) | 2))
|
#define MAKE_ENQUEUED(x) (x = (((int)(x)) | 2))
|
||||||
# define MAKE_DEQUEUED(x) (x = (((int) (x)) & (~2)))
|
#define MAKE_DEQUEUED(x) (x = (((int)(x)) & (~2)))
|
||||||
# define RESET_MARK_BIT(x) (x = (((int) (x)) & (~1)))
|
#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 GET_FORWARD_ADDRESS(x) \
|
||||||
# define SET_FORWARD_ADDRESS(x, addr) (x = ((x & 3) | ((int) (addr)))) // take the last two bits as they are and make all others zero
|
(((size_t)(x)) \
|
||||||
# define EXTRA_ROOM_HEAP_COEFFICIENT 2 // TODO: tune this parameter
|
& (~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
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef DEBUG_VERSION
|
||||||
# define MINIMUM_HEAP_CAPACITY (8)
|
# define MINIMUM_HEAP_CAPACITY (8)
|
||||||
#else
|
#else
|
||||||
# define MINIMUM_HEAP_CAPACITY (1<<10)
|
# define MINIMUM_HEAP_CAPACITY (1 << 10)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
typedef enum { ARRAY, CLOSURE, STRING, SEXP } lama_type;
|
typedef enum { ARRAY, CLOSURE, STRING, SEXP } lama_type;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t *current;
|
size_t *current;
|
||||||
} heap_iterator;
|
} heap_iterator;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// holds type of object, which fields we are iterating over
|
// holds type of object, which fields we are iterating over
|
||||||
lama_type type;
|
lama_type type;
|
||||||
// here a pointer to the object header is stored
|
// here a pointer to the object header is stored
|
||||||
void *obj_ptr;
|
void *obj_ptr;
|
||||||
void *cur_field;
|
void *cur_field;
|
||||||
} obj_field_iterator;
|
} obj_field_iterator;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t * begin;
|
size_t *begin;
|
||||||
size_t * end;
|
size_t *end;
|
||||||
size_t * current;
|
size_t *current;
|
||||||
size_t size;
|
size_t size;
|
||||||
} memory_chunk;
|
} memory_chunk;
|
||||||
|
|
||||||
/* GC extra roots */
|
/* GC extra roots */
|
||||||
# define MAX_EXTRA_ROOTS_NUMBER 32
|
#define MAX_EXTRA_ROOTS_NUMBER 32
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int current_free;
|
int current_free;
|
||||||
void ** roots[MAX_EXTRA_ROOTS_NUMBER];
|
void **roots[MAX_EXTRA_ROOTS_NUMBER];
|
||||||
} extra_roots_pool;
|
} extra_roots_pool;
|
||||||
|
|
||||||
// the only GC-related function that should be exposed, others are useful for tests and internal implementation
|
// 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
|
// allocates object of the given size on the heap
|
||||||
void* alloc(size_t);
|
void *alloc(size_t);
|
||||||
// takes number of words as a parameter
|
// takes number of words as a parameter
|
||||||
void* gc_alloc(size_t);
|
void *gc_alloc(size_t);
|
||||||
// takes number of words as a parameter
|
// takes number of words as a parameter
|
||||||
void *gc_alloc_on_existing_heap(size_t);
|
void *gc_alloc_on_existing_heap(size_t);
|
||||||
|
|
||||||
// specific for mark-and-compact_phase gc
|
// specific for mark-and-compact_phase gc
|
||||||
void mark(void *obj);
|
void mark (void *obj);
|
||||||
void mark_phase(void);
|
void mark_phase (void);
|
||||||
// written in ASM, scans stack for pointers to the heap and starts marking process
|
// written in ASM, scans stack for pointers to the heap and starts marking process
|
||||||
extern void __gc_root_scan_stack(void); // TODO: write without ASM, since it is absolutely not necessary
|
extern void
|
||||||
|
__gc_root_scan_stack (void); // TODO: write without ASM, since it is absolutely not necessary
|
||||||
// marks each pointer from extra roots
|
// marks each pointer from extra roots
|
||||||
void scan_extra_roots(void);
|
void scan_extra_roots (void);
|
||||||
#ifndef DEBUG_VERSION
|
#ifndef DEBUG_VERSION
|
||||||
// marks each valid pointer from global area
|
// marks each valid pointer from global area
|
||||||
void scan_global_area(void);
|
void scan_global_area (void);
|
||||||
#endif
|
#endif
|
||||||
// takes number of words that are required to be allocated somewhere on the heap
|
// takes number of words that are required to be allocated somewhere on the heap
|
||||||
void compact_phase(size_t additional_size);
|
void compact_phase (size_t additional_size);
|
||||||
// specific for Lisp-2 algorithm
|
// specific for Lisp-2 algorithm
|
||||||
size_t compute_locations();
|
size_t compute_locations ();
|
||||||
void update_references(memory_chunk *);
|
void update_references (memory_chunk *);
|
||||||
void physically_relocate(memory_chunk *);
|
void physically_relocate (memory_chunk *);
|
||||||
|
|
||||||
// written in ASM
|
// written in ASM
|
||||||
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 __gc_init (
|
||||||
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
|
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 __shutdown (void); // mostly useful for tests but basically you want to call this in case you want to deallocate all object allocated via GC
|
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
|
||||||
// written in ASM
|
// written in ASM
|
||||||
extern void __pre_gc (void);
|
extern void __pre_gc (void);
|
||||||
// written in ASM
|
// written in ASM
|
||||||
extern void __post_gc (void);
|
extern void __post_gc (void);
|
||||||
|
|
||||||
// invoked from ASM
|
// invoked from ASM
|
||||||
extern void gc_test_and_mark_root(size_t ** root);
|
extern void gc_test_and_mark_root (size_t **root);
|
||||||
inline bool is_valid_heap_pointer(const size_t *);
|
inline bool is_valid_heap_pointer (const size_t *);
|
||||||
inline bool is_valid_pointer(const size_t *);
|
inline bool is_valid_pointer (const size_t *);
|
||||||
|
|
||||||
void clear_extra_roots (void);
|
void clear_extra_roots (void);
|
||||||
|
|
||||||
void push_extra_root (void ** p);
|
void push_extra_root (void **p);
|
||||||
|
|
||||||
void pop_extra_root (void ** p);
|
|
||||||
|
|
||||||
|
void pop_extra_root (void **p);
|
||||||
|
|
||||||
/* Functions for tests */
|
/* Functions for tests */
|
||||||
|
|
||||||
|
|
@ -105,96 +111,95 @@ void pop_extra_root (void ** p);
|
||||||
// returns number of ids dumped
|
// 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 is pointer to area preallocated by user for dumping ids of objects in heap
|
||||||
// object_ids_buf_size is in WORDS, NOT BYTES
|
// object_ids_buf_size is in WORDS, NOT BYTES
|
||||||
size_t objects_snapshot(int *object_ids_buf, size_t object_ids_buf_size);
|
size_t objects_snapshot (int *object_ids_buf, size_t object_ids_buf_size);
|
||||||
|
|
||||||
// essential function to mock program stack
|
// essential function to mock program stack
|
||||||
void set_stack(size_t stack_top, size_t stack_bottom);
|
void set_stack (size_t stack_top, size_t stack_bottom);
|
||||||
|
|
||||||
// function to mock extra roots (Lama specific)
|
// function to mock extra roots (Lama specific)
|
||||||
void set_extra_roots(size_t extra_roots_size, void** extra_roots_ptr);
|
void set_extra_roots (size_t extra_roots_size, void **extra_roots_ptr);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* Utility functions */
|
/* Utility functions */
|
||||||
|
|
||||||
// accepts pointer to the start of the region and to the end of the region
|
// 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
|
// scans it and if it meets a pointer, it should be modified in according to forward address
|
||||||
void scan_and_fix_region(memory_chunk *old_heap, void *start, void *end);
|
void scan_and_fix_region (memory_chunk *old_heap, void *start, void *end);
|
||||||
|
|
||||||
// takes a pointer to an object content as an argument, returns forwarding address
|
// takes a pointer to an object content as an argument, returns forwarding address
|
||||||
size_t get_forward_address(void *obj);
|
size_t get_forward_address (void *obj);
|
||||||
|
|
||||||
// takes a pointer to an object content as an argument, sets forwarding address to value 'addr'
|
// takes a pointer to an object content as an argument, sets forwarding address to value 'addr'
|
||||||
void set_forward_address(void *obj, size_t addr);
|
void set_forward_address (void *obj, size_t addr);
|
||||||
|
|
||||||
// takes a pointer to an object content as an argument, returns whether this object was marked as live
|
// takes a pointer to an object content as an argument, returns whether this object was marked as live
|
||||||
bool is_marked(void *obj);
|
bool is_marked (void *obj);
|
||||||
|
|
||||||
// takes a pointer to an object content as an argument, marks the object as live
|
// takes a pointer to an object content as an argument, marks the object as live
|
||||||
void mark_object(void *obj);
|
void mark_object (void *obj);
|
||||||
|
|
||||||
// takes a pointer to an object content as an argument, marks the object as dead
|
// takes a pointer to an object content as an argument, marks the object as dead
|
||||||
void unmark_object(void *obj);
|
void unmark_object (void *obj);
|
||||||
|
|
||||||
// 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)
|
// 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)
|
||||||
bool is_enqueued(void *obj);
|
bool is_enqueued (void *obj);
|
||||||
|
|
||||||
// takes a pointer to an object content as an argument, marks object as enqueued
|
// takes a pointer to an object content as an argument, marks object as enqueued
|
||||||
void make_enqueued(void *obj);
|
void make_enqueued (void *obj);
|
||||||
|
|
||||||
// takes a pointer to an object content as an argument, unmarks object as enqueued
|
// takes a pointer to an object content as an argument, unmarks object as enqueued
|
||||||
void make_dequeued(void *obj);
|
void make_dequeued (void *obj);
|
||||||
|
|
||||||
// returns iterator to an object with the lowest address
|
// returns iterator to an object with the lowest address
|
||||||
heap_iterator heap_begin_iterator();
|
heap_iterator heap_begin_iterator ();
|
||||||
void heap_next_obj_iterator(heap_iterator *it);
|
void heap_next_obj_iterator (heap_iterator *it);
|
||||||
bool heap_is_done_iterator(heap_iterator *it);
|
bool heap_is_done_iterator (heap_iterator *it);
|
||||||
|
|
||||||
// returns correct type when pointer to actual data is passed (header is excluded)
|
// returns correct type when pointer to actual data is passed (header is excluded)
|
||||||
lama_type get_type_row_ptr(void *ptr);
|
lama_type get_type_row_ptr (void *ptr);
|
||||||
// returns correct type when pointer to an object header is passed
|
// returns correct type when pointer to an object header is passed
|
||||||
lama_type get_type_header_ptr(void *ptr);
|
lama_type get_type_header_ptr (void *ptr);
|
||||||
|
|
||||||
// returns correct object size (together with header) of an object, ptr is pointer to an actual data is passed (header is excluded)
|
// returns correct object size (together with header) of an object, ptr is pointer to an actual data is passed (header is excluded)
|
||||||
size_t obj_size_row_ptr(void *ptr);
|
size_t obj_size_row_ptr (void *ptr);
|
||||||
// returns correct object size (together with header) of an object, ptr is pointer to an object header
|
// returns correct object size (together with header) of an object, ptr is pointer to an object header
|
||||||
size_t obj_size_header_ptr(void *ptr);
|
size_t obj_size_header_ptr (void *ptr);
|
||||||
|
|
||||||
// returns total padding size that we need to store given object type
|
// returns total padding size that we need to store given object type
|
||||||
size_t get_header_size(lama_type type);
|
size_t get_header_size (lama_type type);
|
||||||
// returns number of bytes that are required to allocate array with 'sz' elements (header included)
|
// returns number of bytes that are required to allocate array with 'sz' elements (header included)
|
||||||
size_t array_size(size_t sz);
|
size_t array_size (size_t sz);
|
||||||
// returns number of bytes that are required to allocate string of length 'l' (header included)
|
// returns number of bytes that are required to allocate string of length 'l' (header included)
|
||||||
size_t string_size(size_t len);
|
size_t string_size (size_t len);
|
||||||
// TODO: ask if it is actually so? number of captured elements is actually sz-1 and 1 extra word is code ptr?
|
// 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)
|
// returns number of bytes that are required to allocate closure with 'sz-1' captured values (header included)
|
||||||
size_t closure_size(size_t sz);
|
size_t closure_size (size_t sz);
|
||||||
// returns number of bytes that are required to allocate s-expression with 'members' fields (header included)
|
// returns number of bytes that are required to allocate s-expression with 'members' fields (header included)
|
||||||
size_t sexp_size(size_t members);
|
size_t sexp_size (size_t members);
|
||||||
|
|
||||||
// returns an iterator over object fields, obj is ptr to object header
|
// 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,
|
// (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)
|
// considering that now we store two versions of header in there)
|
||||||
obj_field_iterator field_begin_iterator(void *obj);
|
obj_field_iterator field_begin_iterator (void *obj);
|
||||||
// returns an iterator over object fields which are actual pointers, obj is ptr to object header
|
// 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,
|
// (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)
|
// considering that now we store two versions of header in there)
|
||||||
obj_field_iterator ptr_field_begin_iterator(void *obj);
|
obj_field_iterator ptr_field_begin_iterator (void *obj);
|
||||||
// moves the iterator to next object field
|
// moves the iterator to next object field
|
||||||
void obj_next_field_iterator(obj_field_iterator *it);
|
void obj_next_field_iterator (obj_field_iterator *it);
|
||||||
// moves the iterator to the next object field which is an actual pointer
|
// moves the iterator to the next object field which is an actual pointer
|
||||||
void obj_next_ptr_field_iterator(obj_field_iterator *it);
|
void obj_next_ptr_field_iterator (obj_field_iterator *it);
|
||||||
// returns if we are done iterating over fields of the object
|
// returns if we are done iterating over fields of the object
|
||||||
bool field_is_done_iterator(obj_field_iterator *it);
|
bool field_is_done_iterator (obj_field_iterator *it);
|
||||||
// ptr is pointer to the actual object content, returns pointer to the very beginning of the object (header)
|
// ptr is pointer to the actual object content, returns pointer to the very beginning of the object (header)
|
||||||
void* get_obj_header_ptr(void *ptr, lama_type type);
|
void *get_obj_header_ptr (void *ptr, lama_type type);
|
||||||
void* get_object_content_ptr(void *header_ptr);
|
void *get_object_content_ptr (void *header_ptr);
|
||||||
void* get_end_of_obj(void *header_ptr);
|
void *get_end_of_obj (void *header_ptr);
|
||||||
|
|
||||||
void *alloc_string(int len);
|
void *alloc_string (int len);
|
||||||
void *alloc_array(int len);
|
void *alloc_array (int len);
|
||||||
void *alloc_sexp(int members);
|
void *alloc_sexp (int members);
|
||||||
void *alloc_closure(int captured);
|
void *alloc_closure (int captured);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,63 @@
|
||||||
.data
|
.data
|
||||||
printf_format: .string "Stack root: %lx\n"
|
|
||||||
printf_format2: .string "BOT: %lx\n"
|
|
||||||
printf_format3: .string "TOP: %lx\n"
|
|
||||||
printf_format4: .string "EAX: %lx\n"
|
|
||||||
printf_format5: .string "LOL\n"
|
|
||||||
__gc_stack_bottom: .long 0
|
__gc_stack_bottom: .long 0
|
||||||
__gc_stack_top: .long 0
|
__gc_stack_top: .long 0
|
||||||
|
|
||||||
.globl __pre_gc
|
.globl __pre_gc
|
||||||
.globl __post_gc
|
.globl __post_gc
|
||||||
.globl __gc_init
|
.globl __gc_init
|
||||||
.globl __gc_root_scan_stack
|
.globl __gc_root_scan_stack
|
||||||
.globl __gc_stack_top
|
.globl __gc_stack_top
|
||||||
.globl __gc_stack_bottom
|
.globl __gc_stack_bottom
|
||||||
.extern init_pool
|
.extern __init
|
||||||
.extern gc_test_and_copy_root
|
.extern gc_test_and_mark_root
|
||||||
.text
|
|
||||||
|
.text
|
||||||
|
|
||||||
__gc_init:
|
__gc_init:
|
||||||
movl %ebp, __gc_stack_bottom
|
movl %ebp, __gc_stack_bottom
|
||||||
addl $4, __gc_stack_bottom
|
addl $4, __gc_stack_bottom
|
||||||
call __init
|
call __init
|
||||||
ret
|
ret
|
||||||
|
|
||||||
// if __gc_stack_top is equal to 0
|
// if __gc_stack_top is equal to 0
|
||||||
// then set __gc_stack_top to %ebp
|
// then set __gc_stack_top to %ebp
|
||||||
// else return
|
// else return
|
||||||
__pre_gc:
|
__pre_gc:
|
||||||
pushl %eax
|
pushl %eax
|
||||||
movl __gc_stack_top, %eax
|
movl __gc_stack_top, %eax
|
||||||
cmpl $0, %eax
|
cmpl $0, %eax
|
||||||
jne __pre_gc_2
|
jne __pre_gc_2
|
||||||
movl %ebp, %eax
|
movl %ebp, %eax
|
||||||
// addl $8, %eax
|
// addl $8, %eax
|
||||||
movl %eax, __gc_stack_top
|
movl %eax, __gc_stack_top
|
||||||
__pre_gc_2:
|
__pre_gc_2:
|
||||||
popl %eax
|
popl %eax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
// if __gc_stack_top has been set by the caller
|
// if __gc_stack_top has been set by the caller
|
||||||
// (i.e. it is equal to its %ebp)
|
// (i.e. it is equal to its %ebp)
|
||||||
// then set __gc_stack_top to 0
|
// then set __gc_stack_top to 0
|
||||||
// else return
|
// else return
|
||||||
__post_gc:
|
__post_gc:
|
||||||
pushl %eax
|
pushl %eax
|
||||||
movl __gc_stack_top, %eax
|
movl __gc_stack_top, %eax
|
||||||
cmpl %eax, %ebp
|
cmpl %eax, %ebp
|
||||||
jnz __post_gc2
|
jnz __post_gc2
|
||||||
movl $0, __gc_stack_top
|
movl $0, __gc_stack_top
|
||||||
__post_gc2:
|
__post_gc2:
|
||||||
popl %eax
|
popl %eax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
// Scan stack for roots
|
// Scan stack for roots
|
||||||
// strting from __gc_stack_top
|
// strting from __gc_stack_top
|
||||||
// till __gc_stack_bottom
|
// till __gc_stack_bottom
|
||||||
__gc_root_scan_stack:
|
__gc_root_scan_stack:
|
||||||
pushl %ebp
|
pushl %ebp
|
||||||
movl %esp, %ebp
|
movl %esp, %ebp
|
||||||
pushl %ebx
|
pushl %ebx
|
||||||
pushl %edx
|
pushl %edx
|
||||||
movl __gc_stack_top, %eax
|
movl __gc_stack_top, %eax
|
||||||
jmp next
|
jmp next
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
movl (%eax), %ebx
|
movl (%eax), %ebx
|
||||||
|
|
@ -70,48 +66,48 @@ loop:
|
||||||
// i.e. the following is not true:
|
// i.e. the following is not true:
|
||||||
// __executable_start <= (%eax) <= __etext
|
// __executable_start <= (%eax) <= __etext
|
||||||
check11:
|
check11:
|
||||||
leal __executable_start, %edx
|
leal __executable_start, %edx
|
||||||
cmpl %ebx, %edx
|
cmpl %ebx, %edx
|
||||||
jna check12
|
jna check12
|
||||||
jmp check21
|
jmp check21
|
||||||
|
|
||||||
check12:
|
check12:
|
||||||
leal __etext, %edx
|
leal __etext, %edx
|
||||||
cmpl %ebx, %edx
|
cmpl %ebx, %edx
|
||||||
jnb next
|
jnb next
|
||||||
|
|
||||||
// check that it is not a pointer into the program stack
|
// check that it is not a pointer into the program stack
|
||||||
// i.e. the following is not true:
|
// i.e. the following is not true:
|
||||||
// __gc_stack_bottom <= (%eax) <= __gc_stack_top
|
// __gc_stack_bottom <= (%eax) <= __gc_stack_top
|
||||||
check21:
|
check21:
|
||||||
cmpl %ebx, __gc_stack_top
|
cmpl %ebx, __gc_stack_top
|
||||||
jna check22
|
jna check22
|
||||||
jmp loop2
|
jmp loop2
|
||||||
|
|
||||||
check22:
|
check22:
|
||||||
cmpl %ebx, __gc_stack_bottom
|
cmpl %ebx, __gc_stack_bottom
|
||||||
jnb next
|
jnb next
|
||||||
|
|
||||||
// check if it a valid pointer
|
// check if it a valid pointer
|
||||||
// i.e. the lastest bit is set to zero
|
// i.e. the lastest bit is set to zero
|
||||||
loop2:
|
loop2:
|
||||||
andl $0x00000001, %ebx
|
andl $0x00000001, %ebx
|
||||||
jnz next
|
jnz next
|
||||||
gc_run_t:
|
gc_run_t:
|
||||||
pushl %eax
|
pushl %eax
|
||||||
pushl %eax
|
pushl %eax
|
||||||
call gc_test_and_mark_root
|
call gc_test_and_mark_root
|
||||||
addl $4, %esp
|
addl $4, %esp
|
||||||
popl %eax
|
popl %eax
|
||||||
|
|
||||||
next:
|
next:
|
||||||
addl $4, %eax
|
addl $4, %eax
|
||||||
cmpl %eax, __gc_stack_bottom
|
cmpl %eax, __gc_stack_bottom
|
||||||
jne loop
|
jne loop
|
||||||
returnn:
|
returnn:
|
||||||
movl $0, %eax
|
movl $0, %eax
|
||||||
popl %edx
|
popl %edx
|
||||||
popl %ebx
|
popl %ebx
|
||||||
movl %ebp, %esp
|
movl %ebp, %esp
|
||||||
popl %ebp
|
popl %ebp
|
||||||
ret
|
ret
|
||||||
|
|
|
||||||
1742
runtime/runtime.c
1742
runtime/runtime.c
File diff suppressed because it is too large
Load diff
|
|
@ -1,21 +1,20 @@
|
||||||
# ifndef __LAMA_RUNTIME__
|
#ifndef __LAMA_RUNTIME__
|
||||||
# define __LAMA_RUNTIME__
|
#define __LAMA_RUNTIME__
|
||||||
|
|
||||||
# include <stdio.h>
|
#include <assert.h>
|
||||||
# include <stdio.h>
|
#include <ctype.h>
|
||||||
# include <string.h>
|
#include <errno.h>
|
||||||
# include <stdarg.h>
|
#include <limits.h>
|
||||||
# include <stdlib.h>
|
#include <regex.h>
|
||||||
# include <sys/mman.h>
|
#include <stdarg.h>
|
||||||
# include <assert.h>
|
#include <stdio.h>
|
||||||
# include <errno.h>
|
#include <stdlib.h>
|
||||||
# include <regex.h>
|
#include <string.h>
|
||||||
# include <time.h>
|
#include <sys/mman.h>
|
||||||
# include <limits.h>
|
#include <time.h>
|
||||||
# include <ctype.h>
|
|
||||||
|
|
||||||
# define WORD_SIZE (CHAR_BIT * sizeof(int))
|
#define WORD_SIZE (CHAR_BIT * sizeof(int))
|
||||||
|
|
||||||
void failure (char *s, ...);
|
void failure (char *s, ...);
|
||||||
|
|
||||||
# endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -5,67 +5,65 @@
|
||||||
// this flag makes GC behavior a bit different for testing purposes.
|
// this flag makes GC behavior a bit different for testing purposes.
|
||||||
//#define DEBUG_VERSION
|
//#define DEBUG_VERSION
|
||||||
|
|
||||||
# define STRING_TAG 0x00000001
|
#define STRING_TAG 0x00000001
|
||||||
//# define STRING_TAG 0x00000000
|
//# define STRING_TAG 0x00000000
|
||||||
# define ARRAY_TAG 0x00000003
|
#define ARRAY_TAG 0x00000003
|
||||||
//# define ARRAY_TAG 0x00000002
|
//# define ARRAY_TAG 0x00000002
|
||||||
# define SEXP_TAG 0x00000005
|
#define SEXP_TAG 0x00000005
|
||||||
//# define SEXP_TAG 0x00000004
|
//# define SEXP_TAG 0x00000004
|
||||||
# define CLOSURE_TAG 0x00000007
|
#define CLOSURE_TAG 0x00000007
|
||||||
//# define CLOSURE_TAG 0x00000006
|
//# define CLOSURE_TAG 0x00000006
|
||||||
# define UNBOXED_TAG 0x00000009 // Not actually a data_header; used to return from LkindOf
|
#define UNBOXED_TAG 0x00000009 // Not actually a data_header; used to return from LkindOf
|
||||||
|
|
||||||
# define LEN(x) ((x & 0xFFFFFFF8) >> 3)
|
#define LEN(x) ((x & 0xFFFFFFF8) >> 3)
|
||||||
# define TAG(x) (x & 0x00000007)
|
#define TAG(x) (x & 0x00000007)
|
||||||
//# define TAG(x) (x & 0x00000006)
|
//# define TAG(x) (x & 0x00000006)
|
||||||
|
|
||||||
|
#define SEXP_ONLY_HEADER_SZ (2 * sizeof(int))
|
||||||
|
|
||||||
# define SEXP_ONLY_HEADER_SZ (2 * sizeof(int))
|
#ifndef DEBUG_VERSION
|
||||||
|
# define DATA_HEADER_SZ (sizeof(size_t) + sizeof(int))
|
||||||
# ifndef DEBUG_VERSION
|
#else
|
||||||
# define DATA_HEADER_SZ (sizeof(size_t) + sizeof(int))
|
# define DATA_HEADER_SZ (sizeof(size_t) + sizeof(size_t) + sizeof(int))
|
||||||
# else
|
|
||||||
# define DATA_HEADER_SZ (sizeof(size_t) + sizeof(size_t) + sizeof(int))
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
# define MEMBER_SIZE sizeof(int)
|
#define MEMBER_SIZE sizeof(int)
|
||||||
|
|
||||||
# define TO_DATA(x) ((data*)((char*)(x)-DATA_HEADER_SZ))
|
#define TO_DATA(x) ((data *)((char *)(x)-DATA_HEADER_SZ))
|
||||||
# define TO_SEXP(x) ((sexp*)((char*)(x)-DATA_HEADER_SZ-SEXP_ONLY_HEADER_SZ))
|
#define TO_SEXP(x) ((sexp *)((char *)(x)-DATA_HEADER_SZ - SEXP_ONLY_HEADER_SZ))
|
||||||
|
|
||||||
# define UNBOXED(x) (((int) (x)) & 0x0001)
|
#define UNBOXED(x) (((int)(x)) & 0x0001)
|
||||||
# define UNBOX(x) (((int) (x)) >> 1)
|
#define UNBOX(x) (((int)(x)) >> 1)
|
||||||
# define BOX(x) ((((int) (x)) << 1) | 0x0001)
|
#define BOX(x) ((((int)(x)) << 1) | 0x0001)
|
||||||
|
|
||||||
# define BYTES_TO_WORDS(bytes) (((bytes) - 1) / sizeof(size_t) + 1)
|
#define BYTES_TO_WORDS(bytes) (((bytes)-1) / sizeof(size_t) + 1)
|
||||||
# define WORDS_TO_BYTES(words) ((words) * sizeof(size_t))
|
#define WORDS_TO_BYTES(words) ((words) * sizeof(size_t))
|
||||||
|
|
||||||
// CAREFUL WITH DOUBLE EVALUATION!
|
// CAREFUL WITH DOUBLE EVALUATION!
|
||||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// store tag in the last three bits to understand what structure this is, other bits are filled with
|
// store tag in the last three bits to understand what structure this is, other bits are filled with
|
||||||
// other utility info (i.e., size for array, number of fields for s-expression)
|
// other utility info (i.e., size for array, number of fields for s-expression)
|
||||||
int data_header;
|
int data_header;
|
||||||
|
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef DEBUG_VERSION
|
||||||
size_t id;
|
size_t id;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// last bit is used as MARK-BIT, the rest are used to store address where object should move
|
// last bit is used as MARK-BIT, the rest are used to store address where object should move
|
||||||
// last bit can be used because due to alignment we can assume that last two bits are always 0's
|
// last bit can be used because due to alignment we can assume that last two bits are always 0's
|
||||||
size_t forward_address;
|
size_t forward_address;
|
||||||
char contents[0];
|
char contents[0];
|
||||||
} data;
|
} data;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// duplicates contents.data_header in order to be able to understand if it is s-exp during iteration over heap
|
// duplicates contents.data_header in order to be able to understand if it is s-exp during iteration over heap
|
||||||
int sexp_header;
|
int sexp_header;
|
||||||
// stores hashed s-expression constructor name
|
// stores hashed s-expression constructor name
|
||||||
int tag;
|
int tag;
|
||||||
data contents;
|
data contents;
|
||||||
} sexp;
|
} sexp;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,273 +1,275 @@
|
||||||
#include <assert.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "gc.h"
|
#include "gc.h"
|
||||||
#include "runtime_common.h"
|
#include "runtime_common.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef DEBUG_VERSION
|
||||||
|
|
||||||
// function from runtime that maps string to int value
|
// function from runtime that maps string to int value
|
||||||
extern int LtagHash (char *s);
|
extern int LtagHash (char *s);
|
||||||
|
|
||||||
extern void* Bsexp (int n, ...);
|
extern void *Bsexp (int n, ...);
|
||||||
extern void* Barray (int bn, ...);
|
extern void *Barray (int bn, ...);
|
||||||
extern void* Bstring (void*);
|
extern void *Bstring (void *);
|
||||||
extern void* Bclosure (int bn, void *entry, ...);
|
extern void *Bclosure (int bn, void *entry, ...);
|
||||||
|
|
||||||
extern size_t __gc_stack_top, __gc_stack_bottom;
|
extern size_t __gc_stack_top, __gc_stack_bottom;
|
||||||
|
|
||||||
void test_correct_structure_sizes(void) {
|
void test_correct_structure_sizes (void) {
|
||||||
// something like induction base
|
// something like induction base
|
||||||
assert((array_size(0) == get_header_size(ARRAY)));
|
assert((array_size(0) == get_header_size(ARRAY)));
|
||||||
assert((string_size(0) == get_header_size(STRING) + 1)); // +1 is because of '\0'
|
assert((string_size(0) == get_header_size(STRING) + 1)); // +1 is because of '\0'
|
||||||
assert((sexp_size(0) == get_header_size(SEXP)));
|
assert((sexp_size(0) == get_header_size(SEXP)));
|
||||||
assert((closure_size(0) == get_header_size(CLOSURE)));
|
assert((closure_size(0) == get_header_size(CLOSURE)));
|
||||||
|
|
||||||
// just check correctness for some small sizes
|
// just check correctness for some small sizes
|
||||||
for (int k = 1; k < 20; ++k) {
|
for (int k = 1; k < 20; ++k) {
|
||||||
assert((array_size(k) == get_header_size(ARRAY) + sizeof (int) * k));
|
assert((array_size(k) == get_header_size(ARRAY) + sizeof(int) * k));
|
||||||
assert((string_size(k) == get_header_size(STRING) + k + 1));
|
assert((string_size(k) == get_header_size(STRING) + k + 1));
|
||||||
assert((sexp_size(k) == get_header_size(SEXP) + sizeof (int) * k));
|
assert((sexp_size(k) == get_header_size(SEXP) + sizeof(int) * k));
|
||||||
assert((closure_size(k) == get_header_size(CLOSURE) + sizeof (int) * k));
|
assert((closure_size(k) == get_header_size(CLOSURE) + sizeof(int) * k));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void no_gc_tests(void) {
|
void no_gc_tests (void) { test_correct_structure_sizes(); }
|
||||||
test_correct_structure_sizes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// unfortunately there is no generic function pointer that can hold pointer to function with arbitrary signature
|
// unfortunately there is no generic function pointer that can hold pointer to function with arbitrary signature
|
||||||
extern size_t call_runtime_function(void *virt_stack_pointer, void *function_pointer, size_t num_args, ...);
|
extern size_t call_runtime_function (void *virt_stack_pointer, void *function_pointer,
|
||||||
|
size_t num_args, ...);
|
||||||
|
|
||||||
#include "virt_stack.h"
|
# include "virt_stack.h"
|
||||||
|
|
||||||
virt_stack* init_test() {
|
virt_stack *init_test () {
|
||||||
__init();
|
__init();
|
||||||
virt_stack *st = vstack_create();
|
virt_stack *st = vstack_create();
|
||||||
vstack_init(st);
|
vstack_init(st);
|
||||||
__gc_stack_bottom = (size_t) vstack_top(st);
|
__gc_stack_bottom = (size_t)vstack_top(st);
|
||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanup_test(virt_stack *st) {
|
void cleanup_test (virt_stack *st) {
|
||||||
vstack_destruct(st);
|
vstack_destruct(st);
|
||||||
__shutdown();
|
__shutdown();
|
||||||
}
|
|
||||||
void force_gc_cycle(virt_stack *st) {
|
|
||||||
__gc_stack_top = (size_t) vstack_top(st) - 4;
|
|
||||||
gc_alloc(0);
|
|
||||||
__gc_stack_top = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_simple_string_alloc(void) {
|
void force_gc_cycle (virt_stack *st) {
|
||||||
virt_stack *st = init_test();
|
__gc_stack_top = (size_t)vstack_top(st) - 4;
|
||||||
|
gc_alloc(0);
|
||||||
for (int i = 0; i < 5; ++i) {
|
__gc_stack_top = 0;
|
||||||
vstack_push(st, BOX(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bstring, 1, "abc"));
|
|
||||||
|
|
||||||
const int N = 10;
|
|
||||||
int ids[N];
|
|
||||||
size_t alive = objects_snapshot(ids, N);
|
|
||||||
assert((alive == 1));
|
|
||||||
|
|
||||||
cleanup_test(st);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_simple_array_alloc(void) {
|
void test_simple_string_alloc (void) {
|
||||||
virt_stack* st = init_test();
|
virt_stack *st = init_test();
|
||||||
|
|
||||||
// allocate array [ BOX(1) ] and push it onto the stack
|
for (int i = 0; i < 5; ++i) { vstack_push(st, BOX(i)); }
|
||||||
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Barray, 2, BOX(1), BOX(1)));
|
|
||||||
|
|
||||||
const int N = 10;
|
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bstring, 1, "abc"));
|
||||||
int ids[N];
|
|
||||||
size_t alive = objects_snapshot(ids, N);
|
|
||||||
assert((alive == 1));
|
|
||||||
|
|
||||||
cleanup_test(st);
|
const int N = 10;
|
||||||
|
int ids[N];
|
||||||
|
size_t alive = objects_snapshot(ids, N);
|
||||||
|
assert((alive == 1));
|
||||||
|
|
||||||
|
cleanup_test(st);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_simple_sexp_alloc(void) {
|
void test_simple_array_alloc (void) {
|
||||||
virt_stack* st = init_test();
|
virt_stack *st = init_test();
|
||||||
|
|
||||||
// allocate sexp with one boxed field and push it onto the stack
|
// allocate array [ BOX(1) ] and push it onto the stack
|
||||||
// calling runtime function Bsexp(BOX(2), BOX(1), LtagHash("test"))
|
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Barray, 2, BOX(1), BOX(1)));
|
||||||
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bsexp, 3, BOX(2), BOX(1), LtagHash("test")));
|
|
||||||
|
|
||||||
const int N = 10;
|
const int N = 10;
|
||||||
int ids[N];
|
int ids[N];
|
||||||
size_t alive = objects_snapshot(ids, N);
|
size_t alive = objects_snapshot(ids, N);
|
||||||
assert((alive == 1));
|
assert((alive == 1));
|
||||||
|
|
||||||
cleanup_test(st);
|
cleanup_test(st);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_simple_closure_alloc(void) {
|
void test_simple_sexp_alloc (void) {
|
||||||
virt_stack* st = init_test();
|
virt_stack *st = init_test();
|
||||||
|
|
||||||
// allocate closure with boxed captured value and push it onto the stack
|
// allocate sexp with one boxed field and push it onto the stack
|
||||||
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bclosure, 3, BOX(1), NULL, BOX(1)));
|
// calling runtime function Bsexp(BOX(2), BOX(1), LtagHash("test"))
|
||||||
|
vstack_push(
|
||||||
|
st, call_runtime_function(vstack_top(st) - 4, Bsexp, 3, BOX(2), BOX(1), LtagHash("test")));
|
||||||
|
|
||||||
const int N = 10;
|
const int N = 10;
|
||||||
int ids[N];
|
int ids[N];
|
||||||
size_t alive = objects_snapshot(ids, N);
|
size_t alive = objects_snapshot(ids, N);
|
||||||
assert((alive == 1));
|
assert((alive == 1));
|
||||||
|
|
||||||
cleanup_test(st);
|
cleanup_test(st);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_single_object_allocation_with_collection_virtual_stack(void) {
|
void test_simple_closure_alloc (void) {
|
||||||
virt_stack *st = init_test();
|
virt_stack *st = init_test();
|
||||||
|
|
||||||
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
|
// allocate closure with boxed captured value and push it onto the stack
|
||||||
|
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bclosure, 3, BOX(1), NULL, BOX(1)));
|
||||||
|
|
||||||
const int N = 10;
|
const int N = 10;
|
||||||
int ids[N];
|
int ids[N];
|
||||||
size_t alive = objects_snapshot(ids, N);
|
size_t alive = objects_snapshot(ids, N);
|
||||||
assert((alive == 1));
|
assert((alive == 1));
|
||||||
|
|
||||||
cleanup_test(st);
|
cleanup_test(st);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_garbage_is_reclaimed(void) {
|
void test_single_object_allocation_with_collection_virtual_stack (void) {
|
||||||
virt_stack *st = init_test();
|
virt_stack *st = init_test();
|
||||||
|
|
||||||
call_runtime_function(vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
vstack_push(st,
|
||||||
|
call_runtime_function(
|
||||||
|
vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
|
||||||
|
|
||||||
force_gc_cycle(st);
|
const int N = 10;
|
||||||
|
int ids[N];
|
||||||
|
size_t alive = objects_snapshot(ids, N);
|
||||||
|
assert((alive == 1));
|
||||||
|
|
||||||
const int N = 10;
|
cleanup_test(st);
|
||||||
int ids[N];
|
|
||||||
size_t alive = objects_snapshot(ids, N);
|
|
||||||
assert((alive == 0));
|
|
||||||
|
|
||||||
cleanup_test(st);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_alive_are_not_reclaimed(void) {
|
void test_garbage_is_reclaimed (void) {
|
||||||
virt_stack *st = init_test();
|
virt_stack *st = init_test();
|
||||||
|
|
||||||
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
|
call_runtime_function(vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||||
|
|
||||||
force_gc_cycle(st);
|
force_gc_cycle(st);
|
||||||
|
|
||||||
const int N = 10;
|
const int N = 10;
|
||||||
int ids[N];
|
int ids[N];
|
||||||
size_t alive = objects_snapshot(ids, N);
|
size_t alive = objects_snapshot(ids, N);
|
||||||
assert((alive == 1));
|
assert((alive == 0));
|
||||||
|
|
||||||
cleanup_test(st);
|
cleanup_test(st);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_small_tree_compaction(void) {
|
void test_alive_are_not_reclaimed (void) {
|
||||||
virt_stack *st = init_test();
|
virt_stack *st = init_test();
|
||||||
// this one will increase heap size
|
|
||||||
call_runtime_function(vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaa");
|
|
||||||
|
|
||||||
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bstring, 1, "left-s"));
|
vstack_push(st,
|
||||||
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bstring, 1, "right-s"));
|
call_runtime_function(
|
||||||
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bsexp, 4, BOX(3), vstack_kth_from_start(st, 0), vstack_kth_from_start(st, 1), LtagHash("tree")));
|
vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
|
||||||
force_gc_cycle(st);
|
|
||||||
const int SZ = 10;
|
|
||||||
int ids[SZ];
|
|
||||||
size_t alive = objects_snapshot(ids, SZ);
|
|
||||||
assert((alive == 3));
|
|
||||||
|
|
||||||
// check that order is indeed preserved
|
force_gc_cycle(st);
|
||||||
for (int i = 0; i < alive - 1; ++i) {
|
|
||||||
assert((ids[i] < ids[i + 1]));
|
const int N = 10;
|
||||||
}
|
int ids[N];
|
||||||
cleanup_test(st);
|
size_t alive = objects_snapshot(ids, N);
|
||||||
|
assert((alive == 1));
|
||||||
|
|
||||||
|
cleanup_test(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_small_tree_compaction (void) {
|
||||||
|
virt_stack *st = init_test();
|
||||||
|
// this one will increase heap size
|
||||||
|
call_runtime_function(vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaa");
|
||||||
|
|
||||||
|
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bstring, 1, "left-s"));
|
||||||
|
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bstring, 1, "right-s"));
|
||||||
|
vstack_push(st,
|
||||||
|
call_runtime_function(vstack_top(st) - 4,
|
||||||
|
Bsexp,
|
||||||
|
4,
|
||||||
|
BOX(3),
|
||||||
|
vstack_kth_from_start(st, 0),
|
||||||
|
vstack_kth_from_start(st, 1),
|
||||||
|
LtagHash("tree")));
|
||||||
|
force_gc_cycle(st);
|
||||||
|
const int SZ = 10;
|
||||||
|
int ids[SZ];
|
||||||
|
size_t alive = objects_snapshot(ids, SZ);
|
||||||
|
assert((alive == 3));
|
||||||
|
|
||||||
|
// check that order is indeed preserved
|
||||||
|
for (int i = 0; i < alive - 1; ++i) { assert((ids[i] < ids[i + 1])); }
|
||||||
|
cleanup_test(st);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern size_t cur_id;
|
extern size_t cur_id;
|
||||||
|
|
||||||
size_t generate_random_obj_forest(virt_stack *st, int cnt, int seed) {
|
size_t generate_random_obj_forest (virt_stack *st, int cnt, int seed) {
|
||||||
srand(seed);
|
srand(seed);
|
||||||
int cur_sz = 0;
|
int cur_sz = 0;
|
||||||
size_t alive = 0;
|
size_t alive = 0;
|
||||||
while (cnt) {
|
while (cnt) {
|
||||||
--cnt;
|
--cnt;
|
||||||
if (cur_sz == 0) {
|
if (cur_sz == 0) {
|
||||||
vstack_push(st, BOX(1));
|
vstack_push(st, BOX(1));
|
||||||
++cur_sz;
|
++cur_sz;
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
size_t pos[2] = {rand() % vstack_size(st), rand() % vstack_size(st)};
|
|
||||||
size_t field[2];
|
|
||||||
for (int t = 0; t < 2; ++t) {
|
|
||||||
field[t] = vstack_kth_from_start(st, pos[t]);
|
|
||||||
}
|
|
||||||
size_t obj;
|
|
||||||
|
|
||||||
if (rand() % 2) {
|
|
||||||
obj = call_runtime_function(vstack_top(st) - 4, Bsexp, 4, BOX(3), field[0], field[1], LtagHash("test"));
|
|
||||||
} else {
|
|
||||||
obj = BOX(1);
|
|
||||||
}
|
|
||||||
// whether object is stored on stack
|
|
||||||
if (rand() % 2 != 0) {
|
|
||||||
vstack_push(st, obj);
|
|
||||||
if ((obj & 1) == 0) {
|
|
||||||
++alive;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++cur_sz;
|
|
||||||
}
|
}
|
||||||
force_gc_cycle(st);
|
|
||||||
return alive;
|
size_t pos[2] = {rand() % vstack_size(st), rand() % vstack_size(st)};
|
||||||
|
size_t field[2];
|
||||||
|
for (int t = 0; t < 2; ++t) { field[t] = vstack_kth_from_start(st, pos[t]); }
|
||||||
|
size_t obj;
|
||||||
|
|
||||||
|
if (rand() % 2) {
|
||||||
|
obj = call_runtime_function(
|
||||||
|
vstack_top(st) - 4, Bsexp, 4, BOX(3), field[0], field[1], LtagHash("test"));
|
||||||
|
} else {
|
||||||
|
obj = BOX(1);
|
||||||
|
}
|
||||||
|
// whether object is stored on stack
|
||||||
|
if (rand() % 2 != 0) {
|
||||||
|
vstack_push(st, obj);
|
||||||
|
if ((obj & 1) == 0) { ++alive; }
|
||||||
|
}
|
||||||
|
++cur_sz;
|
||||||
|
}
|
||||||
|
force_gc_cycle(st);
|
||||||
|
return alive;
|
||||||
}
|
}
|
||||||
|
|
||||||
void run_stress_test_random_obj_forest(int seed) {
|
void run_stress_test_random_obj_forest (int seed) {
|
||||||
virt_stack *st = init_test();
|
virt_stack *st = init_test();
|
||||||
|
|
||||||
const int SZ = 100000;
|
const int SZ = 100000;
|
||||||
|
|
||||||
size_t expectedAlive = generate_random_obj_forest(st, SZ, seed);
|
size_t expectedAlive = generate_random_obj_forest(st, SZ, seed);
|
||||||
|
|
||||||
int ids[SZ];
|
int ids[SZ];
|
||||||
size_t alive = objects_snapshot(ids, SZ);
|
size_t alive = objects_snapshot(ids, SZ);
|
||||||
assert(alive == expectedAlive);
|
assert(alive == expectedAlive);
|
||||||
|
|
||||||
// check that order is indeed preserved
|
// check that order is indeed preserved
|
||||||
for (int i = 0; i < alive - 1; ++i) {
|
for (int i = 0; i < alive - 1; ++i) { assert((ids[i] < ids[i + 1])); }
|
||||||
assert((ids[i] < ids[i + 1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup_test(st);
|
cleanup_test(st);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
int main(int argc, char ** argv) {
|
int main (int argc, char **argv) {
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef DEBUG_VERSION
|
||||||
no_gc_tests();
|
no_gc_tests();
|
||||||
|
|
||||||
test_simple_string_alloc();
|
test_simple_string_alloc();
|
||||||
test_simple_array_alloc();
|
test_simple_array_alloc();
|
||||||
test_simple_sexp_alloc();
|
test_simple_sexp_alloc();
|
||||||
test_simple_closure_alloc();
|
test_simple_closure_alloc();
|
||||||
test_single_object_allocation_with_collection_virtual_stack();
|
test_single_object_allocation_with_collection_virtual_stack();
|
||||||
test_garbage_is_reclaimed();
|
test_garbage_is_reclaimed();
|
||||||
test_alive_are_not_reclaimed();
|
test_alive_are_not_reclaimed();
|
||||||
test_small_tree_compaction();
|
test_small_tree_compaction();
|
||||||
|
|
||||||
time_t start, end;
|
time_t start, end;
|
||||||
double diff;
|
double diff;
|
||||||
time(&start);
|
time(&start);
|
||||||
// stress test
|
// stress test
|
||||||
for (int s = 0; s < 100; ++s) {
|
for (int s = 0; s < 100; ++s) { run_stress_test_random_obj_forest(s); }
|
||||||
run_stress_test_random_obj_forest(s);
|
time(&end);
|
||||||
}
|
diff = difftime(end, start);
|
||||||
time(&end);
|
printf("Stress tests took %.2lf seconds to complete\n", diff);
|
||||||
diff = difftime(end, start);
|
|
||||||
printf ("Stress tests took %.2lf seconds to complete\n", diff);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,34 @@
|
||||||
#include "virt_stack.h"
|
#include "virt_stack.h"
|
||||||
|
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
|
|
||||||
virt_stack *vstack_create() {
|
virt_stack *vstack_create () { return malloc(sizeof(virt_stack)); }
|
||||||
return malloc(sizeof (virt_stack));
|
|
||||||
|
void vstack_destruct (virt_stack *st) { free(st); }
|
||||||
|
|
||||||
|
void vstack_init (virt_stack *st) {
|
||||||
|
st->cur = RUNTIME_VSTACK_SIZE;
|
||||||
|
st->buf[st->cur] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vstack_destruct(virt_stack *st) {
|
void vstack_push (virt_stack *st, size_t value) {
|
||||||
free(st);
|
if (st->cur == 0) { assert(0); }
|
||||||
|
--st->cur;
|
||||||
|
st->buf[st->cur] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vstack_init(virt_stack *st) {
|
size_t vstack_pop (virt_stack *st) {
|
||||||
st->cur = RUNTIME_VSTACK_SIZE;
|
if (st->cur == RUNTIME_VSTACK_SIZE) { assert(0); }
|
||||||
st->buf[st->cur] = 0;
|
size_t value = st->buf[st->cur];
|
||||||
|
++st->cur;
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vstack_push(virt_stack *st, size_t value) {
|
void *vstack_top (virt_stack *st) { return st->buf + st->cur; }
|
||||||
if (st->cur == 0) {
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
--st->cur;
|
|
||||||
st->buf[st->cur] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t vstack_pop(virt_stack *st) {
|
size_t vstack_size (virt_stack *st) { return RUNTIME_VSTACK_SIZE - st->cur; }
|
||||||
if (st->cur == RUNTIME_VSTACK_SIZE) {
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
size_t value = st->buf[st->cur];
|
|
||||||
++st->cur;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* vstack_top(virt_stack *st) {
|
size_t vstack_kth_from_start (virt_stack *st, size_t k) {
|
||||||
return st->buf + st->cur;
|
assert(vstack_size(st) > k);
|
||||||
}
|
return st->buf[RUNTIME_VSTACK_SIZE - 1 - k];
|
||||||
|
|
||||||
size_t vstack_size(virt_stack *st) {
|
|
||||||
return RUNTIME_VSTACK_SIZE - st->cur;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t vstack_kth_from_start(virt_stack *st, size_t k) {
|
|
||||||
assert(vstack_size(st) > k);
|
|
||||||
return st->buf[RUNTIME_VSTACK_SIZE - 1 - k];
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,28 +6,28 @@
|
||||||
#define LAMA_RUNTIME_VIRT_STACK_H
|
#define LAMA_RUNTIME_VIRT_STACK_H
|
||||||
#define RUNTIME_VSTACK_SIZE 100000
|
#define RUNTIME_VSTACK_SIZE 100000
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
size_t buf[RUNTIME_VSTACK_SIZE + 1];
|
size_t buf[RUNTIME_VSTACK_SIZE + 1];
|
||||||
size_t cur;
|
size_t cur;
|
||||||
} typedef virt_stack;
|
} typedef virt_stack;
|
||||||
|
|
||||||
virt_stack *vstack_create();
|
virt_stack *vstack_create ();
|
||||||
|
|
||||||
void vstack_destruct(virt_stack *st);
|
void vstack_destruct (virt_stack *st);
|
||||||
|
|
||||||
void vstack_init(virt_stack *st);
|
void vstack_init (virt_stack *st);
|
||||||
|
|
||||||
void vstack_push(virt_stack *st, size_t value);
|
void vstack_push (virt_stack *st, size_t value);
|
||||||
|
|
||||||
size_t vstack_pop(virt_stack *st);
|
size_t vstack_pop (virt_stack *st);
|
||||||
|
|
||||||
void* vstack_top(virt_stack *st);
|
void *vstack_top (virt_stack *st);
|
||||||
|
|
||||||
size_t vstack_size(virt_stack *st);
|
size_t vstack_size (virt_stack *st);
|
||||||
|
|
||||||
size_t vstack_kth_from_start(virt_stack *st, size_t k);
|
size_t vstack_kth_from_start (virt_stack *st, size_t k);
|
||||||
|
|
||||||
#endif //LAMA_RUNTIME_VIRT_STACK_H
|
#endif //LAMA_RUNTIME_VIRT_STACK_H
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue