From 550b91688a94b156a0b4c12f1e99f403c67dd280 Mon Sep 17 00:00:00 2001 From: dzaima Date: Wed, 9 Jun 2021 18:42:36 +0300 Subject: [PATCH] extremely basic x86-64 generator --- .gitignore | 1 + obj/subMakefile | 7 +- src/builtins/sysfn.c | 11 ++ src/core/stuff.c | 8 +- src/jit/nvm.c | 312 +++++++++++++++++++++++++++++++++++++++++++ src/jit/nvm.h | 6 + src/jit/x86_64.h | 246 ++++++++++++++++++++++++++++++++++ src/opt/single.c | 1 + src/utils/builtins.h | 2 +- src/utils/file.c | 2 +- src/utils/file.h | 1 + src/vm.c | 122 +++++++---------- src/vm.h | 85 +++++++++++- 13 files changed, 713 insertions(+), 91 deletions(-) create mode 100644 src/jit/nvm.c create mode 100644 src/jit/nvm.h create mode 100644 src/jit/x86_64.h diff --git a/.gitignore b/.gitignore index 3f6eee7a..6eecbb3d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ perf.* obj/*/*.o obj/*/*.d obj/*/*.tmp +tmp_* \ No newline at end of file diff --git a/obj/subMakefile b/obj/subMakefile index 08efd0ad..9d171bed 100644 --- a/obj/subMakefile +++ b/obj/subMakefile @@ -20,7 +20,7 @@ heapverify: gen rtverify: FLAGS=-DDEBUG -O3 -DRT_VERIFY rtverify: gen -gen: builtins core base utils +gen: builtins core base jit utils @$(CC) -o BQN *.o -lm @mv BQN ../../BQN @echo @@ -40,6 +40,11 @@ utils: utf.o hash.o file.o @echo $< | cut -c 11- @$(CMD) $@.d -c $< +jit: nvm.o +%.o: ../../src/jit/%.c + @echo $< | cut -c 11- + @$(CMD) $@.d -c $< + builtins: arithm.o arithd.o cmp.o sfns.o sort.o md1.o md2.o fns.o sysfn.o internal.o %.o: ../../src/builtins/%.c @echo $< | cut -c 11- diff --git a/src/builtins/sysfn.c b/src/builtins/sysfn.c index 617dab9c..931c1140 100644 --- a/src/builtins/sysfn.c +++ b/src/builtins/sysfn.c @@ -278,6 +278,16 @@ B list_c1(B d, B x) { return file_list(path_resolve(nfn_objU(d), x)); } +B delay_c1(B t, B x) { + f64 sf = o2f(x); + if (sf<0 || sf>1ULL<<63) thrF("•Delay: Bad argument: %f", sf); + struct timespec ts; + u64 s = (u64)sf; + ts.tv_sec = (u64)sf; + ts.tv_nsec = (u64)((sf-s)*1e9); + nanosleep(&ts, &ts); + return x; // TODO figure out how to return an actually correct thing +} B exit_c1(B t, B x) { bqn_exit(q_i32(x)? o2i(x) : 0); } @@ -312,6 +322,7 @@ B sys_c1(B t, B x) { else if (eqStr(c, U"bqn")) r.a[i] = inc(bi_bqn); else if (eqStr(c, U"cmp")) r.a[i] = inc(bi_cmp); else if (eqStr(c, U"timed")) r.a[i] = inc(bi_timed); + else if (eqStr(c, U"delay")) r.a[i] = inc(bi_delay); else if (eqStr(c, U"hash")) r.a[i] = inc(bi_hash); else if (eqStr(c, U"repr")) r.a[i] = inc(bi_repr); else if (eqStr(c, U"makerand")) r.a[i] = inc(bi_makeRand); diff --git a/src/core/stuff.c b/src/core/stuff.c index 99a77eb5..3524abeb 100644 --- a/src/core/stuff.c +++ b/src/core/stuff.c @@ -324,15 +324,15 @@ void slice_print(B x) { arr_print(x); } char* format_type(u8 u) { switch(u) { default: return"(unknown type)"; case t_empty:return"empty"; case t_shape:return"shape"; - case t_funBI:return"builtin fun"; case t_fun_block:return"fun_block"; - case t_md1BI:return"builtin md1"; case t_md1_block:return"md1_block"; - case t_md2BI:return"builtin md2"; case t_md2_block:return"md2_block"; + case t_funBI:return"builtin fun"; case t_fun_block:return"fun block"; + case t_md1BI:return"builtin md1"; case t_md1_block:return"md1 block"; + case t_md2BI:return"builtin md2"; case t_md2_block:return"md2 block"; case t_fork:return"fork"; case t_atop:return"atop"; case t_md1D:return"md1D"; case t_md2D:return"md2D"; case t_md2H:return"md2H"; case t_harr :return"harr" ; case t_i8arr :return"i8arr" ; case t_i32arr :return"i32arr" ; case t_fillarr :return"fillarr" ; case t_c32arr :return"c32arr" ; case t_f64arr :return"f64arr" ; case t_hslice:return"hslice"; case t_i8slice:return"i8slice"; case t_i32slice:return"i32slice"; case t_fillslice:return"fillslice"; case t_c32slice:return"c32slice"; case t_f64slice:return"f64slice"; case t_comp:return"comp"; case t_block:return"block"; case t_body:return"body"; case t_scope:return"scope"; case t_scopeExt:return"scope extension"; case t_blBlocks: return "block list"; - 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_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_nfn:return"nfn"; case t_nfnDesc:return"nfnDesc"; case t_freed:return"(freed by GC)"; case t_harrPartial:return"partHarr"; #ifdef RT_WRAP case t_funWrap:return"wrapped function"; case t_md1Wrap:return"wrapped 1-modifier"; case t_md2Wrap:return "wrapped 2-modifier"; diff --git a/src/jit/nvm.c b/src/jit/nvm.c new file mode 100644 index 00000000..684c0bbf --- /dev/null +++ b/src/jit/nvm.c @@ -0,0 +1,312 @@ +#include "../core.h" +#include "../core/gstack.h" +#include "../ns.h" +#include "../utils/file.h" + +#ifndef USE_PERF + #define USE_PERF 0 // enable writing symbols to /tmp/perf-.map +#endif + +// separate memory management system for executable code; isn't garbage-collected +#define BSZ(X) (1ull<<(X)) +#define BSZI(X) ((u8)(64-__builtin_clzl((X)-1ull))) +#define MMI(X) X +#define BN(X) mmX_##X +#define buckets mmX_buckets +#include "../opt/mm_buddyTemplate.h" +#define MMI(X) X +#define ALSZ 17 +#define PROT PROT_READ|PROT_WRITE|PROT_EXEC +#define FLAGS MAP_NORESERVE|MAP_PRIVATE|MAP_ANON|MAP_32BIT +#include "../opt/mm_buddyTemplate.c" +static void* mmX_allocN(usz sz, u8 type) { assert(sz>=16); return mmX_allocL(BSZI(sz), type); } +#undef mmX_buckets +#undef BN +#undef BSZI +#undef BSZ + + +// all the instructions to be called by the generated code +#define GSA(X) { B tr=X; *(gStack++) = tr; } +#define GSP (*--gStack) +#define P(N) B N=GSP; +#define GS_UPD +#if VM_POS + #define POS_UPD (envCurr-1)->bcL = bc-1; +#else + #define POS_UPD +#endif +#define INS NOINLINE +INS void i_POPS() { + dec(GSP); +} +INS void i_PUSH() { + thrM("NYI PUSH in nvm"); +} +INS void i_ADDI(u64 v) { + B o = b(v); + ptr_inc(v(o)); + GSA(o); +} +INS void i_ADDU(u64 v) { + GSA(b(v)); +} +INS void i_FN1C(u32* bc) { P(f)P(x) // TODO figure out a way to instead pass an offset in bc, so that shorter `mov`s can be used to pass it + GS_UPD;POS_UPD; + GSA(c1(f, x)); dec(f); +} +INS void i_FN1O(u32* bc) { P(f)P(x) + GS_UPD;POS_UPD; + GSA(isNothing(x)? x : c1(f, x)); dec(f); +} +INS void i_FN2C(u32* bc) { P(w)P(f)P(x) + GS_UPD;POS_UPD; + GSA(c2(f, w, x)); dec(f); +} +INS void i_FN2O(u32* bc) { P(w)P(f)P(x) + GS_UPD;POS_UPD; + if (isNothing(x)) { dec(w); GSA(x); } + else GSA(isNothing(w)? c1(f, x) : c2(f, w, x)); + dec(f); +} +INS void i_ARR_0() { + GSA(inc(bi_emptyHVec)); +} +INS void i_ARR_p(i64 sz) { + HArr_p r = m_harrUv(sz); + bool allNum = true; + assert(sz>0); + for (i64 i = 0; i < sz; i++) if (!isNum(r.a[sz-i-1] = GSP)) allNum = false; + if (allNum) { + GS_UPD; + GSA(withFill(r.b, m_f64(0))); + } else GSA(r.b); +} +INS void i_DFND_0(u32* bc, Scope** pscs, Block* bl) { GS_UPD;POS_UPD; GSA(m_funBlock(bl, *pscs)); } +INS void i_DFND_1(u32* bc, Scope** pscs, Block* bl) { GS_UPD;POS_UPD; GSA(m_md1Block(bl, *pscs)); } +INS void i_DFND_2(u32* bc, Scope** pscs, Block* bl) { GS_UPD;POS_UPD; GSA(m_md2Block(bl, *pscs)); } +INS void i_OP1D(u32* bc) { P(f)P(m) GS_UPD;POS_UPD; GSA(m1_d (m,f )); } +INS void i_OP2D(u32* bc) { P(f)P(m)P(g) GS_UPD;POS_UPD; GSA(m2_d (m,f,g)); } +INS void i_OP2H( ) { P(m)P(g) GSA(m2_h (m, g)); } +INS void i_TR2D( ) { P(g)P(h) GSA(m_atop( g,h)); } +INS void i_TR3D( ) { P(f)P(g)P(h) GSA(m_fork(f,g,h)); } +INS void i_TR3O( ) { P(f)P(g)P(h) + if (isNothing(f)) { GSA(m_atop(g,h)); dec(f); } + else GSA(m_fork(f,g,h)); +} +INS void i_LOCM(u32 d, u32 p) { + GSA(tag((u64)d<<32 | (u32)p, VAR_TAG)); +} +INS void i_LOCO(u32 d, u32 p, Scope** pscs, u32* bc) { + B l = pscs[d]->vars[p]; + if(l.u==bi_noVar.u) { POS_UPD; thrM("Reading variable before its defined"); } + GSA(inc(l)); +} +INS void i_LOCU(u32 d, u32 p, Scope** pscs) { + B* vars = pscs[d]->vars; + GSA(vars[p]); + vars[p] = bi_optOut; +} +INS void i_EXTM(u32 d, u32 p) { + GSA(tag((u64)d<<32 | (u32)p, EXT_TAG)); +} +INS void i_EXTO(u32 d, u32 p, Scope** pscs, u32* bc) { + B l = pscs[d]->ext->vars[p]; + if(l.u==bi_noVar.u) { POS_UPD; thrM("Reading variable before its defined"); } + GSA(inc(l)); +} +INS void i_EXTU(u32 d, u32 p, Scope** pscs) { + B* vars = pscs[d]->ext->vars; + GSA(vars[p]); + vars[p] = bi_optOut; +} +INS void i_SETN(Scope** pscs, u32* bc) { P(s) P(x) GS_UPD; POS_UPD; v_set(pscs, s, x, false); dec(s); GSA(x); } +INS void i_SETU(Scope** pscs, u32* bc) { P(s) P(x) GS_UPD; POS_UPD; v_set(pscs, s, x, true ); dec(s); GSA(x); } +INS void i_SETM(Scope** pscs, u32* bc) { P(s)P(f)P(x) GS_UPD; POS_UPD; + B w = v_get(pscs, s); + B r = c2(f,w,x); dec(f); + v_set(pscs, s, r, true); dec(s); + GSA(r); +} +INS void i_FLDO(u32 p, Scope** pscs) { P(ns) GS_UPD; + if (!isNsp(ns)) thrM("Trying to read a field from non-namespace"); + GSA(inc(ns_getU(ns, pscs[0]->body->nsDesc->nameList, p))); + dec(ns); +} +INS void i_NSPM(u32 l) { P(o) + B a = mm_alloc(sizeof(FldAlias), t_fldAlias, ftag(OBJ_TAG)); + c(FldAlias,a)->obj = o; + c(FldAlias,a)->p = l; + GSA(a); +} +INS B i_RETD(Scope** pscs) { + Scope* sc = pscs[0]; + Body* b = sc->body; + ptr_inc(sc); + ptr_inc(b->nsDesc); + return m_ns(sc, b->nsDesc); +} +INS B i_RETN() { P(v) + return v; +} + +#undef INS +#undef P +#undef GSP +#undef GSA +#undef GS_UPD +#undef POS_UPD + + + + + + + +#include "x86_64.h" +#define IMM(O,I) { u64 v=(u64)(I); if(v==0) ASM_RAW(bin, XOR(O,O)); else if(v>=0 & v<(1ULL<<32)) ASM_RAW(bin, MOV4_RI(O,v)); else ASM_RAW(bin, MOV_RI(O,v)); } +#define ASM(INS, O, I) ASM_RAW(bin, INS(O, I)) + +#if USE_PERF +#include +#include "../utils/file.h" +FILE* perf_map; +u32 perfid = 0; +#endif + +static void* nvm_alloc(u64 sz) { + // void* r = mmap(NULL, sz, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON|MAP_32BIT, -1, 0); + // if (r==MAP_FAILED) thrM("JIT: Failed to allocate executable memory"); + // return r; + TmpFile* src = mmX_allocN(fsizeof(TmpFile,a,u8,sz), t_i8arr); + arr_shVec(tag(src,ARR_TAG), sz); + return src->a; +} +void nvm_free(u8* ptr) { + if (!USE_PERF) mmX_free((Value*)RFLD(ptr, TmpFile, a)); +} +static void write_asm(u8* p, u64 sz) { // for debugging; view with objdump -b binary -m i386 -M x86-64,intel -D --adjust-vma=$(cat tmp_off) tmp_bin | tail -n+7 + i32* rp; B r = m_i32arrv(&rp, sz); + for (u64 i = 0; i < sz; i++) rp[i] = p[i]; + file_wBytes(m_str32(U"tmp_bin"), r); + char off[20]; snprintf(off, 20, "%p", p); + B o = m_str8l(off); + file_wChars(m_str32(U"tmp_off"), o); + dec(r); dec(o); +} + + +static u32 readBytes4(u8* d) { + return d[0] | d[1]<<8 | d[2]<<16 | d[3]<<24; +} + +typedef B JITFn(Scope** pscs); +u8* m_nvm(Body* body) { + TSALLOC(u8, bin, 64); + TSALLOC(u32, rel, 64); + #define r_PSCS 13 + // ASM(PUSH, 12, -); + ASM(PUSH, 13, -); + // ASM(PUSH, 14, -); + ASM(MOV, r_PSCS, REG_ARG0); + // #define CCALL(F) { IMM(12, F); ASM(CALL, 12, -); } + #define CCALL(F) { if((u64)F < 1ULL<<31) { TSADD(rel, TSSIZE(bin)); ASM(CALLI, (u32)F, -); } else { IMM(12, F); ASM(CALL, 12, -); } } + u32* bc = body->bc; + Block** blocks = body->blocks->a; + while (true) { + u32* s = bc; + u32* n = nextBC(bc); + bool ret = false; + #define L64 ({ u64 r = bc[0] | ((u64)bc[1])<<32; bc+= 2; r; }) + switch (*bc++) { + case POPS: CCALL(i_POPS); break; + case ADDI: IMM(REG_ARG0, L64); CCALL(i_ADDI); break; // (u64 v) + case ADDU: IMM(REG_ARG0, L64); CCALL(i_ADDU); break; // (u64 v) + case FN1C: IMM(REG_ARG0, s); CCALL(i_FN1C); break; // (u32* bc) + case FN2C: IMM(REG_ARG0, s); CCALL(i_FN2C); break; // (u32* bc) + case FN1O: IMM(REG_ARG0, s); CCALL(i_FN1O); break; // (u32* bc) + case FN2O: IMM(REG_ARG0, s); CCALL(i_FN2O); break; // (u32* bc) + case ARRM: case ARRO: // (i64 sz) + u32 sz = *bc++; + if (sz) { IMM(REG_ARG0, sz); CCALL(i_ARR_p); } + else { CCALL(i_ARR_0); } + break; + case DFND: // (u32* bc, Scope** pscs, Block* bl) + Block* bl = blocks[*bc++]; + IMM(REG_ARG0,s); ASM(MOV,REG_ARG1,r_PSCS); IMM(REG_ARG2,bl); + u64 fn = (u64)(bl->ty==0? i_DFND_0 : bl->ty==1? i_DFND_1 : bl->ty==2? i_DFND_2 : NULL); + if (fn==0) thrM("JIT: Bad DFND argument"); + CCALL(fn); + break; + case OP1D: IMM(REG_ARG0,s); CCALL(i_OP1D) break; // (u32* bc) + case OP2D: IMM(REG_ARG0,s); CCALL(i_OP2D) break; // (u32* bc) + case OP2H: CCALL(i_OP2H) break; + case TR2D: CCALL(i_TR2D) break; + case TR3D: CCALL(i_TR3D) break; + case TR3O: CCALL(i_TR3O) break; + case LOCM: IMM(REG_ARG0,*bc++); IMM(REG_ARG1,*bc++); CCALL(i_LOCM); break; // (u32 d, u32 p) + case LOCO: IMM(REG_ARG0,*bc++); IMM(REG_ARG1,*bc++); ASM(MOV,REG_ARG2,r_PSCS); IMM(REG_ARG3,s); CCALL(i_LOCO); break; // (u32 d, u32 p, Scope** pscs, u32* bc) + case LOCU: IMM(REG_ARG0,*bc++); IMM(REG_ARG1,*bc++); ASM(MOV,REG_ARG2,r_PSCS); CCALL(i_LOCU); break; // (u32 d, u32 p, Scope** pscs) + case EXTM: IMM(REG_ARG0,*bc++); IMM(REG_ARG1,*bc++); CCALL(i_EXTM); break; // (u32 d, u32 p) + case EXTO: IMM(REG_ARG0,*bc++); IMM(REG_ARG1,*bc++); ASM(MOV,REG_ARG2,r_PSCS); IMM(REG_ARG3,s); CCALL(i_EXTO); break; // (u32 d, u32 p, Scope** pscs, u32* bc) + case EXTU: IMM(REG_ARG0,*bc++); IMM(REG_ARG1,*bc++); ASM(MOV,REG_ARG2,r_PSCS); CCALL(i_EXTU); break; // (u32 d, u32 p, Scope** pscs) + case SETN: ASM(MOV,REG_ARG0,r_PSCS); IMM(REG_ARG1,s); CCALL(i_SETN); break; // (Scope** pscs, u32* bc) + case SETU: ASM(MOV,REG_ARG0,r_PSCS); IMM(REG_ARG1,s); CCALL(i_SETU); break; // (Scope** pscs, u32* bc) + case SETM: ASM(MOV,REG_ARG0,r_PSCS); IMM(REG_ARG1,s); CCALL(i_SETM); break; // (Scope** pscs, u32* bc) + case FLDO: IMM(REG_ARG0,*bc++); ASM(MOV,REG_ARG1,r_PSCS); CCALL(i_FLDO); break; // (u32 p, Scope** pscs) + case NSPM: IMM(REG_ARG0,*bc++); CCALL(i_NSPM); break; // (u32 l) + case RETD: ASM(MOV,REG_ARG0,r_PSCS); CCALL(i_RETD); ret=true; break; // (Scope** pscs) + case RETN: CCALL(i_RETN); ret=true; break; + default: thrF("JIT: Unsupported bytecode %i", *s); + } + #undef L64 + if (n!=bc) thrM("JIT: Wrong parsing of bytecode"); + if (ret) break; + } + // ASM(POP, 14, -); + ASM(POP, 13, -); + // ASM(POP, 12, -); + ASM_RAW(bin, RET); + + u64 sz = TSSIZE(bin); + u8* binEx = nvm_alloc(sz); + #if USE_PERF + if (!perf_map) { + B s = m_str32(U"/tmp/perf-"); AFMT("%l.map", getpid()); + perf_map = file_open(s, "open", "wa"); + print(s); printf(": map\n"); + dec(s); + } + u32 bcPos = body->map[0]; + // printf("JIT %d:\n", perfid); + // vm_printPos(body->comp, bcPos, -1); + fprintf(perf_map, "%lx %lx JIT %d: BC@%u\n", (u64)binEx, sz, perfid++, bcPos); + #endif + memcpy(binEx, bin, sz); + u64 relAm = TSSIZE(rel); + // printf("allocated at %p; i_ADDU: %p\n", binEx, i_ADDU); + for (u64 i = 0; i < relAm; i++) { + u8* ins = binEx+rel[i]; + u32 o = readBytes4(ins+1); + u32 n = o-(u32)ins-5; + memcpy(ins+1, (u8[]){BYTES4(n)}, 4); + } + // write_asm(binEx, sz); + TSFREE(bin); + TSFREE(rel); + return binEx; +} +B evalJIT(Body* b, Scope* sc, u8* ptr) { // doesn't consume + u32* bc = b->bc; + pushEnv(sc, bc); + gsReserve(b->maxStack); + Scope* pscs[b->maxPSC+1]; + pscs[0] = sc; + for (i32 i = 0; i < b->maxPSC; i++) pscs[i+1] = pscs[i]->psc; + // write_asm(ptr, RFLD(ptr, TmpFile, a)->ia); + + B r = ((JITFn*)ptr)(pscs); + + popEnv(); + return r; +} diff --git a/src/jit/nvm.h b/src/jit/nvm.h new file mode 100644 index 00000000..2292b305 --- /dev/null +++ b/src/jit/nvm.h @@ -0,0 +1,6 @@ +#pragma once +#include "../vm.h" + +B evalJIT(Body* b, Scope* sc, u8* ptr); +u8* m_nvm(Body* b); +void nvm_free(u8* ptr); diff --git a/src/jit/x86_64.h b/src/jit/x86_64.h new file mode 100644 index 00000000..6e3c298b --- /dev/null +++ b/src/jit/x86_64.h @@ -0,0 +1,246 @@ +/* taken from I: https://github.com/mlochbaum/ILanguage/blob/master/x86_64.h +ISC License + +Copyright (c) 2016, Marshall Lochbaum + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +#pragma once + +// 16 integer registers: +// 0 1 2 (0: not saved, 1: callee saves, 2: caller saves) +// 0 rax result x +// 1 rcx arg 4 x +// 2 rdx arg 3 x +// 3 rbx x +// 4 rsp stack x +// 5 rbp base x +// 6 rsi arg 2 x +// 7 rdi arg 1 x +// 8 r8 arg 5 x +// 9 r9 arg 6 x +// . r10 x +// . r11 x +// . r12-r15 x + +typedef unsigned char UC; +typedef unsigned char Reg; +#define REG_RES 0 +#define REG_ARG0 7 +#define REG_ARG1 6 +#define REG_ARG2 2 +#define REG_ARG3 1 +#define REG_ARG4 8 +#define REG_ARG5 9 +#define REG_SP 4 +#define NO_REG 16 +#define NO_REG_NM 17 +typedef unsigned short RegM; +#define REG_NEVER 48 // Never modify rsp or rbp +#define REG_MASK 61496 // Registers which must be pushed before use +#define REG_SAVE 4039 // Registers which function calls may modify + +#define MAX_C_REG 8 + +// Ignore leading 0x40 +// TODO Doesn't drop 0xF2 0x40, etc. +#define ASM_RAW(A, OP) do { UC aa[] = OP; u8 off=aa[0]==0x40; TSADDA(A, aa+off, sizeof(aa)-off); } while(0) + +// Instructions +#define A_0REG(O,I) ((((I)&7)<<3) + ((O)&7)) +#define A_REG(O,I) (0xC0 + A_0REG(O,I)) +#define REX0(O,I) (((O)>7)+(((I)>7)<<2)) +#define REX0_3(O,I,E) (REX0(O,I)+(((E)>7)<<1)) +#define REX8(O,I) (0x48 + REX0(O,I)) +#define REX4(O,I) (0x40 + REX0(O,I)) +#define REX8_3(O,I,E) (0x48 + REX0_3(O,I,E)) +#define REX4_3(O,I,E) (0x40 + REX0_3(O,I,E)) +#define REX1(O,I) 0x40 , (0x40 + REX0(O,I)) + +#define MOV(O,I) {REX8(O,I),0x89,A_REG(O,I)} +#define ADD(O,I) {REX8(O,I),0x01,A_REG(O,I)} +#define SUB(O,I) {REX8(O,I),0x29,A_REG(O,I)} +#define XOR(O,I) {REX8(O,I),0x31,A_REG(O,I)} +#define TEST(O,I) {REX8(O,I),0x85,A_REG(O,I)} +#define IMUL(I,O) {REX8(O,I),0x0F,0xAF,A_REG(O,I)} +#define CMP(O,I) {REX8(O,I),0x39,A_REG(O,I)} +#define NEG(I,O) {REX8(O,0),0xF7,A_REG(O,3)} + +#define CMP4_MI(O,I) {REX4(O,0),0x81,A_0REG(O,7),BYTES4(I)} + +// TODO REX for sil, etc +#define MOVZX4(I,O) {REX4(O,I),0x0F,0xB6,A_REG(O,I)} + +#define ADDI4(O,I) {REX8(O,0),0x81,A_REG(O,0),BYTES4(I)} +#define SUBI4(O,I) {REX8(O,0),0x81,A_REG(O,5),BYTES4(I)} + +#define ADDI1(O,I) {REX8(O,0),0x83,A_REG(O,0),(U)(I)} +#define SUBI1(O,I) {REX8(O,0),0x83,A_REG(O,5),(U)(I)} +#define SHLI1(O,I) {REX8(O,0),0xC1,A_REG(O,4),(U)(I)} +#define SHRI1(O,I) {REX8(O,0),0xC1,A_REG(O,5),(U)(I)} + +#define XOR4(O,I) {REX4(O,I),0x31,A_REG(O,I)} + +#define LEA1(O,A,B) {REX8_3(A,O,B),0x8D,A_0REG(4,O),A_0REG(A,B)} + +#define CQO(O,I) {0x48,0x99} +#define IDIV(O,I) {REX8(O,0),0xF7,A_REG(O,7)} +#define REG_IDIV_0 REG_RES +#define REG_IDIV_1 REG_ARG2 + +#define CVTSI2SD(O,I) {0xF2,REX8(I,O),0x0F,0x2A,A_REG(I,O)} +#define CVTTSD2SI(O,I) {0xF2,REX8(I,O),0x0F,0x2C,A_REG(I,O)} +#define MOVQ(O,I) {0x66,REX8(O,I),0x0F,0x7E,A_REG(O,I)} + +#define CVTSI2SD_RM(O,I,OFF) {0xF2,REX8(I,O),0x0F,0x2A,0x40+A_0REG(I,O),OFF} +#define CVTSI2SD_RM0(O,I) {0xF2,REX8(I,O),0x0F,0x2A,A_0REG(I,O)} + +#define MOVSD(O,I) {0xF2,REX4(I,O),0x0F,0x10,A_REG(I,O)} +#define ADDSD(O,I) {0xF2,REX4(I,O),0x0F,0x58,A_REG(I,O)} +#define MULSD(O,I) {0xF2,REX4(I,O),0x0F,0x59,A_REG(I,O)} +#define SUBSD(O,I) {0xF2,REX4(I,O),0x0F,0x5C,A_REG(I,O)} +#define MINSD(O,I) {0xF2,REX4(I,O),0x0F,0x5D,A_REG(I,O)} +#define DIVSD(O,I) {0xF2,REX4(I,O),0x0F,0x5E,A_REG(I,O)} +#define MAXSD(O,I) {0xF2,REX4(I,O),0x0F,0x5F,A_REG(I,O)} +#define SQRTSD(O,I) {0xF2,REX4(I,O),0x0F,0x51,A_REG(I,O)} +#define PXOR(O,I) {0x66,REX4(I,O),0x0F,0xEF,A_REG(I,O)} + +#define UCOMISD(O,I) {0x66,REX4(I,O),0x0F,0x2E,A_REG(I,O)} + +#define PUSH(O,I) {REX4(O,0),0x50+((O)&7)} +#define POP(O,I) {REX4(O,0),0x58+((O)&7)} + +#define BYTES4(I) ((UC)(I)),((UC)((I)>>8)),((UC)((I)>>16)),((UC)((I)>>24)) +#define BYTES8(I) BYTES4(I) ,((UC)((I)>>32)),((UC)((I)>>40)) \ + ,((UC)((I)>>48)),((UC)((I)>>56)) + +#define MOV_MR(O,I,OFF) {REX8(O,I),0x89,0x40+A_0REG(O,I),OFF} +#define MOV_MR0(O,I) {REX8(O,I),0x89,A_0REG(O,I)} +#define MOV_RM(I,O,OFF) {REX8(O,I),0x8B,0x40+A_0REG(O,I),OFF} +#define MOV_RM0(I,O) {REX8(O,I),0x8B,A_0REG(O,I)} +#define MOV_RI(O,I) {REX8(O,0),0xB8+((O)&7), BYTES8(I)} + +#define MOV4_MI(O,I,OFF) {REX4(O,0),0xC7,0x40+A_0REG(O,0),OFF,BYTES4(I)} +#define MOV4_RI(O,I) {REX4(O,0),0xB8+((O)&7) , BYTES4(I)} +#define MOV4_MR(O,I,OFF) {REX4(O,I),0x89,0x40+A_0REG(O,I),OFF} +#define MOV4_MR0(O,I) {REX4(O,I),0x89,A_0REG(O,I)} +#define MOV4_RM(I,O,OFF) {REX4(O,I),0x8B,0x40+A_0REG(O,I),OFF} +#define MOV4_RM0(I,O) {REX4(O,I),0x8B,A_0REG(O,I)} + +#define MOV1_MR(O,I,OFF) {REX4(O,I),0x88,0x40+A_0REG(O,I),OFF} +#define MOV1_MR0(O,I) {REX4(O,I),0x88,A_0REG(O,I)} +#define MOV1_RM(I,O,OFF) {REX4(O,I),0x0F,0xB6,0x40+A_0REG(O,I),OFF} +#define MOV1_RM0(I,O) {REX4(O,I),0x0F,0xB6,A_0REG(O,I)} + +#define MOVSD_MR(O,I,OFF) {0xF2,REX4(O,I),0x0F,0x11,0x40+A_0REG(O,I),OFF} +#define MOVSD_MR0(O,I) {0xF2,REX4(O,I),0x0F,0x11,A_0REG(O,I)} +#define MOVSD_RM(I,O,OFF) {0xF2,REX4(O,I),0x0F,0x10,0x40+A_0REG(O,I),OFF} +#define MOVSD_RM0(I,O) {0xF2,REX4(O,I),0x0F,0x10,A_0REG(O,I)} + +#define ASM_MOV_PRE(A,T) if ((T)==R_t) ASM_RAW(A, {0xF2}) +#define MOV1_MR_STUB(O,I,E) {REX4_3(O,I,E),0x88} +#define MOV1_RM_STUB(I,O,E) {REX4_3(O,I,E),0x0F,0xB6} +#define MOV_MR_STUB(O,I,E) {REX8_3(O,I,E),0x89} +#define MOV_RM_STUB(I,O,E) {REX8_3(O,I,E),0x8B} +#define MOVSD_MR_STUB(O,I,E) {REX4_3(O,I,E),0x0F,0x11} +#define MOVSD_RM_STUB(I,O,E) {REX4_3(O,I,E),0x0F,0x10} +#define AD_MR0(O,I) {A_0REG(O,I)} +#define AD_RM0(I,O) AD_MR0(O,I) +#define AD_MR(O,I,OFF) {0x40+A_0REG(O,I),OFF} +#define AD_RM(I,O,OFF) AD_MR(O,I,OFF) +#define AD_MRRS(O,A,B,S) {A_0REG(4,A),(((S)&3)<<6)+A_0REG(O,B)} +#define AD_RMRS(A,O,B,S) AD_MRRS(O,A,B,S) + +#define CALL(O,I) {REX4(O,0),0xFF,A_REG(O,2)} +#define CALLI(I,O) {0xE8,BYTES4((u32)(I))} + +#define LOOP(O,I) {0xE2,((UC)(O)-2)} +#define REG_LOOP 1 + +#define C_O 0x0 +#define C_NO 0x1 +#define C_B 0x2 +#define C_AE 0x3 +#define C_E 0x4 +#define C_NE 0x5 +#define C_BE 0x6 +#define C_A 0x7 +#define C_S 0x8 +#define C_NS 0x9 +#define C_P 0xA +#define C_NP 0xB +#define C_L 0xC +#define C_GE 0xD +#define C_LE 0xE +#define C_G 0xF + +#define JX(O,C) {0x70+(C),(UC)(O)-2} +#define J4X(O,C) {0x0F,0x80+(C),BYTES4((O)-6)} +#define SETX(O,C) {REX1(O,0),0x0F,0x90+(C),A_REG(O,0)} +#define CMOVX(I,O,C) {REX8(O,I),0x0F,0x40+(C),A_REG(O,I)} + +#define JO(O,I) {0x70,((UC)(O)-2)} +#define JNO(O,I) {0x71,((UC)(O)-2)} +#define JB(O,I) {0x72,((UC)(O)-2)} +#define JAE(O,I) {0x73,((UC)(O)-2)} +#define JE(O,I) {0x74,((UC)(O)-2)} +#define JNE(O,I) {0x75,((UC)(O)-2)} +#define JBE(O,I) {0x76,((UC)(O)-2)} +#define JA(O,I) {0x77,((UC)(O)-2)} +#define JS(O,I) {0x78,((UC)(O)-2)} +#define JNS(O,I) {0x79,((UC)(O)-2)} +#define JP(O,I) {0x7A,((UC)(O)-2)} +#define JNP(O,I) {0x7B,((UC)(O)-2)} +#define JL(O,I) {0x7C,((UC)(O)-2)} +#define JGE(O,I) {0x7D,((UC)(O)-2)} +#define JLE(O,I) {0x7E,((UC)(O)-2)} +#define JG(O,I) {0x7F,((UC)(O)-2)} + +#define JMP(O,I) {0xEB,((UC)(O)-2)} +#define JMP4(O,I) {0xE9,BYTES4((O)-5)} + +#define SETO(O,I) {REX1(O,0),0x0F,0x90,A_REG(O,0)} +#define SETNO(O,I) {REX1(O,0),0x0F,0x91,A_REG(O,0)} +#define SETB(O,I) {REX1(O,0),0x0F,0x92,A_REG(O,0)} +#define SETAE(O,I) {REX1(O,0),0x0F,0x93,A_REG(O,0)} +#define SETE(O,I) {REX1(O,0),0x0F,0x94,A_REG(O,0)} +#define SETNE(O,I) {REX1(O,0),0x0F,0x95,A_REG(O,0)} +#define SETBE(O,I) {REX1(O,0),0x0F,0x96,A_REG(O,0)} +#define SETA(O,I) {REX1(O,0),0x0F,0x97,A_REG(O,0)} +#define SETS(O,I) {REX1(O,0),0x0F,0x98,A_REG(O,0)} +#define SETNS(O,I) {REX1(O,0),0x0F,0x99,A_REG(O,0)} +#define SETP(O,I) {REX1(O,0),0x0F,0x9A,A_REG(O,0)} +#define SETNP(O,I) {REX1(O,0),0x0F,0x9B,A_REG(O,0)} +#define SETL(O,I) {REX1(O,0),0x0F,0x9C,A_REG(O,0)} +#define SETGE(O,I) {REX1(O,0),0x0F,0x9D,A_REG(O,0)} +#define SETLE(O,I) {REX1(O,0),0x0F,0x9E,A_REG(O,0)} +#define SETG(O,I) {REX1(O,0),0x0F,0x9F,A_REG(O,0)} + +#define CMOVO(I,O) {REX8(O,I),0x0F,0x40,A_REG(O,I)} +#define CMOVNO(I,O) {REX8(O,I),0x0F,0x41,A_REG(O,I)} +#define CMOVB(I,O) {REX8(O,I),0x0F,0x42,A_REG(O,I)} +#define CMOVAE(I,O) {REX8(O,I),0x0F,0x43,A_REG(O,I)} +#define CMOVE(I,O) {REX8(O,I),0x0F,0x44,A_REG(O,I)} +#define CMOVNE(I,O) {REX8(O,I),0x0F,0x45,A_REG(O,I)} +#define CMOVBE(I,O) {REX8(O,I),0x0F,0x46,A_REG(O,I)} +#define CMOVA(I,O) {REX8(O,I),0x0F,0x47,A_REG(O,I)} +#define CMOVS(I,O) {REX8(O,I),0x0F,0x48,A_REG(O,I)} +#define CMOVNS(I,O) {REX8(O,I),0x0F,0x49,A_REG(O,I)} +#define CMOVP(I,O) {REX8(O,I),0x0F,0x4A,A_REG(O,I)} +#define CMOVNP(I,O) {REX8(O,I),0x0F,0x4B,A_REG(O,I)} +#define CMOVL(I,O) {REX8(O,I),0x0F,0x4C,A_REG(O,I)} +#define CMOVGE(I,O) {REX8(O,I),0x0F,0x4D,A_REG(O,I)} +#define CMOVLE(I,O) {REX8(O,I),0x0F,0x4E,A_REG(O,I)} +#define CMOVG(I,O) {REX8(O,I),0x0F,0x4F,A_REG(O,I)} + +#define RET {0xC3} diff --git a/src/opt/single.c b/src/opt/single.c index 805a567a..8621ad24 100644 --- a/src/opt/single.c +++ b/src/opt/single.c @@ -27,3 +27,4 @@ #include "../rtwrap.c" #include "../load.c" #include "../main.c" +#include "../jit/nvm.c" diff --git a/src/utils/builtins.h b/src/utils/builtins.h index 07837955..953f9141 100644 --- a/src/utils/builtins.h +++ b/src/utils/builtins.h @@ -7,7 +7,7 @@ /* sfns.c*/A(shape,"⥊") A(pick,"⊑") A(pair,"{𝕨‿𝕩}") A(select,"⊏") A(slash,"/") A(join,"∾") A(couple,"≍") A(shiftb,"»") A(shifta,"«") A(take,"↑") A(drop,"↓") A(group,"⊔") A(reverse,"⌽") \ /* sort.c*/A(gradeUp,"⍋") A(gradeDown,"⍒") \ /* sysfn.c*/M(type,"•Type") M(decp,"•Decompose") M(primInd,"•PrimInd") M(glyph,"•Glyph") A(fill,"•FillFn") M(sys,"•getsys") A(grLen,"•GroupLen") D(grOrd,"•groupOrd") \ -/* sysfn.c*/M(repr,"•Repr") A(asrt,"!") M(out,"•Out") M(show,"•Show") M(bqn,"•BQN") D(cmp,"•Cmp") A(hash,"•Hash") M(makeRand,"•MakeRand") M(exit,"•Exit") \ +/* sysfn.c*/M(repr,"•Repr") A(asrt,"!") M(out,"•Out") M(show,"•Show") M(bqn,"•BQN") D(cmp,"•Cmp") A(hash,"•Hash") M(delay,"•Delay") M(makeRand,"•MakeRand") M(exit,"•Exit") \ /*internal.c*/M(itype,"•internal.Type") M(refc,"•internal.Refc") M(squeeze,"•internal.Squeeze") M(isPure,"•internal.IsPure") A(info,"•internal.Info") \ /*internal.c*/D(variation,"•internal.Variation") A(listVariations,"•internal.ListVariations") M(clearRefs,"•internal.ClearRefs") M(unshare,"•internal.Unshare") diff --git a/src/utils/file.c b/src/utils/file.c index ef84f8e1..923f7e61 100644 --- a/src/utils/file.c +++ b/src/utils/file.c @@ -3,7 +3,7 @@ #include "file.h" #include "mut.h" -static FILE* file_open(B path, char* desc, char* mode) { // doesn't consume +FILE* file_open(B path, char* desc, char* mode) { // doesn't consume u64 plen = utf8lenB(path); TALLOC(char, p, plen+1); toUTF8(path, p); diff --git a/src/utils/file.h b/src/utils/file.h index 0c49a18d..42ba39eb 100644 --- a/src/utils/file.h +++ b/src/utils/file.h @@ -9,6 +9,7 @@ typedef struct TmpFile { // to be turned into a proper I8Arr B path_resolve(B base, B rel); // consumes rel; assumes base is a char vector or bi_N B path_dir(B path); // consumes; returns directory part of file path, with trailing slash +FILE* file_open(B path, char* desc, char* mode); // doesn't consume path TmpFile* file_bytes(B path); // consumes B file_chars(B path); // consumes diff --git a/src/vm.c b/src/vm.c index 7fab0d11..6017d75d 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1,53 +1,21 @@ #include #include "core.h" #include "vm.h" +#include "jit/nvm.h" #include "ns.h" #include "utils/utf.h" -enum { - PUSH = 0, // N; push object from objs[N] - VARO = 1, // N; push variable with name strs[N] - VARM = 2, // N; push mutable variable with name strs[N] - ARRO = 3, // N; create a vector of top N items - ARRM = 4, // N; create a mutable vector of top N items - FN1C = 5, // monadic function call ⟨…,x,f ⟩ → F x - FN2C = 6, // dyadic function call ⟨…,x,f,w⟩ → w F x - OP1D = 7, // derive 1-modifier to function; ⟨…, _m,f⟩ → (f _m) - OP2D = 8, // derive 2-modifier to function; ⟨…,g,_m,f⟩ → (f _m_ g) - TR2D = 9, // derive 2-train aka atop; ⟨…, g,f⟩ → (f g) - TR3D = 10, // derive 3-train aka fork; ⟨…,h,g,f⟩ → (f g h) - SETN = 11, // set new; _ ←_; ⟨…,x, mut⟩ → mut←x - SETU = 12, // set upd; _ ↩_; ⟨…,x, mut⟩ → mut↩x - SETM = 13, // set mod; _ F↩_; ⟨…,x,F,mut⟩ → mut F↩x - POPS = 14, // pop object from stack - DFND = 15, // N; push dfns[N], derived to current scope - FN1O = 16, // optional monadic call (FN1C but checks for · at 𝕩) - FN2O = 17, // optional dyadic call (FN2C but checks for · at 𝕩 & 𝕨) - CHKV = 18, // throw error if top of stack is · - TR3O = 19, // TR3D but creates an atop if F is · - OP2H = 20, // derive 2-modifier to 1-modifier ⟨…,g,_m_⟩ → (_m_ g) - LOCO = 21, // N0,N1; push variable at depth N0 and position N1 - LOCM = 22, // N0,N1; push mutable variable at depth N0 and position N1 - VFYM = 23, // push a mutable version of ToS that fails if set to a non-equal value (for header assignment) - SETH = 24, // 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 - RETN = 25, // returns top of stack - FLDO = 26, // N; get field nameList[N] from ToS - FLDM = 27, // N; get mutable field nameList[N] from ToS - NSPM = 28, // N; replace ToS with one with a namespace field alias N - RETD = 29, // return a namespace of exported items - SYSV = 30, // N; get system function N - LOCU = 31, // N0,N1; like LOCO but overrides the slot with bi_optOut - EXTO, EXTM, EXTU, // alternate versions of LOC_ for extended variables - ADDI, ADDU, // separate PUSH for refcounting needed/not needed (stores the object inline, instead of reading from `objs`) - BC_SIZE -}; +#ifndef USE_JIT + #define USE_JIT 1 // enable the extremely basic JIT that just genrates MOVs and CALLs +#endif + #define FOR_BC(F) F(PUSH) F(VARO) F(VARM) F(ARRO) F(ARRM) F(FN1C) F(FN2C) F(OP1D) F(OP2D) F(TR2D) \ F(TR3D) F(SETN) F(SETU) F(SETM) F(POPS) F(DFND) F(FN1O) F(FN2O) F(CHKV) F(TR3O) \ F(OP2H) F(LOCO) F(LOCM) F(VFYM) F(SETH) F(RETN) F(FLDO) F(FLDM) F(NSPM) F(RETD) F(SYSV) F(LOCU) \ F(EXTO) F(EXTM) F(EXTU) -i32* nextBC(i32* p) { +u32* nextBC(u32* p) { switch(*p) { case FN1C: case FN2C: case FN1O: case FN2O: case OP1D: case OP2D: case OP2H: @@ -61,13 +29,14 @@ i32* nextBC(i32* p) { return p+2; case LOCO: case LOCM: case LOCU: case EXTO: case EXTM: case EXTU: + case ADDI: case ADDU: return p+3; default: return 0; } } -i32 stackDiff(i32* p) { +i32 stackDiff(u32* p) { switch(*p) { - case PUSH: case VARO: case VARM: case DFND: case LOCO: case LOCM: case LOCU: case EXTO: case EXTM: case EXTU: case SYSV: return 1; + 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 CHKV: case VFYM: case FLDO: case FLDM: case RETD: case NSPM: 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; @@ -75,16 +44,16 @@ i32 stackDiff(i32* p) { default: return 9999999; } } -char* nameBC(i32* p) { +char* nameBC(u32* p) { switch(*p) { default: return "(unknown)"; #define F(X) case X: return #X; FOR_BC(F) #undef F } } -void printBC(i32* p) { +void printBC(u32* p) { printf("%s", nameBC(p)); - i32* n = nextBC(p); + u32* n = nextBC(p); p++; i64 am = n-p; i32 len = 0; @@ -123,7 +92,7 @@ void gsPrint() { } } -Block* compileBlock(B block, Comp* comp, bool* bDone, i32* bc, usz bcIA, B blocks, B nameList, Scope* sc, i32 depth) { +Block* compileBlock(B block, Comp* comp, bool* bDone, u32* bc, usz bcIA, B blocks, B nameList, Scope* sc, i32 depth) { usz cIA = a(block)->ia; if (cIA!=4 && cIA!=6) thrM("VM compiler: Bad block info size"); BS2B bgetU = TI(block).getU; @@ -160,9 +129,9 @@ Block* compileBlock(B block, Comp* comp, bool* bDone, i32* bc, usz bcIA, B block sc->ext = nE; } } - i32* c = bc+idx; + u32* c = bc+idx; while (true) { - i32* n = nextBC(c); + u32* n = nextBC(c); if (n-bc-1 >= bcIA) thrM("VM compiler: No RETN/RETD found before end of bytecode"); bool ret = false; #define A64(X) { u64 a64=(X); TSADD(nBCT, (u32)a64); TSADD(nBCT, a64>>32); } @@ -210,7 +179,7 @@ Block* compileBlock(B block, Comp* comp, bool* bDone, i32* bc, usz bcIA, B block break; } default: { - i32* ccpy = c; + u32* ccpy = c; while (ccpy!=n) TSADD(nBCT, *ccpy++); break; } @@ -241,6 +210,7 @@ Block* compileBlock(B block, Comp* comp, bool* bDone, i32* bc, usz bcIA, B block Body* body = mm_allocN(fsizeof(Body,varIDs,i32,vam), t_body); body->comp = comp; ptr_inc(comp); body->bc = (u32*)nbc; + body->nvm = NULL; body->map = map; body->blocks = nBl; body->maxStack = hM; @@ -263,7 +233,7 @@ Block* compileBlock(B block, Comp* comp, bool* bDone, i32* bc, usz bcIA, B block NOINLINE Block* compile(B bcq, B objs, B blocks, B indices, B tokenInfo, B src, Scope* sc) { usz bIA = a(blocks)->ia; I32Arr* bca = toI32Arr(bcq); - i32* bc = bca->a; + u32* bc = (u32*)bca->a; usz bcIA = bca->ia; Comp* comp = mm_allocN(sizeof(Comp), t_comp); comp->bc = tag(bca, ARR_TAG); @@ -297,24 +267,8 @@ NOINLINE Block* compile(B bcq, B objs, B blocks, B indices, B tokenInfo, B src, -typedef struct FldAlias { - struct Value; - B obj; - i32 p; -} FldAlias; -static NOINLINE void v_setR(Scope* pscs[], B s, B x, bool upd); -static void v_set(Scope* pscs[], B s, B x, bool upd) { // doesn't consume - if (RARE(!isVar(s))) return v_setR(pscs, s, x, upd);; - Scope* sc = pscs[(u16)(s.u>>32)]; - B prev = sc->vars[(u32)s.u]; - if (upd) { - if (prev.u==bi_noVar.u) thrM("↩: Updating undefined variable"); - dec(prev); - } - sc->vars[(u32)s.u] = inc(x); -} -static NOINLINE void v_setR(Scope* pscs[], B s, B x, bool upd) { +NOINLINE void v_setR(Scope* pscs[], B s, B x, bool upd) { if (isExt(s)) { Scope* sc = pscs[(u16)(s.u>>32)]; B prev = sc->ext->vars[(u32)s.u]; @@ -356,15 +310,7 @@ static NOINLINE void v_setR(Scope* pscs[], B s, B x, bool upd) { -static NOINLINE B v_getR(Scope* pscs[], B s); -static B v_get(Scope* pscs[], B s) { // get value representing s, replacing with bi_optOut; doesn't consume - if (RARE(!isVar(s))) return v_getR(pscs, s); - Scope* sc = pscs[(u16)(s.u>>32)]; - B r = sc->vars[(u32)s.u]; - sc->vars[(u32)s.u] = bi_optOut; - return r; -} -static NOINLINE B v_getR(Scope* pscs[], B s) { +NOINLINE B v_getR(Scope* pscs[], B s) { if (isExt(s)) { Scope* sc = pscs[(u16)(s.u>>32)]; B r = sc->ext->vars[(u32)s.u]; @@ -615,7 +561,14 @@ B actualExec(Block* bl, Scope* psc, i32 ga, B* svar) { // consumes svar contents i32 i = 0; while (ivars[i] = svar[i]; i++; } while (ivars[i++] = bi_noVar; - B r = evalBC(body, sc); + bool jit = USE_JIT; // body->bc[2]==m_f64(123456).u>>32; // enable JIT just for blocks starting with `123456⋄` + B r; + if (jit) { + if (!body->nvm) body->nvm = m_nvm(body); + r = evalJIT(body, sc, body->nvm); + } else { + r = evalBC(body, sc); + } if (sc->refc>1) { usz innerRef = 1; for (i = 0; i < varAm; i++) { @@ -677,8 +630,16 @@ void scope_free(Value* x) { u16 am = c->varAm; for (u32 i = 0; i < am; i++) dec(c->vars[i]); } +void body_free(Value* x) { + Body* c = (Body*) x; + if(c->nsDesc) ptr_decR(c->nsDesc); + if(c->blocks) ptr_decR(c->blocks); + if(c->nvm ) nvm_free(c->nvm); + ptr_decR(c->comp); + ptr_decR(RFLD(c->bc, I32Arr,a)); + ptr_decR(RFLD(c->map,I32Arr,a)); +} void comp_free(Value* x) { Comp* c = (Comp *)x; ptr_decR(c->objs); decR(c->bc); decR(c->src); decR(c->indices); } -void body_free(Value* x) { Body* c = (Body *)x; ptr_decR(c->comp); if(c->nsDesc)ptr_decR(c->nsDesc); if(c->blocks)ptr_decR(c->blocks); ptr_decR(RFLD(c->bc,I32Arr,a)); ptr_decR(RFLD(c->map,I32Arr,a)); } void block_free(Value* x) { Block* c = (Block *)x; ptr_decR(c->body); } void funBl_free(Value* x) { FunBlock* c = (FunBlock*)x; ptr_decR(c->sc); ptr_decR(c->bl); } void md1Bl_free(Value* x) { Md1Block* c = (Md1Block*)x; ptr_decR(c->sc); ptr_decR(c->bl); } @@ -695,8 +656,15 @@ void scope_visit(Value* x) { u16 am = c->varAm; for (u32 i = 0; i < am; i++) mm_visit(c->vars[i]); } +void body_visit(Value* x) { + Body* c = (Body*) x; + if(c->nsDesc) mm_visitP(c->nsDesc); + if(c->blocks) mm_visitP(c->blocks); + mm_visitP(c->comp); + mm_visitP(RFLD(c->bc, I32Arr,a)); + mm_visitP(RFLD(c->map,I32Arr,a)); +} void comp_visit(Value* x) { Comp* c = (Comp *)x; mm_visitP(c->objs); mm_visit(c->bc); mm_visit(c->src); mm_visit(c->indices); } -void body_visit(Value* x) { Body* c = (Body *)x; mm_visitP(c->comp); if(c->nsDesc)mm_visitP(c->nsDesc); if(c->blocks)mm_visitP(c->blocks); mm_visitP(RFLD(c->bc,I32Arr,a)); mm_visitP(RFLD(c->map,I32Arr,a)); } void block_visit(Value* x) { Block* c = (Block *)x; mm_visitP(c->body); } void funBl_visit(Value* x) { FunBlock* c = (FunBlock*)x; mm_visitP(c->sc); mm_visitP(c->bl); } void md1Bl_visit(Value* x) { Md1Block* c = (Md1Block*)x; mm_visitP(c->sc); mm_visitP(c->bl); } diff --git a/src/vm.h b/src/vm.h index f0d12d2b..c4a51de5 100644 --- a/src/vm.h +++ b/src/vm.h @@ -1,4 +1,43 @@ #pragma once + +enum { + PUSH = 0, // N; push object from objs[N] + VARO = 1, // N; push variable with name strs[N] + VARM = 2, // N; push mutable variable with name strs[N] + ARRO = 3, // N; create a vector of top N items + ARRM = 4, // N; create a mutable vector of top N items + FN1C = 5, // monadic function call ⟨…,x,f ⟩ → F x + FN2C = 6, // dyadic function call ⟨…,x,f,w⟩ → w F x + OP1D = 7, // derive 1-modifier to function; ⟨…, _m,f⟩ → (f _m) + OP2D = 8, // derive 2-modifier to function; ⟨…,g,_m,f⟩ → (f _m_ g) + TR2D = 9, // derive 2-train aka atop; ⟨…, g,f⟩ → (f g) + TR3D = 10, // derive 3-train aka fork; ⟨…,h,g,f⟩ → (f g h) + SETN = 11, // set new; _ ←_; ⟨…,x, mut⟩ → mut←x + SETU = 12, // set upd; _ ↩_; ⟨…,x, mut⟩ → mut↩x + SETM = 13, // set mod; _ F↩_; ⟨…,x,F,mut⟩ → mut F↩x + POPS = 14, // pop object from stack + DFND = 15, // N; push dfns[N], derived to current scope + FN1O = 16, // optional monadic call (FN1C but checks for · at 𝕩) + FN2O = 17, // optional dyadic call (FN2C but checks for · at 𝕩 & 𝕨) + CHKV = 18, // throw error if top of stack is · + TR3O = 19, // TR3D but creates an atop if F is · + OP2H = 20, // derive 2-modifier to 1-modifier ⟨…,g,_m_⟩ → (_m_ g) + LOCO = 21, // N0,N1; push variable at depth N0 and position N1 + LOCM = 22, // N0,N1; push mutable variable at depth N0 and position N1 + VFYM = 23, // push a mutable version of ToS that fails if set to a non-equal value (for header assignment) + SETH = 24, // 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 + RETN = 25, // returns top of stack + FLDO = 26, // N; get field nameList[N] from ToS + FLDM = 27, // N; get mutable field nameList[N] from ToS + NSPM = 28, // N; replace ToS with one with a namespace field alias N + RETD = 29, // return a namespace of exported items + SYSV = 30, // N; get system function N + LOCU = 31, // N0,N1; like LOCO but overrides the slot with bi_optOut + EXTO, EXTM, EXTU, // alternate versions of LOC_ for extended variables + ADDI, ADDU, // separate PUSH for refcounting needed/not needed (stores the object inline, instead of reading from `objs`) + BC_SIZE +}; + typedef struct Comp Comp; typedef struct BlBlocks BlBlocks; typedef struct Block Block; @@ -36,6 +75,7 @@ struct Body { // B* objs; u32* bc; // pointer in an owned I32Arr i32* map; // pointer in an owned I32Arr + u8* nvm; // either NULL or a pointer to machine code otherwise u32 maxStack; u16 maxPSC; u16 varAm; @@ -64,13 +104,9 @@ Block* compile(B bcq, B objs, B blocksq, B indices, B tokenInfo, B src, Scope* s Scope* m_scope(Body* body, Scope* psc, u16 varAm); B evalBC(Body* b, Scope* sc); // doesn't consume; executes bytecode of the body directly in the scope -typedef struct Env { - Scope* sc; - union { u32* bcL; i32 bcV; }; -} Env; +u32* nextBC(u32* p); +i32 stackDiff(u32* p); -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; @@ -85,10 +121,13 @@ B m_md2Block(Block* bl, Scope* psc); +typedef struct Env { + Scope* sc; + union { u32* bcL; i32 bcV; }; +} Env; extern Env* envCurr; extern Env* envStart; extern Env* envEnd; - static inline void pushEnv(Scope* sc, u32* bc) { if (envCurr==envEnd) thrM("Stack overflow"); envCurr->sc = sc; @@ -99,3 +138,35 @@ static inline void popEnv() { assert(envCurr>envStart); envCurr--; } +void vm_pst(Env* s, Env* e); +void vm_pstLive(); +void vm_printPos(Comp* comp, i32 bcPos, i64 pos); + + + +typedef struct FldAlias { + struct Value; + B obj; + i32 p; +} FldAlias; + +NOINLINE B v_getR(Scope* pscs[], B s); +static B v_get(Scope* pscs[], B s) { // get value representing s, replacing with bi_optOut; doesn't consume + if (RARE(!isVar(s))) return v_getR(pscs, s); + Scope* sc = pscs[(u16)(s.u>>32)]; + B r = sc->vars[(u32)s.u]; + sc->vars[(u32)s.u] = bi_optOut; + return r; +} + +NOINLINE void v_setR(Scope* pscs[], B s, B x, bool upd); +static void v_set(Scope* pscs[], B s, B x, bool upd) { // doesn't consume + if (RARE(!isVar(s))) return v_setR(pscs, s, x, upd);; + Scope* sc = pscs[(u16)(s.u>>32)]; + B prev = sc->vars[(u32)s.u]; + if (upd) { + if (prev.u==bi_noVar.u) thrM("↩: Updating undefined variable"); + dec(prev); + } + sc->vars[(u32)s.u] = inc(x); +}