add clang-format; reformat files

This commit is contained in:
danyaberezun 2023-05-31 11:01:11 +02:00
parent f20d351dd0
commit ccd04c2159
10 changed files with 1885 additions and 1837 deletions

144
runtime/.clang-format Normal file
View 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
---

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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

View file

@ -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
} }

View file

@ -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];
} }

View file

@ -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