stacktraces

This commit is contained in:
dzaima 2021-05-10 02:44:26 +03:00
parent 03cebc6aff
commit 2202200a6e
5 changed files with 140 additions and 45 deletions

View File

@ -242,6 +242,7 @@ jmp_buf* prepareCatch();
#endif
void popCatch();
B catchMessage;
u64 envPrevHeight; // envStart+prevEnvHeight will give the original envCurr

View File

@ -1,16 +1,16 @@
#include "h.h"
Block* load_compObj(B x) { // consumes
Block* load_compObj(B x, B src) { // consumes
BS2B xget = TI(x).get;
usz xia = a(x)->ia;
if (xia!=5 & xia!=3) thrM("load_compObj: bad item count");
Block* r = xia==5? compile(xget(x,0),xget(x,1),xget(x,2),xget(x,3),xget(x,4))
: compile(xget(x,0),xget(x,1),xget(x,2),bi_N,bi_N);
Block* r = xia==5? compile(xget(x,0),xget(x,1),xget(x,2),xget(x,3),xget(x,4), src)
: compile(xget(x,0),xget(x,1),xget(x,2),bi_N, bi_N, src);
dec(x);
return r;
}
Block* load_compImport(B bc, B objs, B blocks) { // consumes all
return compile(bc, objs, blocks, bi_N, bi_N);
return compile(bc, objs, blocks, bi_N, bi_N, bi_N);
}
B load_comp;
@ -25,7 +25,8 @@ B bqn_fmt(B x) { // consumes
Block* bqn_comp(B str) { // consumes
return load_compObj(c2(load_comp, inc(load_compArg), str));
inc(str);
return load_compObj(c2(load_comp, inc(load_compArg), str), str);
}
B bqn_exec(B str) { // consumes
Block* block = bqn_comp(str);

View File

@ -13,8 +13,9 @@
// #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 EACH_FILLS false // whether to try to squeeze out fills for ¨ and ⌜
#define SFNS_FILLS true // whether to insert fills for structural functions (∾, ≍, etc)
#define VM_POS false // whether to store detailed execution position information for stacktraces
#define EACH_FILLS false // whether to try to squeeze out fills for ¨ and ⌜
#define SFNS_FILLS true // whether to insert fills for structural functions (∾, ≍, etc)
#define FAKE_RUNTIME false // whether to disable the self-hosted runtime
// #define LOG_GC // log GC stats
@ -82,9 +83,8 @@ int main() {
while (CATCH) {
printf("caught: ");
print(catchMessage);
puts("");
printf("Error: "); print(catchMessage); putchar('\n');
vm_pst(envCurr, envStart+envPrevHeight);
dec(catchMessage);
}
while (true) { // exit by evaluating an empty expression

157
src/vm.c
View File

@ -3,7 +3,10 @@
// #define GS_REALLOC // whether to dynamically realloc gStack
#ifndef GS_SIZE
#define GS_SIZE 16384 // if !GS_REALLOC, size in number of B objects of the global object stack
#define GS_SIZE 65536 // if !GS_REALLOC, size in number of B objects of the global object stack
#endif
#ifndef ENV_SIZE
#define ENV_SIZE 4096 // max recursion depth; GS_SIZE and C stack size may limit this
#endif
enum {
@ -65,16 +68,11 @@ i32* nextBC(i32* p) {
}
i32 stackDiff(i32* p) {
switch(*p) {
case PUSH: case VARO: case VARM: case DFND: case LOCO: case LOCM: case LOCU: case SYSV:
return 1;
case CHKV: case VFYM: case FLDO: case FLDM: case RETD:
return 0;
case FN1C: case OP1D: case TR2D: case SETN: case SETU: case POPS: case FN1O: case OP2H: case SETH: case RETN:
return -1;
case FN2C: case OP2D: case TR3D: case SETM: case FN2O: case TR3O:
return -2;
case ARRO: case ARRM: case NSPM:
return 1-p[1];
case PUSH: case VARO: case VARM: case DFND: case LOCO: case LOCM: case LOCU: case SYSV: return 1;
case CHKV: case VFYM: case FLDO: case FLDM: case RETD: return 0;
case FN1C: case OP1D: case TR2D: case SETN: case SETU: case POPS: case FN1O: case OP2H: case SETH: case RETN: return -1;
case FN2C: case OP2D: case TR3D: case SETM: case FN2O: case TR3O: return -2;
case ARRO: case ARRM: case NSPM: return 1-p[1];
default: return 9999999;
}
}
@ -110,6 +108,8 @@ typedef struct Scope Scope;
typedef struct Comp {
struct Value;
B bc;
B src;
B indices;
HArr* objs;
u32 blockAm;
Block* blocks[];
@ -142,9 +142,6 @@ struct Scope {
Scope* psc;
Body* body;
u16 varAm;
#ifdef DEBUG
u64 bcInd; // DEBUG: place of this in bytecode array
#endif
B vars[];
};
@ -153,7 +150,7 @@ typedef struct Md1Block { struct Md1; Scope* sc; Block* bl; } Md1Block;
typedef struct Md2Block { struct Md2; Scope* sc; Block* bl; } Md2Block;
Block* compile(B bcq, B objs, B blocksq, B indices, B tokenInfo) { // consumes all
Block* compile(B bcq, B objs, B blocksq, B indices, B tokenInfo, B src) { // consumes all
HArr* blocksH = toHArr(blocksq);
usz bam = blocksH->ia;
@ -165,10 +162,11 @@ Block* compile(B bcq, B objs, B blocksq, B indices, B tokenInfo) { // consumes a
usz bcl = bca->ia;
Comp* comp = mm_allocN(fsizeof(Comp,blocks,Block*,bam), t_comp);
comp->bc = tag(bca, ARR_TAG);
comp->indices = indices;
comp->src = src;
comp->objs = toHArr(objs);
comp->blockAm = bam;
B* blockDefs = blocksH->a;
dec(indices);
B nameList;
if (isNothing(tokenInfo)) {
nameList = bi_emptyHVec;
@ -176,6 +174,15 @@ Block* compile(B bcq, B objs, B blocksq, B indices, B tokenInfo) { // consumes a
B t = TI(tokenInfo).getU(tokenInfo,2);
nameList = TI(t).getU(t,0);
}
if (!isNothing(src) && !isNothing(indices)) {
if (isAtm(indices) || rnk(indices)!=1 || a(indices)->ia!=2) thrM("compile: bad indices");
for (i32 i = 0; i < 2; i++) {
B ind = TI(indices).getU(indices,i);
if (isAtm(ind) || rnk(ind)!=1 || a(ind)->ia!=bcl) thrM("compile: bad indices");
BS2B indGetU = TI(ind).getU;
for (usz j = 0; j < bcl; j++) o2i(indGetU(ind,j));
}
}
for (usz i = 0; i < bam; i++) {
B cbld = blockDefs[i];
@ -290,10 +297,12 @@ i32* vmStack;
i32 bcCtr = 0;
#endif
B* gStack; // points to after end
B* gStackStart;
B* gStackEnd;
void gsReserve(u64 am) {
#ifdef GS_REALLOC
if (am>gStackEnd-gStack) {
@ -311,7 +320,6 @@ void gsReserve(u64 am) {
NOINLINE
#endif
void gsReserveR(u64 am) { gsReserve(am); }
void gsAdd(B x) {
#ifdef GS_REALLOC
if (gStack==gStackEnd) gsReserveR(1);
@ -335,6 +343,32 @@ void gsPrint() {
}
}
typedef struct Env {
Scope* sc;
union { i32** bcP; i32* bcL; i32 bcV; };
} Env;
Env* envCurr;
Env* envStart;
Env* envEnd;
static inline void pushEnv(Scope* sc, i32** bc) {
if (envCurr==envEnd) thrM("stack overflow");
envCurr->sc = sc;
#if VM_POS
envCurr->bcP = bc;
#else
envCurr->bcL = *bc;
#endif
envCurr++;
}
static inline void popEnv() {
assert(envCurr>envStart);
envCurr--;
}
B evalBC(Body* b, Scope* sc) { // doesn't consume
#ifdef DEBUG_VM
bcDepth+= 2;
@ -346,6 +380,7 @@ B evalBC(Body* b, Scope* sc) { // doesn't consume
B* objs = b->comp->objs->a;
Block** blocks = b->comp->blocks;
i32* bc = b->bc;
pushEnv(sc, &bc);
gsReserve(b->maxStack);
Scope* pscs[b->maxPSC+1];
pscs[0] = sc;
@ -490,6 +525,7 @@ B evalBC(Body* b, Scope* sc) { // doesn't consume
#endif
B r = POP;
GS_UPD;
popEnv();
return r;
#undef P
#undef ADD
@ -504,9 +540,6 @@ B actualExec(Block* bl, Scope* psc, i32 ga, B* svar) { // consumes svar contents
sc->psc = psc; if(psc) ptr_inc(psc);
u16 varAm = sc->varAm = body->varAm;
assert(varAm>=ga);
#ifdef DEBUG
sc->bcInd = body->bc-c(I32Arr,body->comp->bc)->a;
#endif
i32 i = 0;
while (i<ga) { sc->vars[i] = svar[i]; i++; }
while (i<varAm) sc->vars[i++] = bi_noVar;
@ -549,8 +582,7 @@ B m_md2Block(Block* bl, Scope* psc) {
void comp_free(B x) {
Comp* c = c(Comp, x);
ptr_decR(c->objs);
decR(c->bc);
ptr_decR(c->objs); decR(c->bc); decR(c->src); decR(c->indices);
u32 am = c->blockAm; for(u32 i = 0; i < am; i++) ptr_dec(c->blocks[i]);
}
void scope_free(B x) {
@ -568,7 +600,7 @@ void md2Bl_free(B x) { Md2Block* c = c(Md2Block,x); ptr_decR(c->sc); ptr_decR(c-
void comp_visit(B x) {
Comp* c = c(Comp,x);
mm_visitP(c->objs); mm_visit(c->bc);
mm_visitP(c->objs); mm_visit(c->bc); mm_visit(c->src); mm_visit(c->indices);
u32 am = c->blockAm; for(u32 i = 0; i < am; i++) mm_visitP(c->blocks[i]);
}
void scope_visit(B x) {
@ -604,6 +636,15 @@ B block_decompose(B x) { return m_v2(m_i32(1), x); }
B bl_m1d(B m, B f ) { Md1Block* c = c(Md1Block,m); return c->bl->imm? actualExec(c(Md1Block, m)->bl, c(Md1Block, m)->sc, 2, (B[]){m, f }) : m_md1D(m,f ); }
B bl_m2d(B m, B f, B g) { Md2Block* c = c(Md2Block,m); return c->bl->imm? actualExec(c(Md2Block, m)->bl, c(Md2Block, m)->sc, 3, (B[]){m, f, g}) : m_md2D(m,f,g); }
void allocStack(void** curr, void** start, void** end, i32 elSize, i32 count) {
usz pageSize = sysconf(_SC_PAGESIZE);
u64 sz = (elSize*count + pageSize-1)/pageSize * pageSize;
assert(sz%elSize == 0);
*curr = *start = mmap(NULL, sz+pageSize, PROT_READ|PROT_WRITE, MAP_NORESERVE|MAP_PRIVATE|MAP_ANON, -1, 0);
*end = ((char*)*start)+sz;
mprotect(*end, pageSize, PROT_NONE); // idk first way i found to force erroring on overflow
}
static inline void comp_init() {
ti[t_comp ].free = comp_free; ti[t_comp ].visit = comp_visit; ti[t_comp ].print = comp_print;
ti[t_body ].free = body_free; ti[t_body ].visit = body_visit; ti[t_body ].print = body_print;
@ -613,12 +654,9 @@ static inline void comp_init() {
ti[t_md1_block].free = md1Bl_free; ti[t_md1_block].visit = md1Bl_visit; ti[t_md1_block].print = md1Bl_print; ti[t_md1_block].decompose = block_decompose; ti[t_md1_block].m1_d=bl_m1d;
ti[t_md2_block].free = md2Bl_free; ti[t_md2_block].visit = md2Bl_visit; ti[t_md2_block].print = md2Bl_print; ti[t_md2_block].decompose = block_decompose; ti[t_md2_block].m2_d=bl_m2d;
#ifndef GS_REALLOC
usz pageSize = sysconf(_SC_PAGESIZE);
u64 sz = (GS_SIZE + pageSize-1)/pageSize * pageSize;
gStack = gStackStart = mmap(NULL, sz*sizeof(B)+pageSize, PROT_READ|PROT_WRITE, MAP_NORESERVE|MAP_PRIVATE|MAP_ANON, -1, 0);
gStackEnd = gStackStart+sz;
mprotect(gStackEnd, pageSize, PROT_NONE); // idk first way i found to force erroring on overflow
allocStack((void**)&gStack, (void**)&gStackStart, (void**)&gStackEnd, sizeof(B), GS_SIZE);
#endif
allocStack((void**)&envCurr, (void**)&envStart, (void**)&envEnd, sizeof(Env), ENV_SIZE);
}
void print_vmStack() {
@ -633,7 +671,8 @@ void print_vmStack() {
typedef struct CatchFrame {
jmp_buf jmp;
u64 gStackDepth;
u64 gsDepth;
u64 envDepth;
u64 cfDepth;
} CatchFrame;
CatchFrame* cf; // points to after end
@ -650,7 +689,8 @@ jmp_buf* prepareCatch() { // in the case of returning false, must call popCatch(
cfEnd = cfStart+n;
}
cf->cfDepth = cf-cfStart;
cf->gStackDepth = gStack-gStackStart;
cf->gsDepth = gStack-gStackStart;
cf->envDepth = envCurr-envStart;
return &(cf++)->jmp;
}
void popCatch() {
@ -665,9 +705,20 @@ NOINLINE void thr(B msg) {
catchMessage = msg;
cf--;
B* gStackNew = gStackStart + cf->gStackDepth;
if (gStackNew>gStack) err("bad catch gStackDepth");
B* gStackNew = gStackStart + cf->gsDepth;
assert(gStackNew<=gStack);
while (gStack!=gStackNew) dec(*--gStack);
envPrevHeight = envCurr-envStart;
Env* envNew = envStart + cf->envDepth;
assert(envNew<=envCurr);
while (envCurr!=envNew) {
envCurr--;
#if VM_POS
envCurr->bcV = *envCurr->bcP - i32arr_ptr(envCurr->sc->body->comp->bc) - 1;
#else
envCurr->bcV = envCurr->bcL - i32arr_ptr(envCurr->sc->body->comp->bc);
#endif
}
if (cfStart+cf->cfDepth > cf) err("bad catch cfDepth");
cf = cfStart+cf->cfDepth;
@ -687,3 +738,43 @@ NOINLINE void thr(B msg) {
NOINLINE void thrM(char* s) {
thr(fromUTF8(s, strlen(s)));
}
NOINLINE void vm_pst(Env* s, Env* e) {
assert(s<=e);
i64 l = e-s;
i64 i = l-1;
while (i>=0) {
Env* c = s+i;
if (l>30 && i==l-10) {
printf("(%ld entries omitted)\n", l-20);
i = 10;
}
i32 bcPos = c->bcV;
Comp* comp = c->sc->body->comp;
B src = comp->src;
if (!isNothing(src) && !isNothing(comp->indices)) {
B inds = TI(comp->indices).getU(comp->indices, 0); usz cs = o2s(TI(inds).getU(inds,bcPos));
B inde = TI(comp->indices).getU(comp->indices, 1); usz ce = o2s(TI(inde).getU(inde,bcPos))+1;
int start = printf("%ld: ", i);
usz srcL = a(src)->ia;
BS2B srcGetU = TI(src).getU;
usz srcS = cs;
while (srcS>0 && o2cu(srcGetU(src,srcS-1))!='\n') srcS--;
usz srcE = srcS;
while (srcE<srcL) { u32 chr = o2cu(srcGetU(src, srcE)); if(chr=='\n')break; printUTF8(chr); srcE++; }
if (ce>srcE) ce = srcE;
cs-= srcS;
ce-= srcS;
putchar('\n');
for (i32 i = 0; i < cs+start; i++) putchar(' ');
for (i32 i = cs; i < ce; i++) putchar('^');
putchar('\n');
// printf(" inds:%d…%d\n", cinds, cinde);
} else {
printf("%ld: source unknown\n", i);
}
i--;
}
}

View File

@ -12,11 +12,13 @@ tests ← •FLines path∾"/test/cases/"∾case
tests/˜ (('#')0<)¨tests
{𝕊: tests/˜'!'¨tests} 's'opt
{𝕊: tests/˜'!'=¨tests} 'e'opt
verify 'v'opt
RMC '#' # doesn't work for token.bqn but whatever
Q {
i𝕩'%'
(('!'𝕩)'v'opt)(1+i) {"(" i𝕩 ") ≡ {" ('#') (1+i)𝕩 " }"} 𝕩
((1+'!'𝕩)×verify)(1+i) { """should've errored!""∘•BQN⎊1 """ {𝕩/˜1+𝕩='"'}𝕩˜1+𝕩'%' """"} {"""incorrect!""‿1⊑˜(" i𝕩 ") ≡ {" (1+i)𝕩 " }"} RMC 𝕩
}
Out {•Out'%'Q𝕩}
Out {•Out'%'(verify{"""incorrect!""‿1⊑˜1≡{" RMC 𝕩 " }"})Q𝕩}
(('m'opt)Out{•Show𝕩Out𝕩})¨tests
# ('%'⊸∊∨ '#'= ·⊑ ∾⟜"#")◶•Out‿@¨tests