extremely basic x86-64 generator
This commit is contained in:
parent
96f3089410
commit
550b91688a
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@ perf.*
|
|||||||
obj/*/*.o
|
obj/*/*.o
|
||||||
obj/*/*.d
|
obj/*/*.d
|
||||||
obj/*/*.tmp
|
obj/*/*.tmp
|
||||||
|
tmp_*
|
||||||
@ -20,7 +20,7 @@ heapverify: gen
|
|||||||
rtverify: FLAGS=-DDEBUG -O3 -DRT_VERIFY
|
rtverify: FLAGS=-DDEBUG -O3 -DRT_VERIFY
|
||||||
rtverify: gen
|
rtverify: gen
|
||||||
|
|
||||||
gen: builtins core base utils
|
gen: builtins core base jit utils
|
||||||
@$(CC) -o BQN *.o -lm
|
@$(CC) -o BQN *.o -lm
|
||||||
@mv BQN ../../BQN
|
@mv BQN ../../BQN
|
||||||
@echo
|
@echo
|
||||||
@ -40,6 +40,11 @@ utils: utf.o hash.o file.o
|
|||||||
@echo $< | cut -c 11-
|
@echo $< | cut -c 11-
|
||||||
@$(CMD) $@.d -c $<
|
@$(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
|
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
|
%.o: ../../src/builtins/%.c
|
||||||
@echo $< | cut -c 11-
|
@echo $< | cut -c 11-
|
||||||
|
|||||||
@ -278,6 +278,16 @@ B list_c1(B d, B x) {
|
|||||||
return file_list(path_resolve(nfn_objU(d), 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) {
|
B exit_c1(B t, B x) {
|
||||||
bqn_exit(q_i32(x)? o2i(x) : 0);
|
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"bqn")) r.a[i] = inc(bi_bqn);
|
||||||
else if (eqStr(c, U"cmp")) r.a[i] = inc(bi_cmp);
|
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"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"hash")) r.a[i] = inc(bi_hash);
|
||||||
else if (eqStr(c, U"repr")) r.a[i] = inc(bi_repr);
|
else if (eqStr(c, U"repr")) r.a[i] = inc(bi_repr);
|
||||||
else if (eqStr(c, U"makerand")) r.a[i] = inc(bi_makeRand);
|
else if (eqStr(c, U"makerand")) r.a[i] = inc(bi_makeRand);
|
||||||
|
|||||||
@ -324,15 +324,15 @@ void slice_print(B x) { arr_print(x); }
|
|||||||
char* format_type(u8 u) {
|
char* format_type(u8 u) {
|
||||||
switch(u) { default: return"(unknown type)";
|
switch(u) { default: return"(unknown type)";
|
||||||
case t_empty:return"empty"; case t_shape:return"shape";
|
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_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_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_md2BI:return"builtin md2"; case t_md2_block:return"md2 block";
|
||||||
case t_fork:return"fork"; case t_atop:return"atop";
|
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_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_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_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_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";
|
case t_freed:return"(freed by GC)"; case t_harrPartial:return"partHarr";
|
||||||
#ifdef RT_WRAP
|
#ifdef RT_WRAP
|
||||||
case t_funWrap:return"wrapped function"; case t_md1Wrap:return"wrapped 1-modifier"; case t_md2Wrap:return "wrapped 2-modifier";
|
case t_funWrap:return"wrapped function"; case t_md1Wrap:return"wrapped 1-modifier"; case t_md2Wrap:return "wrapped 2-modifier";
|
||||||
|
|||||||
312
src/jit/nvm.c
Normal file
312
src/jit/nvm.c
Normal file
@ -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-<pid>.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 <unistd.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
6
src/jit/nvm.h
Normal file
6
src/jit/nvm.h
Normal file
@ -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);
|
||||||
246
src/jit/x86_64.h
Normal file
246
src/jit/x86_64.h
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
/* taken from I: https://github.com/mlochbaum/ILanguage/blob/master/x86_64.h
|
||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2016, Marshall Lochbaum <mwlochbaum@gmail.com>
|
||||||
|
|
||||||
|
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}
|
||||||
@ -27,3 +27,4 @@
|
|||||||
#include "../rtwrap.c"
|
#include "../rtwrap.c"
|
||||||
#include "../load.c"
|
#include "../load.c"
|
||||||
#include "../main.c"
|
#include "../main.c"
|
||||||
|
#include "../jit/nvm.c"
|
||||||
|
|||||||
@ -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,"⌽") \
|
/* 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,"⍒") \
|
/* 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(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*/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")
|
/*internal.c*/D(variation,"•internal.Variation") A(listVariations,"•internal.ListVariations") M(clearRefs,"•internal.ClearRefs") M(unshare,"•internal.Unshare")
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
#include "file.h"
|
#include "file.h"
|
||||||
#include "mut.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);
|
u64 plen = utf8lenB(path);
|
||||||
TALLOC(char, p, plen+1);
|
TALLOC(char, p, plen+1);
|
||||||
toUTF8(path, p);
|
toUTF8(path, p);
|
||||||
|
|||||||
@ -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_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
|
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
|
TmpFile* file_bytes(B path); // consumes
|
||||||
B file_chars(B path); // consumes
|
B file_chars(B path); // consumes
|
||||||
|
|
||||||
|
|||||||
122
src/vm.c
122
src/vm.c
@ -1,53 +1,21 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
|
#include "jit/nvm.h"
|
||||||
#include "ns.h"
|
#include "ns.h"
|
||||||
#include "utils/utf.h"
|
#include "utils/utf.h"
|
||||||
|
|
||||||
enum {
|
#ifndef USE_JIT
|
||||||
PUSH = 0, // N; push object from objs[N]
|
#define USE_JIT 1 // enable the extremely basic JIT that just genrates MOVs and CALLs
|
||||||
VARO = 1, // N; push variable with name strs[N]
|
#endif
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
#define FOR_BC(F) F(PUSH) F(VARO) F(VARM) F(ARRO) F(ARRM) F(FN1C) F(FN2C) F(OP1D) F(OP2D) F(TR2D) \
|
#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(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(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)
|
F(EXTO) F(EXTM) F(EXTU)
|
||||||
|
|
||||||
i32* nextBC(i32* p) {
|
u32* nextBC(u32* p) {
|
||||||
switch(*p) {
|
switch(*p) {
|
||||||
case FN1C: case FN2C: case FN1O: case FN2O:
|
case FN1C: case FN2C: case FN1O: case FN2O:
|
||||||
case OP1D: case OP2D: case OP2H:
|
case OP1D: case OP2D: case OP2H:
|
||||||
@ -61,13 +29,14 @@ i32* nextBC(i32* p) {
|
|||||||
return p+2;
|
return p+2;
|
||||||
case LOCO: case LOCM: case LOCU:
|
case LOCO: case LOCM: case LOCU:
|
||||||
case EXTO: case EXTM: case EXTU:
|
case EXTO: case EXTM: case EXTU:
|
||||||
|
case ADDI: case ADDU:
|
||||||
return p+3;
|
return p+3;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i32 stackDiff(i32* p) {
|
i32 stackDiff(u32* p) {
|
||||||
switch(*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 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 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 FN2C: case OP2D: case TR3D: case SETM: case FN2O: case TR3O: return -2;
|
||||||
@ -75,16 +44,16 @@ i32 stackDiff(i32* p) {
|
|||||||
default: return 9999999;
|
default: return 9999999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
char* nameBC(i32* p) {
|
char* nameBC(u32* p) {
|
||||||
switch(*p) { default: return "(unknown)";
|
switch(*p) { default: return "(unknown)";
|
||||||
#define F(X) case X: return #X;
|
#define F(X) case X: return #X;
|
||||||
FOR_BC(F)
|
FOR_BC(F)
|
||||||
#undef F
|
#undef F
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void printBC(i32* p) {
|
void printBC(u32* p) {
|
||||||
printf("%s", nameBC(p));
|
printf("%s", nameBC(p));
|
||||||
i32* n = nextBC(p);
|
u32* n = nextBC(p);
|
||||||
p++;
|
p++;
|
||||||
i64 am = n-p;
|
i64 am = n-p;
|
||||||
i32 len = 0;
|
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;
|
usz cIA = a(block)->ia;
|
||||||
if (cIA!=4 && cIA!=6) thrM("VM compiler: Bad block info size");
|
if (cIA!=4 && cIA!=6) thrM("VM compiler: Bad block info size");
|
||||||
BS2B bgetU = TI(block).getU;
|
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;
|
sc->ext = nE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i32* c = bc+idx;
|
u32* c = bc+idx;
|
||||||
while (true) {
|
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");
|
if (n-bc-1 >= bcIA) thrM("VM compiler: No RETN/RETD found before end of bytecode");
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
#define A64(X) { u64 a64=(X); TSADD(nBCT, (u32)a64); TSADD(nBCT, a64>>32); }
|
#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;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
i32* ccpy = c;
|
u32* ccpy = c;
|
||||||
while (ccpy!=n) TSADD(nBCT, *ccpy++);
|
while (ccpy!=n) TSADD(nBCT, *ccpy++);
|
||||||
break;
|
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* body = mm_allocN(fsizeof(Body,varIDs,i32,vam), t_body);
|
||||||
body->comp = comp; ptr_inc(comp);
|
body->comp = comp; ptr_inc(comp);
|
||||||
body->bc = (u32*)nbc;
|
body->bc = (u32*)nbc;
|
||||||
|
body->nvm = NULL;
|
||||||
body->map = map;
|
body->map = map;
|
||||||
body->blocks = nBl;
|
body->blocks = nBl;
|
||||||
body->maxStack = hM;
|
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) {
|
NOINLINE Block* compile(B bcq, B objs, B blocks, B indices, B tokenInfo, B src, Scope* sc) {
|
||||||
usz bIA = a(blocks)->ia;
|
usz bIA = a(blocks)->ia;
|
||||||
I32Arr* bca = toI32Arr(bcq);
|
I32Arr* bca = toI32Arr(bcq);
|
||||||
i32* bc = bca->a;
|
u32* bc = (u32*)bca->a;
|
||||||
usz bcIA = bca->ia;
|
usz bcIA = bca->ia;
|
||||||
Comp* comp = mm_allocN(sizeof(Comp), t_comp);
|
Comp* comp = mm_allocN(sizeof(Comp), t_comp);
|
||||||
comp->bc = tag(bca, ARR_TAG);
|
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);
|
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) {
|
|
||||||
if (isExt(s)) {
|
if (isExt(s)) {
|
||||||
Scope* sc = pscs[(u16)(s.u>>32)];
|
Scope* sc = pscs[(u16)(s.u>>32)];
|
||||||
B prev = sc->ext->vars[(u32)s.u];
|
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);
|
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) {
|
|
||||||
if (isExt(s)) {
|
if (isExt(s)) {
|
||||||
Scope* sc = pscs[(u16)(s.u>>32)];
|
Scope* sc = pscs[(u16)(s.u>>32)];
|
||||||
B r = sc->ext->vars[(u32)s.u];
|
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;
|
i32 i = 0;
|
||||||
while (i<ga) { sc->vars[i] = svar[i]; i++; }
|
while (i<ga) { sc->vars[i] = svar[i]; i++; }
|
||||||
while (i<varAm) sc->vars[i++] = bi_noVar;
|
while (i<varAm) sc->vars[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) {
|
if (sc->refc>1) {
|
||||||
usz innerRef = 1;
|
usz innerRef = 1;
|
||||||
for (i = 0; i < varAm; i++) {
|
for (i = 0; i < varAm; i++) {
|
||||||
@ -677,8 +630,16 @@ void scope_free(Value* x) {
|
|||||||
u16 am = c->varAm;
|
u16 am = c->varAm;
|
||||||
for (u32 i = 0; i < am; i++) dec(c->vars[i]);
|
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 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 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 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); }
|
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;
|
u16 am = c->varAm;
|
||||||
for (u32 i = 0; i < am; i++) mm_visit(c->vars[i]);
|
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 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 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 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); }
|
void md1Bl_visit(Value* x) { Md1Block* c = (Md1Block*)x; mm_visitP(c->sc); mm_visitP(c->bl); }
|
||||||
|
|||||||
85
src/vm.h
85
src/vm.h
@ -1,4 +1,43 @@
|
|||||||
#pragma once
|
#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 Comp Comp;
|
||||||
typedef struct BlBlocks BlBlocks;
|
typedef struct BlBlocks BlBlocks;
|
||||||
typedef struct Block Block;
|
typedef struct Block Block;
|
||||||
@ -36,6 +75,7 @@ struct Body {
|
|||||||
// B* objs;
|
// B* objs;
|
||||||
u32* bc; // pointer in an owned I32Arr
|
u32* bc; // pointer in an owned I32Arr
|
||||||
i32* map; // 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;
|
u32 maxStack;
|
||||||
u16 maxPSC;
|
u16 maxPSC;
|
||||||
u16 varAm;
|
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);
|
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
|
B evalBC(Body* b, Scope* sc); // doesn't consume; executes bytecode of the body directly in the scope
|
||||||
|
|
||||||
typedef struct Env {
|
u32* nextBC(u32* p);
|
||||||
Scope* sc;
|
i32 stackDiff(u32* p);
|
||||||
union { u32* bcL; i32 bcV; };
|
|
||||||
} Env;
|
|
||||||
|
|
||||||
void vm_pst(Env* s, Env* e);
|
|
||||||
void vm_pstLive();
|
|
||||||
|
|
||||||
typedef struct FunBlock { struct Fun; Scope* sc; Block* bl; } FunBlock;
|
typedef struct FunBlock { struct Fun; Scope* sc; Block* bl; } FunBlock;
|
||||||
typedef struct Md1Block { struct Md1; Scope* sc; Block* bl; } Md1Block;
|
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* envCurr;
|
||||||
extern Env* envStart;
|
extern Env* envStart;
|
||||||
extern Env* envEnd;
|
extern Env* envEnd;
|
||||||
|
|
||||||
static inline void pushEnv(Scope* sc, u32* bc) {
|
static inline void pushEnv(Scope* sc, u32* bc) {
|
||||||
if (envCurr==envEnd) thrM("Stack overflow");
|
if (envCurr==envEnd) thrM("Stack overflow");
|
||||||
envCurr->sc = sc;
|
envCurr->sc = sc;
|
||||||
@ -99,3 +138,35 @@ static inline void popEnv() {
|
|||||||
assert(envCurr>envStart);
|
assert(envCurr>envStart);
|
||||||
envCurr--;
|
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);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user