diff --git a/LICENSE b/LICENSE index 099df14..36c4b17 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 ValKmjolnir +Copyright (c) 2019-2023 ValKmjolnir Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/main.cpp b/main.cpp index 0f3a85e..a1f2741 100644 --- a/main.cpp +++ b/main.cpp @@ -13,8 +13,6 @@ #include -using ch_clk=std::chrono::high_resolution_clock; - const u32 VM_AST =0x01; const u32 VM_CODE =0x02; const u32 VM_TIME =0x04; @@ -73,6 +71,8 @@ void err() { } void execute(const string& file,const std::vector& argv,const u32 cmd) { + using clk=std::chrono::high_resolution_clock; + error err; lexer lex(err); parse parse(err); @@ -103,12 +103,12 @@ void execute(const string& file,const std::vector& argv,const u32 cmd) { // run if (cmd&VM_DEBUG) { - debugger(err).run(gen,ld,argv); + dbg(err).run(gen,ld,argv); } else if (cmd&VM_TIME) { - auto start=ch_clk::now(); + auto start=clk::now(); ctx.run(gen,ld,argv,cmd&VM_DETAIL); - auto end=ch_clk::now(); - std::clog<<"process exited after "<<(end-start).count()*1.0/ch_clk::duration::period::den<<"s.\n\n"; + auto end=clk::now(); + std::clog<<"process exited after "<<(end-start).count()*1.0/clk::duration::period::den<<"s.\n\n"; } else if (cmd&VM_EXEC) { ctx.run(gen,ld,argv,cmd&VM_DETAIL); } diff --git a/makefile b/makefile index 0975f38..5d2a28e 100644 --- a/makefile +++ b/makefile @@ -58,7 +58,7 @@ test:nasal @ ./nasal -t test/mandelbrot.nas @ ./nasal -t test/md5.nas @ ./nasal -t -d test/md5compare.nas - -@ ./nasal -d test/module_test.nas + @ ./nasal -d test/module_test.nas @ ./nasal -e test/nasal_test.nas @ ./nasal -t -d test/occupation.nas 2 @ ./nasal -t -d test/pi.nas diff --git a/nasal_builtin.h b/nasal_builtin.h index 79fab65..bd37eff 100644 --- a/nasal_builtin.h +++ b/nasal_builtin.h @@ -981,7 +981,7 @@ var builtin_dlcall(var* local,gc& ngc) { return nas_err("dlcall","\"ptr\" is not a valid function pointer"); } // arguments' stored place begins at local +2 - return ((mod)fp.obj().ptr)(local+2,ngc.top-local-2,&ngc); + return ((mod)fp.obj().ptr)(local+2,ngc.rctx->top-local-2,&ngc); } var builtin_platform(var* local,gc& ngc) { @@ -1108,19 +1108,19 @@ var builtin_cocreate(var* local,gc& ngc) { } var co=ngc.alloc(vm_co); nas_co& cort=co.co(); - cort.pc=func.func().entry-1; + cort.ctx.pc=func.func().entry-1; - cort.top[0]=nil; - cort.localr=cort.top+1; - cort.top=cort.localr+func.func().lsize; - cort.localr[0]=func.func().local[0]; - cort.top[0]=nil; // old upvalr - cort.top++; - cort.top[0]=var::addr((var*)nullptr); // old localr - cort.top++; - cort.top[0]=var::ret(0); // old pc, set to zero to make op_ret recognizing this as coroutine function + cort.ctx.top[0]=nil; + cort.ctx.localr=cort.ctx.top+1; + cort.ctx.top=cort.ctx.localr+func.func().lsize; + cort.ctx.localr[0]=func.func().local[0]; + cort.ctx.top[0]=nil; // old upvalr + cort.ctx.top++; + cort.ctx.top[0]=var::addr((var*)nullptr); // old localr + cort.ctx.top++; + cort.ctx.top[0]=var::ret(0); // old pc, set to zero to make op_ret recognizing this as coroutine function - cort.funcr=func; // make sure the coroutine function can use correct upvalues + cort.ctx.funcr=func; // make sure the coroutine function can use correct upvalues cort.status=nas_co::suspended; return co; @@ -1145,9 +1145,9 @@ var builtin_coresume(var* local,gc& ngc) { // fetch coroutine's stack top and return // so the coroutine's stack top in fact is not changed - if (ngc.top[0].type==vm_ret) { + if (ngc.rctx->top[0].type==vm_ret) { // when first calling this coroutine, the stack top must be vm_ret - return ngc.top[0]; + return ngc.rctx->top[0]; } // after first calling the coroutine, each time coroutine.yield triggered diff --git a/nasal_dbg.h b/nasal_dbg.h index 4b1803f..b736da8 100644 --- a/nasal_dbg.h +++ b/nasal_dbg.h @@ -6,7 +6,7 @@ #include "nasal_vm.h" #include -class debugger:public vm { +class dbg:public vm { private: bool next; usize fsize; @@ -22,7 +22,7 @@ private: void stepinfo(); void interact(); public: - debugger(error& err): + dbg(error& err): next(false),fsize(0), bk_fidx(0),bk_line(0), src(err) {} @@ -33,7 +33,7 @@ public: ); }; -std::vector debugger::parse(const string& cmd) { +std::vector dbg::parse(const string& cmd) { std::vector res; usize last=0,pos=cmd.find(" ",0); while(pos!=string::npos) { @@ -49,7 +49,7 @@ std::vector debugger::parse(const string& cmd) { return res; } -u16 debugger::fileindex(const string& filename) { +u16 dbg::fileindex(const string& filename) { for(u16 i=0;i\n" <<"\th, help | get help\n" @@ -82,7 +82,7 @@ void debugger::help() { <<"\tbk, break | set break point\n"; } -void debugger::callsort(const u64* arr) { +void dbg::callsort(const u64* arr) { typedef std::pair op; std::vector opcall; u64 total=0; @@ -104,36 +104,38 @@ void debugger::callsort(const u64* arr) { std::clog<<" total : "<>3)==0?0:((line>>3)<<3); u32 end=(1+(line>>3))<<3; - src.load(files[bytecode[pc].fidx]); + src.load(files[bytecode[ctx.pc].fidx]); std::cout<<"\nsource code:\n"; for(u32 i=begin;i ":" ")<>3)==0?0:((pc>>3)<<3); - end=(1+(pc>>3))<<3; + + begin=(ctx.pc>>3)==0?0:((ctx.pc>>3)<<3); + end=(1+(ctx.pc>>3))<<3; codestream::set(cnum,cstr,files); + std::cout<<"next bytecode:\n"; for(u32 i=begin;i ":" ") + <<(i==ctx.pc?back_white:reset) + <<(i==ctx.pc?"--> ":" ") <> "; std::getline(std::cin,cmd); auto res=parse(cmd); - if (res.size()==1) { + if (res.size()==0) { + stepinfo(); + } else if (res.size()==1) { if (res[0]=="h" || res[0]=="help") { help(); } else if (res[0]=="bt" || res[0]=="backtrace") { @@ -193,7 +197,7 @@ void debugger::interact() { } } -void debugger::run( +void dbg::run( const codegen& gen, const linker& linker, const std::vector& argv) @@ -202,205 +206,71 @@ void debugger::run( fsize=linker.filelist().size(); init(gen.strs(),gen.nums(),gen.codes(),linker.filelist(),argv); u64 count[op_ret+1]={0}; -#ifndef _MSC_VER - const void* oprs[]={ - &&vmexit, &&intg, &&intl, &&loadg, - &&loadl, &&loadu, &&pnum, &&pnil, - &&pstr, &&newv, &&newh, &&newf, - &&happ, &¶, &&deft, &&dyn, - &&lnot, &&usub, &&bnot, &&btor, - &&btxor, &&btand, &&add, &&sub, - &&mul, &&div, &&lnk, &&addc, - &&subc, &&mulc, &&divc, &&lnkc, - &&addeq, &&subeq, &&muleq, &&diveq, - &&lnkeq, &&bandeq, &&boreq, &&bxoreq, - &&addeqc, &&subeqc, &&muleqc, &&diveqc, - &&lnkeqc, &&addecp, &&subecp, &&mulecp, - &&divecp, &&lnkecp, &&meq, &&eq, - &&neq, &&less, &&leq, &&grt, - &&geq, &&lessc, &&leqc, &&grtc, - &&geqc, &&pop, &&jmp, &&jt, - &&jf, &&cnt, &&findex, &&feach, - &&callg, &&calll, &&upval, &&callv, - &&callvi, &&callh, &&callfv, &&callfh, - &&callb, &&slcbeg, &&slcend, &&slc, - &&slc2, &&mcallg, &&mcalll, &&mupval, - &&mcallv, &&mcallh, &&ret - }; - std::vector code; - for(auto& i:gen.codes()) { - code.push_back(oprs[i.op]); - imm.push_back(i.num); - } - // goto the first operand - goto *code[pc]; -#else - typedef void (debugger::*nafunc)(); + typedef void (dbg::*nafunc)(); const nafunc oprs[]={ - nullptr, &debugger::o_intg, - &debugger::o_intl, &debugger::o_loadg, - &debugger::o_loadl, &debugger::o_loadu, - &debugger::o_pnum, &debugger::o_pnil, - &debugger::o_pstr, &debugger::o_newv, - &debugger::o_newh, &debugger::o_newf, - &debugger::o_happ, &debugger::o_para, - &debugger::o_deft, &debugger::o_dyn, - &debugger::o_lnot, &debugger::o_usub, - &debugger::o_bnot, &debugger::o_btor, - &debugger::o_btxor, &debugger::o_btand, - &debugger::o_add, &debugger::o_sub, - &debugger::o_mul, &debugger::o_div, - &debugger::o_lnk, &debugger::o_addc, - &debugger::o_subc, &debugger::o_mulc, - &debugger::o_divc, &debugger::o_lnkc, - &debugger::o_addeq, &debugger::o_subeq, - &debugger::o_muleq, &debugger::o_diveq, - &debugger::o_lnkeq, &debugger::o_bandeq, - &debugger::o_boreq, &debugger::o_bxoreq, - &debugger::o_addeqc, &debugger::o_subeqc, - &debugger::o_muleqc, &debugger::o_diveqc, - &debugger::o_lnkeqc, &debugger::o_addecp, - &debugger::o_subecp, &debugger::o_mulecp, - &debugger::o_divecp, &debugger::o_lnkecp, - &debugger::o_meq, &debugger::o_eq, - &debugger::o_neq, &debugger::o_less, - &debugger::o_leq, &debugger::o_grt, - &debugger::o_geq, &debugger::o_lessc, - &debugger::o_leqc, &debugger::o_grtc, - &debugger::o_geqc, &debugger::o_pop, - &debugger::o_jmp, &debugger::o_jt, - &debugger::o_jf, &debugger::o_cnt, - &debugger::o_findex, &debugger::o_feach, - &debugger::o_callg, &debugger::o_calll, - &debugger::o_upval, &debugger::o_callv, - &debugger::o_callvi, &debugger::o_callh, - &debugger::o_callfv, &debugger::o_callfh, - &debugger::o_callb, &debugger::o_slcbeg, - &debugger::o_slcend, &debugger::o_slc, - &debugger::o_slc2, &debugger::o_mcallg, - &debugger::o_mcalll, &debugger::o_mupval, - &debugger::o_mcallv, &debugger::o_mcallh, - &debugger::o_ret + nullptr, &dbg::o_intg, + &dbg::o_intl, &dbg::o_loadg, + &dbg::o_loadl, &dbg::o_loadu, + &dbg::o_pnum, &dbg::o_pnil, + &dbg::o_pstr, &dbg::o_newv, + &dbg::o_newh, &dbg::o_newf, + &dbg::o_happ, &dbg::o_para, + &dbg::o_deft, &dbg::o_dyn, + &dbg::o_lnot, &dbg::o_usub, + &dbg::o_bnot, &dbg::o_btor, + &dbg::o_btxor, &dbg::o_btand, + &dbg::o_add, &dbg::o_sub, + &dbg::o_mul, &dbg::o_div, + &dbg::o_lnk, &dbg::o_addc, + &dbg::o_subc, &dbg::o_mulc, + &dbg::o_divc, &dbg::o_lnkc, + &dbg::o_addeq, &dbg::o_subeq, + &dbg::o_muleq, &dbg::o_diveq, + &dbg::o_lnkeq, &dbg::o_bandeq, + &dbg::o_boreq, &dbg::o_bxoreq, + &dbg::o_addeqc, &dbg::o_subeqc, + &dbg::o_muleqc, &dbg::o_diveqc, + &dbg::o_lnkeqc, &dbg::o_addecp, + &dbg::o_subecp, &dbg::o_mulecp, + &dbg::o_divecp, &dbg::o_lnkecp, + &dbg::o_meq, &dbg::o_eq, + &dbg::o_neq, &dbg::o_less, + &dbg::o_leq, &dbg::o_grt, + &dbg::o_geq, &dbg::o_lessc, + &dbg::o_leqc, &dbg::o_grtc, + &dbg::o_geqc, &dbg::o_pop, + &dbg::o_jmp, &dbg::o_jt, + &dbg::o_jf, &dbg::o_cnt, + &dbg::o_findex, &dbg::o_feach, + &dbg::o_callg, &dbg::o_calll, + &dbg::o_upval, &dbg::o_callv, + &dbg::o_callvi, &dbg::o_callh, + &dbg::o_callfv, &dbg::o_callfh, + &dbg::o_callb, &dbg::o_slcbeg, + &dbg::o_slcend, &dbg::o_slc, + &dbg::o_slc2, &dbg::o_mcallg, + &dbg::o_mcalll, &dbg::o_mupval, + &dbg::o_mcallv, &dbg::o_mcallh, + &dbg::o_ret }; std::vector code; for(auto& i:gen.codes()) { code.push_back(i.op); imm.push_back(i.num); } - while(oprs[code[pc]]) { + while(oprs[code[ctx.pc]]) { interact(); - ++count[code[pc]]; - (this->*oprs[code[pc]])(); - if (top>=canary) { + ++count[code[ctx.pc]]; + (this->*oprs[code[ctx.pc]])(); + if (ctx.top>=ctx.canary) { die("stack overflow"); } - ++pc; + ++ctx.pc; } -#endif -vmexit: callsort(count); ngc.info(); ngc.clear(); imm.clear(); return; -#ifndef _MSC_VER -#define dbg(op,num) {\ - interact();\ - op();\ - ++count[num];\ - if (top strs; // reserved address for const vm_str @@ -535,13 +526,9 @@ struct gc { u64 size[gc_tsize]; u64 gcnt[gc_tsize]; u64 acnt[gc_tsize]; - i64 worktime; + i64 worktime=0; - gc(u32& _pc, var*& _localr, var*& _memr, var& _funcr, - var& _upvalr, var*& _canary, var*& _top, var* _stk): - pc(_pc),localr(_localr),memr(_memr),funcr(_funcr),upvalr(_upvalr), - canary(_canary),top(_top),stack(_stk),cort(nullptr),temp(nil), - worktime(0) {} + gc(context* _ctx): rctx(_ctx) {} void mark(); void sweep(); void extend(u8); @@ -563,11 +550,11 @@ void gc::mark() { // scan main process stack when coroutine ptr is null // this scan process must execute because when running coroutine, // the nas_co related to it will not update it's context(like `top`) until the coroutine suspends or exits. - for(var* i=stack;i<=top;++i) { + for(var* i=rctx->stack;i<=rctx->top;++i) { bfs.push_back(*i); } - bfs.push_back(funcr); - bfs.push_back(upvalr); + bfs.push_back(rctx->funcr); + bfs.push_back(rctx->upvalr); bfs.push_back(temp); // if coroutine is running, scan main process stack from mctx @@ -611,9 +598,9 @@ void gc::mark() { } break; case vm_co: - bfs.push_back(tmp.co().funcr); - bfs.push_back(tmp.co().upvalr); - for(var* i=tmp.co().stack;i<=tmp.co().top;++i) { + bfs.push_back(tmp.co().ctx.funcr); + bfs.push_back(tmp.co().ctx.upvalr); + for(var* i=tmp.co().stack;i<=tmp.co().ctx.top;++i) { bfs.push_back(*i); } break; @@ -653,7 +640,7 @@ void gc::extend(u8 type) { void gc::init(const std::vector& s,const std::vector& argv) { // initialize function register - funcr=nil; + rctx->funcr=nil; worktime=0; // initialize counters @@ -762,29 +749,15 @@ var gc::newstr(const string& buff) { return s; } -void gc::ctxchg(nas_co& ctx) { +void gc::ctxchg(nas_co& co) { // store running state to main context - mctx.pc=pc; - mctx.top=top; - mctx.localr=localr; - mctx.memr=memr; - mctx.funcr=funcr; - mctx.upvalr=upvalr; - mctx.canary=canary; - mctx.stack=stack; + mctx=*rctx; // restore coroutine context state - pc=ctx.pc; - top=ctx.top; - localr=ctx.localr; - memr=ctx.memr; - funcr=ctx.funcr; - upvalr=ctx.upvalr; - canary=ctx.canary; - stack=ctx.stack; + *rctx=co.ctx; // set coroutine pointer - cort=&ctx; + cort=&co; // set coroutine state to running cort->status=nas_co::running; @@ -792,26 +765,13 @@ void gc::ctxchg(nas_co& ctx) { void gc::ctxreserve() { // pc=0 means this coroutine is finished - cort->status=pc? nas_co::suspended:nas_co::dead; + cort->status=rctx->pc? nas_co::suspended:nas_co::dead; // store running state to coroutine - cort->pc=pc; - cort->localr=localr; - cort->memr=memr; - cort->funcr=funcr; - cort->upvalr=upvalr; - cort->canary=canary; - cort->top=top; + cort->ctx=*rctx; // restore main context state - pc=mctx.pc; - localr=mctx.localr; - memr=mctx.memr; - funcr=mctx.funcr; - upvalr=mctx.upvalr; - canary=mctx.canary; - top=mctx.top; - stack=mctx.stack; + *rctx=mctx; // set coroutine pointer to nullptr cort=nullptr; diff --git a/nasal_opcode.h b/nasal_opcode.h index 2f42718..208d024 100644 --- a/nasal_opcode.h +++ b/nasal_opcode.h @@ -141,11 +141,6 @@ public: codestream(const opcode& c,const u32 i):code(c),index(i) {} static void set(const f64*,const string*,const string*); void dump(std::ostream&) const; - - friend std::ostream& operator<<(std::ostream& out,const codestream& ins) { - ins.dump(out); - return out; - } }; const f64* codestream::nums=nullptr; @@ -172,25 +167,19 @@ void codestream::dump(std::ostream& out) const { case op_addeq: case op_subeq: case op_muleq: case op_diveq: case op_lnkeq: case op_meq: case op_btandeq: case op_btoreq: case op_btxoreq: - out< tokname { + const std::unordered_map tokname { {tok::rfor ,"for" }, {tok::forindex,"forindex"}, {tok::foreach ,"foreach" }, @@ -204,7 +204,7 @@ void parse::match(tok type,const char* info) { case tok::num:die(thisspan,"expected number"); break; case tok::str:die(thisspan,"expected string"); break; case tok::id: die(thisspan,"expected identifier");break; - default: die(thisspan,"expected '"+tokname[type]+"'"); break; + default: die(thisspan,"expected '"+tokname.at(type)+"'"); break; } return; } diff --git a/nasal_vm.h b/nasal_vm.h index 521c6ff..b743ddf 100644 --- a/nasal_vm.h +++ b/nasal_vm.h @@ -11,18 +11,12 @@ class vm { protected: /* registers and constants of vm */ - u32 pc; // program counter - var* localr; // local frame pointer - var* memr; // store address got by memory call - var funcr; // function register - var upvalr; // upvalue register - var* canary; // avoid stack overflow, at the top of main stack - var* top; // stack top pointer + context ctx; /* constants */ - const f64* cnum; // constant numbers - const string* cstr; // constant symbols and strings - std::vector imm; // immediate number table + const f64* cnum=nullptr; // constant numbers + const string* cstr=nullptr; // constant symbols and strings + std::vector imm; // immediate number table /* garbage collector */ gc ngc; @@ -31,8 +25,8 @@ protected: var stack[STACK_DEPTH]; /* values used for debugger */ - const string* files; // file name list - const opcode* bytecode; // bytecode buffer address + const string* files=nullptr; // file name list + const opcode* bytecode=nullptr; // bytecode buffer address /* vm initializing function */ void init( @@ -147,12 +141,7 @@ protected: public: /* constructor of vm instance */ - vm(): - pc(0),localr(nullptr),memr(nullptr),funcr(nil), - upvalr(nil),canary(nullptr),top(stack), - cnum(nullptr),cstr(nullptr), - ngc(pc,localr,memr,funcr,upvalr,canary,top,stack), - files(nullptr),bytecode(nullptr),verbose(false) {} + vm():ngc(&ctx),verbose(false) {} /* execution entry */ void run( @@ -167,24 +156,28 @@ void vm::init( const std::vector& nums, const std::vector& code, const std::vector& filenames, - const std::vector& argv) { - ngc.init(strs,argv); + const std::vector& argv +) { cnum=nums.data(); cstr=strs.data(); bytecode=code.data(); files=filenames.data(); /* set canary and program counter */ - pc=0; - localr=memr=nullptr; - funcr=upvalr=nil; - canary=stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1] - top=stack; + ctx.pc=0; + ctx.localr=ctx.memr=nullptr; + ctx.funcr=ctx.upvalr=nil; + ctx.canary=stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1] + ctx.top=stack; + ctx.stack=stack; /* clear main stack */ for(u32 i=0;istack==stack?stack+bytecode[0].num:ngc.rctx->stack; + var* ctx_top=ngc.rctx->stack==stack?ctx.top:ngc.rctx->top; std::stack ret; for(var* i=bottom;i<=ctx_top;++i) { if (i->type==vm_ret && i->ret()!=0) { ret.push(i->ret()); } } - ret.push(pc); // store the position program crashed - std::clog<<"trace back ("<<(ngc.stack==stack?"main":"coroutine")<<")\n"; + ret.push(ctx.pc); // store the position program crashed + std::clog<<"trace back ("<<(ngc.rctx->stack==stack?"main":"coroutine")<<")\n"; codestream::set(cnum,cstr,files); for(u32 p=0,same=0,prev=0xffffffff;!ret.empty();prev=p,ret.pop()) { if ((p=ret.top())==prev) { @@ -254,16 +247,16 @@ void vm::traceback() { void vm::stackinfo(const u32 limit=10) { /* bytecode[0].num is the global size */ - const u32 gsize=ngc.stack==stack?bytecode[0].num:0; - var* t=top; - var* bottom=ngc.stack+gsize; + const u32 gsize=ngc.rctx->stack==stack?bytecode[0].num:0; + var* t=ctx.top; + var* bottom=ngc.rctx->stack+gsize; std::clog<<"stack (0x"<, limit "<=bottom;++i,--t) { std::clog<<" 0x"<stack)<)\n"<stack)<<">)\n"< upval["<stack==stack) { // in main context, exit directly std::exit(1); - } else { - // in coroutine, shut down the coroutine - // and return to main context - pc=0; // mark coroutine 'dead' + } else { // in coroutine, shut down the coroutine and return to main context + ctx.pc=0; // mark coroutine 'dead' ngc.ctxreserve(); - top[0]=nil; + ctx.top[0]=nil; } } @@ -369,110 +359,110 @@ inline bool vm::cond(var& val) { void vm::o_intg() { // global values store on stack - top+=imm[pc]; - --top;// point to the top + ctx.top+=imm[ctx.pc]; + --ctx.top;// point to the top } void vm::o_intl() { - top[0].func().local.resize(imm[pc],nil); - top[0].func().lsize=imm[pc]; + ctx.top[0].func().local.resize(imm[ctx.pc],nil); + ctx.top[0].func().lsize=imm[ctx.pc]; } void vm::o_loadg() { - stack[imm[pc]]=(top--)[0]; + stack[imm[ctx.pc]]=(ctx.top--)[0]; } void vm::o_loadl() { - localr[imm[pc]]=(top--)[0]; + ctx.localr[imm[ctx.pc]]=(ctx.top--)[0]; } void vm::o_loadu() { - funcr.func().upval[(imm[pc]>>16)&0xffff] - .upval()[imm[pc]&0xffff]=(top--)[0]; + ctx.funcr.func().upval[(imm[ctx.pc]>>16)&0xffff] + .upval()[imm[ctx.pc]&0xffff]=(ctx.top--)[0]; } void vm::o_pnum() { - (++top)[0]=var::num(cnum[imm[pc]]); + (++ctx.top)[0]=var::num(cnum[imm[ctx.pc]]); } void vm::o_pnil() { - (++top)[0]=nil; + (++ctx.top)[0]=nil; } void vm::o_pstr() { - (++top)[0]=ngc.strs[imm[pc]]; + (++ctx.top)[0]=ngc.strs[imm[ctx.pc]]; } void vm::o_newv() { var newv=ngc.alloc(vm_vec); auto& vec=newv.vec().elems; - vec.resize(imm[pc]); + vec.resize(imm[ctx.pc]); // use top-=imm[pc]-1 here will cause error if imm[pc] is 0 - top=top-imm[pc]+1; - for(u32 i=0;isize has 1 place reserved for "me" - func.keys[imm[pc]]=func.psize; + func.keys[imm[ctx.pc]]=func.psize; func.local[func.psize++]=var::none(); } void vm::o_deft() { - var val=top[0]; - nas_func& func=(--top)[0].func(); + var val=ctx.top[0]; + nas_func& func=(--ctx.top)[0].func(); // func->size has 1 place reserved for "me" - func.keys[imm[pc]]=func.psize; + func.keys[imm[ctx.pc]]=func.psize; func.local[func.psize++]=val; } void vm::o_dyn() { - top[0].func().dpara=imm[pc]; + ctx.top[0].func().dpara=imm[ctx.pc]; } void vm::o_lnot() { - var val=top[0]; + var val=ctx.top[0]; switch(val.type) { - case vm_nil:top[0]=one;break; - case vm_num:top[0]=val.num()?zero:one;break; + case vm_nil:ctx.top[0]=one;break; + case vm_num:ctx.top[0]=val.num()?zero:one;break; case vm_str:{ const f64 num=str2num(val.str().c_str()); if (std::isnan(num)) { - top[0]=var::num((f64)val.str().empty()); + ctx.top[0]=var::num((f64)val.str().empty()); } else { - top[0]=num?zero:one; + ctx.top[0]=num?zero:one; } } break; default:{ @@ -483,50 +473,50 @@ void vm::o_lnot() { } void vm::o_usub() { - top[0]=var::num(-top[0].tonum()); + ctx.top[0]=var::num(-ctx.top[0].tonum()); } void vm::o_bnot() { - top[0]=var::num(~static_cast(top[0].num())); + ctx.top[0]=var::num(~static_cast(ctx.top[0].num())); } void vm::o_btor() { - top[-1]=var::num(static_cast(top[-1].tonum())|static_cast(top[0].tonum())); - --top; + ctx.top[-1]=var::num(static_cast(ctx.top[-1].tonum())|static_cast(ctx.top[0].tonum())); + --ctx.top; } void vm::o_btxor() { - top[-1]=var::num(static_cast(top[-1].tonum())^static_cast(top[0].tonum())); - --top; + ctx.top[-1]=var::num(static_cast(ctx.top[-1].tonum())^static_cast(ctx.top[0].tonum())); + --ctx.top; } void vm::o_btand() { - top[-1]=var::num(static_cast(top[-1].tonum())&static_cast(top[0].tonum())); - --top; + ctx.top[-1]=var::num(static_cast(ctx.top[-1].tonum())&static_cast(ctx.top[0].tonum())); + --ctx.top; } #define op_calc(type)\ - top[-1]=var::num(top[-1].tonum() type top[0].tonum());\ - --top; + ctx.top[-1]=var::num(ctx.top[-1].tonum() type ctx.top[0].tonum());\ + --ctx.top; void vm::o_add() {op_calc(+);} void vm::o_sub() {op_calc(-);} void vm::o_mul() {op_calc(*);} void vm::o_div() {op_calc(/);} void vm::o_lnk() { - top[-1]=ngc.newstr(top[-1].tostr()+top[0].tostr()); - --top; + ctx.top[-1]=ngc.newstr(ctx.top[-1].tostr()+ctx.top[0].tostr()); + --ctx.top; } #define op_calc_const(type)\ - top[0]=var::num(top[0].tonum() type cnum[imm[pc]]); + ctx.top[0]=var::num(ctx.top[0].tonum() type cnum[imm[ctx.pc]]); void vm::o_addc() {op_calc_const(+);} void vm::o_subc() {op_calc_const(-);} void vm::o_mulc() {op_calc_const(*);} void vm::o_divc() {op_calc_const(/);} void vm::o_lnkc() { - top[0]=ngc.newstr(top[0].tostr()+cstr[imm[pc]]); + ctx.top[0]=ngc.newstr(ctx.top[0].tostr()+cstr[imm[ctx.pc]]); } // top[0] stores the value of memr[0], to avoid being garbage-collected @@ -535,36 +525,36 @@ void vm::o_lnkc() { // like this: func{a+=c;}(); the result of 'a+c' will no be used later, imm[pc]=1 // but if b+=a+=c; the result of 'a+c' will be used later, imm[pc]=0 #define op_calc_eq(type)\ - top[-1]=memr[0]=var::num(memr[0].tonum() type top[-1].tonum());\ - memr=nullptr;\ - top-=imm[pc]+1; + ctx.top[-1]=ctx.memr[0]=var::num(ctx.memr[0].tonum() type ctx.top[-1].tonum());\ + ctx.memr=nullptr;\ + ctx.top-=imm[ctx.pc]+1; void vm::o_addeq() {op_calc_eq(+);} void vm::o_subeq() {op_calc_eq(-);} void vm::o_muleq() {op_calc_eq(*);} void vm::o_diveq() {op_calc_eq(/);} void vm::o_lnkeq() { - top[-1]=memr[0]=ngc.newstr(memr[0].tostr()+top[-1].tostr()); - memr=nullptr; - top-=imm[pc]+1; + ctx.top[-1]=ctx.memr[0]=ngc.newstr(ctx.memr[0].tostr()+ctx.top[-1].tostr()); + ctx.memr=nullptr; + ctx.top-=imm[ctx.pc]+1; } void vm::o_bandeq() { - top[-1]=memr[0]=var::num(i32(memr[0].tonum())&i32(top[-1].tonum())); - memr=nullptr; - top-=imm[pc]+1; + ctx.top[-1]=ctx.memr[0]=var::num(i32(ctx.memr[0].tonum())&i32(ctx.top[-1].tonum())); + ctx.memr=nullptr; + ctx.top-=imm[ctx.pc]+1; } void vm::o_boreq() { - top[-1]=memr[0]=var::num(i32(memr[0].tonum())|i32(top[-1].tonum())); - memr=nullptr; - top-=imm[pc]+1; + ctx.top[-1]=ctx.memr[0]=var::num(i32(ctx.memr[0].tonum())|i32(ctx.top[-1].tonum())); + ctx.memr=nullptr; + ctx.top-=imm[ctx.pc]+1; } void vm::o_bxoreq() { - top[-1]=memr[0]=var::num(i32(memr[0].tonum())^i32(top[-1].tonum())); - memr=nullptr; - top-=imm[pc]+1; + ctx.top[-1]=ctx.memr[0]=var::num(i32(ctx.memr[0].tonum())^i32(ctx.top[-1].tonum())); + ctx.memr=nullptr; + ctx.top-=imm[ctx.pc]+1; } // top[0] stores the value of memr[0], to avoid being garbage-collected @@ -573,31 +563,31 @@ void vm::o_bxoreq() { // like this: func{a+=1;}(); the result of 'a+1' will no be used later, imm[pc]>>31=1 // but if b+=a+=1; the result of 'a+1' will be used later, imm[pc]>>31=0 #define op_calc_eq_const(type)\ - top[0]=memr[0]=var::num(memr[0].tonum() type cnum[imm[pc]]);\ - memr=nullptr; + ctx.top[0]=ctx.memr[0]=var::num(ctx.memr[0].tonum() type cnum[imm[ctx.pc]]);\ + ctx.memr=nullptr; void vm::o_addeqc() {op_calc_eq_const(+);} void vm::o_subeqc() {op_calc_eq_const(-);} void vm::o_muleqc() {op_calc_eq_const(*);} void vm::o_diveqc() {op_calc_eq_const(/);} void vm::o_lnkeqc() { - top[0]=memr[0]=ngc.newstr(memr[0].tostr()+cstr[imm[pc]]); - memr=nullptr; + ctx.top[0]=ctx.memr[0]=ngc.newstr(ctx.memr[0].tostr()+cstr[imm[ctx.pc]]); + ctx.memr=nullptr; } #define op_calc_eq_const_and_pop(type)\ - top[0]=memr[0]=var::num(memr[0].tonum() type cnum[imm[pc]]);\ - memr=nullptr;\ - --top; + ctx.top[0]=ctx.memr[0]=var::num(ctx.memr[0].tonum() type cnum[imm[ctx.pc]]);\ + ctx.memr=nullptr;\ + --ctx.top; void vm::o_addecp() {op_calc_eq_const_and_pop(+);} void vm::o_subecp() {op_calc_eq_const_and_pop(-);} void vm::o_mulecp() {op_calc_eq_const_and_pop(*);} void vm::o_divecp() {op_calc_eq_const_and_pop(/);} void vm::o_lnkecp() { - top[0]=memr[0]=ngc.newstr(memr[0].tostr()+cstr[imm[pc]]); - memr=nullptr; - --top; + ctx.top[0]=ctx.memr[0]=ngc.newstr(ctx.memr[0].tostr()+cstr[imm[ctx.pc]]); + ctx.memr=nullptr; + --ctx.top; } void vm::o_meq() { @@ -606,44 +596,44 @@ void vm::o_meq() { // is that when lnkeq/lnkeqc is called, there will be // a new gc object vm_str which is returned by gc::alloc // this may cause gc, so we should temporarily put it on stack - memr[0]=top[-1]; - memr=nullptr; - top-=imm[pc]+1; + ctx.memr[0]=ctx.top[-1]; + ctx.memr=nullptr; + ctx.top-=imm[ctx.pc]+1; } void vm::o_eq() { - var val2=top[0]; - var val1=(--top)[0]; + var val2=ctx.top[0]; + var val1=(--ctx.top)[0]; if (val1.type==vm_nil && val2.type==vm_nil) { - top[0]=one; + ctx.top[0]=one; } else if (val1.type==vm_str && val2.type==vm_str) { - top[0]=(val1.str()==val2.str())?one:zero; + ctx.top[0]=(val1.str()==val2.str())?one:zero; } else if ((val1.type==vm_num || val2.type==vm_num) && val1.type!=vm_nil && val2.type!=vm_nil) { - top[0]=(val1.tonum()==val2.tonum())?one:zero; + ctx.top[0]=(val1.tonum()==val2.tonum())?one:zero; } else { - top[0]=(val1==val2)?one:zero; + ctx.top[0]=(val1==val2)?one:zero; } } void vm::o_neq() { - var val2=top[0]; - var val1=(--top)[0]; + var val2=ctx.top[0]; + var val1=(--ctx.top)[0]; if (val1.type==vm_nil && val2.type==vm_nil) { - top[0]=zero; + ctx.top[0]=zero; } else if (val1.type==vm_str && val2.type==vm_str) { - top[0]=(val1.str()!=val2.str())?one:zero; + ctx.top[0]=(val1.str()!=val2.str())?one:zero; } else if ((val1.type==vm_num || val2.type==vm_num) && val1.type!=vm_nil && val2.type!=vm_nil) { - top[0]=(val1.tonum()!=val2.tonum())?one:zero; + ctx.top[0]=(val1.tonum()!=val2.tonum())?one:zero; } else { - top[0]=(val1!=val2)?one:zero; + ctx.top[0]=(val1!=val2)?one:zero; } } #define op_cmp(type)\ - --top;\ - top[0]=(top[0].tonum() type top[1].tonum())?one:zero; + --ctx.top;\ + ctx.top[0]=(ctx.top[0].tonum() type ctx.top[1].tonum())?one:zero; void vm::o_less() {op_cmp(<);} void vm::o_leq() {op_cmp(<=);} @@ -651,7 +641,7 @@ void vm::o_grt() {op_cmp(>);} void vm::o_geq() {op_cmp(>=);} #define op_cmp_const(type)\ - top[0]=(top[0].tonum() type cnum[imm[pc]])?one:zero; + ctx.top[0]=(ctx.top[0].tonum() type cnum[imm[ctx.pc]])?one:zero; void vm::o_lessc() {op_cmp_const(<);} void vm::o_leqc() {op_cmp_const(<=);} @@ -659,76 +649,77 @@ void vm::o_grtc() {op_cmp_const(>);} void vm::o_geqc() {op_cmp_const(>=);} void vm::o_pop() { - --top; + --ctx.top; } void vm::o_jmp() { - pc=imm[pc]-1; + ctx.pc=imm[ctx.pc]-1; } void vm::o_jt() { // jump true needs to reserve the result on stack // because conditional expression in nasal has return value - if (cond(top[0])) { - pc=imm[pc]-1; + if (cond(ctx.top[0])) { + ctx.pc=imm[ctx.pc]-1; } } void vm::o_jf() { // jump false doesn't need to reserve result - if (!cond(top[0])) { - pc=imm[pc]-1; + if (!cond(ctx.top[0])) { + ctx.pc=imm[ctx.pc]-1; } - --top; + --ctx.top; } void vm::o_cnt() { - if (top[0].type!=vm_vec) { + if (ctx.top[0].type!=vm_vec) { die("must use vector in forindex/foreach"); return; } - (++top)[0]=var::cnt(-1); + (++ctx.top)[0]=var::cnt(-1); } void vm::o_findex() { - if ((usize)(++top[0].cnt())>=top[-1].vec().size()) { - pc=imm[pc]-1; + if ((usize)(++ctx.top[0].cnt())>=ctx.top[-1].vec().size()) { + ctx.pc=imm[ctx.pc]-1; return; } - top[1]=var::num(top[0].cnt()); - ++top; + ctx.top[1]=var::num(ctx.top[0].cnt()); + ++ctx.top; } void vm::o_feach() { - auto& ref=top[-1].vec().elems; - if ((usize)(++top[0].cnt())>=ref.size()) { - pc=imm[pc]-1; + auto& ref=ctx.top[-1].vec().elems; + if ((usize)(++ctx.top[0].cnt())>=ref.size()) { + ctx.pc=imm[ctx.pc]-1; return; } - top[1]=ref[top[0].cnt()]; - ++top; + ctx.top[1]=ref[ctx.top[0].cnt()]; + ++ctx.top; } void vm::o_callg() { // get main stack directly - (++top)[0]=stack[imm[pc]]; + (++ctx.top)[0]=stack[imm[ctx.pc]]; } void vm::o_calll() { - (++top)[0]=localr[imm[pc]]; + (++ctx.top)[0]=ctx.localr[imm[ctx.pc]]; } void vm::o_upval() { - (++top)[0]=funcr.func().upval[(imm[pc]>>16)&0xffff] - .upval()[imm[pc]&0xffff]; + (++ctx.top)[0]=ctx.funcr.func() + .upval[(imm[ctx.pc]>>16)&0xffff] + .upval()[imm[ctx.pc]&0xffff]; } void vm::o_callv() { - var val=top[0]; - var vec=(--top)[0]; + var val=ctx.top[0]; + var vec=(--ctx.top)[0]; if (vec.type==vm_vec) { - top[0]=vec.vec().get_val(val.tonum()); - if (top[0].type==vm_none) { + ctx.top[0]=vec.vec().get_val(val.tonum()); + if (ctx.top[0].type==vm_none) { die("out of range:"+std::to_string(val.tonum())); return; } @@ -737,12 +728,12 @@ void vm::o_callv() { die("must use string as the key"); return; } - top[0]=vec.hash().get_val(val.str()); - if (top[0].type==vm_none) { + ctx.top[0]=vec.hash().get_val(val.str()); + if (ctx.top[0].type==vm_none) { die("cannot find member \""+val.str()+"\""); return; - } else if (top[0].type==vm_func) { - top[0].func().local[0]=val; // 'me' + } else if (ctx.top[0].type==vm_func) { + ctx.top[0].func().local[0]=val; // 'me' } } else if (vec.type==vm_str) { string& str=vec.str(); @@ -752,7 +743,7 @@ void vm::o_callv() { die("out of range:"+std::to_string(val.tonum())); return; } - top[0]=var::num(f64((u8)str[num>=0? num:num+len])); + ctx.top[0]=var::num(f64((u8)str[num>=0? num:num+len])); } else { die("must call a vector/hash/string"); return; @@ -760,47 +751,47 @@ void vm::o_callv() { } void vm::o_callvi() { - var val=top[0]; + var val=ctx.top[0]; if (val.type!=vm_vec) { die("must use a vector"); return; } // cannot use operator[],because this may cause overflow - (++top)[0]=val.vec().get_val(imm[pc]); - if (top[0].type==vm_none) { - die("out of range:"+std::to_string(imm[pc])); + (++ctx.top)[0]=val.vec().get_val(imm[ctx.pc]); + if (ctx.top[0].type==vm_none) { + die("out of range:"+std::to_string(imm[ctx.pc])); return; } } void vm::o_callh() { - var val=top[0]; + var val=ctx.top[0]; if (val.type!=vm_hash) { die("must call a hash"); return; } - top[0]=val.hash().get_val(cstr[imm[pc]]); - if (top[0].type==vm_none) { - die("member \""+cstr[imm[pc]]+"\" does not exist"); + ctx.top[0]=val.hash().get_val(cstr[imm[ctx.pc]]); + if (ctx.top[0].type==vm_none) { + die("member \""+cstr[imm[ctx.pc]]+"\" does not exist"); return; - } else if (top[0].type==vm_func) { - top[0].func().local[0]=val; // 'me' + } else if (ctx.top[0].type==vm_func) { + ctx.top[0].func().local[0]=val; // 'me' } } void vm::o_callfv() { - u32 argc=imm[pc]; // arguments counter - var* local=top-argc+1; // arguments begin address + u32 argc=imm[ctx.pc]; // arguments counter + var* local=ctx.top-argc+1; // arguments begin address if (local[-1].type!=vm_func) { die("must call a function"); return; } auto& func=local[-1].func(); var tmp=local[-1]; - local[-1]=funcr; - funcr=tmp; + local[-1]=ctx.funcr; + ctx.funcr=tmp; // top-argc+lsize(local) +1(old pc) +1(old localr) +1(old upvalr) - if (top-argc+func.lsize+3>=canary) { + if (ctx.top-argc+func.lsize+3>=ctx.canary) { die("stack overflow"); return; } @@ -823,7 +814,7 @@ void vm::o_callfv() { // then all the available values the vector needs // are all outside the stack top and may be // collected incorrectly - top=local+func.lsize; + ctx.top=local+func.lsize; u32 min_size=(std::min)(psize,argc); // avoid error in MSVC for(u32 i=min_size;i>=1;--i) { // load arguments @@ -838,26 +829,27 @@ void vm::o_callfv() { local[psize+1]=dynamic; } - top[0]=upvalr; - (++top)[0]=var::addr(localr); - (++top)[0]=var::ret(pc); - pc=func.entry-1; - localr=local; - upvalr=nil; + ctx.top[0]=ctx.upvalr; + (++ctx.top)[0]=var::addr(ctx.localr); + (++ctx.top)[0]=var::ret(ctx.pc); + ctx.pc=func.entry-1; + ctx.localr=local; + ctx.upvalr=nil; } void vm::o_callfh() { - auto& hash=top[0].hash().elems; - if (top[-1].type!=vm_func) { + auto& hash=ctx.top[0].hash().elems; + if (ctx.top[-1].type!=vm_func) { die("must call a function"); return; } - auto& func=top[-1].func(); - var tmp=top[-1]; - top[-1]=funcr; - funcr=tmp; + auto& func=ctx.top[-1].func(); + var tmp=ctx.top[-1]; + ctx.top[-1]=ctx.funcr; + ctx.funcr=tmp; + // top -1(hash) +lsize(local) +1(old pc) +1(old localr) +1(old upvalr) - if (top+func.lsize+2>=canary) { + if (ctx.top+func.lsize+2>=ctx.canary) { die("stack overflow"); return; } @@ -866,8 +858,8 @@ void vm::o_callfh() { return; } - var* local=top; - top+=func.lsize; + var* local=ctx.top; + ctx.top+=func.lsize; for(u32 i=0;i>16)&0xffff].upval()[imm[pc]&0xffff]); - (++top)[0]=memr[0]; + ctx.memr=&(ctx.funcr.func().upval[(imm[ctx.pc]>>16)&0xffff].upval()[imm[ctx.pc]&0xffff]); + (++ctx.top)[0]=ctx.memr[0]; // push value in this memory space on stack // to avoid being garbage collected } void vm::o_mcallv() { - var val=top[0]; // index - var vec=(--top)[0]; // mcall vector, reserved on stack to avoid gc + var val=ctx.top[0]; // index + var vec=(--ctx.top)[0]; // mcall vector, reserved on stack to avoid gc if (vec.type==vm_vec) { - memr=vec.vec().get_mem(val.tonum()); - if (!memr) { + ctx.memr=vec.vec().get_mem(val.tonum()); + if (!ctx.memr) { die("index "+std::to_string(val.tonum())+" out of range"); return; } @@ -1004,10 +996,10 @@ void vm::o_mcallv() { } nas_hash& ref=vec.hash(); string& str=val.str(); - memr=ref.get_mem(str); - if (!memr) { + ctx.memr=ref.get_mem(str); + if (!ctx.memr) { ref.elems[str]=nil; - memr=ref.get_mem(str); + ctx.memr=ref.get_mem(str); } } else { die("cannot get memory space in this type"); @@ -1016,17 +1008,17 @@ void vm::o_mcallv() { } void vm::o_mcallh() { - var hash=top[0]; // mcall hash, reserved on stack to avoid gc + var hash=ctx.top[0]; // mcall hash, reserved on stack to avoid gc if (hash.type!=vm_hash) { die("must call a hash"); return; } auto& ref=hash.hash(); - auto& str=cstr[imm[pc]]; - memr=ref.get_mem(str); - if (!memr) { // create a new key + auto& str=cstr[imm[ctx.pc]]; + ctx.memr=ref.get_mem(str); + if (!ctx.memr) { // create a new key ref.elems[str]=nil; - memr=ref.get_mem(str); + ctx.memr=ref.get_mem(str); } } @@ -1045,18 +1037,18 @@ void vm::o_ret() { * | old funcr | <- old function stored in funcr * +-------------+ */ - var ret =top[0]; - var* local=localr; - var func =funcr; - var up =upvalr; + var ret =ctx.top[0]; + var* local=ctx.localr; + var func =ctx.funcr; + var up =ctx.upvalr; - pc =top[-1].ret(); - localr=top[-2].addr(); - upvalr=top[-3]; + ctx.pc =ctx.top[-1].ret(); + ctx.localr=ctx.top[-2].addr(); + ctx.upvalr=ctx.top[-3]; - top=local-1; - funcr=top[0]; - top[0]=ret; // rewrite func with returned value + ctx.top=local-1; + ctx.funcr=ctx.top[0]; + ctx.top[0]=ret; // rewrite func with returned value if (up.type==vm_upval) { // synchronize upvalue auto& upval=up.upval(); @@ -1067,9 +1059,11 @@ void vm::o_ret() { upval.elems[i]=local[i]; } } + // cannot use gc.cort to judge, - // because there maybe another function call inside - if (!pc) { + // because there maybe another function call inside but return here + // coroutine function ends with setting pc to 0 + if (!ctx.pc) { ngc.ctxreserve(); } } @@ -1112,7 +1106,7 @@ void vm::run( imm.push_back(i.num); } // goto the first operand - goto *code[pc]; + goto *code[ctx.pc]; #else typedef void (vm::*nafunc)(); const nafunc oprs[]={ @@ -1166,12 +1160,12 @@ void vm::run( code.push_back(oprs[i.op]); imm.push_back(i.num); } - while(code[pc]) { - (this->*code[pc])(); - if (top>=canary) { + while(code[ctx.pc]) { + (this->*code[ctx.pc])(); + if (ctx.top>=ctx.canary) { die("stack overflow"); } - ++pc; + ++ctx.pc; } #endif @@ -1187,13 +1181,13 @@ vmexit: // may cause stackoverflow #define exec_check(op) {\ op();\ - if (top