From 3991efd3cd2f8105a230ca7c0afa7bc30433bf50 Mon Sep 17 00:00:00 2001 From: dzaima Date: Sat, 21 Aug 2021 03:32:11 +0300 Subject: [PATCH] full SETH --- src/core/stuff.c | 2 +- src/h.h | 2 +- src/jit/nvm.h | 7 ---- src/jit/nvm_placeholder.c | 2 +- src/jit/nvm_x86_64.c | 41 ++++++++++++++------ src/jit/x86_64.h | 30 +++++++++++++- src/vm.c | 82 ++++++++++++++++++++++++--------------- src/vm.h | 30 +++++++++++++- 8 files changed, 140 insertions(+), 56 deletions(-) delete mode 100644 src/jit/nvm.h diff --git a/src/core/stuff.c b/src/core/stuff.c index a2cecb24..1ab7674e 100644 --- a/src/core/stuff.c +++ b/src/core/stuff.c @@ -132,7 +132,7 @@ NOINLINE void print(B x) { else if (x.u==bi_N.u) printf("·"); else if (x.u==bi_optOut.u) printf("(value optimized out)"); else if (x.u==bi_noVar.u) printf("(unset variable placeholder)"); - else if (x.u==bi_badHdr.u) printf("(bad header note)"); + else if (x.u==bi_okHdr.u) printf("(accepted SETH placeholder)"); else if (x.u==bi_noFill.u) printf("(no fill placeholder)"); else printf("(todo tag "N64x")", x.u>>48); } diff --git a/src/h.h b/src/h.h index 9f67f084..54ff5f3b 100644 --- a/src/h.h +++ b/src/h.h @@ -231,7 +231,7 @@ void gc_visitRoots(void); // some primitive actions static const B bi_N = b((u64)0x7FF2000000000000ull); // tag(0, TAG_TAG); // make gcc happy static const B bi_noVar = b((u64)0x7FF2000000000001ull); // tag(1, TAG_TAG); -static const B bi_badHdr = b((u64)0x7FF2000000000002ull); // tag(2, TAG_TAG); +static const B bi_okHdr = b((u64)0x7FF2000000000002ull); // tag(2, TAG_TAG); static const B bi_optOut = b((u64)0x7FF2000000000003ull); // tag(3, TAG_TAG); static const B bi_noFill = b((u64)0x7FF2000000000005ull); // tag(5, TAG_TAG); extern B bi_emptyHVec, bi_emptyIVec, bi_emptyCVec, bi_emptySVec; diff --git a/src/jit/nvm.h b/src/jit/nvm.h deleted file mode 100644 index eceec0d7..00000000 --- a/src/jit/nvm.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include "../vm.h" - -B evalJIT(Body* b, Scope* sc, u8* ptr); -typedef struct Nvm_res { u8* p; B refs; } Nvm_res; -Nvm_res m_nvm(Body* b); -void nvm_free(u8* ptr); diff --git a/src/jit/nvm_placeholder.c b/src/jit/nvm_placeholder.c index 94b740ae..d88ba632 100644 --- a/src/jit/nvm_placeholder.c +++ b/src/jit/nvm_placeholder.c @@ -1,5 +1,5 @@ #include "../core.h" -#include "nvm.h" +#include "../vm.h" B evalJIT(Body* b, Scope* sc, u8* ptr) { thrM("JIT: Not supported"); } Nvm_res m_nvm(Body* b) { thrM("JIT: Not supported"); } void nvm_free(u8* ptr) { thrM("JIT: Not supported"); } diff --git a/src/jit/nvm_x86_64.c b/src/jit/nvm_x86_64.c index a27060ae..46a9f7f6 100644 --- a/src/jit/nvm_x86_64.c +++ b/src/jit/nvm_x86_64.c @@ -4,7 +4,7 @@ #include "../utils/file.h" #include "../utils/talloc.h" #include "../utils/mut.h" -#include "nvm.h" +#include "../vm.h" #ifndef USE_PERF #define USE_PERF 0 // enable writing symbols to /tmp/perf-.map @@ -124,9 +124,18 @@ INS B i_SETM(B s, B f, B x, Scope** pscs, u32* bc) { POS_UPD; v_set(pscs, s, r, true); dec(s); return r; } -INS void i_SETH(B s, B x, Scope** pscs, u32* bc) { POS_UPD; +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) thrM("VM: Header fallback NYI"); + if (ok) return bi_okHdr; + Scope* sc = pscs[0]; + Body* body = q_N(sc->vars[2])? v1 : v2; + 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? + u64 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); + return execBodyInlineI(bl, body, nsc); } 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; } @@ -155,8 +164,8 @@ INS B i_CHKV(B x, u32* bc, B* cStack) { if(q_N(x)) { POS_UPD; GS_UPD; thrM("Unexpected Nothing (·)"); } return x; } -INS B i_FAIL(u32* bc, B* cStack) { - POS_UPD; GS_UPD; thrM("No body matched"); +INS B i_FAIL(u32* bc, Scope* sc, B* cStack) { + POS_UPD; GS_UPD; thrM(q_N(sc->vars[2])? "This block cannot be called monadically" : "This block cannot be called dyadically"); } INS B i_RETD(Scope* sc) { Body* b = sc->body; @@ -502,6 +511,7 @@ Nvm_res m_nvm(Body* body) { u32* bc = optRes.bc; i32 lGPos = 0; // last updated gStack offset + TSALLOC(u64, retLbls, 1); while (true) { u32* s = bc; u32* n = nextBC(bc); @@ -576,10 +586,12 @@ Nvm_res m_nvm(Body* body) { // case LOCU: TOPs; { u64 d=*bc++; IMM(R_A0,*bc++); LSC(R_A1,d); CCALL(i_LOCU); } 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 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) - case SETH: TOPp; GET(R_A1,1,1); LEAi(R_A2,R_SP,VAR(pscs,0)); IMM(R_A3,off); CCALL(i_SETH); break; // (B s, B x, Scope** pscs, u32* bc) + 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 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) // TODO SETNi doesn't really need to update gStack case SETNi:TOPp; { u64 d=*bc++; u64 p=*bc++; GET(R_A1,0,2); LSC(R_A1,d); IMM(R_A2,p); IMM(R_A3,off); CCALL(i_SETNi); break; } // ( B x, Scope* sc, u32 p, u32* bc) case SETUi:TOPp; { u64 d=*bc++; u64 p=*bc++; GET(R_A1,0,2); LSC(R_A1,d); IMM(R_A2,p); IMM(R_A3,off); CCALL(i_SETUi); break; } // ( B x, Scope* sc, u32 p, u32* bc) @@ -591,9 +603,9 @@ Nvm_res m_nvm(Body* body) { case NSPM: TOPp; IMM(R_A1,*bc++); CCALL(i_NSPM); break; // (B, u32 l) case VFYM: TOPp; CCALL(i_VFYM); break; // (B) case CHKV: TOPp; IMM(R_A1,off); INV(2,0,i_CHKV); break; // (B, u32* bc, S) - case RETD: if (lGPos) GS_SET(r_CS); MOV(R_A0,r_SC); CCALL(i_RETD); ret=true; break; // (Scope* sc) - case RETN: if (lGPos) GS_SET(r_CS); ret=true; break; - case FAIL: TOPs; IMM(R_A0,off); INV(1,0,i_FAIL); ret=true; break; + case RETD: if (lGPos!=0) GS_SET(r_CS); MOV(R_A0,r_SC); CCALL(i_RETD); ret=true; break; // (Scope* sc) + case RETN: if (lGPos!=0) GS_SET(r_CS); ret=true; break; + case FAIL: TOPs; IMM(R_A0,off); MOV(R_A1,r_SC); INV(2,0,i_FAIL); ret=true; break; default: thrF("JIT: Unsupported bytecode %i/%S", *s, nameBC(s)); } #undef GET @@ -612,6 +624,11 @@ Nvm_res m_nvm(Body* body) { if (ret) break; } freeOpt(optRes); + u64 retLblAm = TSSIZE(retLbls); + for (u64 i = 0; i < retLblAm; i++) { + u64 l = retLbls[i]; LBL4(l); + } + TSFREE(retLbls); ADDi(R_SP, lsz); POP(r_SC); POP(r_CS); diff --git a/src/jit/x86_64.h b/src/jit/x86_64.h index 946a47fb..7d5b4a60 100644 --- a/src/jit/x86_64.h +++ b/src/jit/x86_64.h @@ -92,6 +92,7 @@ static inline TStack* asm_r(TStack* o) { return o; } + static inline void asm_1(TStack* o, i8 v) { o->data[o->size++] = v; } @@ -101,6 +102,14 @@ static inline void asm_4(TStack* o, i32 v) { int size = o->size; memcpy(o->data+size, (i32[]){v}, 4); // slightly less UB than an unaligned move o->size = size+4; } +static inline void asm_w4(u8* data, i32 v) { + memcpy(data, (i32[]){v}, 4); +} +static inline i32 asm_r4(u8* data) { + i32 v; + memcpy(&v, data, 4); + return v; +} static inline void asm_8(TStack* o, i64 v) { int size = o->size; // o->data[size+0] = (u8)(v ); o->data[size+1] = (u8)(v>> 8); o->data[size+2] = (u8)(v>>16); o->data[size+3] = (u8)(v>>24); // o->data[size+4] = (u8)(v>>32); o->data[size+5] = (u8)(v>>40); o->data[size+6] = (u8)(v>>48); o->data[size+7] = (u8)(v>>56); @@ -414,7 +423,7 @@ static inline void asm_a(TStack* o, u64 len, u8 v[]) { #define CALLi(POS) {AC1(CALLi,(u64)(POS)); TSADD(b_r, ASM_SIZE-4);} // POS must be 32-bit #define RET() {b_o=asm_r(b_o); ASM1(0xC3);} -#define JO(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x70);ASM1(-2);} +#define JO(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x70);ASM1(-2);} // -2 comes from the instruction being 2 bytes long and L being defined at the start #define JNO(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x71);ASM1(-2);} #define JB(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x72);ASM1(-2);} #define JAE(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x73);ASM1(-2);} @@ -430,7 +439,24 @@ static inline void asm_a(TStack* o, u64 len, u8 v[]) { #define JGE(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x7D);ASM1(-2);} #define JLE(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x7E);ASM1(-2);} #define JG(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x7F);ASM1(-2);} -#define LBL1(L) { i64 t=(i8)b_o->data[L+1] + ASM_SIZE-(i64)L; if(t!=(i8)t)err("x86-64 codegen: jump too long!"); b_o->data[L+1] = t; } +#define LBL1(L) { i64 t=(i8)b_o->data[L+1] + ASM_SIZE-(i64)L; if(t!=(i8)t) err("x86-64 codegen: jump too long!"); b_o->data[L+1] = t; } +#define JO4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x80);ASM4(-6);} +#define JNO4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x81);ASM4(-6);} +#define JB4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x82);ASM4(-6);} +#define JAE4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x83);ASM4(-6);} +#define JE4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x84);ASM4(-6);} +#define JNE4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x85);ASM4(-6);} +#define JBE4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x86);ASM4(-6);} +#define JA4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x87);ASM4(-6);} +#define JS4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x88);ASM4(-6);} +#define JNS4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x89);ASM4(-6);} +#define JP4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x8A);ASM4(-6);} +#define JNP4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x8B);ASM4(-6);} +#define JL4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x8C);ASM4(-6);} +#define JGE4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x8D);ASM4(-6);} +#define JLE4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x8E);ASM4(-6);} +#define JG4(L) u64 L=ASM_SIZE; {b_o=asm_r(b_o); ASM1(0x0f);ASM1(0x8F);ASM4(-6);} +#define LBL4(L) { i64 t=asm_r4(b_o->data+(L+2)) + ASM_SIZE-(i64)L; if(t!=(i32)t) err("x86-64 codegen: jump too long!"); asm_w4(b_o->data+(L+2), t); } ASMI(ADD, Reg o, Reg i) { nREX8(o,i); ASM1(0x01); nA_REG(o,i); } ASMI(SUB, Reg o, Reg i) { nREX8(o,i); ASM1(0x29); nA_REG(o,i); } diff --git a/src/vm.c b/src/vm.c index 439eb524..001eb9a3 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1,7 +1,6 @@ #include #include "core.h" #include "vm.h" -#include "jit/nvm.h" #include "ns.h" #include "utils/utf.h" #include "utils/talloc.h" @@ -36,7 +35,7 @@ u32* nextBC(u32* p) { case ADDI: case ADDU: case FN1Ci: case FN1Oi: case FN2Ci: case SETNi: case SETUi: case SETMi: case SETNv: case SETUv: case SETMv: off = 3; break; - case FN2Oi: + case FN2Oi: case SETHi: off = 5; break; default: UD; } @@ -48,7 +47,7 @@ i32 stackDiff(u32* p) { case PUSH: case VARO: case VARM: case DFND: case LOCO: case LOCM: case LOCU: 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 NSPM: return 0; case FN2Ci:case FN2Oi:case FN1C: case FN1O: case OP1D: case TR2D: case POPS: case OP2H: case RETN: return -1; - case OP2D: case TR3D: case FN2C: case FN2O: case TR3O: case SETH: return -2; + case OP2D: 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; case SETU: return -1; case SETUi:return 0; case SETUv:return -1; @@ -62,7 +61,7 @@ i32 stackConsumed(u32* p) { case PUSH: case VARO: case VARM: case DFND: case LOCO: case LOCM: case LOCU: case EXTO: case EXTM: case EXTU: case SYSV: case ADDI: case ADDU: return 0; case CHKV: case RETD: return 0; case FN1Ci:case FN1Oi:case FLDO: case FLDM: case NSPM: case RETN: case POPS: case VFYM: return 1; - case FN2Ci:case FN2Oi:case FN1C: case FN1O: case OP1D: case TR2D: case OP2H: case SETH: return 2; + case FN2Ci:case FN2Oi:case FN1C: case FN1O: case OP1D: case TR2D: case OP2H: case SETH: case SETHi:return 2; case OP2D: case TR3D: case FN2C: case FN2O: case TR3O: return 3; case SETN: return 2; case SETNi: case SETNv: return 1; @@ -144,7 +143,11 @@ static Body* m_body(i32 vam, i32 pos, u16 maxStack, u16 maxPSC) { // leaves varI body->varAm = (u16)vam; return body; } - +typedef struct SETHRequest { + 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; 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"); @@ -179,12 +182,14 @@ Block* compileBlock(B block, Comp* comp, bool* bDone, u32* bc, usz bcIA, B allBl bodyAm2 = 1; } // for (int i = 0; i < bodyILen+2; i++) printf("%d ", bodyI[i]); putchar('\n'); printf("things: %d %d\n", bodyAm1, bodyAm2); - 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 TSALLOC(i32, newBC, 20); // transformed bytecode TSALLOC(i32, mapBC, 20); // map of original bytecode to transformed + 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 - i32 pos1 = 0; + i32 pos1 = 0; // pos1 and pos2 always stay valid indexes in bodyI because bodyI is padded with -1s i32 pos2 = bodyAm1+1; i32 index1 = -1; i32 index2 = -1; @@ -200,6 +205,7 @@ Block* compileBlock(B block, Comp* comp, bool* bDone, u32* bc, usz bcIA, B allBl body->nsDesc = NULL; TSADD(bodies, body); } + bodyMap[bodyAm1] = bodyMap[bodyILen+1] = NULL; while (true) { i32 curr1 = bodyI[pos1]; @@ -207,8 +213,9 @@ Block* compileBlock(B block, Comp* comp, bool* bDone, u32* bc, usz bcIA, B allBl i32 currBody = curr1>32; + u64 v2 = (u64)bodyMap[r.pos2]; newBC[r.off+2] = (u32)v2; newBC[r.off+3] = v2>>32; + } + TSFREE(sethReqs); + TFREE(bodyMap); TFREE(bodyI); usz blC = TSSIZE(usedBlocks); @@ -491,6 +513,7 @@ NOINLINE B v_getR(Scope* pscs[], B s) { +FORCE_INLINE B execBodyInlineI(Block* block, Body* body, Scope* sc); #ifdef DEBUG_VM i32 bcDepth=-2; @@ -662,9 +685,22 @@ B evalBC(Block* bl, Body* b, Scope* sc) { // doesn't consume ADD(r); break; } - case SETH: { P(s) P(x) GS_UPD; POS_UPD; + 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) thrM("VM: Header fallback NYI"); + 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); + } break; } case FLDO: { P(ns) GS_UPD; u32 p = *bc++; POS_UPD; @@ -697,7 +733,7 @@ B evalBC(Block* bl, Body* b, Scope* sc) { // doesn't consume ADD(tag(a,OBJ_TAG)); break; } - case FAIL: thrM("No body matched"); + case FAIL: thrM(q_N(sc->vars[2])? "This block cannot be called monadically" : "This block cannot be called dyadically"); // not implemented: VARO VARM FLDM SYSV default: #ifdef DEBUG @@ -729,7 +765,7 @@ B evalBC(Block* bl, Body* b, Scope* sc) { // doesn't consume #undef GS_UPD } -Scope* m_scope(Body* body, Scope* psc, u16 varAm, i32 initVarAm, B* initVars) { // doesn't consume +Scope* m_scope(Body* body, Scope* psc, u16 varAm, i32 initVarAm, B* initVars) { // consumes initVarAm items of initVars Scope* sc = mm_alloc(fsizeof(Scope, vars, B, varAm), t_scope); sc->body = body; ptr_inc(body); sc->psc = psc; if(psc) ptr_inc(psc); @@ -741,28 +777,12 @@ Scope* m_scope(Body* body, Scope* psc, u16 varAm, i32 initVarAm, B* initVars) { return sc; } -FORCE_INLINE B execBodyInlineI(Block* block, Body* body, Scope* sc) { // consumes sc, unlike execBlockInline - #if JIT_START != -1 - if (body->nvm) { toJIT: return evalJIT(body, sc, body->nvm); } - bool jit = true; - #if JIT_START > 0 - jit = body->callCount++ >= JIT_START; - #endif - // jit = body->bc[2]==m_f64(123456).u>>32; // enable JIT for blocks starting with `123456⋄` - if (jit) { - Nvm_res r = m_nvm(body); - body->nvm = r.p; - body->nvmRefs = r.refs; - goto toJIT; - } - #endif - return evalBC(block, body, sc); -} B execBlockInline(Block* block, Scope* sc) { ptr_inc(sc); return execBodyInlineI(block, block->bodies[0], sc); } FORCE_INLINE B execBlock(Block* block, Body* body, Scope* psc, i32 ga, B* svar) { // consumes svar contents u16 varAm = body->varAm; assert(varAm>=ga); + assert(ga == blockGivenVars(block)); Scope* sc = m_scope(body, psc, varAm, ga, svar); B r = execBodyInlineI(block, body, sc); return r; diff --git a/src/vm.h b/src/vm.h index 58b094a9..b9387c80 100644 --- a/src/vm.h +++ b/src/vm.h @@ -48,7 +48,8 @@ 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, // SET_ alternatives that expect the set variable as a depth-position pair like LOC_ SETNv, SETUv, SETMv, // SET_i alternatives that also don't return the result - FAIL, // no body matched + SETHi, // internal version of SETH, with 2×u64 arguments specifying bodies to jump to on fail (or NULL if is last) + FAIL, // this body cannot be called monadically/dyadically BC_SIZE }; @@ -127,7 +128,31 @@ Block* bqn_comp(B str, B path, B args); Block* bqn_compSc(B str, B path, B args, Scope* sc, bool repl); Block* compile(B bcq, B objs, B blocks, B bodies, B indices, B tokenInfo, B src, B path, Scope* sc); Scope* m_scope(Body* body, Scope* psc, u16 varAm, i32 initVarAm, B* initVars); + +typedef struct Nvm_res { u8* p; B refs; } Nvm_res; +Nvm_res m_nvm(Body* b); +void nvm_free(u8* ptr); + +B evalJIT(Body* b, Scope* sc, u8* ptr); +B evalBC(Block* bl, Body* b, Scope* sc); B execBlockInline(Block* block, Scope* sc); // doesn't consume; executes bytecode of the monadic body directly in the scope +FORCE_INLINE B execBodyInlineI(Block* block, Body* body, Scope* sc) { // consumes sc, unlike execBlockInline + #if JIT_START != -1 + if (body->nvm) { toJIT: return evalJIT(body, sc, body->nvm); } + bool jit = true; + #if JIT_START > 0 + jit = body->callCount++ >= JIT_START; + #endif + // jit = body->bc[2]==m_f64(123456).u>>32; // enable JIT for blocks starting with `123456⋄` + if (jit) { + Nvm_res r = m_nvm(body); + body->nvm = r.p; + body->nvmRefs = r.refs; + goto toJIT; + } + #endif + return evalBC(block, body, sc); +} u32* nextBC(u32* p); i32 stackDiff(u32* p); @@ -188,6 +213,9 @@ FORCE_INLINE void scope_dec(Scope* sc) { // version of ptr_dec for scopes, that } ptr_dec(sc); } +FORCE_INLINE i32 blockGivenVars(Block* bl) { + return (bl->imm?0:3) + bl->ty + (bl->ty>0); +} void vm_pst(Env* s, Env* e); void vm_pstLive(void); void vm_printPos(Comp* comp, i32 bcPos, i64 pos);