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
|
||||
|
||||
---
|
||||
363
runtime/gc.c
363
runtime/gc.c
|
|
@ -1,18 +1,18 @@
|
|||
# define _GNU_SOURCE 1
|
||||
#define _GNU_SOURCE 1
|
||||
|
||||
#include "gc.h"
|
||||
|
||||
#include "runtime_common.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <execinfo.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <execinfo.h>
|
||||
|
||||
static const size_t INIT_HEAP_SIZE = MINIMUM_HEAP_CAPACITY;
|
||||
|
||||
|
|
@ -34,10 +34,10 @@ static memory_chunk heap;
|
|||
#endif
|
||||
|
||||
#ifdef DEBUG_VERSION
|
||||
void dump_heap();
|
||||
void dump_heap ();
|
||||
#endif
|
||||
|
||||
void handler(int sig) {
|
||||
void handler (int sig) {
|
||||
void *array[10];
|
||||
size_t size;
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ void handler(int sig) {
|
|||
exit(1);
|
||||
}
|
||||
|
||||
void *alloc(size_t size) {
|
||||
void *alloc (size_t size) {
|
||||
#ifdef DEBUG_VERSION
|
||||
++cur_id;
|
||||
#endif
|
||||
|
|
@ -61,9 +61,9 @@ void *alloc(size_t size) {
|
|||
return p;
|
||||
}
|
||||
|
||||
void *gc_alloc_on_existing_heap(size_t size) {
|
||||
void *gc_alloc_on_existing_heap (size_t size) {
|
||||
if (heap.current + size <= heap.end) {
|
||||
void *p = (void *) heap.current;
|
||||
void *p = (void *)heap.current;
|
||||
heap.current += size;
|
||||
memset(p, 0, size * sizeof(size_t));
|
||||
return p;
|
||||
|
|
@ -71,7 +71,7 @@ void *gc_alloc_on_existing_heap(size_t size) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void *gc_alloc(size_t size) {
|
||||
void *gc_alloc (size_t size) {
|
||||
mark_phase();
|
||||
|
||||
compact_phase(size);
|
||||
|
|
@ -79,7 +79,7 @@ void *gc_alloc(size_t size) {
|
|||
return gc_alloc_on_existing_heap(size);
|
||||
}
|
||||
|
||||
void mark_phase(void) {
|
||||
void mark_phase (void) {
|
||||
__gc_root_scan_stack();
|
||||
scan_extra_roots();
|
||||
#ifndef DEBUG_VERSION
|
||||
|
|
@ -87,22 +87,20 @@ void mark_phase(void) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void compact_phase(size_t additional_size) {
|
||||
void compact_phase (size_t additional_size) {
|
||||
size_t live_size = compute_locations();
|
||||
|
||||
// all in words
|
||||
size_t next_heap_size = MAX(live_size * EXTRA_ROOM_HEAP_COEFFICIENT + additional_size, MINIMUM_HEAP_CAPACITY);
|
||||
size_t next_heap_pseudo_size = MAX(next_heap_size, heap.size); // this is weird but here is why it happens:
|
||||
size_t next_heap_size =
|
||||
MAX(live_size * EXTRA_ROOM_HEAP_COEFFICIENT + additional_size, MINIMUM_HEAP_CAPACITY);
|
||||
size_t next_heap_pseudo_size =
|
||||
MAX(next_heap_size, heap.size); // this is weird but here is why it happens:
|
||||
// if we allocate too little heap right now, we may lose access to some alive objects
|
||||
// however, after we physically relocate all of our objects we will shrink allocated memory if it is possible
|
||||
|
||||
memory_chunk old_heap = heap;
|
||||
heap.begin = mremap(
|
||||
heap.begin,
|
||||
WORDS_TO_BYTES(heap.size),
|
||||
WORDS_TO_BYTES(next_heap_pseudo_size),
|
||||
MREMAP_MAYMOVE
|
||||
);
|
||||
heap.begin, WORDS_TO_BYTES(heap.size), WORDS_TO_BYTES(next_heap_pseudo_size), MREMAP_MAYMOVE);
|
||||
if (heap.begin == MAP_FAILED) {
|
||||
perror("ERROR: compact_phase: mremap failed\n");
|
||||
exit(1);
|
||||
|
|
@ -114,7 +112,8 @@ void compact_phase(size_t additional_size) {
|
|||
update_references(&old_heap);
|
||||
physically_relocate(&old_heap);
|
||||
|
||||
// shrink it if possible, otherwise this code won'test_small_tree_compaction do anything, in both cases references will remain valid
|
||||
// shrink it if possible, otherwise this code won'test_small_tree_compaction do anything, in both cases references
|
||||
// will remain valid
|
||||
heap.begin = mremap(
|
||||
heap.begin,
|
||||
WORDS_TO_BYTES(heap.size),
|
||||
|
|
@ -130,7 +129,7 @@ void compact_phase(size_t additional_size) {
|
|||
heap.current = heap.begin + live_size;
|
||||
}
|
||||
|
||||
size_t compute_locations() {
|
||||
size_t compute_locations () {
|
||||
size_t *free_ptr = heap.begin;
|
||||
heap_iterator scan_iter = heap_begin_iterator();
|
||||
|
||||
|
|
@ -140,7 +139,7 @@ size_t compute_locations() {
|
|||
if (is_marked(obj_content)) {
|
||||
size_t sz = BYTES_TO_WORDS(obj_size_header_ptr(header_ptr));
|
||||
// forward address is responsible for object header pointer
|
||||
set_forward_address(obj_content, (size_t) free_ptr);
|
||||
set_forward_address(obj_content, (size_t)free_ptr);
|
||||
free_ptr += sz;
|
||||
}
|
||||
}
|
||||
|
|
@ -149,70 +148,70 @@ size_t compute_locations() {
|
|||
return free_ptr - heap.begin;
|
||||
}
|
||||
|
||||
void scan_and_fix_region(memory_chunk *old_heap, void *start, void *end) {
|
||||
for (size_t *ptr = (size_t *) start; ptr < (size_t *) end; ++ptr) {
|
||||
void scan_and_fix_region (memory_chunk *old_heap, void *start, void *end) {
|
||||
for (size_t *ptr = (size_t *)start; ptr < (size_t *)end; ++ptr) {
|
||||
size_t ptr_value = *ptr;
|
||||
// this can't be expressed via is_valid_heap_pointer, because this pointer may point area corresponding to the old heap
|
||||
if (is_valid_pointer((size_t *) ptr_value)
|
||||
&& (size_t) old_heap->begin <= ptr_value
|
||||
&& ptr_value <= (size_t) old_heap->current
|
||||
) {
|
||||
void *obj_ptr = (void*) heap.begin + ((void *) ptr_value - (void *) old_heap->begin);
|
||||
void *new_addr = (void*) heap.begin + ((void *) get_forward_address(obj_ptr) - (void *) old_heap->begin);
|
||||
// this can't be expressed via is_valid_heap_pointer, because this pointer may point area corresponding to the old
|
||||
// heap
|
||||
if (is_valid_pointer((size_t *)ptr_value) && (size_t)old_heap->begin <= ptr_value
|
||||
&& ptr_value <= (size_t)old_heap->current) {
|
||||
void *obj_ptr = (void *)heap.begin + ((void *)ptr_value - (void *)old_heap->begin);
|
||||
void *new_addr =
|
||||
(void *)heap.begin + ((void *)get_forward_address(obj_ptr) - (void *)old_heap->begin);
|
||||
size_t content_offset = get_header_size(get_type_row_ptr(obj_ptr));
|
||||
*(void **) ptr = new_addr + content_offset;
|
||||
*(void **)ptr = new_addr + content_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_references(memory_chunk *old_heap) {
|
||||
void update_references (memory_chunk *old_heap) {
|
||||
heap_iterator it = heap_begin_iterator();
|
||||
while (!heap_is_done_iterator(&it)) {
|
||||
if (is_marked(get_object_content_ptr(it.current))) {
|
||||
for (
|
||||
obj_field_iterator field_iter = ptr_field_begin_iterator(it.current);
|
||||
for (obj_field_iterator field_iter = ptr_field_begin_iterator(it.current);
|
||||
!field_is_done_iterator(&field_iter);
|
||||
obj_next_ptr_field_iterator(&field_iter)
|
||||
) {
|
||||
obj_next_ptr_field_iterator(&field_iter)) {
|
||||
|
||||
|
||||
size_t *field_value = *(size_t **) field_iter.cur_field;
|
||||
if (field_value < old_heap->begin || field_value > old_heap->current) {
|
||||
continue;
|
||||
}
|
||||
size_t *field_value = *(size_t **)field_iter.cur_field;
|
||||
if (field_value < old_heap->begin || field_value > old_heap->current) { continue; }
|
||||
// this pointer should also be modified according to old_heap->begin
|
||||
void *field_obj_content_addr = (void *) heap.begin + (*(void **) field_iter.cur_field - (void *) old_heap->begin);
|
||||
void *field_obj_content_addr =
|
||||
(void *)heap.begin + (*(void **)field_iter.cur_field - (void *)old_heap->begin);
|
||||
// important, we calculate new_addr very carefully here, because objects may relocate to another memory chunk
|
||||
void *new_addr =
|
||||
heap.begin + ((size_t *) get_forward_address(field_obj_content_addr) - (size_t *) old_heap->begin);
|
||||
heap.begin
|
||||
+ ((size_t *)get_forward_address(field_obj_content_addr) - (size_t *)old_heap->begin);
|
||||
// update field reference to point to new_addr
|
||||
// since, we want fields to point to an actual content, we need to add this extra content_offset
|
||||
// because forward_address itself is a pointer to the object's header
|
||||
size_t content_offset = get_header_size(get_type_row_ptr(field_obj_content_addr));
|
||||
#ifdef DEBUG_VERSION
|
||||
if (!is_valid_heap_pointer((void *) (new_addr + content_offset))) {
|
||||
fprintf(stderr, "ur: incorrect pointer assignment: on object with id %d", TO_DATA(get_object_content_ptr(it.current))->id);
|
||||
if (!is_valid_heap_pointer((void *)(new_addr + content_offset))) {
|
||||
fprintf(stderr,
|
||||
"ur: incorrect pointer assignment: on object with id %d",
|
||||
TO_DATA(get_object_content_ptr(it.current))->id);
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
*(void **) field_iter.cur_field = new_addr + content_offset;
|
||||
*(void **)field_iter.cur_field = new_addr + content_offset;
|
||||
}
|
||||
}
|
||||
heap_next_obj_iterator(&it);
|
||||
}
|
||||
// fix pointers from stack
|
||||
scan_and_fix_region(old_heap, (void*) __gc_stack_top + 4, (void*) __gc_stack_bottom);
|
||||
scan_and_fix_region(old_heap, (void *)__gc_stack_top + 4, (void *)__gc_stack_bottom);
|
||||
|
||||
// fix pointers from extra_roots
|
||||
scan_and_fix_region(old_heap, (void*) extra_roots.roots, (size_t*) extra_roots.roots + extra_roots.current_free);
|
||||
scan_and_fix_region(
|
||||
old_heap, (void *)extra_roots.roots, (size_t *)extra_roots.roots + extra_roots.current_free);
|
||||
|
||||
#ifndef DEBUG_VERSION
|
||||
// fix pointers from static area
|
||||
scan_and_fix_region(old_heap, (void*) &__start_custom_data, (void*) &__stop_custom_data);
|
||||
scan_and_fix_region(old_heap, (void *)&__start_custom_data, (void *)&__stop_custom_data);
|
||||
#endif
|
||||
}
|
||||
|
||||
void physically_relocate(memory_chunk *old_heap) {
|
||||
void physically_relocate (memory_chunk *old_heap) {
|
||||
heap_iterator from_iter = heap_begin_iterator();
|
||||
|
||||
while (!heap_is_done_iterator(&from_iter)) {
|
||||
|
|
@ -222,7 +221,7 @@ void physically_relocate(memory_chunk *old_heap) {
|
|||
if (is_marked(obj)) {
|
||||
// Move the object from its old location to its new location relative to
|
||||
// the heap's (possibly new) location, 'to' points to future object header
|
||||
size_t *to = heap.begin + ((size_t *) get_forward_address(obj) - (size_t *) old_heap->begin);
|
||||
size_t *to = heap.begin + ((size_t *)get_forward_address(obj) - (size_t *)old_heap->begin);
|
||||
memmove(to, from_iter.current, obj_size_header_ptr(from_iter.current));
|
||||
unmark_object(get_object_content_ptr(to));
|
||||
}
|
||||
|
|
@ -230,39 +229,31 @@ void physically_relocate(memory_chunk *old_heap) {
|
|||
}
|
||||
}
|
||||
|
||||
bool is_valid_heap_pointer(const size_t *p) {
|
||||
return !UNBOXED(p) && (size_t) heap.begin <= (size_t) p && (size_t) p <= (size_t) heap.current;
|
||||
bool is_valid_heap_pointer (const size_t *p) {
|
||||
return !UNBOXED(p) && (size_t)heap.begin <= (size_t)p && (size_t)p <= (size_t)heap.current;
|
||||
}
|
||||
|
||||
bool is_valid_pointer(const size_t *p) {
|
||||
return !UNBOXED(p);
|
||||
}
|
||||
bool is_valid_pointer (const size_t *p) { return !UNBOXED(p); }
|
||||
|
||||
static inline void queue_enqueue(heap_iterator *tail_iter, void *obj) {
|
||||
static inline void queue_enqueue (heap_iterator *tail_iter, void *obj) {
|
||||
void *tail = tail_iter->current;
|
||||
void *tail_content = get_object_content_ptr(tail);
|
||||
set_forward_address(tail_content, (size_t) obj);
|
||||
set_forward_address(tail_content, (size_t)obj);
|
||||
make_enqueued(obj);
|
||||
heap_next_obj_iterator(tail_iter);
|
||||
}
|
||||
|
||||
static inline void *queue_dequeue(heap_iterator *head_iter) {
|
||||
static inline void *queue_dequeue (heap_iterator *head_iter) {
|
||||
void *head = head_iter->current;
|
||||
void *head_content = get_object_content_ptr(head);
|
||||
void *value = (void*) get_forward_address(head_content);
|
||||
void *value = (void *)get_forward_address(head_content);
|
||||
make_dequeued(value);
|
||||
heap_next_obj_iterator(head_iter);
|
||||
return value;
|
||||
}
|
||||
|
||||
void mark(void *obj) {
|
||||
if (!is_valid_heap_pointer(obj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_marked(obj)) {
|
||||
return;
|
||||
}
|
||||
void mark (void *obj) {
|
||||
if (!is_valid_heap_pointer(obj) || is_marked(obj)) { return; }
|
||||
|
||||
// TL;DR: [q_head_iter, q_tail_iter) q_head_iter -- current dequeue's victim, q_tail_iter -- place for next enqueue
|
||||
// in forward_address of corresponding element we store address of element to be removed after dequeue operation
|
||||
|
|
@ -271,19 +262,19 @@ void mark(void *obj) {
|
|||
heap_iterator q_tail_iter = q_head_iter;
|
||||
queue_enqueue(&q_tail_iter, obj);
|
||||
|
||||
// invariant: queue contains only objects that are valid heap pointers (each corresponding to content of unmarked object)
|
||||
// also each object is in queue only once
|
||||
while (q_head_iter.current != q_tail_iter.current) { // means the queue is not empty
|
||||
// invariant: queue contains only objects that are valid heap pointers (each corresponding to content of unmarked
|
||||
// object) also each object is in queue only once
|
||||
while (q_head_iter.current != q_tail_iter.current) {
|
||||
// while the queue is non-empty
|
||||
void *cur_obj = queue_dequeue(&q_head_iter);
|
||||
mark_object(cur_obj);
|
||||
void *header_ptr = get_obj_header_ptr(cur_obj, get_type_row_ptr(cur_obj));
|
||||
for (
|
||||
obj_field_iterator ptr_field_it = ptr_field_begin_iterator(header_ptr);
|
||||
for (obj_field_iterator ptr_field_it = ptr_field_begin_iterator(header_ptr);
|
||||
!field_is_done_iterator(&ptr_field_it);
|
||||
obj_next_ptr_field_iterator(&ptr_field_it)
|
||||
) {
|
||||
void *field_value = * (void **) ptr_field_it.cur_field;
|
||||
if (!is_valid_heap_pointer(field_value) || is_marked(field_value) || is_enqueued(field_value)) {
|
||||
obj_next_ptr_field_iterator(&ptr_field_it)) {
|
||||
void *field_value = *(void **)ptr_field_it.cur_field;
|
||||
if (!is_valid_heap_pointer(field_value) || is_marked(field_value)
|
||||
|| is_enqueued(field_value)) {
|
||||
continue;
|
||||
}
|
||||
// if we came to this point it must be true that field_value is unmarked and not currently in queue
|
||||
|
|
@ -293,7 +284,7 @@ void mark(void *obj) {
|
|||
}
|
||||
}
|
||||
|
||||
void scan_extra_roots(void) {
|
||||
void scan_extra_roots (void) {
|
||||
for (int i = 0; i < extra_roots.current_free; ++i) {
|
||||
// this dereferencing is safe since runtime is pushing correct pointers into extra_roots
|
||||
mark(*extra_roots.roots[i]);
|
||||
|
|
@ -301,26 +292,24 @@ void scan_extra_roots(void) {
|
|||
}
|
||||
|
||||
#ifndef DEBUG_VERSION
|
||||
void scan_global_area(void) {
|
||||
void scan_global_area (void) {
|
||||
// __start_custom_data is pointing to beginning of global area, thus all dereferencings are safe
|
||||
for (size_t *ptr = (size_t *) &__start_custom_data; ptr < (size_t *) &__stop_custom_data; ++ptr) {
|
||||
for (size_t *ptr = (size_t *)&__start_custom_data; ptr < (size_t *)&__stop_custom_data; ++ptr) {
|
||||
mark(*(void **)ptr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
extern void gc_test_and_mark_root(size_t **root) {
|
||||
mark((void *) *root);
|
||||
}
|
||||
extern void gc_test_and_mark_root (size_t **root) { mark((void *)*root); }
|
||||
|
||||
extern void __init(void) {
|
||||
extern void __init (void) {
|
||||
signal(SIGSEGV, handler);
|
||||
size_t space_size = INIT_HEAP_SIZE * sizeof(size_t);
|
||||
|
||||
srandom(time(NULL));
|
||||
|
||||
heap.begin = mmap(NULL, space_size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
|
||||
heap.begin = mmap(
|
||||
NULL, space_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
|
||||
if (heap.begin == MAP_FAILED) {
|
||||
perror("ERROR: __init: mmap failed\n");
|
||||
exit(1);
|
||||
|
|
@ -331,7 +320,7 @@ extern void __init(void) {
|
|||
clear_extra_roots();
|
||||
}
|
||||
|
||||
extern void __shutdown(void) {
|
||||
extern void __shutdown (void) {
|
||||
munmap(heap.begin, heap.size);
|
||||
#ifdef DEBUG_VERSION
|
||||
cur_id = 0;
|
||||
|
|
@ -344,11 +333,9 @@ extern void __shutdown(void) {
|
|||
__gc_stack_bottom = 0;
|
||||
}
|
||||
|
||||
void clear_extra_roots(void) {
|
||||
extra_roots.current_free = 0;
|
||||
}
|
||||
void clear_extra_roots (void) { extra_roots.current_free = 0; }
|
||||
|
||||
void push_extra_root(void **p) {
|
||||
void push_extra_root (void **p) {
|
||||
if (extra_roots.current_free >= MAX_EXTRA_ROOTS_NUMBER) {
|
||||
perror("ERROR: push_extra_roots: extra_roots_pool overflow");
|
||||
exit(1);
|
||||
|
|
@ -357,7 +344,7 @@ void push_extra_root(void **p) {
|
|||
extra_roots.current_free++;
|
||||
}
|
||||
|
||||
void pop_extra_root(void **p) {
|
||||
void pop_extra_root (void **p) {
|
||||
if (extra_roots.current_free == 0) {
|
||||
perror("ERROR: pop_extra_root: extra_roots are empty");
|
||||
exit(1);
|
||||
|
|
@ -373,14 +360,12 @@ void pop_extra_root(void **p) {
|
|||
|
||||
#ifdef DEBUG_VERSION
|
||||
|
||||
size_t objects_snapshot(int *object_ids_buf, size_t object_ids_buf_size) {
|
||||
size_t *ids_ptr = (size_t *) object_ids_buf;
|
||||
size_t objects_snapshot (int *object_ids_buf, size_t object_ids_buf_size) {
|
||||
size_t *ids_ptr = (size_t *)object_ids_buf;
|
||||
size_t i = 0;
|
||||
for (
|
||||
heap_iterator it = heap_begin_iterator();
|
||||
for (heap_iterator it = heap_begin_iterator();
|
||||
!heap_is_done_iterator(&it) && i < object_ids_buf_size;
|
||||
heap_next_obj_iterator(&it), ++i
|
||||
) {
|
||||
heap_next_obj_iterator(&it), ++i) {
|
||||
void *header_ptr = it.current;
|
||||
data *d = TO_DATA(get_object_content_ptr(header_ptr));
|
||||
ids_ptr[i] = d->id;
|
||||
|
|
@ -388,29 +373,20 @@ size_t objects_snapshot(int *object_ids_buf, size_t object_ids_buf_size) {
|
|||
return i;
|
||||
}
|
||||
|
||||
extern char* de_hash (int);
|
||||
extern char *de_hash (int);
|
||||
|
||||
void dump_heap() {
|
||||
void dump_heap () {
|
||||
size_t i = 0;
|
||||
for (
|
||||
heap_iterator it = heap_begin_iterator();
|
||||
!heap_is_done_iterator(&it);
|
||||
heap_next_obj_iterator(&it), ++i
|
||||
) {
|
||||
for (heap_iterator it = heap_begin_iterator(); !heap_is_done_iterator(&it);
|
||||
heap_next_obj_iterator(&it), ++i) {
|
||||
void *header_ptr = it.current;
|
||||
void *content_ptr = get_object_content_ptr(header_ptr);
|
||||
data *d = TO_DATA(content_ptr);
|
||||
lama_type t = get_type_header_ptr(header_ptr);
|
||||
switch (t) {
|
||||
case ARRAY:
|
||||
fprintf(stderr, "of kind ARRAY\n");
|
||||
break;
|
||||
case CLOSURE:
|
||||
fprintf(stderr, "of kind CLOSURE\n");
|
||||
break;
|
||||
case STRING:
|
||||
fprintf(stderr, "of kind STRING\n");
|
||||
break;
|
||||
case ARRAY: fprintf(stderr, "of kind ARRAY\n"); break;
|
||||
case CLOSURE: fprintf(stderr, "of kind CLOSURE\n"); break;
|
||||
case STRING: fprintf(stderr, "of kind STRING\n"); break;
|
||||
case SEXP:
|
||||
fprintf(stderr, "of kind SEXP with tag %s\n", de_hash(TO_SEXP(content_ptr)->tag));
|
||||
break;
|
||||
|
|
@ -418,68 +394,67 @@ void dump_heap() {
|
|||
}
|
||||
}
|
||||
|
||||
void set_stack(size_t stack_top, size_t stack_bottom) {
|
||||
void set_stack (size_t stack_top, size_t stack_bottom) {
|
||||
__gc_stack_top = stack_top;
|
||||
__gc_stack_bottom = stack_bottom;
|
||||
}
|
||||
|
||||
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) {
|
||||
memcpy(extra_roots.roots, extra_roots_ptr, MIN(sizeof(extra_roots.roots), extra_roots_size));
|
||||
clear_extra_roots();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* Utility functions */
|
||||
|
||||
size_t get_forward_address(void *obj) {
|
||||
size_t get_forward_address (void *obj) {
|
||||
data *d = TO_DATA(obj);
|
||||
return GET_FORWARD_ADDRESS(d->forward_address);
|
||||
}
|
||||
|
||||
void set_forward_address(void *obj, size_t addr) {
|
||||
void set_forward_address (void *obj, size_t addr) {
|
||||
data *d = TO_DATA(obj);
|
||||
SET_FORWARD_ADDRESS(d->forward_address, addr);
|
||||
}
|
||||
|
||||
bool is_marked(void *obj) {
|
||||
bool is_marked (void *obj) {
|
||||
data *d = TO_DATA(obj);
|
||||
int mark_bit = GET_MARK_BIT(d->forward_address);
|
||||
return mark_bit;
|
||||
}
|
||||
|
||||
void mark_object(void *obj) {
|
||||
void mark_object (void *obj) {
|
||||
data *d = TO_DATA(obj);
|
||||
SET_MARK_BIT(d->forward_address);
|
||||
}
|
||||
|
||||
void unmark_object(void *obj) {
|
||||
void unmark_object (void *obj) {
|
||||
data *d = TO_DATA(obj);
|
||||
RESET_MARK_BIT(d->forward_address);
|
||||
}
|
||||
|
||||
bool is_enqueued(void *obj) {
|
||||
bool is_enqueued (void *obj) {
|
||||
data *d = TO_DATA(obj);
|
||||
return IS_ENQUEUED(d->forward_address) != 0;
|
||||
}
|
||||
|
||||
void make_enqueued(void *obj) {
|
||||
void make_enqueued (void *obj) {
|
||||
data *d = TO_DATA(obj);
|
||||
MAKE_ENQUEUED(d->forward_address);
|
||||
}
|
||||
|
||||
void make_dequeued(void *obj) {
|
||||
void make_dequeued (void *obj) {
|
||||
data *d = TO_DATA(obj);
|
||||
MAKE_DEQUEUED(d->forward_address);
|
||||
}
|
||||
|
||||
heap_iterator heap_begin_iterator() {
|
||||
heap_iterator it = {.current=heap.begin};
|
||||
heap_iterator heap_begin_iterator () {
|
||||
heap_iterator it = {.current = heap.begin};
|
||||
return it;
|
||||
}
|
||||
|
||||
void heap_next_obj_iterator(heap_iterator *it) {
|
||||
void heap_next_obj_iterator (heap_iterator *it) {
|
||||
void *ptr = it->current;
|
||||
size_t obj_size = obj_size_header_ptr(ptr);
|
||||
// make sure we take alignment into consideration
|
||||
|
|
@ -487,61 +462,53 @@ void heap_next_obj_iterator(heap_iterator *it) {
|
|||
it->current += obj_size;
|
||||
}
|
||||
|
||||
bool heap_is_done_iterator(heap_iterator *it) {
|
||||
return it->current >= heap.current;
|
||||
}
|
||||
bool heap_is_done_iterator (heap_iterator *it) { return it->current >= heap.current; }
|
||||
|
||||
lama_type get_type_row_ptr(void *ptr) {
|
||||
lama_type get_type_row_ptr (void *ptr) {
|
||||
data *data_ptr = TO_DATA(ptr);
|
||||
return get_type_header_ptr(data_ptr);
|
||||
}
|
||||
|
||||
lama_type get_type_header_ptr(void *ptr) {
|
||||
int *header = (int *) ptr;
|
||||
lama_type get_type_header_ptr (void *ptr) {
|
||||
int *header = (int *)ptr;
|
||||
switch (TAG(*header)) {
|
||||
case ARRAY_TAG:
|
||||
return ARRAY;
|
||||
case STRING_TAG:
|
||||
return STRING;
|
||||
case CLOSURE_TAG:
|
||||
return CLOSURE;
|
||||
case SEXP_TAG:
|
||||
return SEXP;
|
||||
case ARRAY_TAG: return ARRAY;
|
||||
case STRING_TAG: return STRING;
|
||||
case CLOSURE_TAG: return CLOSURE;
|
||||
case SEXP_TAG: return SEXP;
|
||||
default: {
|
||||
#ifdef DEBUG_VERSION
|
||||
fprintf(stderr, "ERROR: get_type_header_ptr: unknown object header, cur_id=%d", cur_id);
|
||||
raise(SIGINT); // only for debug purposes
|
||||
#else
|
||||
|
||||
fprintf(stderr, "ERROR: get_type_header_ptr: unknown object header, ptr is %p, heap size is %d\n", ptr, heap.size);
|
||||
fprintf(stderr,
|
||||
"ERROR: get_type_header_ptr: unknown object header, ptr is %p, "
|
||||
"heap size is %d\n",
|
||||
ptr,
|
||||
heap.size);
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t obj_size_row_ptr(void *ptr) {
|
||||
size_t obj_size_row_ptr (void *ptr) {
|
||||
data *data_ptr = TO_DATA(ptr);
|
||||
return obj_size_header_ptr(data_ptr);
|
||||
}
|
||||
|
||||
size_t obj_size_header_ptr(void *ptr) {
|
||||
int len = LEN(*(int *) ptr);
|
||||
size_t obj_size_header_ptr (void *ptr) {
|
||||
int len = LEN(*(int *)ptr);
|
||||
switch (get_type_header_ptr(ptr)) {
|
||||
case ARRAY:
|
||||
return array_size(len);
|
||||
case STRING:
|
||||
return string_size(len);
|
||||
case CLOSURE:
|
||||
return closure_size(len);
|
||||
case SEXP:
|
||||
return sexp_size(len);
|
||||
case ARRAY: return array_size(len);
|
||||
case STRING: return string_size(len);
|
||||
case CLOSURE: return closure_size(len);
|
||||
case SEXP: return sexp_size(len);
|
||||
default: {
|
||||
#ifdef DEBUG_VERSION
|
||||
fprintf(stderr, "ERROR: obj_size_header_ptr: unknown object header, cur_id=%d", cur_id);
|
||||
raise(SIGINT); // only for debug purposes
|
||||
#else
|
||||
|
||||
perror("ERROR: obj_size_header_ptr: unknown object header");
|
||||
#endif
|
||||
exit(1);
|
||||
|
|
@ -549,88 +516,64 @@ size_t obj_size_header_ptr(void *ptr) {
|
|||
}
|
||||
}
|
||||
|
||||
size_t array_size(size_t sz) {
|
||||
return get_header_size(ARRAY) + MEMBER_SIZE * sz;
|
||||
}
|
||||
size_t array_size (size_t sz) { return get_header_size(ARRAY) + MEMBER_SIZE * sz; }
|
||||
|
||||
size_t string_size(size_t len) {
|
||||
size_t string_size (size_t len) {
|
||||
// string should be null terminated
|
||||
return get_header_size(STRING) + len + 1;
|
||||
}
|
||||
|
||||
size_t closure_size(size_t sz) {
|
||||
return get_header_size(CLOSURE) + MEMBER_SIZE * sz;
|
||||
}
|
||||
size_t closure_size (size_t sz) { return get_header_size(CLOSURE) + MEMBER_SIZE * sz; }
|
||||
|
||||
size_t sexp_size(size_t members) {
|
||||
return get_header_size(SEXP) + MEMBER_SIZE * members;
|
||||
}
|
||||
size_t sexp_size (size_t members) { return get_header_size(SEXP) + MEMBER_SIZE * members; }
|
||||
|
||||
|
||||
obj_field_iterator field_begin_iterator(void *obj) {
|
||||
obj_field_iterator field_begin_iterator (void *obj) {
|
||||
lama_type type = get_type_header_ptr(obj);
|
||||
obj_field_iterator it = {.type=type, .obj_ptr=obj, .cur_field=get_object_content_ptr(obj)};
|
||||
obj_field_iterator it = {.type = type, .obj_ptr = obj, .cur_field = get_object_content_ptr(obj)};
|
||||
// since string doesn't have any actual fields we set cur_field to the end of object
|
||||
if (type == STRING) {
|
||||
it.cur_field = get_end_of_obj(it.obj_ptr);
|
||||
}
|
||||
if (type == STRING) { it.cur_field = get_end_of_obj(it.obj_ptr); }
|
||||
// skip first member which is basically pointer to the code
|
||||
if (type == CLOSURE) {
|
||||
it.cur_field += MEMBER_SIZE;
|
||||
}
|
||||
if (type == CLOSURE) { it.cur_field += MEMBER_SIZE; }
|
||||
return it;
|
||||
}
|
||||
|
||||
obj_field_iterator ptr_field_begin_iterator(void *obj) {
|
||||
obj_field_iterator ptr_field_begin_iterator (void *obj) {
|
||||
obj_field_iterator it = field_begin_iterator(obj);
|
||||
// corner case when obj has no fields
|
||||
if (field_is_done_iterator(&it)) {
|
||||
return it;
|
||||
}
|
||||
if (is_valid_pointer(*(size_t **) it.cur_field)) {
|
||||
return it;
|
||||
}
|
||||
if (field_is_done_iterator(&it)) { return it; }
|
||||
if (is_valid_pointer(*(size_t **)it.cur_field)) { return it; }
|
||||
obj_next_ptr_field_iterator(&it);
|
||||
return it;
|
||||
}
|
||||
|
||||
void obj_next_field_iterator(obj_field_iterator *it) {
|
||||
it->cur_field += MEMBER_SIZE;
|
||||
}
|
||||
void obj_next_field_iterator (obj_field_iterator *it) { it->cur_field += MEMBER_SIZE; }
|
||||
|
||||
void obj_next_ptr_field_iterator(obj_field_iterator *it) {
|
||||
void obj_next_ptr_field_iterator (obj_field_iterator *it) {
|
||||
do {
|
||||
obj_next_field_iterator(it);
|
||||
} while (!field_is_done_iterator(it) && !is_valid_pointer(*(size_t **) it->cur_field));
|
||||
} while (!field_is_done_iterator(it) && !is_valid_pointer(*(size_t **)it->cur_field));
|
||||
}
|
||||
|
||||
bool field_is_done_iterator(obj_field_iterator *it) {
|
||||
bool field_is_done_iterator (obj_field_iterator *it) {
|
||||
return it->cur_field >= get_end_of_obj(it->obj_ptr);
|
||||
}
|
||||
|
||||
void *get_obj_header_ptr(void *ptr, lama_type type) {
|
||||
return ptr - get_header_size(type);
|
||||
}
|
||||
void *get_obj_header_ptr (void *ptr, lama_type type) { return ptr - get_header_size(type); }
|
||||
|
||||
void *get_object_content_ptr(void *header_ptr) {
|
||||
void *get_object_content_ptr (void *header_ptr) {
|
||||
lama_type type = get_type_header_ptr(header_ptr);
|
||||
return header_ptr + get_header_size(type);
|
||||
}
|
||||
|
||||
void *get_end_of_obj(void *header_ptr) {
|
||||
return header_ptr + obj_size_header_ptr(header_ptr);
|
||||
}
|
||||
void *get_end_of_obj (void *header_ptr) { return header_ptr + obj_size_header_ptr(header_ptr); }
|
||||
|
||||
size_t get_header_size(lama_type type) {
|
||||
size_t get_header_size (lama_type type) {
|
||||
switch (type) {
|
||||
case STRING:
|
||||
case CLOSURE:
|
||||
case ARRAY:
|
||||
return DATA_HEADER_SZ;
|
||||
case SEXP:
|
||||
return SEXP_ONLY_HEADER_SZ + DATA_HEADER_SZ;
|
||||
default:
|
||||
perror("ERROR: get_header_size: unknown object type");
|
||||
case ARRAY: return DATA_HEADER_SZ;
|
||||
case SEXP: return SEXP_ONLY_HEADER_SZ + DATA_HEADER_SZ;
|
||||
default: perror("ERROR: get_header_size: unknown object type");
|
||||
#ifdef DEBUG_VERSION
|
||||
raise(SIGINT); // only for debug purposes
|
||||
#endif
|
||||
|
|
@ -638,7 +581,7 @@ size_t get_header_size(lama_type type) {
|
|||
}
|
||||
}
|
||||
|
||||
void *alloc_string(int len) {
|
||||
void *alloc_string (int len) {
|
||||
data *obj = alloc(string_size(len));
|
||||
obj->data_header = STRING_TAG | (len << 3);
|
||||
#ifdef DEBUG_VERSION
|
||||
|
|
@ -648,7 +591,7 @@ void *alloc_string(int len) {
|
|||
return obj;
|
||||
}
|
||||
|
||||
void *alloc_array(int len) {
|
||||
void *alloc_array (int len) {
|
||||
data *obj = alloc(array_size(len));
|
||||
obj->data_header = ARRAY_TAG | (len << 3);
|
||||
#ifdef DEBUG_VERSION
|
||||
|
|
@ -658,7 +601,7 @@ void *alloc_array(int len) {
|
|||
return obj;
|
||||
}
|
||||
|
||||
void *alloc_sexp(int members) {
|
||||
void *alloc_sexp (int members) {
|
||||
sexp *obj = alloc(sexp_size(members));
|
||||
obj->sexp_header = obj->contents.data_header = SEXP_TAG | (members << 3);
|
||||
#ifdef DEBUG_VERSION
|
||||
|
|
@ -669,7 +612,7 @@ void *alloc_sexp(int members) {
|
|||
return obj;
|
||||
}
|
||||
|
||||
void *alloc_closure(int captured) {
|
||||
void *alloc_closure (int captured) {
|
||||
data *obj = alloc(closure_size(captured));
|
||||
obj->data_header = CLOSURE_TAG | (captured << 3);
|
||||
#ifdef DEBUG_VERSION
|
||||
|
|
|
|||
153
runtime/gc.h
153
runtime/gc.h
|
|
@ -3,24 +3,26 @@
|
|||
|
||||
#include "runtime_common.h"
|
||||
|
||||
# define GET_MARK_BIT(x) (((int) (x)) & 1)
|
||||
# define SET_MARK_BIT(x) (x = (((int) (x)) | 1))
|
||||
# define IS_ENQUEUED(x) (((int) (x)) & 2)
|
||||
# define MAKE_ENQUEUED(x) (x = (((int) (x)) | 2))
|
||||
# define MAKE_DEQUEUED(x) (x = (((int) (x)) & (~2)))
|
||||
# define RESET_MARK_BIT(x) (x = (((int) (x)) & (~1)))
|
||||
# define GET_FORWARD_ADDRESS(x) (((size_t) (x)) & (~3)) // since last 2 bits are used for mark-bit and enqueued-bit and due to correct alignment we can expect that last 2 bits don't influence address (they should always be zero)
|
||||
# define SET_FORWARD_ADDRESS(x, addr) (x = ((x & 3) | ((int) (addr)))) // take the last two bits as they are and make all others zero
|
||||
# define EXTRA_ROOM_HEAP_COEFFICIENT 2 // TODO: tune this parameter
|
||||
#define GET_MARK_BIT(x) (((int)(x)) & 1)
|
||||
#define SET_MARK_BIT(x) (x = (((int)(x)) | 1))
|
||||
#define IS_ENQUEUED(x) (((int)(x)) & 2)
|
||||
#define MAKE_ENQUEUED(x) (x = (((int)(x)) | 2))
|
||||
#define MAKE_DEQUEUED(x) (x = (((int)(x)) & (~2)))
|
||||
#define RESET_MARK_BIT(x) (x = (((int)(x)) & (~1)))
|
||||
#define GET_FORWARD_ADDRESS(x) \
|
||||
(((size_t)(x)) \
|
||||
& (~3)) // since last 2 bits are used for mark-bit and enqueued-bit and due to correct alignment we can expect that last 2 bits don't influence address (they should always be zero)
|
||||
#define SET_FORWARD_ADDRESS(x, addr) \
|
||||
(x = ((x & 3) | ((int)(addr)))) // take the last two bits as they are and make all others zero
|
||||
#define EXTRA_ROOM_HEAP_COEFFICIENT 2 // TODO: tune this parameter
|
||||
#ifdef DEBUG_VERSION
|
||||
# define MINIMUM_HEAP_CAPACITY (8)
|
||||
#else
|
||||
# define MINIMUM_HEAP_CAPACITY (1<<10)
|
||||
# define MINIMUM_HEAP_CAPACITY (1 << 10)
|
||||
#endif
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef enum { ARRAY, CLOSURE, STRING, SEXP } lama_type;
|
||||
|
||||
|
|
@ -37,65 +39,69 @@ typedef struct {
|
|||
} obj_field_iterator;
|
||||
|
||||
typedef struct {
|
||||
size_t * begin;
|
||||
size_t * end;
|
||||
size_t * current;
|
||||
size_t *begin;
|
||||
size_t *end;
|
||||
size_t *current;
|
||||
size_t size;
|
||||
} memory_chunk;
|
||||
|
||||
/* GC extra roots */
|
||||
# define MAX_EXTRA_ROOTS_NUMBER 32
|
||||
#define MAX_EXTRA_ROOTS_NUMBER 32
|
||||
|
||||
typedef struct {
|
||||
int current_free;
|
||||
void ** roots[MAX_EXTRA_ROOTS_NUMBER];
|
||||
void **roots[MAX_EXTRA_ROOTS_NUMBER];
|
||||
} extra_roots_pool;
|
||||
|
||||
// the only GC-related function that should be exposed, others are useful for tests and internal implementation
|
||||
// allocates object of the given size on the heap
|
||||
void* alloc(size_t);
|
||||
void *alloc(size_t);
|
||||
// takes number of words as a parameter
|
||||
void* gc_alloc(size_t);
|
||||
void *gc_alloc(size_t);
|
||||
// takes number of words as a parameter
|
||||
void *gc_alloc_on_existing_heap(size_t);
|
||||
|
||||
// specific for mark-and-compact_phase gc
|
||||
void mark(void *obj);
|
||||
void mark_phase(void);
|
||||
void mark (void *obj);
|
||||
void mark_phase (void);
|
||||
// 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
|
||||
void scan_extra_roots(void);
|
||||
void scan_extra_roots (void);
|
||||
#ifndef DEBUG_VERSION
|
||||
// marks each valid pointer from global area
|
||||
void scan_global_area(void);
|
||||
void scan_global_area (void);
|
||||
#endif
|
||||
// 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
|
||||
size_t compute_locations();
|
||||
void update_references(memory_chunk *);
|
||||
void physically_relocate(memory_chunk *);
|
||||
size_t compute_locations ();
|
||||
void update_references (memory_chunk *);
|
||||
void physically_relocate (memory_chunk *);
|
||||
|
||||
// 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 __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
|
||||
extern void __gc_init (
|
||||
void); // MANDATORY TO CALL BEFORE ANY INTERACTION WITH GC (apart from cases where we are working with virtual stack as happens in tests)
|
||||
extern void __init (
|
||||
void); // should be called before interaction with GC in case of using in tests with virtual stack, otherwise it is automatically invoked by __gc_init
|
||||
extern void __shutdown (
|
||||
void); // mostly useful for tests but basically you want to call this in case you want to deallocate all object allocated via GC
|
||||
// written in ASM
|
||||
extern void __pre_gc (void);
|
||||
// written in ASM
|
||||
extern void __post_gc (void);
|
||||
|
||||
// invoked from ASM
|
||||
extern void gc_test_and_mark_root(size_t ** root);
|
||||
inline bool is_valid_heap_pointer(const size_t *);
|
||||
inline bool is_valid_pointer(const size_t *);
|
||||
extern void gc_test_and_mark_root (size_t **root);
|
||||
inline bool is_valid_heap_pointer (const size_t *);
|
||||
inline bool is_valid_pointer (const size_t *);
|
||||
|
||||
void clear_extra_roots (void);
|
||||
|
||||
void push_extra_root (void ** p);
|
||||
|
||||
void pop_extra_root (void ** p);
|
||||
void push_extra_root (void **p);
|
||||
|
||||
void pop_extra_root (void **p);
|
||||
|
||||
/* Functions for tests */
|
||||
|
||||
|
|
@ -105,96 +111,95 @@ void pop_extra_root (void ** p);
|
|||
// returns number of ids dumped
|
||||
// object_ids_buf is pointer to area preallocated by user for dumping ids of objects in heap
|
||||
// object_ids_buf_size is in WORDS, NOT BYTES
|
||||
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
|
||||
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)
|
||||
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
|
||||
|
||||
|
||||
/* Utility functions */
|
||||
|
||||
// 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
|
||||
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
|
||||
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'
|
||||
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
|
||||
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
|
||||
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
|
||||
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)
|
||||
bool is_enqueued(void *obj);
|
||||
bool is_enqueued (void *obj);
|
||||
|
||||
// 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
|
||||
void make_dequeued(void *obj);
|
||||
void make_dequeued (void *obj);
|
||||
|
||||
// returns iterator to an object with the lowest address
|
||||
heap_iterator heap_begin_iterator();
|
||||
void heap_next_obj_iterator(heap_iterator *it);
|
||||
bool heap_is_done_iterator(heap_iterator *it);
|
||||
heap_iterator heap_begin_iterator ();
|
||||
void heap_next_obj_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)
|
||||
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
|
||||
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)
|
||||
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
|
||||
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
|
||||
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)
|
||||
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)
|
||||
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?
|
||||
// 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)
|
||||
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
|
||||
// (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)
|
||||
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
|
||||
// (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)
|
||||
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
|
||||
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
|
||||
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
|
||||
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)
|
||||
void* get_obj_header_ptr(void *ptr, lama_type type);
|
||||
void* get_object_content_ptr(void *header_ptr);
|
||||
void* get_end_of_obj(void *header_ptr);
|
||||
void *get_obj_header_ptr (void *ptr, lama_type type);
|
||||
void *get_object_content_ptr (void *header_ptr);
|
||||
void *get_end_of_obj (void *header_ptr);
|
||||
|
||||
void *alloc_string(int len);
|
||||
void *alloc_array(int len);
|
||||
void *alloc_sexp(int members);
|
||||
void *alloc_closure(int captured);
|
||||
void *alloc_string (int len);
|
||||
void *alloc_array (int len);
|
||||
void *alloc_sexp (int members);
|
||||
void *alloc_closure (int captured);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,9 +1,4 @@
|
|||
.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_top: .long 0
|
||||
|
||||
|
|
@ -13,8 +8,9 @@ __gc_stack_top: .long 0
|
|||
.globl __gc_root_scan_stack
|
||||
.globl __gc_stack_top
|
||||
.globl __gc_stack_bottom
|
||||
.extern init_pool
|
||||
.extern gc_test_and_copy_root
|
||||
.extern __init
|
||||
.extern gc_test_and_mark_root
|
||||
|
||||
.text
|
||||
|
||||
__gc_init:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,21 +1,20 @@
|
|||
# ifndef __LAMA_RUNTIME__
|
||||
# define __LAMA_RUNTIME__
|
||||
#ifndef __LAMA_RUNTIME__
|
||||
#define __LAMA_RUNTIME__
|
||||
|
||||
# include <stdio.h>
|
||||
# include <stdio.h>
|
||||
# include <string.h>
|
||||
# include <stdarg.h>
|
||||
# include <stdlib.h>
|
||||
# include <sys/mman.h>
|
||||
# include <assert.h>
|
||||
# include <errno.h>
|
||||
# include <regex.h>
|
||||
# include <time.h>
|
||||
# include <limits.h>
|
||||
# include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <regex.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
|
||||
# define WORD_SIZE (CHAR_BIT * sizeof(int))
|
||||
#define WORD_SIZE (CHAR_BIT * sizeof(int))
|
||||
|
||||
void failure (char *s, ...);
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,46 +5,44 @@
|
|||
// this flag makes GC behavior a bit different for testing purposes.
|
||||
//#define DEBUG_VERSION
|
||||
|
||||
# define STRING_TAG 0x00000001
|
||||
#define STRING_TAG 0x00000001
|
||||
//# define STRING_TAG 0x00000000
|
||||
# define ARRAY_TAG 0x00000003
|
||||
#define ARRAY_TAG 0x00000003
|
||||
//# define ARRAY_TAG 0x00000002
|
||||
# define SEXP_TAG 0x00000005
|
||||
#define SEXP_TAG 0x00000005
|
||||
//# define SEXP_TAG 0x00000004
|
||||
# define CLOSURE_TAG 0x00000007
|
||||
#define CLOSURE_TAG 0x00000007
|
||||
//# 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 TAG(x) (x & 0x00000007)
|
||||
#define LEN(x) ((x & 0xFFFFFFF8) >> 3)
|
||||
#define TAG(x) (x & 0x00000007)
|
||||
//# define TAG(x) (x & 0x00000006)
|
||||
|
||||
#define SEXP_ONLY_HEADER_SZ (2 * sizeof(int))
|
||||
|
||||
# define SEXP_ONLY_HEADER_SZ (2 * sizeof(int))
|
||||
|
||||
# ifndef DEBUG_VERSION
|
||||
#ifndef DEBUG_VERSION
|
||||
# define DATA_HEADER_SZ (sizeof(size_t) + sizeof(int))
|
||||
# else
|
||||
#else
|
||||
# define DATA_HEADER_SZ (sizeof(size_t) + sizeof(size_t) + sizeof(int))
|
||||
#endif
|
||||
|
||||
# define MEMBER_SIZE sizeof(int)
|
||||
#define MEMBER_SIZE sizeof(int)
|
||||
|
||||
# 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_DATA(x) ((data *)((char *)(x)-DATA_HEADER_SZ))
|
||||
#define TO_SEXP(x) ((sexp *)((char *)(x)-DATA_HEADER_SZ - SEXP_ONLY_HEADER_SZ))
|
||||
|
||||
# define UNBOXED(x) (((int) (x)) & 0x0001)
|
||||
# define UNBOX(x) (((int) (x)) >> 1)
|
||||
# define BOX(x) ((((int) (x)) << 1) | 0x0001)
|
||||
#define UNBOXED(x) (((int)(x)) & 0x0001)
|
||||
#define UNBOX(x) (((int)(x)) >> 1)
|
||||
#define BOX(x) ((((int)(x)) << 1) | 0x0001)
|
||||
|
||||
# define BYTES_TO_WORDS(bytes) (((bytes) - 1) / sizeof(size_t) + 1)
|
||||
# define WORDS_TO_BYTES(words) ((words) * sizeof(size_t))
|
||||
#define BYTES_TO_WORDS(bytes) (((bytes)-1) / sizeof(size_t) + 1)
|
||||
#define WORDS_TO_BYTES(words) ((words) * sizeof(size_t))
|
||||
|
||||
// CAREFUL WITH DOUBLE EVALUATION!
|
||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
|
||||
|
||||
typedef struct {
|
||||
// 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)
|
||||
|
|
|
|||
|
|
@ -1,23 +1,24 @@
|
|||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "gc.h"
|
||||
#include "runtime_common.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef DEBUG_VERSION
|
||||
|
||||
// function from runtime that maps string to int value
|
||||
extern int LtagHash (char *s);
|
||||
|
||||
extern void* Bsexp (int n, ...);
|
||||
extern void* Barray (int bn, ...);
|
||||
extern void* Bstring (void*);
|
||||
extern void* Bclosure (int bn, void *entry, ...);
|
||||
extern void *Bsexp (int n, ...);
|
||||
extern void *Barray (int bn, ...);
|
||||
extern void *Bstring (void *);
|
||||
extern void *Bclosure (int bn, void *entry, ...);
|
||||
|
||||
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
|
||||
assert((array_size(0) == get_header_size(ARRAY)));
|
||||
assert((string_size(0) == get_header_size(STRING) + 1)); // +1 is because of '\0'
|
||||
|
|
@ -26,46 +27,44 @@ void test_correct_structure_sizes(void) {
|
|||
|
||||
// just check correctness for some small sizes
|
||||
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((sexp_size(k) == get_header_size(SEXP) + sizeof (int) * k));
|
||||
assert((closure_size(k) == get_header_size(CLOSURE) + 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));
|
||||
}
|
||||
}
|
||||
|
||||
void no_gc_tests(void) {
|
||||
test_correct_structure_sizes();
|
||||
}
|
||||
void no_gc_tests (void) { test_correct_structure_sizes(); }
|
||||
|
||||
// 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();
|
||||
virt_stack *st = vstack_create();
|
||||
vstack_init(st);
|
||||
__gc_stack_bottom = (size_t) vstack_top(st);
|
||||
__gc_stack_bottom = (size_t)vstack_top(st);
|
||||
return st;
|
||||
}
|
||||
|
||||
void cleanup_test(virt_stack *st) {
|
||||
void cleanup_test (virt_stack *st) {
|
||||
vstack_destruct(st);
|
||||
__shutdown();
|
||||
}
|
||||
void force_gc_cycle(virt_stack *st) {
|
||||
__gc_stack_top = (size_t) vstack_top(st) - 4;
|
||||
|
||||
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 test_simple_string_alloc (void) {
|
||||
virt_stack *st = init_test();
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
vstack_push(st, BOX(i));
|
||||
}
|
||||
for (int i = 0; i < 5; ++i) { vstack_push(st, BOX(i)); }
|
||||
|
||||
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bstring, 1, "abc"));
|
||||
|
||||
|
|
@ -77,8 +76,8 @@ void test_simple_string_alloc(void) {
|
|||
cleanup_test(st);
|
||||
}
|
||||
|
||||
void test_simple_array_alloc(void) {
|
||||
virt_stack* st = init_test();
|
||||
void test_simple_array_alloc (void) {
|
||||
virt_stack *st = init_test();
|
||||
|
||||
// allocate array [ BOX(1) ] and push it onto the stack
|
||||
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Barray, 2, BOX(1), BOX(1)));
|
||||
|
|
@ -91,12 +90,13 @@ void test_simple_array_alloc(void) {
|
|||
cleanup_test(st);
|
||||
}
|
||||
|
||||
void test_simple_sexp_alloc(void) {
|
||||
virt_stack* st = init_test();
|
||||
void test_simple_sexp_alloc (void) {
|
||||
virt_stack *st = init_test();
|
||||
|
||||
// allocate sexp with one boxed field 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, Bsexp, 3, 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;
|
||||
int ids[N];
|
||||
|
|
@ -106,8 +106,8 @@ void test_simple_sexp_alloc(void) {
|
|||
cleanup_test(st);
|
||||
}
|
||||
|
||||
void test_simple_closure_alloc(void) {
|
||||
virt_stack* st = init_test();
|
||||
void test_simple_closure_alloc (void) {
|
||||
virt_stack *st = init_test();
|
||||
|
||||
// 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)));
|
||||
|
|
@ -120,10 +120,12 @@ void test_simple_closure_alloc(void) {
|
|||
cleanup_test(st);
|
||||
}
|
||||
|
||||
void test_single_object_allocation_with_collection_virtual_stack(void) {
|
||||
void test_single_object_allocation_with_collection_virtual_stack (void) {
|
||||
virt_stack *st = init_test();
|
||||
|
||||
vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
|
||||
vstack_push(st,
|
||||
call_runtime_function(
|
||||
vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
|
||||
|
||||
const int N = 10;
|
||||
int ids[N];
|
||||
|
|
@ -133,7 +135,7 @@ void test_single_object_allocation_with_collection_virtual_stack(void) {
|
|||
cleanup_test(st);
|
||||
}
|
||||
|
||||
void test_garbage_is_reclaimed(void) {
|
||||
void test_garbage_is_reclaimed (void) {
|
||||
virt_stack *st = init_test();
|
||||
|
||||
call_runtime_function(vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||
|
|
@ -148,10 +150,12 @@ void test_garbage_is_reclaimed(void) {
|
|||
cleanup_test(st);
|
||||
}
|
||||
|
||||
void test_alive_are_not_reclaimed(void) {
|
||||
void test_alive_are_not_reclaimed (void) {
|
||||
virt_stack *st = init_test();
|
||||
|
||||
vstack_push(st, 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);
|
||||
|
||||
|
|
@ -163,14 +167,21 @@ void test_alive_are_not_reclaimed(void) {
|
|||
cleanup_test(st);
|
||||
}
|
||||
|
||||
void test_small_tree_compaction(void) {
|
||||
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")));
|
||||
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];
|
||||
|
|
@ -178,15 +189,13 @@ void test_small_tree_compaction(void) {
|
|||
assert((alive == 3));
|
||||
|
||||
// check that order is indeed preserved
|
||||
for (int i = 0; i < alive - 1; ++i) {
|
||||
assert((ids[i] < ids[i + 1]));
|
||||
}
|
||||
for (int i = 0; i < alive - 1; ++i) { assert((ids[i] < ids[i + 1])); }
|
||||
cleanup_test(st);
|
||||
}
|
||||
|
||||
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);
|
||||
int cur_sz = 0;
|
||||
size_t alive = 0;
|
||||
|
|
@ -200,22 +209,19 @@ size_t generate_random_obj_forest(virt_stack *st, int cnt, int seed) {
|
|||
|
||||
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]);
|
||||
}
|
||||
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"));
|
||||
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;
|
||||
}
|
||||
if ((obj & 1) == 0) { ++alive; }
|
||||
}
|
||||
++cur_sz;
|
||||
}
|
||||
|
|
@ -223,7 +229,7 @@ size_t generate_random_obj_forest(virt_stack *st, int cnt, int seed) {
|
|||
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();
|
||||
|
||||
const int SZ = 100000;
|
||||
|
|
@ -235,9 +241,7 @@ void run_stress_test_random_obj_forest(int seed) {
|
|||
assert(alive == expectedAlive);
|
||||
|
||||
// check that order is indeed preserved
|
||||
for (int i = 0; i < alive - 1; ++i) {
|
||||
assert((ids[i] < ids[i + 1]));
|
||||
}
|
||||
for (int i = 0; i < alive - 1; ++i) { assert((ids[i] < ids[i + 1])); }
|
||||
|
||||
cleanup_test(st);
|
||||
}
|
||||
|
|
@ -246,7 +250,7 @@ void run_stress_test_random_obj_forest(int seed) {
|
|||
|
||||
#include <time.h>
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
int main (int argc, char **argv) {
|
||||
#ifdef DEBUG_VERSION
|
||||
no_gc_tests();
|
||||
|
||||
|
|
@ -263,11 +267,9 @@ int main(int argc, char ** argv) {
|
|||
double diff;
|
||||
time(&start);
|
||||
// stress test
|
||||
for (int s = 0; s < 100; ++s) {
|
||||
run_stress_test_random_obj_forest(s);
|
||||
}
|
||||
for (int s = 0; s < 100; ++s) { run_stress_test_random_obj_forest(s); }
|
||||
time(&end);
|
||||
diff = difftime(end, start);
|
||||
printf ("Stress tests took %.2lf seconds to complete\n", diff);
|
||||
printf("Stress tests took %.2lf seconds to complete\n", diff);
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1,45 +1,34 @@
|
|||
#include "virt_stack.h"
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
virt_stack *vstack_create() {
|
||||
return malloc(sizeof (virt_stack));
|
||||
}
|
||||
virt_stack *vstack_create () { return malloc(sizeof(virt_stack)); }
|
||||
|
||||
void vstack_destruct(virt_stack *st) {
|
||||
free(st);
|
||||
}
|
||||
void vstack_destruct (virt_stack *st) { free(st); }
|
||||
|
||||
void vstack_init(virt_stack *st) {
|
||||
void vstack_init (virt_stack *st) {
|
||||
st->cur = RUNTIME_VSTACK_SIZE;
|
||||
st->buf[st->cur] = 0;
|
||||
}
|
||||
|
||||
void vstack_push(virt_stack *st, size_t value) {
|
||||
if (st->cur == 0) {
|
||||
assert(0);
|
||||
}
|
||||
void vstack_push (virt_stack *st, size_t value) {
|
||||
if (st->cur == 0) { assert(0); }
|
||||
--st->cur;
|
||||
st->buf[st->cur] = value;
|
||||
}
|
||||
|
||||
size_t vstack_pop(virt_stack *st) {
|
||||
if (st->cur == RUNTIME_VSTACK_SIZE) {
|
||||
assert(0);
|
||||
}
|
||||
size_t vstack_pop (virt_stack *st) {
|
||||
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) {
|
||||
return st->buf + st->cur;
|
||||
}
|
||||
void *vstack_top (virt_stack *st) { return st->buf + st->cur; }
|
||||
|
||||
size_t vstack_size(virt_stack *st) {
|
||||
return RUNTIME_VSTACK_SIZE - st->cur;
|
||||
}
|
||||
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) {
|
||||
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 RUNTIME_VSTACK_SIZE 100000
|
||||
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct {
|
||||
size_t buf[RUNTIME_VSTACK_SIZE + 1];
|
||||
size_t cur;
|
||||
} 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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue