209 lines
6.9 KiB
C
209 lines
6.9 KiB
C
#pragma once
|
||
|
||
|
||
#if (defined(__x86_64) || defined(__amd64__)) && defined(MAP_32BIT) && MM!=0
|
||
#ifndef JIT_START
|
||
#define JIT_START 2 // number of calls for when to start JITting. -1: never JIT; 0: JIT everything, n: JIT after n non-JIT invocations; max ¯1+2⋆16
|
||
#endif
|
||
#else
|
||
#undef JIT_START
|
||
#define JIT_START -1
|
||
#endif
|
||
|
||
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 as 2 u32s, instead of reading from `objs`)
|
||
FN1Ci, FN1Oi, FN2Ci, FN2Oi, // FN__ alternatives that don't take the function from the stack, but instead as an 2×u32 immediate in the bytecode
|
||
SETNi, SETUi, SETMi, // SET_ alternatives that expect the set variable as a depth-position pair like LOC_
|
||
SETNv, SETUv, SETMv, // SET_i alternatives that also don't return the result
|
||
BC_SIZE
|
||
};
|
||
|
||
typedef struct Comp Comp;
|
||
typedef struct BlBlocks BlBlocks;
|
||
typedef struct Block Block;
|
||
typedef struct Body Body;
|
||
typedef struct Scope Scope;
|
||
typedef struct ScopeExt ScopeExt;
|
||
|
||
typedef struct Comp {
|
||
struct Value;
|
||
B bc;
|
||
B src;
|
||
B path;
|
||
B indices;
|
||
HArr* objs;
|
||
u32 blockAm;
|
||
} Comp;
|
||
|
||
struct BlBlocks {
|
||
struct Value;
|
||
u32 am;
|
||
Block* a[];
|
||
};
|
||
|
||
struct Block {
|
||
struct Value;
|
||
bool imm;
|
||
u8 ty;
|
||
Body* body;
|
||
};
|
||
|
||
typedef struct NSDesc NSDesc;
|
||
struct Body {
|
||
struct Value;
|
||
Comp* comp;
|
||
BlBlocks* blocks;
|
||
// B* objs;
|
||
u32* bc; // pointer in an owned I32Arr
|
||
i32* map; // pointer in an owned I32Arr
|
||
#if JIT_START > 255
|
||
u16 callCount;
|
||
#elif JIT_START > 0
|
||
u8 callCount;
|
||
#endif
|
||
#if JIT_START != -1
|
||
u8* nvm; // either NULL or a pointer to machine code
|
||
#endif
|
||
u32 maxStack;
|
||
u16 maxPSC;
|
||
u16 varAm;
|
||
NSDesc* nsDesc;
|
||
#if JIT_START != -1
|
||
B nvmRefs;
|
||
#endif
|
||
i32 varIDs[];
|
||
};
|
||
|
||
struct ScopeExt {
|
||
struct Value;
|
||
u16 varAm;
|
||
B vars[]; // vars has length varAm*2; position varAm and onwards are corresponding names to variables at regular indexes
|
||
};
|
||
|
||
struct Scope {
|
||
struct Value;
|
||
Scope* psc;
|
||
Body* body; // last place where code was executed in this scope; also used for variable name resolution, so can only safely be replaced if varAm==0
|
||
u16 varAm;
|
||
ScopeExt* ext; // will probably be NULL
|
||
B vars[];
|
||
};
|
||
|
||
Block* bqn_comp(B str, B path, B args);
|
||
Block* bqn_compSc(B str, B path, B args, Scope* sc, bool repl);
|
||
Block* compile(B bcq, B objs, B blocksq, B indices, B tokenInfo, B src, B path, Scope* sc);
|
||
Scope* m_scope(Body* body, Scope* psc, u16 varAm, i32 initVarAm, B* initVars);
|
||
B execBodyInline(Body* b, Scope* sc); // doesn't consume; executes bytecode of the body directly in the scope
|
||
|
||
u32* nextBC(u32* p);
|
||
i32 stackDiff(u32* p);
|
||
i32 stackConsumed(u32* p);
|
||
i32 stackAdded(u32* p);
|
||
char* nameBC(u32* p);
|
||
|
||
|
||
typedef struct FunBlock { struct Fun; Scope* sc; Block* bl; } FunBlock;
|
||
typedef struct Md1Block { struct Md1; Scope* sc; Block* bl; } Md1Block;
|
||
typedef struct Md2Block { struct Md2; Scope* sc; Block* bl; } Md2Block;
|
||
// all don't consume anything
|
||
B m_funBlock(Block* bl, Scope* psc); // may return evaluated result, whatever
|
||
B m_md1Block(Block* bl, Scope* psc);
|
||
B m_md2Block(Block* bl, Scope* psc);
|
||
|
||
|
||
|
||
|
||
|
||
|
||
typedef struct Env {
|
||
u64 pos; // if top bit set, ((u32)pos)>>1 is an offset into bytecode; otherwise, it's a pointer in the bytecode
|
||
Scope* sc;
|
||
} Env;
|
||
extern Env* envCurr;
|
||
extern Env* envStart;
|
||
extern Env* envEnd;
|
||
extern u64 envPrevHeight; // envStart+prevEnvHeight gives envCurr+1 from before the error
|
||
static inline void pushEnv(Scope* sc, u32* bc) {
|
||
if (envCurr+1==envEnd) thrM("Stack overflow");
|
||
envCurr++;
|
||
envCurr->sc = sc;
|
||
envCurr->pos = (u64)bc;
|
||
}
|
||
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);
|
||
NOINLINE B vm_fmtPoint(B src, B prepend, B path, usz cs, usz ce); // consumes prepend
|
||
NOINLINE void printErrMsg(B msg);
|
||
NOINLINE void unwindEnv(Env* envNew); // envNew==envStart-1 for emptying the env stack
|
||
NOINLINE void unwindCompiler(); // unwind to the env of the invocation of the compiler; UB when not in compiler!
|
||
|
||
|
||
typedef struct FldAlias {
|
||
struct Value;
|
||
B obj;
|
||
i32 p;
|
||
} FldAlias;
|
||
|
||
|
||
NOINLINE B v_getR(Scope* pscs[], B s); // doesn't consume
|
||
static inline B v_getI(Scope* sc, u32 p) {
|
||
B r = sc->vars[p];
|
||
sc->vars[p] = bi_optOut;
|
||
return r;
|
||
}
|
||
static inline 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);
|
||
return v_getI(pscs[(u16)(s.u>>32)], (u32)s.u);
|
||
}
|
||
|
||
NOINLINE void v_setR(Scope* pscs[], B s, B x, bool upd); // doesn't consume
|
||
static inline void v_setI(Scope* sc, u32 p, B x, bool upd) { // consumes x
|
||
B prev = sc->vars[p];
|
||
if (upd) {
|
||
if (prev.u==bi_noVar.u) thrM("↩: Updating undefined variable");
|
||
dec(prev);
|
||
}
|
||
sc->vars[p] = x;
|
||
}
|
||
static inline 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);
|
||
v_setI(pscs[(u16)(s.u>>32)], (u32)s.u, inc(x), upd);
|
||
}
|