11 #define FREE_OBJECT(t,p) krk_reallocate(p,sizeof(t),0)
13 #if defined(KRK_EXTENSIVE_MEMORY_DEBUGGING)
65 static struct DHE * _debug_mem[DHE_SIZE];
67 static inline unsigned int _debug_mem_hash(
const void * ptr) {
71 uintptr_t p = (uintptr_t)ptr;
72 return (p >> 4) & (DHE_SIZE-1);
75 static void _debug_mem_set(
const void* ptr,
size_t size) {
76 unsigned int hash = _debug_mem_hash(ptr);
77 _dhe * x = _debug_mem[hash];
79 x = malloc(
sizeof(_dhe));
95 x = malloc(
sizeof(_dhe));
103 static size_t _debug_mem_get(
const void * ptr) {
104 unsigned int hash = _debug_mem_hash(ptr);
105 _dhe * x = _debug_mem[hash];
108 if (x->ptr == ptr)
return x->size;
114 static void _debug_mem_remove(
const void *ptr) {
115 unsigned int hash = _debug_mem_hash(ptr);
116 _dhe * x = _debug_mem[hash];
120 _debug_mem[hash] = x->next;
137 static int _debug_mem_has(
const void *ptr) {
138 unsigned int hash = _debug_mem_hash(ptr);
139 _dhe * x = _debug_mem[hash];
142 if (x->ptr == ptr)
return 1;
150 #if defined(KRK_EXTENSIVE_MEMORY_DEBUGGING)
151 _debug_mem_set(ptr, size);
154 vm.bytesAllocated += size;
159 vm.bytesAllocated -= old;
160 vm.bytesAllocated +=
new;
163 #ifndef KRK_NO_STRESS_GC
164 if (
vm.globalFlags & KRK_GLOBAL_ENABLE_STRESS_GC) {
168 if (
vm.bytesAllocated >
vm.nextGC) {
178 out = realloc(ptr,
new);
181 #if defined(KRK_EXTENSIVE_MEMORY_DEBUGGING)
183 if (!_debug_mem_has(ptr)) {
184 fprintf(stderr,
"Invalid reallocation of %p from %zu to %zu\n", ptr, old,
new);
188 size_t t = _debug_mem_get(ptr);
190 fprintf(stderr,
"Invalid reallocation of %p from %zu - should be %zu - to %zu\n", ptr, old, t,
new);
194 _debug_mem_remove(ptr);
197 _debug_mem_set(out,
new);
204 static void freeObject(
KrkObj *
object) {
205 switch (object->
type) {
206 case KRK_OBJ_STRING: {
208 KRK_FREE_ARRAY(
char, string->
chars, string->
length + 1);
213 case KRK_OBJ_CODEOBJECT: {
218 KRK_FREE_ARRAY(
KrkLocalEntry, function->localNames, function->localNameCount);
219 KRK_FREE_ARRAY(
KrkExpressionsMap, function->expressions, function->expressionsCapacity);
220 KRK_FREE_ARRAY(
KrkOverlongJump, function->overlongJumps, function->overlongJumpsCapacity);
221 function->localNameCount = 0;
225 case KRK_OBJ_NATIVE: {
229 case KRK_OBJ_CLOSURE: {
236 case KRK_OBJ_UPVALUE: {
240 case KRK_OBJ_CLASS: {
250 case KRK_OBJ_INSTANCE: {
259 case KRK_OBJ_BOUND_METHOD:
262 case KRK_OBJ_TUPLE: {
268 case KRK_OBJ_BYTES: {
270 KRK_FREE_ARRAY(uint8_t, bytes->
bytes, bytes->
length);
283 if (object->
type == KRK_OBJ_INSTANCE) {
286 object->next = other;
294 if (other->
type == KRK_OBJ_CLASS) {
304 void krk_freeMemoryDebugger(
void) {
305 #if defined(KRK_EXTENSIVE_MEMORY_DEBUGGING)
306 for (
unsigned int i = 0; i < DHE_SIZE; ++i) {
307 _dhe * x = _debug_mem[i];
308 _debug_mem[i] = NULL;
315 if (
vm.bytesAllocated != 0) {
323 if (object->
flags & KRK_OBJ_FLAGS_IS_MARKED)
return;
324 object->flags |= KRK_OBJ_FLAGS_IS_MARKED;
326 if (
vm.grayCapacity <
vm.grayCount + 1) {
327 vm.grayCapacity = KRK_GROW_CAPACITY(
vm.grayCapacity);
328 vm.grayStack = realloc(
vm.grayStack,
sizeof(
KrkObj*) *
vm.grayCapacity);
329 if (!
vm.grayStack) exit(1);
331 vm.grayStack[
vm.grayCount++] = object;
335 if (!IS_OBJECT(value))
return;
340 for (
size_t i = 0; i < array->
count; ++i) {
345 static void blackenObject(
KrkObj *
object) {
346 switch (object->
type) {
347 case KRK_OBJ_CLOSURE: {
358 case KRK_OBJ_CODEOBJECT: {
364 markArray(&function->positionalArgNames);
365 markArray(&function->keywordArgNames);
366 markArray(&function->chunk.constants);
367 for (
size_t i = 0; i <
function->localNameCount; ++i) {
373 case KRK_OBJ_UPVALUE:
376 case KRK_OBJ_CLASS: {
385 case KRK_OBJ_INSTANCE: {
391 case KRK_OBJ_BOUND_METHOD: {
397 case KRK_OBJ_TUPLE: {
399 markArray(&tuple->
values);
409 static void traceReferences(
void) {
410 while (
vm.grayCount > 0) {
411 KrkObj *
object =
vm.grayStack[--
vm.grayCount];
412 blackenObject(
object);
416 static size_t sweep(
void) {
421 if (object->
flags & (KRK_OBJ_FLAGS_IMMORTAL | KRK_OBJ_FLAGS_IS_MARKED)) {
422 object->flags &= ~(KRK_OBJ_FLAGS_IS_MARKED | KRK_OBJ_FLAGS_SECOND_CHANCE);
424 object =
object->
next;
425 }
else if (object->
flags & KRK_OBJ_FLAGS_SECOND_CHANCE) {
426 KrkObj * unreached = object;
427 object =
object->
next;
428 if (previous != NULL) {
429 previous->
next = object;
433 freeObject(unreached);
436 object->flags |= KRK_OBJ_FLAGS_SECOND_CHANCE;
438 object =
object->
next;
445 for (
size_t i = 0; i < table->
used; ++i) {
452 static void tableRemoveWhite(
KrkTable * table) {
453 for (
size_t i = 0; i < table->
used; ++i) {
455 if (IS_OBJECT(entry->key) && !((AS_OBJECT(entry->key))->flags & KRK_OBJ_FLAGS_IS_MARKED)) {
462 for (
KrkValue * slot = thread->
stack; slot && slot < thread->stackTop; ++slot) {
477 static void markRoots(
void) {
480 markThreadRoots(thread);
481 thread = thread->
next;
487 if (
vm.specialMethodNames) {
488 for (
int i = 0; i < METHOD__MAX; ++i) {
494 #ifndef KRK_NO_GC_TRACING
495 static int smartSize(
char _out[100],
size_t s) {
496 #if UINTPTR_MAX == 0xFFFFFFFF
498 char * prefix =
"GMK";
501 char * prefix =
"PTGMK";
503 for (; count > 0 && *prefix; count--, prefix++) {
504 size_t base = 1UL << (count * 10);
507 return snprintf(_out, 100,
"%zu.%1zu %ciB", t, (s - t * base) / (base / 10), *prefix);
510 return snprintf(_out, 100,
"%d B", (
int)s);
515 #ifndef KRK_NO_GC_TRACING
516 struct timespec outTime, inTime;
518 if (
vm.globalFlags & KRK_GLOBAL_REPORT_GC_COLLECTS) {
519 clock_gettime(CLOCK_MONOTONIC, &inTime);
522 size_t bytesBefore =
vm.bytesAllocated;
527 tableRemoveWhite(&
vm.strings);
528 size_t out = sweep();
540 if (
vm.bytesAllocated < 0x4000000) {
541 vm.nextGC =
vm.bytesAllocated * 2;
543 vm.nextGC =
vm.bytesAllocated + 0x4000000;
546 #ifndef KRK_NO_GC_TRACING
547 if (
vm.globalFlags & KRK_GLOBAL_REPORT_GC_COLLECTS) {
548 clock_gettime(CLOCK_MONOTONIC, &outTime);
549 struct timespec diff;
550 diff.tv_sec = outTime.tv_sec - inTime.tv_sec;
551 diff.tv_nsec = outTime.tv_nsec - inTime.tv_nsec;
552 if (diff.tv_nsec < 0) { diff.tv_sec--; diff.tv_nsec += 1000000000L; }
554 char smartBefore[100];
555 smartSize(smartBefore, bytesBefore);
556 char smartAfter[100];
557 smartSize(smartAfter,
vm.bytesAllocated);
558 char smartFreed[100];
559 smartSize(smartFreed, bytesBefore -
vm.bytesAllocated);
561 smartSize(smartNext,
vm.nextGC);
563 fprintf(stderr,
"[gc] %lld.%.9lds %s before; %s after; freed %s in %llu objects; next collection at %s\n",
564 (
long long)diff.tv_sec, diff.tv_nsec,
565 smartBefore,smartAfter,smartFreed,(
unsigned long long)out, smartNext);
Exported methods for the source compiler.
Functions for dealing with garbage collection and memory allocation.
void krk_markValue(KrkValue value)
During a GC scan cycle, mark a value as used.
void krk_markTable(KrkTable *table)
During a GC scan cycle, mark the contents of a table as used.
void krk_freeObjects(void)
Release all objects.
void krk_markObject(KrkObj *object)
During a GC scan cycle, mark an object as used.
void * krk_reallocate(void *ptr, size_t old, size_t new)
Resize an allocated heap object.
void krk_gcTakeBytes(const void *ptr, size_t size)
Assume ownership of size bytes at ptr.
size_t krk_collectGarbage(void)
Run a cycle of the garbage collector.
Struct definitions for core object types.
A function that has been attached to an object to serve as a method.
KrkValue receiver
Object to pass as implicit first argument.
KrkObj * method
Function to call.
Immutable sequence of bytes.
size_t length
Length of data in bytes.
uint8_t * bytes
Pointer to separately-stored bytes data.
void krk_freeChunk(KrkChunk *chunk)
Release the resources allocated to an opcode chunk.
KrkString * filename
Filename of the original source that defined the codeobject for the class.
KrkCleanupCallback _ongcsweep
C function to call when the garbage collector is discarding an instance of this class.
KrkCleanupCallback _ongcscan
C function to call when the garbage collector visits an instance of this class in the scan phase.
KrkString * name
Name of the class.
struct KrkClass * base
Pointer to base class implementation.
KrkTable subclasses
Set of classes that subclass this class.
size_t allocSize
Size to allocate when creating instances of this class.
KrkTable methods
General attributes table.
struct KrkClass * _class
Metaclass.
KrkCodeObject * function
The codeobject containing the bytecode run when this function is called.
KrkValue globalsOwner
Owner of the globals table for this function.
size_t upvalueCount
Number of entries in upvalues.
KrkValue annotations
Dictionary of type hints.
KrkUpvalue ** upvalues
Array of upvalues collected from the surrounding context when the closure was created.
KrkTable fields
Object attributes table.
Map entry of opcode offsets to expressions spans.
KrkTable fields
Attributes table.
Metadata on a local variable name in a function.
Managed binding to a C function.
The most basic object type.
uint16_t flags
General object flags, mostly related to garbage collection.
uint16_t type
Tag indicating core type.
struct KrkObj * next
Invasive linked list of all objects in the VM.
Immutable sequence of Unicode codepoints.
char * chars
UTF8 canonical data.
void * codes
Codepoint data.
size_t length
String length in bytes.
One (key,value) pair in a table.
Simple hash table of arbitrary keys to values.
int krk_tableDeleteExact(KrkTable *table, KrkValue key)
Remove a key from a hash table, with identity lookup.
void krk_freeTable(KrkTable *table)
Release resources associated with a hash table.
Execution state of a VM thread.
KrkValue currentException
struct KrkThreadState * next
KrkUpvalue * openUpvalues
KrkValue scratchSpace[KRK_THREAD_SCRATCH_SIZE]
Immutable sequence of arbitrary values.
KrkValueArray values
Stores the length, capacity, and actual values of the tuple.
Storage for values referenced from nested functions.
struct KrkUpvalue * next
Invasive linked list pointer to next upvalue.
Flexible vector of stack references.
void krk_freeValueArray(KrkValueArray *array)
Release relesources used by a value array.
Stack reference or primative value.
Implementation of a generic hash table.
Utilities for creating native bindings.
Core API for the bytecode virtual machine.
krk_threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
#define vm
Convenience macro for namespacing.
#define KRK_THREAD_SCRATCH_SIZE
Extra space for each thread to store a set of working values safe from the GC.