diff --git a/README.md b/README.md index 1808889b..cd009ada 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ build/run: 1. `./genRuntime path/to/mlochbaum/BQN` 2. Optionally choose what to build by changing `src/h.h` 3. `make` - - Options: `make o3`, `make debug` (`make rtperf` and `make heapverify` may also be useful) - - Do `make clean` or `make [o3|debug]-clean` before to force recompile + - Options: `make o3`, `make debug` (`make rtperf`, `make heapverify` and `make rtverify` also exist for further testing/debugging) + - Do `make clean` or `make [o3|debug|…]-clean` before to force recompile - `./build` and `./debugBuild` compile everything at once and allow specifying extra compiler arguments, but may be slower 4. `./BQN` (or `rlwrap ./BQN` for a fancier interface) diff --git a/makefile b/makefile index 6a18866e..73002158 100644 --- a/makefile +++ b/makefile @@ -9,6 +9,8 @@ rtperf: @$(MAKE) $(J) -C obj/rtperf rtperf heapverify: @$(MAKE) $(J) -C obj/heapverify heapverify +rtverify: + @$(MAKE) $(J) -C obj/rtverify rtverify o3-clean: @$(MAKE) -C obj/o3 clean @@ -18,5 +20,7 @@ rtperf-clean: @$(MAKE) -C obj/rtperf clean heapverify-clean: @$(MAKE) -C obj/heapverify clean +rtverify-clean: + @$(MAKE) -C obj/rtverify clean -clean: o3-clean debug-clean rtperf-clean heapverify-clean +clean: o3-clean debug-clean rtperf-clean heapverify-clean rtverify-clean diff --git a/obj/rtverify/makefile b/obj/rtverify/makefile new file mode 120000 index 00000000..26ebd524 --- /dev/null +++ b/obj/rtverify/makefile @@ -0,0 +1 @@ +../subMakefile \ No newline at end of file diff --git a/obj/subMakefile b/obj/subMakefile index ab4d79e0..f46a997b 100644 --- a/obj/subMakefile +++ b/obj/subMakefile @@ -13,6 +13,8 @@ rtperf: FLAGS=-O3 -DRT_PERF rtperf: gen heapverify: FLAGS=-DDEBUG -g -DHEAP_VERIFY heapverify: gen +rtverify: FLAGS=-O3 -DRT_VERIFY +rtverify: gen gen: builtins core base utils @$(CC) -o BQN *.o -lm @@ -24,7 +26,7 @@ core: i32arr.o c32arr.o f64arr.o harr.o fillarr.o stuff.o derv.o mm.o heap.o @echo $< | cut -c 11- @$(CMD) $@.d -c $< -base: load.o main.o rtPerf.o vm.o ns.o +base: load.o main.o rtwrap.o vm.o ns.o %.o: ../../src/%.c @echo $< | cut -c 11- @$(CMD) $@.d -c $< diff --git a/src/builtins/md1.c b/src/builtins/md1.c index 0ddbb123..44a101c7 100644 --- a/src/builtins/md1.c +++ b/src/builtins/md1.c @@ -99,14 +99,14 @@ B scan_c1(B d, B x) { B f = c(Md1D,d)->f; i64 c = 0; for (usz i = 0; i < ia; i++) { rp[i] = c+= xp[i]; - if (c>I32_MAX) goto base; + if (c>I32_MAX) { dec(r); goto base; } } dec(x); return r; } if (rtid==7) { i32* rp; B r = m_i32arrv(&rp, ia); - i32 c = 0; + i32 c = I32_MIN; for (usz i = 0; i < ia; i++) { if (xp[i]>c) c = xp[i]; rp[i] = c; @@ -155,7 +155,7 @@ B scan_c2(B d, B w, B x) { B f = c(Md1D,d)->f; i64 c = wv; for (usz i = 0; i < ia; i++) { rp[i] = c+= xp[i]; - if (c>I32_MAX) goto base; + if (c>I32_MAX) { dec(r); goto base; } } dec(x); return r; diff --git a/src/core.h b/src/core.h index e84728d9..21992dd8 100644 --- a/src/core.h +++ b/src/core.h @@ -21,6 +21,10 @@ #include "core/fillarr.h" #include "core/derv.h" +#ifdef RT_VERIFY + extern B r1Objs[rtLen]; +#endif + typedef struct BFn { struct Fun; B ident; diff --git a/src/core/stuff.c b/src/core/stuff.c index d9f7beb3..45f722e3 100644 --- a/src/core/stuff.c +++ b/src/core/stuff.c @@ -231,7 +231,6 @@ i32 compare(B w, B x) { #undef CMP -static NOINLINE bool equalR(B w, B x) { return equal(w, x); } bool atomEqual(B w, B x) { // doesn't consume (not that that matters really currently) if(isF64(w)&isF64(x)) return w.f==x.f; if (w.u==x.u) return true; @@ -244,7 +243,7 @@ bool atomEqual(B w, B x) { // doesn't consume (not that that matters really curr if (o2i(wdp[0])<=1) { dec(wd);dec(xd); return false; } usz wia = a(wd)->ia; if (wia!=a(xd)->ia) { dec(wd);dec(xd); return false; } - for (i32 i = 0; itype!=v(x)->type) return false; + B2B dcf = TI(w).decompose; + if (dcf == def_decompose) return false; + B wd=dcf(inc(w)); B* wdp = harr_ptr(wd); + B xd=dcf(inc(x)); B* xdp = harr_ptr(xd); + if (o2i(wdp[0])<=1) { dec(wd);dec(xd); return false; } + usz wia = a(wd)->ia; + if (wia!=a(xd)->ia) { dec(wd);dec(xd); return false; } + for (i32 i = 0; iia; + BS2B xgetU = TI(x).getU; + BS2B wgetU = TI(w).getU; + for (usz i = 0; i < ia; i++) if(!eequal(wgetU(w,i),xgetU(x,i))) return false; + return true; +} + u64 depth(B x) { // doesn't consume if (isAtm(x)) return 0; if (TI(x).arrD1) return 1; @@ -289,8 +324,8 @@ char* format_type(u8 u) { case t_comp:return"comp"; case t_block:return"block"; case t_body:return"body"; case t_scope:return"scope"; case t_ns:return"ns"; case t_nsDesc:return"nsDesc"; case t_fldAlias:return"alias"; case t_hashmap:return"hashmap"; case t_temp:return"temporary"; case t_freed:return"(freed by GC)"; case t_harrPartial:return"partHarr"; - #ifdef RT_PERF - case t_funPerf:return"perf fn"; case t_md1Perf:return"perf m1"; case t_md2Perf:return "perf m2"; + #ifdef RT_WRAP + case t_funWrap:return"wrapped function"; case t_md1Wrap:return"wrapped 1-modifier"; case t_md2Wrap:return "wrapped 2-modifier"; #endif } } diff --git a/src/h.h b/src/h.h index 6caf1750..472bcde1 100644 --- a/src/h.h +++ b/src/h.h @@ -26,6 +26,7 @@ // #define FORMATTER // use self-hosted formatter for output // #define TIME // output runtime of every expression // #define RT_PERF // time runtime primitives +// #define RT_VERIFY // compare native and runtime versions of primitives // #define NO_COMP // don't load the compiler, instead execute src/interp; needed for ./precompiled.bqn @@ -42,6 +43,7 @@ #endif #define rtLen 63 + #ifdef CATCH_ERRORS #define PROPER_FILLS (EACH_FILLS&SFNS_FILLS) #else @@ -49,6 +51,12 @@ #define EACH_FILLS false #define PROPER_FILLS false #endif +#if defined(RT_PERF) || defined(RT_VERIFY) + #define RT_WRAP + #if defined(RT_PERF) && defined(RT_VERIFY) + #error "can't have both RT_PERF and RT_VERIFY" + #endif +#endif #define i8 int8_t #define u8 uint8_t @@ -127,8 +135,8 @@ enum Type { /*25*/ t_comp, t_block, t_body, t_scope, /*29*/ t_ns, t_nsDesc, t_fldAlias, t_hashmap, t_temp, /*34*/ t_freed, t_harrPartial, - #ifdef RT_PERF - /*36*/ t_funPerf, t_md1Perf, t_md2Perf, + #ifdef RT_WRAP + /*36*/ t_funWrap, t_md1Wrap, t_md2Wrap, #endif t_COUNT }; @@ -257,6 +265,7 @@ void printRaw(B x); // doesn't consume void print(B x); // doesn't consume void arr_print(B x); // doesn't consume bool equal(B w, B x); // doesn't consume +bool eequal(B w, B x); // doesn't consume i32 compare(B w, B x); // doesn't consume; -1 if wx, 0 if w≡x; 0==compare(NaN,NaN) bool atomEqual(B w, B x); // doesn't consume u64 depth(B x); // doesn't consume diff --git a/src/load.c b/src/load.c index 2faf19bb..e21413cc 100644 --- a/src/load.c +++ b/src/load.c @@ -14,7 +14,9 @@ FOR_PM2(F) #undef F TypeInfo ti[t_COUNT]; -B rtPerf_wrap(B x); // consumes +B r1Objs[rtLen]; +B rtWrap_wrap(B x); // consumes + _Thread_local B comp_currPath; _Thread_local B comp_currArgs; @@ -154,6 +156,9 @@ static inline void load_init() { rt_cell = rtObjGet(rtObjRaw, 45); gc_add(rt_cell); for (usz i = 0; i < rtLen; i++) { + #ifdef RT_WRAP + r1Objs[i] = rtObjGet(rtObjRaw, i); gc_add(r1Objs[i]); + #endif #ifdef ALL_R1 B r = rtObjGet(rtObjRaw, i); #else @@ -161,8 +166,8 @@ static inline void load_init() { #endif if (isNothing(r)) { printf("· in runtime!\n"); exit(1); } if (isVal(r)) v(r)->flags|= i+1; - #ifdef RT_PERF - r = rtPerf_wrap(r); + #ifdef RT_WRAP + r = rtWrap_wrap(r); if (isVal(r)) v(r)->flags|= i+1; #endif runtimeH.a[i] = r; @@ -272,7 +277,7 @@ static inline void base_init() { assert((MD1_TAG>>1) == (MD2_TAG>>1)); // just to be sure it isn't changed incorrectly, `isMd` depends on this } -#define FOR_INIT(F) F(base) F(harr) F(fillarr) F(i32arr) F(c32arr) F(f64arr) F(hash) F(fns) F(sfns) F(arith) F(sort) F(md1) F(md2) F(sysfn) F(derv) F(comp) F(rtPerf) F(ns) F(load) +#define FOR_INIT(F) F(base) F(harr) F(fillarr) F(i32arr) F(c32arr) F(f64arr) F(hash) F(fns) F(sfns) F(arith) F(sort) F(md1) F(md2) F(sysfn) F(derv) F(comp) F(rtWrap) F(ns) F(load) #define F(X) void X##_init(); FOR_INIT(F) #undef F diff --git a/src/main.c b/src/main.c index f6de2c38..c579040b 100644 --- a/src/main.c +++ b/src/main.c @@ -4,7 +4,7 @@ // TODO these are hacks around not needing tiny headers Block* bqn_comp(B str, B path, B args); -void rtPerf_print(); +void rtWrap_print(); int main(int argc, char* argv[]) { cbqn_init(); @@ -27,7 +27,7 @@ int main(int argc, char* argv[]) { #ifdef COMP_COMP_TIME gc_add(srcB); for (i32 i = 0; i < 100; i++) { dec(bqn_exec(inc(srcB), bi_N, bi_N)); gc_maybeGC(); } - rtPerf_print(); + rtWrap_print(); CTR_FOR(CTR_PRINT) exit(0); #endif @@ -155,7 +155,7 @@ int main(int argc, char* argv[]) { #ifdef HEAP_VERIFY heapVerify(); #endif - rtPerf_print(); + rtWrap_print(); CTR_FOR(CTR_PRINT) // printf("done\n");fflush(stdout); while(1); printAllocStats(); diff --git a/src/opt/single.c b/src/opt/single.c index f171ecc0..02bc1719 100644 --- a/src/opt/single.c +++ b/src/opt/single.c @@ -20,6 +20,6 @@ #include "../builtins/md2.c" #include "../vm.c" #include "../ns.c" -#include "../rtPerf.c" +#include "../rtwrap.c" #include "../load.c" #include "../main.c" diff --git a/src/rtPerf.c b/src/rtwrap.c similarity index 61% rename from src/rtPerf.c rename to src/rtwrap.c index f3211977..ae4d8a4e 100644 --- a/src/rtPerf.c +++ b/src/rtwrap.c @@ -1,13 +1,19 @@ #include "core.h" +#include "vm.h" -#ifdef RT_PERF + +#ifdef RT_WRAP typedef struct WFun WFun; struct WFun { struct Fun; - u64 c1t, c2t; - u32 c1a, c2a; B v; WFun* prev; + #ifdef RT_PERF + u64 c1t, c2t; + u32 c1a, c2a; + #else + B r1; + #endif }; WFun* lastWF; void wf_visit(Value* x) { mm_visit(((WFun*)x)->v); } @@ -15,33 +21,47 @@ B wf_identity(B x) { B f = c(WFun,x)->v; return inc(TI(f).identity(f)); } - +#define CHK(A,B) { if (!eequal(A,B)) { print(f); printf(": failed RT_VERIFY\n"); vm_pstLive(); exit(1); } } u64 fwTotal; B wf_c1(B t, B x) { WFun* c = c(WFun,t); B f = c->v; BB2B fi = c(Fun,f)->c1; - u64 s = nsTime(); - B r = fi(f, x); - u64 e = nsTime(); - c->c1a++; - c->c1t+= e-s; - fwTotal+= e-s+20; + #ifdef RT_PERF + u64 s = nsTime(); + B r = fi(f, x); + u64 e = nsTime(); + c->c1a++; + c->c1t+= e-s; + fwTotal+= e-s+20; + #else + B exp = c1(c->r1, inc(x)); + B r = fi(f, x); + CHK(exp, r); + dec(exp); + #endif return r; } B wf_c2(B t, B w, B x) { WFun* c = c(WFun,t); B f = c->v; BBB2B fi = c(Fun,f)->c2; - u64 s = nsTime(); - B r = fi(f, w, x); - u64 e = nsTime(); - c->c2a++; - c->c2t+= e-s; - fwTotal+= e-s+20; + #ifdef RT_PERF + u64 s = nsTime(); + B r = fi(f, w, x); + u64 e = nsTime(); + c->c2a++; + c->c2t+= e-s; + fwTotal+= e-s+20; + #else + B exp = c2(c->r1, inc(w), inc(x)); + B r = fi(f, w, x); + CHK(exp, r); + dec(exp); + #endif return r; } - +#undef CHK typedef struct WMd1 WMd1; struct WMd1 { @@ -126,22 +146,30 @@ B wm2_c2(B d, B w, B x) { B f = c(Md2D,d)->f; B g = c(Md2D,d)->g; B t = c(Md2D,d -B rtPerf_wrap(B t) { +B rtWrap_wrap(B t) { if (isFun(t)) { - B r = mm_alloc(sizeof(WFun), t_funPerf, ftag(FUN_TAG)); + #ifdef RT_VERIFY + if(v(t)->flags==0) return t; + #endif + B r = mm_alloc(sizeof(WFun), t_funWrap, ftag(FUN_TAG)); c(Value,r)->extra = v(t)->extra; c(Value,r)->flags = v(t)->flags; c(Fun,r)->c1 = wf_c1; c(Fun,r)->c2 = wf_c2; c(WFun,r)->v = t; c(WFun,r)->prev = lastWF; - c(WFun,r)->c1t = 0; c(WFun,r)->c1a = 0; - c(WFun,r)->c2t = 0; c(WFun,r)->c2a = 0; lastWF = c(WFun,r); + #ifdef RT_VERIFY + c(WFun,r)->r1 = r1Objs[v(t)->flags-1]; + #else + c(WFun,r)->c1t = 0; c(WFun,r)->c1a = 0; + c(WFun,r)->c2t = 0; c(WFun,r)->c2a = 0; + #endif return r; } + #ifdef RT_PERF if (isMd1(t)) { - B r = mm_alloc(sizeof(WMd1), t_md1Perf, ftag(MD1_TAG)); + B r = mm_alloc(sizeof(WMd1), t_md1Wrap, ftag(MD1_TAG)); c(Value,r)->extra = v(t)->extra; c(Value,r)->flags = v(t)->flags; c(Md1,r)->c1 = wm1_c1; @@ -155,7 +183,7 @@ B rtPerf_wrap(B t) { } if (isMd2(t)) { Md2* fc = c(Md2,t); - B r = mm_alloc(sizeof(WMd2), t_md2Perf, ftag(MD2_TAG)); + B r = mm_alloc(sizeof(WMd2), t_md2Wrap, ftag(MD2_TAG)); c(Md2,r)->c1 = wm2_c1; c(Md2,r)->c2 = wm2_c2; c(Md2,r)->extra = fc->extra; @@ -167,29 +195,10 @@ B rtPerf_wrap(B t) { lastWM2 = c(WMd2,r); return r; } + #endif return t; } -void rtPerf_print() { - WFun* cf = lastWF; - while (cf) { - printRaw(c1(bi_fmtF, tag(cf,FUN_TAG))); - printf(": m=%d %.3fms | d=%d %.3fms\n", cf->c1a, cf->c1t/1e6, cf->c2a, cf->c2t/1e6); - cf = cf->prev; - } - WMd1* cm1 = lastWM1; - while (cm1) { - printRaw(c1(bi_fmtF, tag(cm1,MD1_TAG))); - printf(": m=%d %.3fms | d=%d %.3fms\n", cm1->c1a, cm1->c1t/1e6, cm1->c2a, cm1->c2t/1e6); - cm1 = cm1->prev; - } - WMd2* cm2 = lastWM2; - while (cm2) { - printRaw(c1(bi_fmtF, tag(cm2,MD2_TAG))); - printf(": m=%d %.3fms | d=%d %.3fms\n", cm2->c1a, cm2->c1t/1e6, cm2->c2a, cm2->c2t/1e6); - cm2 = cm2->prev; - } -} B wfn_uc1(B t, B o, B x) { B t2 = c(WFun,t)->v; return TI(t2).fn_uc1(t2, o, x); } @@ -199,18 +208,39 @@ B wm1_ucw(B t, B o, B f, B w, B x) { B t2 = c(WMd1,t)->v; return TI(t2).m1_ B wm2_uc1(B t, B o, B f, B g, B x) { B t2 = c(WMd2,t)->v; return TI(t2).m2_uc1(t2, o, f, g, x); } B wm2_ucw(B t, B o, B f, B g, B w, B x) { B t2 = c(WMd2,t)->v; return TI(t2).m2_ucw(t2, o, f, g, w, x); } -void rtPerf_init() { - ti[t_funPerf].visit = wf_visit; ti[t_funPerf].identity = wf_identity; - ti[t_md1Perf].visit = wm1_visit; ti[t_md1Perf].m1_d = m_md1D; - ti[t_md2Perf].visit = wm2_visit; ti[t_md2Perf].m2_d = m_md2D; - ti[t_funPerf].fn_uc1 = wfn_uc1; - ti[t_funPerf].fn_ucw = wfn_ucw; - ti[t_md1Perf].m1_uc1 = wm1_uc1; - ti[t_md1Perf].m1_ucw = wm1_ucw; - ti[t_md2Perf].m2_uc1 = wm2_uc1; - ti[t_md2Perf].m2_ucw = wm2_ucw; +void rtWrap_init() { + ti[t_funWrap].visit = wf_visit; ti[t_funWrap].identity = wf_identity; + ti[t_md1Wrap].visit = wm1_visit; ti[t_md1Wrap].m1_d = m_md1D; + ti[t_md2Wrap].visit = wm2_visit; ti[t_md2Wrap].m2_d = m_md2D; + ti[t_funWrap].fn_uc1 = wfn_uc1; + ti[t_funWrap].fn_ucw = wfn_ucw; + ti[t_md1Wrap].m1_uc1 = wm1_uc1; + ti[t_md1Wrap].m1_ucw = wm1_ucw; + ti[t_md2Wrap].m2_uc1 = wm2_uc1; + ti[t_md2Wrap].m2_ucw = wm2_ucw; } #else -void rtPerf_init() { } -void rtPerf_print() { } +void rtWrap_init() { } #endif +void rtWrap_print() { + #ifdef RT_PERF + WFun* cf = lastWF; + while (cf) { + printRaw(c1(bi_fmtF, tag(cf,FUN_TAG))); + printf(": m=%d %.3fms | d=%d %.3fms\n", cf->c1a, cf->c1t/1e6, cf->c2a, cf->c2t/1e6); + cf = cf->prev; + } + WMd1* cm1 = lastWM1; + while (cm1) { + printRaw(c1(bi_fmtF, tag(cm1,MD1_TAG))); + printf(": m=%d %.3fms | d=%d %.3fms\n", cm1->c1a, cm1->c1t/1e6, cm1->c2a, cm1->c2t/1e6); + cm1 = cm1->prev; + } + WMd2* cm2 = lastWM2; + while (cm2) { + printRaw(c1(bi_fmtF, tag(cm2,MD2_TAG))); + printf(": m=%d %.3fms | d=%d %.3fms\n", cm2->c1a, cm2->c1t/1e6, cm2->c2a, cm2->c2t/1e6); + cm2 = cm2->prev; + } + #endif +} diff --git a/src/utils/hash.h b/src/utils/hash.h index 147d5d5f..06b9ff03 100644 --- a/src/utils/hash.h +++ b/src/utils/hash.h @@ -53,7 +53,7 @@ static u64 bqn_hashP(B x, const u64 secret[4]) { // bqn_hash but never zero #define EQUAL(A,B) equal(A,B) #define VALS #define VT i32 -#include "hashmap.h" +#include "hashmapTemplate.h" #define N(X) X##_Sb #define HT u64 @@ -65,4 +65,4 @@ static u64 bqn_hashP(B x, const u64 secret[4]) { // bqn_hash but never zero #define HDEF 0 #define KEYS #define EQUAL(A,B) equal(A,B) -#include "hashmap.h" +#include "hashmapTemplate.h" diff --git a/src/utils/hashmap.h b/src/utils/hashmapTemplate.h similarity index 100% rename from src/utils/hashmap.h rename to src/utils/hashmapTemplate.h diff --git a/src/vm.c b/src/vm.c index c6a991db..802b3f21 100644 --- a/src/vm.c +++ b/src/vm.c @@ -692,7 +692,7 @@ NOINLINE void vm_pst(Env* s, Env* e) { i--; } } -NOINLINE void vm_pstLive(Env* s, Env* e) { +NOINLINE void vm_pstLive() { vm_pst(envStart, envCurr); } diff --git a/src/vm.h b/src/vm.h index 4ce90334..f595c72d 100644 --- a/src/vm.h +++ b/src/vm.h @@ -47,8 +47,9 @@ typedef struct Env { union { i32* bcL; i32 bcV; }; } Env; -NOINLINE Block* compile(B bcq, B objs, B blocksq, B indices, B tokenInfo, B src); -NOINLINE void vm_pst(Env* s, Env* e); +Block* compile(B bcq, B objs, B blocksq, B indices, B tokenInfo, B src); +void vm_pst(Env* s, Env* e); +void vm_pstLive(); typedef struct FunBlock { struct Fun; Scope* sc; Block* bl; } FunBlock; typedef struct Md1Block { struct Md1; Scope* sc; Block* bl; } Md1Block;