From 9631702b14b743b1dec033b185900e74f37ec60b Mon Sep 17 00:00:00 2001 From: dzaima Date: Tue, 28 Sep 2021 18:08:51 +0300 Subject: [PATCH] PRED --- src/jit/nvm_x86_64.c | 41 ++++++++++++++++----- src/vm.c | 88 +++++++++++++++++++++++--------------------- src/vm.h | 3 +- 3 files changed, 80 insertions(+), 52 deletions(-) diff --git a/src/jit/nvm_x86_64.c b/src/jit/nvm_x86_64.c index 078adb0b..803dc7ea 100644 --- a/src/jit/nvm_x86_64.c +++ b/src/jit/nvm_x86_64.c @@ -166,11 +166,7 @@ INS B i_SETC(B s, B f, Scope** pscs, u32* bc) { POS_UPD; v_set(pscs, s, r, true); dec(s); return r; } -INS B i_SETH(B s, B x, Scope** pscs, u32* bc, Body* v1, Body* v2) { POS_UPD; - bool ok = v_seth(pscs, s, x); dec(x); dec(s); - if (ok) return bi_okHdr; - Scope* sc = pscs[0]; - Body* body = q_N(sc->vars[2])? v1 : v2; +FORCE_INLINE B gotoNextBodyJIT(Scope* sc, Body* body) { if (body==NULL) thrF("No header matched argument%S", q_N(sc->vars[2])?"":"s"); Block* bl = body->bl; // because of nvm returning semantics, we cannot quick-skip to the next body. TODO maybe we can by moving tail of evalJIT into i_RETN etc and some magic™ here? @@ -179,6 +175,19 @@ INS B i_SETH(B s, B x, Scope** pscs, u32* bc, Body* v1, Body* v2) { POS_UPD; Scope* nsc = m_scope(body, sc->psc, body->varAm, ga, sc->vars); return execBodyInlineI(bl, body, nsc); } +INS B i_SETH(B s, B x, Scope** pscs, u32* bc, Body* v1, Body* v2) { POS_UPD; + bool ok = v_seth(pscs, s, x); dec(x); dec(s); + if (ok) return bi_okHdr; + return gotoNextBodyJIT(pscs[0], q_N(pscs[0]->vars[2])? v1 : v2); +} +INS B i_PRED1(B x, Scope* sc, u32* bc, Body* v) { POS_UPD; + if (o2b(x)) return bi_okHdr; + return gotoNextBodyJIT(sc, v); +} +INS B i_PRED2(B x, Scope* sc, u32* bc, Body* v1, Body* v2) { POS_UPD; + if (o2b(x)) return bi_okHdr; + return gotoNextBodyJIT(sc, q_N(sc->vars[2])? v1 : v2); +} INS B i_SETNi( B x, Scope* sc, u32 p, u32* bc) { POS_UPD; v_setI(sc, p, inc(x), false); return x; } INS B i_SETUi( B x, Scope* sc, u32 p, u32* bc) { POS_UPD; v_setI(sc, p, inc(x), true ); return x; } INS B i_SETMi(B f, B x, Scope* sc, u32 p, u32* bc) { POS_UPD; B r = c2(f,v_getI(sc, p),x); dec(f); v_setI(sc, p, inc(r), true); return r; } @@ -484,7 +493,7 @@ static u32 readBytes4(u8* d) { file_wChars(m_str8l("asm_off"), o); dec(o); B s = emptyCVec(); #define F(X) AFMT("s/%p$/%p # i_" #X "/;", i_##X, i_##X); - F(POPS) F(INC) F(FN1C) F(FN1O) F(FN2C) F(FN2O) F(FN1Oi) F(FN2Oi) F(ARR_0) F(ARR_p) F(DFND_0) F(DFND_1) F(DFND_2) F(MD1C) F(MD2C) F(MD2R) F(TR2D) F(TR3D) F(TR3O) F(EXTO) F(EXTU) F(SETN) F(SETU) F(SETM) F(FLDO) F(ALIM) F(RETD) F(SETNi) F(SETUi) F(SETMi) + F(POPS)F(INC)F(FN1C)F(FN1O)F(FN2C)F(FN2O)F(FN1Oi)F(FN2Oi)F(ARR_0)F(ARR_2)F(ARR_p)F(DFND_0)F(DFND_1)F(DFND_2)F(MD1C)F(MD2C)F(MD2R)F(TR2D)F(TR3D)F(TR3O)F(NOVAR)F(EXTO)F(EXTU)F(SETN)F(SETU)F(SETM)F(SETC)F(SETH)F(PRED1)F(PRED2)F(SETNi)F(SETUi)F(SETMi)F(SETCi)F(SETNv)F(SETUv)F(SETMv)F(SETCv)F(FLDO)F(VFYM)F(ALIM)F(CHKV)F(FAIL)F(RETD) #undef F file_wChars(m_str8l("asm_sed"), s); dec(s); } @@ -579,7 +588,7 @@ Nvm_res m_nvm(Body* body) { #define POS_UPD(R1,R2) MOV4moi(r_ENV, offsetof(Env,pos), body->bl->map[bcpos + bodyOff]<<1 | 1); #define GS_SET(R) MOV8pr(&gStack, R) #define GET(R,P,U) { i32 p = SPOSq(-(P)); if (U && lGPos!=p) { Reg t=LEA0(R,r_CS,p,0); GS_SET(t); lGPos=p; if(U!=2) MOV8rm(R,t); } else { MOV8rmo(R, r_CS, p); } } - // use GET(R_A1,0,2); as GS_UPD when there's only one argument, and GET(R_A3,-1,2); when there are zero arguments (i think?) + // use GET(R_A1,0,2); as GS_UPD when there's one argument, and GET(R_A3,-1,2); when there are zero arguments (i think?) #define NORES(D) if (depth>D) MOV8rm(R_RES, SPOS(R_A3, -D, 0)); // call at end if rax is unset; arg is removed stack item count switch (*bc++) { case POPS: TOPp; @@ -631,9 +640,21 @@ Nvm_res m_nvm(Body* body) { // case VARU: TOPs; { u64 d=*bc++; IMM(R_A0,*bc++); LSC(R_A1,d); CCALL(i_VARU); } break; // (u32 p, Scope* sc) case EXTO: TOPs; { u64 d=*bc++; IMM(R_A0,*bc++); LSC(R_A1,d); IMM(R_A2,off); INV(3,1,i_EXTO); } break; // (u32 p, Scope* sc, u32* bc, S) case EXTU: TOPs; { u64 d=*bc++; IMM(R_A0,*bc++); LSC(R_A1,d); CCALL(i_EXTU); } break; // (u32 p, Scope* sc) - case SETHi:TOPp; { u64 v1=L64; u64 v2=L64; // (B s, B x, Scope** pscs, u32* bc, Body* v1, Body* v2) - if (lGPos!=0) GS_SET(r_CS); lGPos=0; IMM(R_A4,v1); IMM(R_A5,v2); - /**/ GET(R_A1,1,1); LEAi(R_A2,R_SP,VAR(pscs,0)); IMM(R_A3,off); CCALL(i_SETH); IMM(R_A0, bi_okHdr.u); CMP(R_A0,R_RES); JNE4(l); TSADD(retLbls, l); break; } + case SETHi:TOPp; { u64 v1=L64; u64 v2=L64; if (lGPos!=0) GS_SET(r_CS); lGPos=0; + GET(R_A1,1,1); LEAi(R_A2,R_SP,VAR(pscs,0)); IMM(R_A3,off); IMM(R_A4,v1); IMM(R_A5,v2); CCALL(i_SETH); // (B s, B x, Scope** pscs, u32* bc, Body* v1, Body* v2) + IMM(R_A0, bi_okHdr.u); CMP(R_A0,R_RES); JNE4(l); TSADD(retLbls, l); + break; + } + case PRED1:TOPp; { u64 v1=L64; + GET(R_A1,0,2); MOV(R_A1,r_SC); IMM(R_A2,off); IMM(R_A3,v1); CCALL(i_PRED1); // (B x, Scope** pscs, u32* bc, Body* v) + IMM(R_A0, bi_okHdr.u); CMP(R_A0,R_RES); JNE4(l); TSADD(retLbls, l); NORES(1); + break; + } + case PRED2:TOPp; { u64 v1=L64; u64 v2=L64; + GET(R_A1,0,2); MOV(R_A1,r_SC); IMM(R_A2,off); IMM(R_A3,v1); IMM(R_A4,v2); CCALL(i_PRED2); // (B x, Scope** pscs, u32* bc, Body* v1, Body* v2) + IMM(R_A0, bi_okHdr.u); CMP(R_A0,R_RES); JNE4(l); TSADD(retLbls, l); NORES(1); + break; + } case SETN: TOPp; GET(R_A1,1,1); LEAi(R_A2,R_SP,VAR(pscs,0)); IMM(R_A3,off); CCALL(i_SETN); break; // (B s, B x, Scope** pscs, u32* bc) case SETU: TOPp; GET(R_A1,1,1); LEAi(R_A2,R_SP,VAR(pscs,0)); IMM(R_A3,off); CCALL(i_SETU); break; // (B s, B x, Scope** pscs, u32* bc) case SETM: TOPp; GET(R_A1,1,1); GET(R_A2,2,1); LEAi(R_A3,R_SP,VAR(pscs,0)); IMM(R_A4,off); CCALL(i_SETM); break; // (B s, B f, B x, Scope** pscs, u32* bc) diff --git a/src/vm.c b/src/vm.c index 2bbd9260..02731c67 100644 --- a/src/vm.c +++ b/src/vm.c @@ -12,7 +12,7 @@ #define FOR_BC(F) F(PUSH) F(DYNO) F(DYNM) F(ARRO) F(ARRM) F(FN1C) F(FN2C) F(MD1C) F(MD2C) F(TR2D) \ F(TR3D) F(SETN) F(SETU) F(SETM) F(SETC) F(POPS) F(DFND) F(FN1O) F(FN2O) F(CHKV) F(TR3O) \ - F(MD2R) F(VARO) F(VARM) F(VFYM) F(SETH) F(RETN) F(FLDO) F(FLDM) F(ALIM) F(RETD) F(SYSV) F(VARU) \ + F(MD2R) F(VARO) F(VARM) F(VFYM) F(SETH) F(RETN) F(FLDO) F(FLDM) F(ALIM) F(RETD) F(SYSV) F(VARU) F(PRED) F(PRED1) F(PRED2) \ F(EXTO) F(EXTM) F(EXTU) F(ADDI) F(ADDU) F(FN1Ci)F(FN1Oi)F(FN2Ci)F(FN2Oi) \ F(SETNi)F(SETUi)F(SETMi)F(SETCi)F(SETNv)F(SETUv)F(SETMv)F(SETCv)F(FAIL) @@ -24,7 +24,7 @@ u32* nextBC(u32* p) { case TR2D: case TR3D: case TR3O: case SETN: case SETU: case SETM: case SETH: case SETC: case POPS: case CHKV: case VFYM: case RETN: case RETD: - case FAIL: + case FAIL: case PRED: off = 1; break; case PUSH: case DFND: case ARRO: case ARRM: case DYNO: case DYNM: case FLDO: case FLDM: @@ -35,9 +35,9 @@ u32* nextBC(u32* p) { case ADDI: case ADDU: case FN1Ci: case FN1Oi: case FN2Ci: case DFND0: case DFND1: case DFND2: case SETNi: case SETUi: case SETMi: case SETCi: - case SETNv: case SETUv: case SETMv: case SETCv: + case SETNv: case SETUv: case SETMv: case SETCv: case PRED1: off = 3; break; - case FN2Oi: case SETHi: + case FN2Oi: case SETHi: case PRED2: off = 5; break; } return p+off; @@ -48,7 +48,7 @@ i32 stackDiff(u32* p) { case PUSH: case DYNO: case DYNM: case DFND: case VARO: case VARM: case DFND0:case DFND1:case DFND2: case VARU: case EXTO: case EXTM: case EXTU: case SYSV: case ADDI: case ADDU: return 1; case FN1Ci:case FN1Oi:case CHKV: case VFYM: case FLDO: case FLDM: case RETD: case ALIM: return 0; - case FN2Ci:case FN2Oi:case FN1C: case FN1O: case MD1C: case TR2D: case POPS: case MD2R: case RETN: return -1; + case FN2Ci:case FN2Oi:case FN1C: case FN1O: case MD1C: case TR2D: case POPS: case MD2R: case RETN: case PRED: case PRED1: case PRED2: return -1; case MD2C: case TR3D: case FN2C: case FN2O: case TR3O: case SETH: case SETHi:return -2; case SETN: return -1; case SETNi:return 0; case SETNv:return -1; @@ -64,8 +64,8 @@ i32 stackConsumed(u32* p) { case PUSH: case DYNO: case DYNM: case DFND: case VARO: case VARM: case VARU: case EXTO: case EXTM: case EXTU: case SYSV: case ADDI: case ADDU: case DFND0:case DFND1:case DFND2:return 0; case CHKV: case RETD: return 0; - case FN1Ci:case FN1Oi:case FLDO: case FLDM: case ALIM: case RETN: case POPS: case VFYM: return 1; - case FN2Ci:case FN2Oi:case FN1C: case FN1O: case MD1C: case TR2D: case MD2R: case SETH: case SETHi:return 2; + case FN1Ci:case FN1Oi:case FLDO: case FLDM: case ALIM: case RETN: case POPS: case PRED: case PRED1: case PRED2: case VFYM: return 1; + case FN2Ci:case FN2Oi:case FN1C: case FN1O: case MD1C: case TR2D: case MD2R: case SETH: case SETHi: return 2; case MD2C: case TR3D: case FN2C: case FN2O: case TR3O: return 3; case SETN: return 2; case SETNi: case SETNv: return 1; @@ -155,11 +155,11 @@ static Body* m_body(i32 vam, i32 pos, u16 maxStack, u16 maxPSC) { // leaves varI body->varAm = (u16)vam; return body; } -typedef struct SETHRequest { +typedef struct NextRequest { u32 off; // offset into bytecode where the two integers must be inserted u32 pos1; // offset into bodyI/bodyMap of what's wanted for monadic - u32 pos2; // ↑ for dyadic -} SETHRequest; + u32 pos2; // ↑ for dyadic; U32_MAX if not wanted +} NextRequest; Block* compileBlock(B block, Comp* comp, bool* bDone, u32* bc, usz bcIA, B allBlocks, B allBodies, B nameList, Scope* sc, i32 depth) { usz blIA = a(block)->ia; if (blIA!=3) thrM("VM compiler: Bad block info size"); @@ -170,11 +170,12 @@ Block* compileBlock(B block, Comp* comp, bool* bDone, u32* bc, usz bcIA, B allBl i32* bodyI; i32 bodyAm1, bodyAm2, bodyILen; if (isArr(bodyObj)) { - if (a(bodyObj)->ia!=2) thrM("VM compiler: Unexpected body list length"); + usz boia = a(bodyObj)->ia; + if (boia!=1 && boia!=2) thrM("VM compiler: Unexpected body list length"); // print(bodyObj); putchar('\n'); SGetU(bodyObj) - B b1 = GetU(bodyObj,0); - B b2 = GetU(bodyObj,1); + B b1 = GetU(bodyObj,0); + B b2 = boia==1? b1 : GetU(bodyObj,1); if (!isArr(b1) || !isArr(b2)) thrM("VM compiler: Body list contained non-arrays"); bodyAm1 = a(b1)->ia; SGetU(b1) bodyAm2 = a(b2)->ia; SGetU(b2) @@ -199,7 +200,7 @@ Block* compileBlock(B block, Comp* comp, bool* bDone, u32* bc, usz bcIA, B allBl TSALLOC(Block*, usedBlocks, 2); // list of blocks to be referenced by DFND, stored in result->blocks TSALLOC(Body*, bodies, 2); // list of bodies of this block TALLOC(Body*, bodyMap, bodyILen+2); // map from index in bodyI to the corresponding body - TSALLOC(SETHRequest, sethReqs, 10); // list of SETHi's to fill out when bodyMap is complete + TSALLOC(NextRequest, bodyReqs, 10); // list of SETH/PRED-s to fill out when bodyMap is complete i32 pos1 = 0; // pos1 and pos2 always stay valid indexes in bodyI because bodyI is padded with -1s i32 pos2 = bodyAm1+1; @@ -314,10 +315,11 @@ Block* compileBlock(B block, Comp* comp, bool* bDone, u32* bc, usz bcIA, B allBl TSADD(newBC, cpos); break; } - case SETH: - TSADD(newBC, SETHi); - TSADD(sethReqs, ((SETHRequest){.off = TSSIZE(newBC), .pos1 = pos1, .pos2 = pos2})); - A64(0); A64(0); // to be filled in by later sethReqs handling + case SETH: case PRED: + if (mpsc<1) mpsc=1; // SETH and PRED may want to have a parent scope pointer + TSADD(newBC, *c==SETH? SETHi : imm? PRED1 : PRED2); + TSADD(bodyReqs, ((NextRequest){.off = TSSIZE(newBC), .pos1 = pos1, .pos2 = imm? U32_MAX : pos2})); + A64(0); if(!imm)A64(0); // to be filled in by later bodyReqs handling break; default: { u32* ccpy = c; @@ -349,13 +351,13 @@ Block* compileBlock(B block, Comp* comp, bool* bDone, u32* bc, usz bcIA, B allBl if (is1) bodyMap[pos1-1] = body; if (is2) bodyMap[pos2-1] = body; } - u64 sethReqAm = TSSIZE(sethReqs); - for (u64 i = 0; i < sethReqAm; i++) { - SETHRequest r = sethReqs[i]; - u64 v1 = (u64)bodyMap[r.pos1]; newBC[r.off+0] = (u32)v1; newBC[r.off+1] = v1>>32; - u64 v2 = (u64)bodyMap[r.pos2]; newBC[r.off+2] = (u32)v2; newBC[r.off+3] = v2>>32; + u64 bodyReqAm = TSSIZE(bodyReqs); + for (u64 i = 0; i < bodyReqAm; i++) { + NextRequest r = bodyReqs[i]; + /*ugly, but whatever*/ u64 v1 = (u64)bodyMap[r.pos1]; newBC[r.off+0] = (u32)v1; newBC[r.off+1] = v1>>32; + if (r.pos2!=U32_MAX) { u64 v2 = (u64)bodyMap[r.pos2]; newBC[r.off+2] = (u32)v2; newBC[r.off+3] = v2>>32; } } - TSFREE(sethReqs); + TSFREE(bodyReqs); TFREE(bodyMap); TFREE(bodyI); @@ -528,9 +530,18 @@ NOINLINE B v_getR(Scope* pscs[], B s) { } } - - -FORCE_INLINE B execBodyInlineI(Block* block, Body* body, Scope* sc); +FORCE_INLINE B gotoNextBody(Block* bl, Scope* sc, Body* body) { + if (body==NULL) thrF("No header matched argument%S", q_N(sc->vars[2])?"":"s"); + + popEnv(); + + i32 ga = blockGivenVars(bl); + + for (u64 i = 0; i < ga; i++) inc(sc->vars[i]); + Scope* nsc = m_scope(body, sc->psc, body->varAm, ga, sc->vars); + scope_dec(sc); + return execBodyInlineI(bl, body, nsc); +} #ifdef DEBUG_VM i32 bcDepth=-2; @@ -711,20 +722,15 @@ B evalBC(Block* bl, Body* b, Scope* sc) { // doesn't consume } case SETHi:{ P(s) P(x) GS_UPD; POS_UPD; u64 v1 = L64; u64 v2 = L64; bool ok = v_seth(pscs, s, x); dec(x); dec(s); - if (!ok) { - Body* body = (Body*)(q_N(sc->vars[2])? v1 : v2); - if (body==NULL) thrF("No header matched argument%S", q_N(sc->vars[2])?"":"s"); - - GS_UPD; - popEnv(); - - i32 ga = blockGivenVars(bl); - - for (u64 i = 0; i < ga; i++) inc(sc->vars[i]); - Scope* nsc = m_scope(body, sc->psc, body->varAm, ga, sc->vars); - scope_dec(sc); - return execBodyInlineI(bl, body, nsc); - } + if (!ok) { GS_UPD; return gotoNextBody(bl, sc, (Body*)(q_N(sc->vars[2])? v1 : v2)); } + break; + } + case PRED1:{ P(x) GS_UPD; POS_UPD; u64 v1 = L64; + if (!o2b(x)) { GS_UPD; return gotoNextBody(bl, sc, (Body*)v1); } + break; + } + case PRED2:{ P(x) GS_UPD; POS_UPD; u64 v1 = L64; u64 v2 = L64; + if (!o2b(x)) { GS_UPD; return gotoNextBody(bl, sc, (Body*)(q_N(sc->vars[2])? v1 : v2)); } break; } case FLDO: { P(ns) GS_UPD; u32 p = *bc++; POS_UPD; diff --git a/src/vm.h b/src/vm.h index bf56cce3..fff6f999 100644 --- a/src/vm.h +++ b/src/vm.h @@ -41,6 +41,7 @@ enum { DYNO = 0x26, // N; push variable with name objs[N] DYNM = 0x27, // N; push mutable variable with name objs[N] + PRED = 0x2A, // pop item, go to next body if 0, continue if 1 VFYM = 0x2B, // push a mutable version of ToS that fails if set to a non-equal value (for header assignment) SETH = 0x2F, // set header; acts like SETN, but it doesn't push to stack, and, instead of erroring in cases it would, it skips to the next body SETN = 0x30, // set new; _ ←_; ⟨…,x, mut⟩ → mut←x @@ -55,7 +56,7 @@ enum { FN1Ci, FN1Oi, FN2Ci, FN2Oi, // FN__ alternatives that don't take the function from the stack, but instead as an 2×u32 immediate in the bytecode SETNi, SETUi, SETMi, SETCi, // SET_ alternatives that expect the set variable as a depth-position pair like VAR_ SETNv, SETUv, SETMv, SETCv, // SET_i alternatives that also don't return the result - SETHi, // internal version of SETH, with 2×u64 arguments specifying bodies to jump to on fail (or NULL if is last) + SETHi, PRED1, PRED2, // internal versions of SETH and PRED, with 2×u64 arguments (only 1 for PRED1) specifying bodies to jump to on fail (or NULL if is last) DFND0, DFND1, DFND2, // internal versions of DFND with a specific type, and a u64 argument representing the block pointer FAIL, // this body cannot be called monadically/dyadically BC_SIZE