diff --git a/docs/commands.md b/docs/commands.md index 5869a65f..faf4fe4f 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -62,7 +62,8 @@ 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 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` diff --git a/src/README.md b/src/README.md index b150e9e0..9acf7ce7 100644 --- a/src/README.md +++ b/src/README.md @@ -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. @@ -393,4 +393,67 @@ B g_t (void* x) // tag pointer with OBJ_TAG 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 -``` \ No newline at end of file +``` + +## 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-.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 +``` diff --git a/src/builtins/arithd.c b/src/builtins/arithd.c index cb75299c..1be2c76b 100644 --- a/src/builtins/arithd.c +++ b/src/builtins/arithd.c @@ -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 diff --git a/src/builtins/sysfn.c b/src/builtins/sysfn.c index d669525a..4ebef478 100644 --- a/src/builtins/sysfn.c +++ b/src/builtins/sysfn.c @@ -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); diff --git a/src/core/fillarr.c b/src/core/fillarr.c index c56c1f37..e935ec1d 100644 --- a/src/core/fillarr.c +++ b/src/core/fillarr.c @@ -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); diff --git a/src/core/heap.c b/src/core/heap.c index 36297f8c..c6d93226 100644 --- a/src/core/heap.c +++ b/src/core/heap.c @@ -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); diff --git a/src/core/mm.c b/src/core/mm.c index 44294319..00506045 100644 --- a/src/core/mm.c +++ b/src/core/mm.c @@ -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 diff --git a/src/core/stuff.c b/src/core/stuff.c index 52aad7ed..1bd75f33 100644 --- a/src/core/stuff.c +++ b/src/core/stuff.c @@ -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) diff --git a/src/core/stuff.h b/src/core/stuff.h index 2e2d3c0f..2f12d02a 100644 --- a/src/core/stuff.h +++ b/src/core/stuff.h @@ -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=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; diff --git a/src/h.h b/src/h.h index c8ef35d3..5f753149 100644 --- a/src/h.h +++ b/src/h.h @@ -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 #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 diff --git a/src/jit/nvm_x86_64.c b/src/jit/nvm_x86_64.c index 2c956a84..5ff5469f 100644 --- a/src/jit/nvm_x86_64.c +++ b/src/jit/nvm_x86_64.c @@ -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 diff --git a/src/load.c b/src/load.c index 4a43d374..b526f3f0 100644 --- a/src/load.c +++ b/src/load.c @@ -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 diff --git a/src/main.c b/src/main.c index e6b7c0f2..a0a56826 100644 --- a/src/main.c +++ b/src/main.c @@ -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"); diff --git a/src/opt/gc.c b/src/opt/gc.c index c49bf2c1..83bd6268 100644 --- a/src/opt/gc.c +++ b/src/opt/gc.c @@ -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 } diff --git a/src/opt/gc.h b/src/opt/gc.h index 0f0c0e35..53bc0ee9 100644 --- a/src/opt/gc.h +++ b/src/opt/gc.h @@ -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*); diff --git a/src/opt/mm_2buddy.c b/src/opt/mm_2buddy.c index d3211b52..96369015 100644 --- a/src/opt/mm_2buddy.c +++ b/src/opt/mm_2buddy.c @@ -4,7 +4,7 @@ #endif #include "gc.c" -#ifdef OBJ_COUNTER +#if OBJ_COUNTER u64 currObjCounter; #endif #if VERIFY_TAIL diff --git a/src/opt/mm_2buddy.h b/src/opt/mm_2buddy.h index f5d55450..f9cd7125 100644 --- a/src/opt/mm_2buddy.h +++ b/src/opt/mm_2buddy.h @@ -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; diff --git a/src/opt/mm_buddy.c b/src/opt/mm_buddy.c index b7053fb6..9f2aa6c0 100644 --- a/src/opt/mm_buddy.c +++ b/src/opt/mm_buddy.c @@ -4,7 +4,7 @@ #endif #include "gc.c" -#ifdef OBJ_COUNTER +#if OBJ_COUNTER u64 currObjCounter; #endif diff --git a/src/opt/mm_buddy.h b/src/opt/mm_buddy.h index fc64aa11..5ac089e4 100644 --- a/src/opt/mm_buddy.h +++ b/src/opt/mm_buddy.h @@ -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; diff --git a/src/opt/mm_buddyTemplate.c b/src/opt/mm_buddyTemplate.c index 3d8ab986..c2dbc5d2 100644 --- a/src/opt/mm_buddyTemplate.c +++ b/src/opt/mm_buddyTemplate.c @@ -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 diff --git a/src/opt/mm_buddyTemplate.h b/src/opt/mm_buddyTemplate.h index f789e2c4..2cb2383a 100644 --- a/src/opt/mm_buddyTemplate.h +++ b/src/opt/mm_buddyTemplate.h @@ -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) { diff --git a/src/utils/mut.c b/src/utils/mut.c index c4648b3f..60682a68 100644 --- a/src/utils/mut.c +++ b/src/utils/mut.c @@ -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)); diff --git a/src/utils/ryu.c b/src/utils/ryu.c index f927c146..485901d8 100644 --- a/src/utils/ryu.c +++ b/src/utils/ryu.c @@ -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); diff --git a/src/utils/valgrind.c b/src/utils/valgrind.c index 35c3f0d5..53f4e473 100644 --- a/src/utils/valgrind.c +++ b/src/utils/valgrind.c @@ -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?"¹":"⁰"); } diff --git a/src/vm.c b/src/vm.c index a7d099f0..925d5150 100644 --- a/src/vm.c +++ b/src/vm.c @@ -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); diff --git a/src/vm.h b/src/vm.h index 56512e65..5bd4cc82 100644 --- a/src/vm.h +++ b/src/vm.h @@ -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] diff --git a/test/moreCfgs.sh b/test/moreCfgs.sh index 111a0514..70af2e7c 100755 --- a/test/moreCfgs.sh +++ b/test/moreCfgs.sh @@ -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