refactor & document #define-s, always-available ')gc log' and ')mem log'

This commit is contained in:
dzaima 2023-10-31 02:57:07 +02:00
parent ae6763f5e3
commit ad574d2269
27 changed files with 205 additions and 149 deletions

View File

@ -63,6 +63,7 @@ Get statistics on memory usage.
`)mem t` to get usage per object type.
`)mem s` to get a breakdown of the number of objects with a specific size.
`)mem f` to get breakdown of free bucket counts per size.
`)mem log` enables/disabled printing a message on OS requests for more memory.
## `)gc`
@ -70,7 +71,10 @@ Force garbage collection.
`)gc disable` disables automatic garabage collection, and `)gc enable` enables it again.
Not a system function because currently CBQN doesn't support garbage collection in the middle of program execution.
A plain `)gc` differs from `•internal.GC@` in that it can do precise marking as there's no in-progress C stack.
`)gc log` enables/disables printing a message on GC.
## `)internalPrint expr`

View File

@ -145,7 +145,7 @@ type field for heap-allocated objects:
t_funWrap, t_md1Wrap, t_md2Wrap // types wrapping builtins for RT_WRAP; see rtwrap.c
See src/h.h for more basics
See src/h.h for more basic operations
```
An object can be allocated with `mm_alloc(sizeInBytes, t_something)`. The returned object starts with the structure of `Value`, so custom data must be after that. `mm_free` can be used to force-free an object regardless of its reference count.
@ -394,3 +394,66 @@ B g_ta(void* x) // tag pointer with ARR_TAG
B g_tf(void* x) // tag pointer with FUN_TAG
// invoke with "p g_p(whatever)"; requires a build with debug symbols to be usable, but e.g. "p (void)g_pst()" can be used without one
```
## Various `#define`s
Most toggles require a value of `1` to be enabled.
```c
// (effective) usual default value is listed; (u) marks being not defined
// default may change under some conditions (DEBUG, CATCH_ERRORS, heapverify, among maybe other things)
// some things fully configured by the build system may not be listed
// general config:
#define REPL_INTERRUPT 0 // support ctrl+c for interrupting some REPL execution
#define ENABLE_GC 1 // enable garbage collection
#define MM 1 // memory manager; 0 - malloc (no GC); 1 - buddy; 2 - 2buddy
#define HEAP_MAX ~0ULL // initial heap max size (overridden by -M)
#define JIT_ENABLED (u) // force-enable or force-disable JIT (x86_64-only)
#define RANDSEED 0 // random seed used to make •rand (0 uses time)
#define JIT_START 2 // number of calls for when to start JITting (x86_64-only); default is 2, defined in vm.h
// -1: never JIT (≈ JIT_ENABLED=0)
// 0: JIT everything
// >0: JIT after n non-JIT invocations; max ¯1+2⋆16
// runtime configuration:
#define ALL_R0 0 // use all of r0.bqn for runtime_0
#define ALL_R1 0 // use all of r1.bqn for runtime
#define NO_RT 0 // whether to completely disable self-hosted runtime loading
#define CATCH_ERRORS 1 // allow catching errors; means refcounts might stay too high if forgotten over a throw-catch; disabled for heapverify
#define FAKE_RUNTIME 0 // disable the self-hosted runtime
#define FORMATTER 1 // use self-hosted formatter for output
#define NO_EXPLAIN 0 // disable )explain
#define NO_RYU 0 // disable usage of Ryu
#define EACH_FILLS 0 // compute fills for ¨ and ⌜; may be forcibly disabled
#define SFNS_FILLS 1 // compute fills for structural functions (∾, ≍, etc)
#define CHECK_VALID 1 // check for valid arguments in places where that would be detrimental to performance
// e.g. left argument sortedness of ⍋/⍒, incompatible changes in ⌾, etc
#define RYU_OPTIMIZE_SIZE 0 // reduce size of Ryu tables at the cost of some performance for number •Repr
#define FFI_CHECKS 1 // check for valid arguments passed to FFI-d functions
#define UNSAFE_SIZES 0 // disable safety checks on array length overflows
// debugging stuff:
#define DEBUG 0 // the regular debug build
#define HEAP_VERIFY 0 // heapverify
#define WARN_SLOW 0 // log on various slow operations
#define USE_PERF 0 // write a /tmp/perf-<pid>.map for JITted things for linux perf
#define GC_LOG_DETAILED 0 // slightly more stats on GC logging
#define DEBUG_VM 0 // print evaluation of every bytecode
#define USE_VALGRIND 0 // adjust memory manager & code for valgrind usage
#define VERIFY_TAIL (u) // number of bytes after the end of an array to verify not being improperly modified; 64 in DEBUG
#define NEEQUAL_NEGZERO 0 // make negative zero not equal zero for •internal.EEqual
#define RT_VERIFY_ARGS 1 // rtverify: preserve arguments for printing on failure
// some somewhat-outdated/unmaintained things:
#define RT_PERF 0 // time runtime primitives
#define RT_VERIFY 0 // compare native and runtime versions of primitives
#define ALLOC_STAT 0 // store basic allocation statistics
#define ALLOC_SIZES 0 // store per-type allocation size statistics
#define DONT_FREE 0 // don't actually ever free objects, such that they can be printed after being freed for debugging
#define TYPED_ARITH 1 // enable specialized loops for typed arith
#define VM_POS 1 // whether to store detailed execution position information for stacktraces
#define OBJ_COUNTER 0 // store a unique allocation number with each object; superseded by the existence of https://rr-project.org/
#define OBJ_TRACK (u) // object ID to track
```

