extremely basic x86-64 generator

This commit is contained in:
dzaima 2021-06-09 18:42:36 +03:00
parent 96f3089410
commit 550b91688a
13 changed files with 713 additions and 91 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ perf.*
obj/*/*.o
obj/*/*.d
obj/*/*.tmp
tmp_*

View File

@ -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-

View File

@ -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);

View File

@ -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";

312
src/jit/nvm.c Normal file
View 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
View 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
View 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}

View File

@ -27,3 +27,4 @@
#include "../rtwrap.c"
#include "../load.c"
#include "../main.c"
#include "../jit/nvm.c"

View File

@ -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")

View File

@ -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);

View File

@ -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

122
src/vm.c
View File

@ -1,53 +1,21 @@
#include <unistd.h>
#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 (i<ga) { sc->vars[i] = svar[i]; i++; }
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) {
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); }

View File

@ -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);
}