View File

@ -106,7 +106,7 @@ static B modint_AS(B w, B xv) { return modint_AA(w, C2(shape, C1(fne, incG(w))
#define ARITH_SLOW(N) SLOWIF((!isArr(w) || TI(w,elType)!=el_B) && (!isArr(x) || TI(x,elType)!=el_B)) SLOW2("arithd " #N, w, x)
#define P2(N) { if(isArr(w)|isArr(x)) { ARITH_SLOW(N); return arith_recd(N##_c2, w, x); }}
#if !TYPED_ARITH
#if defined(TYPED_ARITH) && !TYPED_ARITH
#define AR_I_TO_ARR(NAME) P2(NAME)
#define AR_F_TO_ARR AR_I_TO_ARR
#else

View File

@ -917,7 +917,7 @@ B delay_c1(B t, B x) {
return m_f64(ts.tv_sec-ts0.tv_sec+(ts.tv_nsec-ts0.tv_nsec)*1e-9);
}
B exit_c1(B t, B x) {
#ifdef HEAP_VERIFY
#if HEAP_VERIFY
printf("(heapverify doesn't run on •Exit)\n");
#endif
bqn_exit(q_i32(x)? o2i(x) : 0);

View File

@ -105,7 +105,7 @@ NOINLINE bool fillEqualF(B w, B x) { // doesn't consume; both args must be array
B withFill(B x, B fill) { // consumes both
assert(isArr(x));
#ifdef DEBUG
#if DEBUG
validateFill(fill);
#endif
u8 xt = TY(x);

View File

@ -1,13 +1,13 @@
#include "../core.h"
#ifdef HEAP_VERIFY
#if HEAP_VERIFY
u32 heapVerify_mode = -1;
Value* heap_observed;
Value* heap_curr;
void heapVerify_checkFn(Value* v) {
if (v->refc!=0) {
#ifdef OBJ_COUNTER
#if OBJ_COUNTER
printf("delta %d for %s @ %p, uid "N64d": ", v->refc, type_repr(v->type), v, v->uid);
#else
printf("delta %d for %s @ %p: ", (i32)v->refc, type_repr(v->type), v);

View File

@ -12,6 +12,8 @@ static u64 prepAllocSize(u64 sz) {
}
#define MMAP(SZ) mmap(NULL, prepAllocSize(SZ), PROT_READ|PROT_WRITE, MAP_NORESERVE|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
bool mem_log_enabled;
#if MM==0
#include "../opt/mm_malloc.c"
#elif MM==1

View File

@ -16,7 +16,7 @@ NORETURN NOINLINE void fatal(char* s) {
#endif
if (inErr) {
fputs("\nCBQN encountered a fatal error during information printing of another fatal error. Exiting without printing more info.", stderr);
#ifdef DEBUG
#if DEBUG
__builtin_trap();
#endif
exit(1);
@ -28,7 +28,7 @@ NORETURN NOINLINE void fatal(char* s) {
vm_pstLive(); fflush(stderr); fflush(stdout);
print_vmStack(); fflush(stderr);
before_exit();
#ifdef DEBUG
#if DEBUG
__builtin_trap();
#endif
exit(1);
@ -147,7 +147,7 @@ NOINLINE void fprintI(FILE* f, B x) {
else if (o2cG(x)>=16) fprintf(f, "\\x%x", o2cG(x));
else fprintf(f, "\\x0%x", o2cG(x));
} else if (isVal(x)) {
#ifdef DEBUG
#if DEBUG
if (isVal(x) && (TY(x)==t_freed || TY(x)==t_empty)) {
u8 t = TY(x);
v(x)->type = v(x)->flags;
@ -444,18 +444,18 @@ B bqn_merge(B x, u32 type) {
return taga(APD_SH_GET(r, type));
}
#ifdef ALLOC_STAT
#if ALLOC_STAT
u64* ctr_a = 0;
u64* ctr_f = 0;
u64 actrc = 21000;
u64 talloc = 0;
#ifdef ALLOC_SIZES
#if ALLOC_SIZES
u32** actrs;
#endif
#endif
NOINLINE void print_allocStats() {
#ifdef ALLOC_STAT
#if ALLOC_STAT
printf("total ever allocated: "N64u"\n", talloc);
printf("allocated heap size: "N64u"\n", mm_heapAlloc);
printf("used heap size: "N64u"\n", mm_heapUsed());
@ -467,7 +467,7 @@ NOINLINE void print_allocStats() {
u64 leakedCount = 0;
for (i64 i = 0; i < t_COUNT; i++) leakedCount+= ctr_a[i]-ctr_f[i];
printf("leaked object count: "N64u"\n", leakedCount);
#ifdef ALLOC_SIZES
#if ALLOC_SIZES
for(i64 i = 0; i < actrc; i++) {
u32* c = actrs[i];
bool any = false;
@ -501,10 +501,10 @@ void g_pv(void* x) { ignore_bad_tag=true; fprintI(stderr,tag(x,OBJ_TAG)); fput
void g_iv(void* x) { ignore_bad_tag=true; B xo = tag(x, OBJ_TAG); B r = C2(info, m_f64(1), inc(xo)); fprintI(stderr,r); dec(r); fputc(10,stderr); fflush(stderr); ignore_bad_tag=false; }
void g_pst(void) { vm_pstLive(); fflush(stdout); fflush(stderr); }
#ifdef DEBUG
#if DEBUG
bool cbqn_noAlloc;
NOINLINE void cbqn_NOGC_start() { cbqn_noAlloc=true; }
#ifdef OBJ_COUNTER
#if OBJ_COUNTER
#define PRINT_ID(X) fprintf(stderr, "Object ID: "N64u"\n", (X)->uid)
#else
#define PRINT_ID(X)

View File

@ -290,7 +290,7 @@ static usz depth(B x) { // doesn't consume
#ifdef USE_VALGRIND
#if USE_VALGRIND
#include "../utils/valgrind.h"
#else
#define vg_def_p(X, L)
@ -323,12 +323,12 @@ static FC2 c2fn(B f) {
}
// alloc stuff
#ifdef ALLOC_STAT
#if ALLOC_STAT
extern u64* ctr_a;
extern u64* ctr_f;
extern u64 actrc;
extern u64 talloc;
#ifdef ALLOC_SIZES
#if ALLOC_SIZES
extern u32** actrs;
#endif
#endif
@ -342,9 +342,9 @@ FORCE_INLINE void preAlloc(usz sz, u8 type) {
#ifdef OOM_TEST
if (--oomTestLeft==0) thrOOMTest();
#endif
#ifdef ALLOC_STAT
#if ALLOC_STAT
if (!ctr_a) {
#ifdef ALLOC_SIZES
#if ALLOC_SIZES
actrs = malloc(sizeof(u32*)*actrc);
for (i32 i = 0; i < actrc; i++) actrs[i] = calloc(t_COUNT, sizeof(u32));
#endif
@ -352,7 +352,7 @@ FORCE_INLINE void preAlloc(usz sz, u8 type) {
ctr_f = calloc(t_COUNT, sizeof(u64));
}
assert(type<t_COUNT);
#ifdef ALLOC_SIZES
#if ALLOC_SIZES
actrs[(sz+3)/4>=actrc? actrc-1 : (sz+3)/4][type]++;
#endif
ctr_a[type]++;
@ -369,13 +369,13 @@ void tailVerifyReinit(void* ptr, u64 s, u64 e);
#endif
#define FINISH_OVERALLOC_A(A, S, L) FINISH_OVERALLOC(a(A), offsetof(TyArr,a)+(S), offsetof(TyArr,a)+(S)+(L));
FORCE_INLINE void preFree(Value* x, bool mmx) {
#ifdef ALLOC_STAT
#if ALLOC_STAT
ctr_f[x->type]++;
#endif
#if VERIFY_TAIL
if (!mmx) tailVerifyFree(x);
#endif
#ifdef DEBUG
#if DEBUG
if (x->type==t_empty) fatal("double-free");
// u32 undef;
// x->refc = undef;

67
src/h.h
View File

@ -1,43 +1,31 @@
#pragma once
#ifdef DEBUG
// #define DEBUG_VM
#endif
#ifndef CATCH_ERRORS
#define CATCH_ERRORS 1 // whether to allow catching errors
#endif // currently means refcounts won't be accurate and can't be tested for
#define CATCH_ERRORS 1
#endif
#ifndef ENABLE_GC
#define ENABLE_GC 1 // whether to ever garbage-collect
#endif
#ifndef TYPED_ARITH
#define TYPED_ARITH 1 // whether to use typed arith
#endif
#ifndef VM_POS
#define VM_POS 1 // whether to store detailed execution position information for stacktraces
#define ENABLE_GC 1
#endif
#ifndef CHECK_VALID
#define CHECK_VALID 1 // whether to check for valid arguments in places where that would be detrimental to performance
#endif // e.g. left argument sortedness of ⍋/⍒, incompatible changes in ⌾, etc
#define CHECK_VALID 1
#endif
#ifndef EACH_FILLS
#define EACH_FILLS 0 // whether to try to squeeze out fills for ¨ and ⌜
#define EACH_FILLS 0
#endif
#ifndef SFNS_FILLS
#define SFNS_FILLS 1 // whether to generate fills for structural functions (∾, ≍, etc)
#endif
#ifndef FAKE_RUNTIME
#define FAKE_RUNTIME 0 // whether to disable the self-hosted runtime
#define SFNS_FILLS 1
#endif
#ifndef MM
#define MM 1 // memory manager; 0 - malloc (no GC); 1 - buddy; 2 - 2buddy
#define MM 1
#endif
#ifndef HEAP_MAX
#define HEAP_MAX ~0ULL // default heap max size
#define HEAP_MAX ~0ULL
#endif
#ifndef FORMATTER
#define FORMATTER 1 // use self-hosted formatter for output
#define FORMATTER 1
#endif
#ifndef RANDSEED
#define RANDSEED 0 // random seed used to make •rand (0 for using time)
#define RANDSEED 0
#endif
#ifndef FFI
#define FFI 2
@ -46,25 +34,6 @@
#endif
#endif
// #define ALLOC_STAT // store basic allocation statistics
// #define ALLOC_SIZES // store per-type allocation size statistics
// #define USE_VALGRIND // whether to mark freed memory for valgrind
// #define DONT_FREE // don't actually ever free objects, such that they can be printed after being freed for debugging
// #define OBJ_COUNTER // store a unique allocation number with each object for easier analysis
// #define ALL_R0 // use all of r0.bqn for runtime_0
// #define ALL_R1 // use all of r1.bqn for runtime
// #define JIT_START 2 // number of calls for when to start JITting (x86_64-only); default is 2, defined in vm.h
// -1: never JIT
// 0: JIT everything
// >0: JIT after n non-JIT invocations; max ¯1+2⋆16
// #define LOG_GC // log GC stats
// #define RT_PERF // time runtime primitives
// #define RT_VERIFY // compare native and runtime versions of primitives
// #define NO_RT // whether to completely disable self-hosted runtime loading
#ifdef __OpenBSD__
#define __wchar_t __wchar_t2 // horrible hack for BSD
#endif
@ -77,7 +46,7 @@
#if CATCH_ERRORS
#include <setjmp.h>
#endif
#ifdef HEAP_VERIFY
#if HEAP_VERIFY
#undef CATCH_ERRORS
#define CATCH_ERRORS 0
#endif
@ -92,13 +61,13 @@
#define PROPER_FILLS 0
#endif
#if defined(ALL_R0) || defined (ALL_R1)
#if ALL_R0 || ALL_R1
#define WRAP_NNBI 1
#endif
#if defined(RT_PERF) || defined(RT_VERIFY)
#if RT_PERF || RT_VERIFY
#define RT_WRAP 1
#if defined(RT_PERF) && defined(RT_VERIFY)
#if RT_PERF && RT_VERIFY
#error "can't have both RT_PERF and RT_VERIFY"
#endif
#endif
@ -312,7 +281,7 @@ typedef struct Value {
u8 flags; // self-hosted primitive index (plus 1) for callable, fl_* flags for arrays
u8 type; // used by TI, among generally knowing what type of object this is
ur extra; // whatever object-specific stuff. Rank for arrays, internal id for functions
#ifdef OBJ_COUNTER
#if OBJ_COUNTER
u64 uid;
#endif
} Value;
@ -322,7 +291,7 @@ typedef struct Arr {
usz* sh;
} Arr;
#ifdef DEBUG
#if DEBUG
NOINLINE NORETURN void assert_fail(char* expr, char* file, int line, const char fn[]);
#define assert(X) do {if (!(X)) assert_fail(#X, __FILE__, __LINE__, __PRETTY_FUNCTION__);} while(0)
B VALIDATE(B x);
@ -450,7 +419,7 @@ void freeThrown(void);
#define VTY(X,T) assert(isVal(X) && TY(X)==(T))
void print_vmStack(void);
#ifdef DEBUG
#if DEBUG
B validate(B x);
Value* validateP(Value* x);
#endif

View File

@ -59,6 +59,7 @@ static void* mmap_nvm(u64 sz) {
}
}
extern bool mem_log_enabled;
#define MMAP(SZ) mmap_nvm(sz);
#define MUL 1
#define ALLOC_MODE 1

View File

@ -4,6 +4,10 @@
#include "ns.h"
#include "builtins.h"
#ifndef FAKE_RUNTIME
#define FAKE_RUNTIME 0
#endif
#define PRECOMPILED_FILE(END) STR1(../build/BYTECODE_DIR/gen/END)
#define FOR_INIT(F) \
@ -402,14 +406,14 @@ void load_init() { // very last init function
for (u64 i = 0; i < RT_LEN; i++) inc(fruntime[i]);
B frtObj = m_caB(RT_LEN, fruntime);
#ifndef NO_RT
#if !NO_RT
B provide[] = {
/* actual provide: */
bi_type,bi_fill,bi_log,bi_grLen,bi_grOrd,bi_asrt,bi_add,bi_sub,bi_mul,bi_div,bi_pow,bi_floor,bi_eq,bi_le,bi_fne,bi_shape,bi_pick,bi_ud,bi_tbl,bi_scan,bi_fillBy,bi_val,bi_catch
/* result list from commented-out •Out line in cc.bqn: */,
bi_root,bi_not,bi_and,bi_or,bi_feq,bi_couple,bi_shifta,bi_shiftb,bi_reverse,bi_transp,bi_gradeUp,bi_gradeDown,bi_indexOf,bi_count,bi_memberOf,bi_cell,bi_rank
};
#ifndef ALL_R0
#if !ALL_R0
B runtime_0[] = {bi_floor,bi_ceil,bi_stile,bi_lt,bi_gt,bi_ne,bi_ge,bi_rtack,bi_ltack,bi_join,bi_pair,bi_take,bi_drop,bi_select,bi_const,bi_swap,bi_each,bi_fold,bi_atop,bi_over,bi_before,bi_after,bi_cond,bi_repeat};
#else
Block* runtime0_b = load_compImport("(self-hosted runtime0)",
@ -427,7 +431,7 @@ void load_init() { // very last init function
#endif
);
#ifdef ALL_R0
#if ALL_R0
dec(r0r);
#endif
@ -459,7 +463,7 @@ void load_init() { // very last init function
#ifdef RT_WRAP
r1Objs[i] = Get(rtObjRaw, i); gc_add(r1Objs[i]);
#endif
#ifdef ALL_R1
#if ALL_R1
bool nnbi = true;
B r = Get(rtObjRaw, i);
#else
@ -632,7 +636,7 @@ B def_m2_im(Md2D* d, B x) { return def_fn_im(tag(d,FUN_TAG), x); }
B def_m2_iw(Md2D* d, B w, B x) { return def_fn_iw(tag(d,FUN_TAG), w, x); }
B def_m2_ix(Md2D* d, B w, B x) { return def_fn_ix(tag(d,FUN_TAG), w, x); }
#ifdef DONT_FREE
#if DONT_FREE
static B empty_get(Arr* x, usz n) {
x->type = x->flags;
if (x->type==t_empty) fatal("getting from empty without old type data");
@ -753,7 +757,7 @@ void base_init() { // very first init function
TIi(t_empty,freeF) = empty_free; TIi(t_invalid,freeF) = empty_free; TIi(t_freed,freeF) = def_freeF;
TIi(t_invalid,visit) = freed_visit;
TIi(t_freed,visit) = freed_visit;
#ifdef DONT_FREE
#if DONT_FREE
TIi(t_empty,get) = empty_get;
TIi(t_empty,getU) = empty_getU;
#endif

View File

@ -119,14 +119,14 @@ static NOINLINE i64 readInt(char** p) {
")escaped ",
")profile ", ")profile@",
")t ", ")t:", ")time ", ")time:",
")mem", ")mem t", ")mem s", ")mem f",
")mem", ")mem t", ")mem s", ")mem f", ")mem log",
")erase ",
")clearImportCache",
")kb",
")theme dark", ")theme light", ")theme none",
")exit", ")off",
")vars",
")gc", ")gc off", ")gc on",
")gc", ")gc off", ")gc on", ")gc log",
")internalPrint ", ")heapdump",
#if NATIVE_COMPILER && !ONLY_NATIVE_COMP
")switchCompiler",
@ -602,6 +602,7 @@ bool ryu_s2d_n(u8* buffer, int len, f64* result);
#endif
void heap_printInfoStr(char* str);
extern bool gc_log_enabled, mem_log_enabled;
void cbqn_runLine0(char* ln, i64 read) {
if (ln[0]==0 || read==0) return;
@ -667,7 +668,12 @@ void cbqn_runLine0(char* ln, i64 read) {
timeRep = am;
output = 0;
} else if (isCmd(cmdS, &cmdE, "mem ")) {
heap_printInfoStr(cmdE);
if (strcmp(cmdE,"log")==0) {
mem_log_enabled^= true;
printf("Allocation logging %s\n", mem_log_enabled? "enabled" : "disabled");
} else {
heap_printInfoStr(cmdE);
}
return;
} else if (isCmd(cmdS, &cmdE, "erase ")) {
char* name = cmdE;
@ -773,6 +779,9 @@ void cbqn_runLine0(char* ln, i64 read) {
gc_disable();
printf("GC disabled\n");
}
} else if (strcmp(cmdE,"log")==0) {
gc_log_enabled^= true;
printf("GC logging %s\n", gc_log_enabled? "enabled" : "disabled");
} else printf("Unknown GC command\n");
#else
printf("Macro ENABLE_GC was false at compile-time, cannot GC\n");

View File

@ -2,11 +2,14 @@
#if ENABLE_GC
static void mm_freeFreedAndMerge(void);
#include "../utils/time.h"
#if GC_LOG_DETAILED
bool gc_log_enabled = true;
#else
bool gc_log_enabled;
#endif
#endif
#ifdef LOG_GC
#include "../utils/time.h"
#endif
u64 gc_depth = 1;
@ -48,20 +51,20 @@ void gc_visitRoots() {
static void gc_tryFree(Value* v) {
u8 t = v->type;
#if defined(DEBUG) && !defined(CATCH_ERRORS)
#if DEBUG && !CATCH_ERRORS
if (t==t_freed) fatal("GC found t_freed\n");
#endif
if (t!=t_empty && !(v->mmInfo&0x80)) {
if (t==t_shape || t==t_temp || t==t_talloc) return;
#ifdef DONT_FREE
#if DONT_FREE
v->flags = t;
#else
#if CATCH_ERRORS
if (t==t_freed) { mm_free(v); return; }
#endif
#endif
#ifdef LOG_GC
gc_freedBytes+= mm_size(v); gc_freedCount++;
#if GC_LOG_DETAILED
gcs_freedBytes+= mm_size(v); gcs_freedCount++;
#endif
v->type = t_freed;
ptr_inc(v); // required as otherwise the object may free itself while not done reading its own fields
@ -72,8 +75,8 @@ static void gc_tryFree(Value* v) {
}
}
#ifdef LOG_GC
u64 gc_visitBytes, gc_visitCount, gc_freedBytes, gc_freedCount, gc_unkRefsBytes, gc_unkRefsCount;
#if GC_LOG_DETAILED
u64 gcs_visitBytes, gcs_visitCount, gcs_freedBytes, gcs_freedCount, gcs_unkRefsBytes, gcs_unkRefsCount; // GC stat counters
#endif
i32 visit_mode;
@ -105,8 +108,8 @@ void gc_onVisit(Value* x) {
case GC_MARK: {
if (x->mmInfo&0x80) return;
x->mmInfo|= 0x80;
#ifdef LOG_GC
gc_visitBytes+= mm_size(x); gc_visitCount++;
#if GC_LOG_DETAILED
gcs_visitBytes+= mm_size(x); gcs_visitCount++;
#endif
TIv(x,visit)(x);
return;
@ -133,8 +136,8 @@ static void gcv2_unmark_visit(Value* x) {
#if ENABLE_GC
static void gc_visitRefcNonzero(Value* x) {
if (x->refc == 0) return;
#ifdef LOG_GC
gc_unkRefsBytes+= mm_size(x); gc_unkRefsCount++;
#if GC_LOG_DETAILED
gcs_unkRefsBytes+= mm_size(x); gcs_unkRefsCount++;
#endif
mm_visitP(x);
}
@ -164,20 +167,28 @@ static void gcv2_unmark_visit(Value* x) {
u64 gc_lastAlloc;
void gc_forceGC(bool toplevel) {
#if ENABLE_GC
#ifdef LOG_GC
u64 start = nsTime();
gc_visitBytes = 0; gc_freedBytes = 0;
gc_visitCount = 0; gc_freedCount = 0;
gc_unkRefsBytes = 0; gc_unkRefsCount = 0;
u64 startSize = mm_heapUsed();
#endif
gc_run(toplevel);
u64 startTime=0, startSize=0;
if (gc_log_enabled) {
startTime = nsTime();
startSize = mm_heapUsed();
#if GC_LOG_DETAILED
gcs_visitBytes = 0; gcs_freedBytes = 0;
gcs_visitCount = 0; gcs_freedCount = 0;
gcs_unkRefsBytes = 0; gcs_unkRefsCount = 0;
#endif
}
gc_run(toplevel);
u64 endSize = mm_heapUsed();
#ifdef LOG_GC
fprintf(stderr, "GC kept "N64d"B/"N64d" objs, freed "N64d"B, incl. directly "N64d"B/"N64d" objs", gc_visitBytes, gc_visitCount, startSize-endSize, gc_freedBytes, gc_freedCount);
fprintf(stderr, "; unknown refs: "N64d"B/"N64d" objs", gc_unkRefsBytes, gc_unkRefsCount);
fprintf(stderr, "; took %.3fms\n", (nsTime()-start)/1e6);
#endif
if (gc_log_enabled) {
fprintf(stderr, "GC: before: "N64d"B/"N64d"B", startSize, mm_heapAlloc);
#if GC_LOG_DETAILED
fprintf(stderr, "; kept "N64d"B="N64d" objs, freed "N64d"B, incl. directly "N64d"B="N64d" objs", gcs_visitBytes, gcs_visitCount, startSize-endSize, gcs_freedBytes, gcs_freedCount);
fprintf(stderr, "; unknown refs: "N64d"B="N64d" objs", gcs_unkRefsBytes, gcs_unkRefsCount);
#else
fprintf(stderr, "; freed "N64d"B, left "N64d"B", startSize-endSize, endSize);
#endif
fprintf(stderr, "; took %.3fms\n", (nsTime()-startTime)/1e6);
}
gc_lastAlloc = endSize;
#endif
}

View File

@ -4,8 +4,8 @@ extern u64 gc_depth;
static void gc_disable() { gc_depth++; }
static void gc_enable() { gc_depth--; }
#ifdef LOG_GC
extern u64 gc_visitBytes, gc_visitCount, gc_freedBytes, gc_freedCount;
#if GC_LOG_DETAILED
extern u64 gcs_visitBytes, gcs_visitCount, gcs_freedBytes, gcs_freedCount;
#endif
extern void gc_onVisit(Value*);

View File

@ -4,7 +4,7 @@
#endif
#include "gc.c"
#ifdef OBJ_COUNTER
#if OBJ_COUNTER
u64 currObjCounter;
#endif
#if VERIFY_TAIL

View File

@ -5,7 +5,7 @@ struct EmptyValue { // needs set: mmInfo; type=t_empty; next; everything else ca
struct Value;
EmptyValue* next;
};
#ifdef OBJ_COUNTER
#if OBJ_COUNTER
extern u64 currObjCounter;
#endif
extern u64 mm_heapAlloc;

View File

@ -4,7 +4,7 @@
#endif
#include "gc.c"
#ifdef OBJ_COUNTER
#if OBJ_COUNTER
u64 currObjCounter;
#endif

View File

@ -5,7 +5,7 @@ struct EmptyValue { // needs set: mmInfo; type=t_empty; next; everything else ca
struct Value;
EmptyValue* next;
};
#ifdef OBJ_COUNTER
#if OBJ_COUNTER
extern u64 currObjCounter;
#endif
extern u64 mm_heapAlloc;

View File

@ -60,19 +60,13 @@ static NOINLINE void* BN(allocateMore)(i64 bucket, u8 type, i64 from, i64 to) {
thrOOM();
}
#if LOG_GC || LOG_MM_MORE
fprintf(stderr, "allocating "N64u" more " STR1(BN()) " heap (from allocation of "N64u"B/bucket %d)", sz, (u64)BSZ(bucket), (int)bucket);
#endif
if (mem_log_enabled) fprintf(stderr, "requesting "N64u" more " STR1(BN()) " heap (during allocation of "N64u"B t_%s)", sz, (u64)BSZ(bucket), type_repr(type));
#if NO_MMAP
EmptyValue* c = calloc(sz+getPageSize(), 1);
#if LOG_GC || LOG_MM_MORE
fprintf(stderr, "\n");
#endif
if (mem_log_enabled) fprintf(stderr, "\n");
#else
u8* mem = MMAP(sz);
#if LOG_GC || LOG_MM_MORE
fprintf(stderr, ": got %p\n", mem);
#endif
if (mem_log_enabled) fprintf(stderr, ": %s\n", mem==MAP_FAILED? "failed" : "success");
if (mem==MAP_FAILED) thrOOM();
if (ptr2u64(mem)+sz > (1ULL<<48)) fatal("mmap returned address range above 2⋆48");
#if ALLOC_MODE==0

View File

@ -8,7 +8,7 @@ FORCE_INLINE void BN(freeLink)(Value* x, bool link) {
#else
preFree(x, false);
#endif
#ifdef DONT_FREE
#if DONT_FREE
if (x->type!=t_freed) x->flags = x->type;
#else
u8 b = x->mmInfo&127;
@ -36,7 +36,7 @@ static void* BN(allocL)(i64 bucket, u8 type) {
x->refc = 1;
x->type = type;
x->mmInfo = bucket;
#ifdef OBJ_COUNTER
#if OBJ_COUNTER
x->uid = currObjCounter++;
#ifdef OBJ_TRACK
if (x->uid == OBJ_TRACK) {

View File

@ -55,10 +55,10 @@ NOINLINE void mut_to(Mut* m, u8 n) {
m->fns = &mutFns[n];
SPRNK(m->val, 1);
m->val->sh = &m->val->ia;
#ifdef USE_VALGRIND
#if USE_VALGRIND
VALGRIND_MAKE_MEM_DEFINED(m->val, mm_size((Value*)m->val)); // it's incomplete, but it's a typed array so garbage is acceptable
#endif
#ifdef DEBUG
#if DEBUG
if (n==el_B && o==el_f64) { // hack to make toHArr calling f64arr_get not cry about possible sNaN floats
usz ia = m->val->ia;
f64* p = f64arr_ptr(taga(m->val));

View File

@ -28,7 +28,7 @@ void ryu_init(void) { }
#include "ryu/ryu_common.h"
// Include either the small or the full lookup tables depending on the mode.
#if defined(RYU_OPTIMIZE_SIZE)
#if RYU_OPTIMIZE_SIZE
#include "ryu/d2s_small_table.h"
#else
#include "ryu/d2s_full_table.h"
@ -83,7 +83,7 @@ static inline floating_decimal_64 d2d(const uint64_t mantissa, const uint32_t ex
e10 = (int32_t) q;
const int32_t k = DOUBLE_POW5_INV_BITCOUNT + pow5bits((int32_t) q) - 1;
const int32_t i = -e2 + (int32_t) q + k;
#if defined(RYU_OPTIMIZE_SIZE)
#if RYU_OPTIMIZE_SIZE
uint64_t pow5[2];
double_computeInvPow5(q, pow5);
vr = mulShiftAll64(m2, pow5, i, &vp, &vm, mmShift);
@ -118,7 +118,7 @@ static inline floating_decimal_64 d2d(const uint64_t mantissa, const uint32_t ex
const int32_t i = -e2 - (int32_t) q;
const int32_t k = pow5bits(i) - DOUBLE_POW5_BITCOUNT;
const int32_t j = (int32_t) q - k;
#if defined(RYU_OPTIMIZE_SIZE)
#if RYU_OPTIMIZE_SIZE
uint64_t pow5[2];
double_computePow5(i, pow5);
vr = mulShiftAll64(m2, pow5, j, &vp, &vm, mmShift);
@ -558,7 +558,7 @@ bool ryu_s2d_n(u8* buffer, int len, f64* result) {
// To that end, we use the DOUBLE_POW5_SPLIT table.
int j = e2 - e10 - ceil_log2pow5(e10) + DOUBLE_POW5_BITCOUNT;
assert(j >= 0);
#if defined(RYU_OPTIMIZE_SIZE)
#if RYU_OPTIMIZE_SIZE
uint64_t pow5[2];
double_computePow5(e10, pow5);
m2 = mulShift64(m10, pow5, j);
@ -575,7 +575,7 @@ bool ryu_s2d_n(u8* buffer, int len, f64* result) {
} else {
e2 = floor_log2(m10) + e10 - ceil_log2pow5(-e10) - (DOUBLE_MANTISSA_BITS + 1);
int j = e2 - e10 + ceil_log2pow5(-e10) - 1 + DOUBLE_POW5_INV_BITCOUNT;
#if defined(RYU_OPTIMIZE_SIZE)
#if RYU_OPTIMIZE_SIZE
uint64_t pow5[2];
double_computeInvPow5(-e10, pow5);
m2 = mulShift64(m10, pow5, j);

View File

@ -1,7 +1,3 @@
#ifndef DBG_VG_OVERRIDES
#define DBG_VG_OVERRIDES 0
#endif
static void printBitDef(u8 val, u8 def) {
printf("%s", def&1? val&1?"1":"0" : val&1?"¹":"");
}

View File

@ -692,14 +692,14 @@ FORCE_INLINE B gotoNextBody(Block* bl, Scope* sc, Body* body) {
return execBodyInplaceI(body, nsc, bl);
}
#ifdef DEBUG_VM
#if DEBUG_VM
i32 bcDepth=-2;
i32* vmStack;
i32 bcCtr = 0;
#endif
#define BCPOS(B,P) (B->bl->map[(P)-(u32*)B->bl->bc])
B evalBC(Body* b, Scope* sc, Block* bl) { // doesn't consume
#ifdef DEBUG_VM
#if DEBUG_VM
bcDepth+= 2;
if (!vmStack) vmStack = malloc(400);
i32 stackNum = bcDepth>>1;
@ -738,7 +738,7 @@ B evalBC(Body* b, Scope* sc, Block* bl) { // doesn't consume
#endif
while(true) {
#ifdef DEBUG_VM
#if DEBUG_VM
u32* sbc = bc;
i32 bcPos = BCPOS(b,sbc);
vmStack[stackNum] = bcPos;
@ -937,20 +937,20 @@ B evalBC(Body* b, Scope* sc, Block* bl) { // doesn't consume
case RETN: goto end;
default:
#ifdef DEBUG
#if DEBUG
printf("todo %d\n", bc[-1]); bc++; break;
#else
UD;
#endif
}
#ifdef DEBUG_VM
#if DEBUG_VM
for(i32 i = 0; i < bcDepth; i++) fprintf(stderr," ");
print_BC(stderr,sbc,20); fprintf(stderr,"@%d out: ",BCPOS(b, sbc));
for (i32 i = 0; i < lgStack-origStack; i++) { if(i)fprintf(stderr,"; "); fprintI(stderr,origStack[i]); } fputc('\n',stderr); fflush(stderr);
#endif
}
end:;
#ifdef DEBUG_VM
#if DEBUG_VM
bcDepth-= 2;
#endif
B r = POP;
@ -1183,7 +1183,7 @@ static void allocStack(void** curr, void** start, void** end, i32 elSize, i32 co
#endif
}
void print_vmStack() {
#ifdef DEBUG_VM
#if DEBUG_VM
printf("vm stack:");
for (i32 i = 0; i < (bcDepth>>1) + 1; i++) { printf(" %d", vmStack[i]); fflush(stdout); }
printf("\n"); fflush(stdout);
@ -1392,7 +1392,7 @@ native_print:
//print_BCStream((u32*)i32arr_ptr(comp->bc)+bcPos);
} else {
#ifdef DEBUG
#if DEBUG
if (pos!=-1) fprintf(stderr, N64d": ", pos);
fprintf(stderr, "source unknown\n");
#endif
@ -1707,7 +1707,7 @@ NOINLINE NORETURN void throwImpl(bool rethrow) {
unwindEnv(envStart-1);
vm_pst(envCurr+1, envEnd);
before_exit();
#ifdef DEBUG
#if DEBUG
__builtin_trap();
#else
exit(1);

View File

@ -15,6 +15,9 @@
#ifndef EXT_ONLY_GLOBAL
#define EXT_ONLY_GLOBAL 1
#endif
#ifndef VM_POS
#define VM_POS 1
#endif
enum {
PUSH = 0x00, // N; push object from objs[N]

View File

@ -12,7 +12,7 @@ build/build f='-DWARN_SLOW' c && ./BQN -p 2+2 2> /dev/null || exit
build/build f='-DMM=0 -DENABLE_GC=0' c && ./BQN -p 2+2 || exit
build/build f='-DMM=1' c && ./BQN -p 2+2 || exit
build/build f='-DMM=2' debug c && ./BQN "$1/test/this.bqn" || exit
build/build usz=64 debug singeli=1 arch=generic c && ./BQN "$1/test/this.bqn" || exit
build/build usz=64 debug singeli arch=generic c && ./BQN "$1/test/this.bqn" || exit
build/build f='-DTYPED_ARITH=0' c && ./BQN -p 2+2 || exit
build/build f='-DFAKE_RUNTIME' c && ./BQN -p 2+2 || exit
build/build f='-DALL_R0' c && ./BQN -p 2+2 || exit
@ -26,7 +26,7 @@ build/build f='-DOBJ_COUNTER' c && ./BQN -p 2+2 || exit
build/build f='-DNO_RT' c && ./BQN -p 2+2 || exit
build/build f='-DNATIVE_COMPILER' c && ./BQN -p 2+2 || exit
build/build f='-DNATIVE_COMPILER -DONLY_NATIVE_COMP -DFORMATTER=0 -DNO_RT -DNO_EXPLAIN' c && ./BQN -p 2+2 || exit
build/build f='-DLOG_GC' c && ./BQN -p 2+2 || exit
build/build f='-DGC_LOG_DETAILED' c && ./BQN -p 2+2 || exit
build/build f='-DUSE_PERF' c && ./BQN -p 2+2 || exit
build/build f='-DREPL_INTERRUPT=0' c && ./BQN -p 2+2 || exit
build/build f='-DREPL_INTERRUPT=1' c && ./BQN -p 2+2 || exit