diff --git a/README.md b/README.md index 5328558..13eff3a 100644 --- a/README.md +++ b/README.md @@ -1386,6 +1386,8 @@ When `op_callb` is called, the stack frame is like this: +----------------------------+ | old localr(vm_addr) | <- top[-1] +----------------------------+ +| old upvalr(vm_upval) | <- top[-2] ++----------------------------+ | local scope(nasal_ref) | | ... | +----------------------------+ <- local pointer stored in localr @@ -1403,6 +1405,8 @@ In `op_callb`'s progress, next step the stack frame is: +----------------------------+ | old localr(vm_addr) | +----------------------------+ +| old upvalr(vm_upval) | ++----------------------------+ | local scope(nasal_ref) | | ... | +----------------------------+ <- local pointer stored in localr @@ -1435,6 +1439,8 @@ but where is the returned `local[1]` sent? +----------------------------+ | old localr(vm_addr) | +----------------------------+ +| old upvalr(vm_upval) | ++----------------------------+ | local scope(nasal_ref) | | ... | +----------------------------+ <- local pointer stored in localr @@ -1453,6 +1459,8 @@ and the returned `local[1]` in fact is set to the top of the main stack by `op_c +----------------------------+ | old localr(vm_addr) | +----------------------------+ +| old upvalr(vm_upval) | ++----------------------------+ | local scope(nasal_ref) | | ... | +----------------------------+ <- local pointer stored in localr diff --git a/makefile b/makefile index c4b8bff..050b851 100644 --- a/makefile +++ b/makefile @@ -16,6 +16,7 @@ test:nasal @ ./nasal -op -e -d test/calc.nas @ ./nasal -op -e test/choice.nas @ ./nasal -op -e test/class.nas + @ ./nasal -op -d -o test/coroutine.nas @ ./nasal -op -e test/diff.nas -@ ./nasal -op -d test/exception.nas @ ./nasal -op -t -d test/fib.nas diff --git a/nasal_builtin.h b/nasal_builtin.h index d5489fc..2c23853 100644 --- a/nasal_builtin.h +++ b/nasal_builtin.h @@ -1343,6 +1343,8 @@ nasal_ref builtin_cocreate(nasal_ref* local,nasal_gc& gc) // +-----------------+ // | old localr | <- top[-1] // +-----------------+ + // | old upvalr | <- top[-2] + // +-----------------+ // | local scope | // | ... | // +-----------------+ <- local pointer stored in localr @@ -1361,11 +1363,13 @@ nasal_ref builtin_cocreate(nasal_ref* local,nasal_gc& gc) coroutine.localr=coroutine.top+1; coroutine.top=coroutine.localr+func.func().lsize; coroutine.localr[0]=func.func().local[0]; - coroutine.top[0]={vm_addr,(nasal_ref*)nullptr}; + coroutine.top[0]=nil; // old upvalr coroutine.top++; - coroutine.top[0]={vm_ret,(uint32_t)0}; + coroutine.top[0]={vm_addr,(nasal_ref*)nullptr}; // old localr + coroutine.top++; + coroutine.top[0]={vm_ret,(uint32_t)0}; // old pc, set to zero to make op_ret recognizing this as coroutine function - coroutine.funcr=func; + coroutine.funcr=func; // make sure the coroutine function can use correct upvalues coroutine.status=nasal_co::suspended; return co; diff --git a/nasal_gc.h b/nasal_gc.h index 51cd3c8..758ea0c 100644 --- a/nasal_gc.h +++ b/nasal_gc.h @@ -218,6 +218,7 @@ struct nasal_co nasal_ref* localr; nasal_ref* memr; nasal_ref funcr; + nasal_ref upvalr; uint32_t status; nasal_co(): @@ -227,6 +228,7 @@ struct nasal_co localr(nullptr), memr(nullptr), funcr({vm_nil,(double)0}), + upvalr({vm_nil,(double)0}), status(nasal_co::suspended) { for(uint32_t i=0;i strs; // reserved address for const vm_str std::vector memory; // gc memory std::queue free_list[vm_type_size]; // gc free list - /* upvalue is a temporary space to store upvalues */ - /* if no new functions generated in local scope */ - /* upvalue will pushback(nil) */ - /* if new functions generated in local scope */ - /* they will share the same upvalue stored here */ - std::vector upvalue; /* values for analysis */ uint64_t size[vm_type_size]; @@ -502,19 +499,19 @@ void nasal_gc::mark() { std::queue bfs; - for(auto& i:upvalue) - bfs.push(i); if(!coroutine) { for(nasal_ref* i=stack;i<=top;++i) bfs.push(*i); bfs.push(funcr); + bfs.push(upvalr); } else { for(nasal_ref* i=main_ctx.stack;i<=main_ctx.top;++i) bfs.push(*i); bfs.push(main_ctx.funcr); + bfs.push(main_ctx.upvalr); } while(!bfs.empty()) @@ -545,6 +542,7 @@ void nasal_gc::mark() break; case vm_co: bfs.push(tmp.co().funcr); + bfs.push(tmp.co().upvalr); for(nasal_ref* i=tmp.co().stack;i<=tmp.co().top;++i) bfs.push(*i); break; @@ -587,8 +585,7 @@ void nasal_gc::init(const std::vector& s) nasal_val* tmp=new nasal_val(i); memory.push_back(tmp); free_list[i].push(tmp); - } - global=main_ctx.stack; + } stack=main_ctx.stack; top=main_ctx.stack; coroutine=nullptr; @@ -606,7 +603,6 @@ void nasal_gc::clear() for(auto i:memory) delete i; memory.clear(); - upvalue.clear(); for(uint8_t i=0;istatus=nasal_co::running; } void nasal_gc::ctxreserve() @@ -705,6 +702,7 @@ void nasal_gc::ctxreserve() coroutine->localr=localr; coroutine->memr=memr; coroutine->funcr=funcr; + coroutine->upvalr=upvalr; coroutine->canary=canary; pc=main_ctx.pc; @@ -712,10 +710,9 @@ void nasal_gc::ctxreserve() localr=main_ctx.localr; memr=main_ctx.memr; funcr=main_ctx.funcr; + upvalr=main_ctx.upvalr; canary=main_ctx.canary; stack=main_ctx.stack; coroutine=nullptr; - - upvalue.pop_back(); } #endif \ No newline at end of file diff --git a/nasal_vm.h b/nasal_vm.h index 34d52aa..39b41fe 100644 --- a/nasal_vm.h +++ b/nasal_vm.h @@ -6,10 +6,11 @@ class nasal_vm protected: /* values of nasal_vm */ uint32_t& pc; // program counter - nasal_ref*& global; // global scope register + nasal_ref* global; // global scope register nasal_ref*& localr; // local scope register nasal_ref*& memr; // used for mem_call nasal_ref& funcr; // function register + nasal_ref& upvalr; // upvalue register nasal_ref*& canary; // avoid stackoverflow nasal_ref*& top; // stack top /* constant */ @@ -120,10 +121,11 @@ protected: public: nasal_vm(): pc(gc.pc), - global(gc.global), + global(gc.main_ctx.stack), localr(gc.localr), memr(gc.memr), funcr(gc.funcr), + upvalr(gc.upvalr), canary(gc.canary), top(gc.top){} void run( @@ -147,9 +149,11 @@ void nasal_vm::init( files_size=filenames.size(); /* set canary and program counter */ canary=gc.stack+nasal_gc::stack_depth-1; // gc.stack[nasal_gc::stack_depth-1] - memr=nullptr; - localr=nullptr; pc=0; + localr=nullptr; + memr=nullptr; + funcr=nil; + upvalr=nil; } void nasal_vm::valinfo(nasal_ref& val) { @@ -169,6 +173,7 @@ void nasal_vm::valinfo(nasal_ref& val) printf("| str | <0x" PRTHEX64 "> %.16s%s\n",(uint64_t)p,tmp.c_str(),tmp.length()>16?"...":""); }break; case vm_func: printf("| func | <0x" PRTHEX64 "> entry:0x%x\n",(uint64_t)p,val.func().entry);break; + case vm_upval:printf("| upval| <0x" PRTHEX64 "> [%u val]\n",(uint64_t)p,val.upval().size);break; case vm_vec: printf("| vec | <0x" PRTHEX64 "> [%zu val]\n",(uint64_t)p,val.vec().size());break; case vm_hash: printf("| hash | <0x" PRTHEX64 "> {%zu val}\n",(uint64_t)p,val.hash().size());break; case vm_obj: printf("| obj | <0x" PRTHEX64 "> obj:0x" PRTHEX64 "\n",(uint64_t)p,(uint64_t)val.obj().ptr);break; @@ -216,7 +221,7 @@ void nasal_vm::bytecodeinfo(const char* header,const uint32_t p) case op_callb: printf("0x%x <%s@0x" PRTHEX64 ">",c.num,builtin[c.num].name,(uint64_t)builtin[c.num].func);break; case op_upval: case op_mupval: case op_loadu: - printf(" (0x%x[0x%x])",(c.num>>16)&0xffff,c.num&0xffff);break; + printf("0x%x[0x%x]",(c.num>>16)&0xffff,c.num&0xffff);break; case op_happ: case op_pstr: case op_lnkc: case op_callh: case op_mcallh: @@ -318,14 +323,25 @@ void nasal_vm::upval_state() } void nasal_vm::detail() { - printf("maddr:\n (0x" PRTHEX64 ")\n",(uint64_t)memr); - printf("localr:\n (0x" PRTHEX64 ")\n",(uint64_t)localr); + printf("registers(%s):\n",gc.coroutine?"coroutine":"main"); + printf(" [ pc ] | pc | 0x%.x\n",pc); + printf(" [ global ] | addr | 0x" PRTHEX64 "\n",(uint64_t)global); + printf(" [ localr ] | addr | 0x" PRTHEX64 "\n",(uint64_t)localr); + printf(" [ memr ] | addr | 0x" PRTHEX64 "\n",(uint64_t)memr); if(funcr.type==vm_nil) - printf("funcr:\n (nil)\n"); + printf(" [ funcr ] | nil |\n"); else - printf("funcr:\n (<0x" PRTHEX64 "> entry:0x%x)\n", + printf(" [ funcr ] | func | <0x" PRTHEX64 "> entry:0x%x\n", (uint64_t)funcr.value.gcobj, funcr.func().entry); + if(upvalr.type==vm_nil) + printf(" [ upvalr ] | nil |\n"); + else + printf(" [ upvalr ] | upval| <0x" PRTHEX64 "> [%u val]\n", + (uint64_t)upvalr.value.gcobj, + upvalr.upval().size); + printf(" [ canary ] | addr | 0x" PRTHEX64 "\n",(uint64_t)canary); + printf(" [ top ] | addr | 0x" PRTHEX64 "\n",(uint64_t)top); global_state(); local_state(); upval_state(); @@ -436,11 +452,11 @@ inline void nasal_vm::opr_newf() if(localr) { func.upvalue=funcr.func().upvalue; - nasal_ref upval=(gc.upvalue.back().type==vm_nil)?gc.alloc(vm_upval):gc.upvalue.back(); + nasal_ref upval=(upvalr.type==vm_nil)?gc.alloc(vm_upval):upvalr; upval.upval().size=funcr.func().lsize; upval.upval().stk=localr; func.upvalue.push_back(upval); - gc.upvalue.back()=upval; + upvalr=upval; } } inline void nasal_vm::opr_happ() @@ -739,7 +755,7 @@ inline void nasal_vm::opr_callfv() nasal_ref tmp=local[-1]; local[-1]=funcr; funcr=tmp; - if(top-argc+func.lsize+2>=canary) // top-argc+lsize(local) +1(old pc) +1(old localr) + if(top-argc+func.lsize+3>=canary) // top-argc+lsize(local) +1(old pc) +1(old localr) +1(old upvalr) die("stack overflow"); uint32_t psize=func.psize-1; // parameter size is func->psize-1, 1 is reserved for "me" @@ -763,11 +779,12 @@ inline void nasal_vm::opr_callfv() if(func.dynpara>=0) local[psize+1]=dynamic; - top[0]={vm_addr,localr}; + top[0]=upvalr; + (++top)[0]={vm_addr,localr}; (++top)[0]={vm_ret,pc}; pc=func.entry-1; localr=local; - gc.upvalue.push_back(nil); + upvalr=nil; } inline void nasal_vm::opr_callfh() { @@ -779,7 +796,7 @@ inline void nasal_vm::opr_callfh() nasal_ref tmp=top[-1]; top[-1]=funcr; funcr=tmp; - if(top+func.lsize+1>=canary) // top -1(hash) +lsize(local) +1(old pc) +1(old localr) + if(top+func.lsize+2>=canary) // top -1(hash) +lsize(local) +1(old pc) +1(old localr) +1(old upvalr) die("stack overflow"); if(func.dynpara>=0) die("callfh: special call cannot use dynamic argument"); @@ -797,11 +814,12 @@ inline void nasal_vm::opr_callfh() die("callfh: lack argument(s): \""+i.first+"\""); } - top[0]={vm_addr,localr}; + top[0]=upvalr; + (++top)[0]={vm_addr,localr}; (++top)[0]={vm_ret,pc}; // rewrite top with vm_ret pc=func.entry-1; localr=local; - gc.upvalue.push_back(nil); + upvalr=nil; } inline void nasal_vm::opr_callb() { @@ -939,6 +957,8 @@ inline void nasal_vm::opr_ret() // +-----------------+ // | old localr | <- top[-2] // +-----------------+ + // | old upvalr | <- top[-3] + // +-----------------+ // | local scope | // | ... | // +-----------------+ <- local pointer stored in localr @@ -947,9 +967,11 @@ inline void nasal_vm::opr_ret() nasal_ref ret=top[0]; nasal_ref* local=localr; nasal_ref func=funcr; + nasal_ref up=upvalr; pc=top[-1].ret(); localr=top[-2].addr(); + upvalr=top[-3]; top=local-1; func.func().local[0]=nil;// get func and set 'me' to nil @@ -957,21 +979,19 @@ inline void nasal_vm::opr_ret() top[0]=ret; // rewrite func with returned value - if(gc.upvalue.back().type==vm_upval) // synchronize upvalue + if(up.type==vm_upval) // synchronize upvalue { - auto& upval=gc.upvalue.back().upval(); + auto& upval=up.upval(); auto size=func.func().lsize; upval.onstk=false; for(uint32_t i=0;istatus=nasal_co::dead; gc.ctxreserve(); - return; } - gc.upvalue.pop_back(); } void nasal_vm::run( const nasal_codegen& gen, @@ -1101,6 +1121,6 @@ mcalll: exec_operand(opr_mcalll ,op_mcalll ); // +1 mupval: exec_operand(opr_mupval ,op_mupval ); // +1 mcallv: exec_opnodie(opr_mcallv ,op_mcallv ); // -0 mcallh: exec_opnodie(opr_mcallh ,op_mcallh ); // -0 -ret: exec_opnodie(opr_ret ,op_ret ); // -1 +ret: exec_opnodie(opr_ret ,op_ret ); // -2 } #endif \ No newline at end of file diff --git a/test/coroutine.nas b/test/coroutine.nas index 5a39ca3..0858604 100644 --- a/test/coroutine.nas +++ b/test/coroutine.nas @@ -2,16 +2,11 @@ # 2022/5/19 var fib=func(){ var (a,b)=(1,1); - if(coroutine.running()){ - coroutine.yield(a); - coroutine.yield(b); - } + coroutine.yield(a); + coroutine.yield(b); while(1){ (a,b)=(b,a+b); - if(coroutine.running()) - coroutine.yield(b); - else - break; + coroutine.yield(b); } return; } @@ -22,4 +17,34 @@ for(var i=0;i<45;i+=1){ println('coroutine[0]:',res[0]==nil?nil:res[0][0],'\ncoroutine[1]:',res[1]==nil?nil:res[1][0]); } -fib(); \ No newline at end of file +var productor=func(){ + for(var i=0;;i+=1) + coroutine.yield(i); +} +var counter=0; +var consumer=func(){ + counter+=1; + print('[',counter,']: '); + for(var i=0;i<5;i+=1){ + print('[',i,']',coroutine.resume(co)[0],' '); + } + print('\n'); +} + +var co=coroutine.create(productor); +var tm=maketimestamp(); +tm.stamp(); +while(tm.elapsedMSec()<1000) + consumer(); + +func(){ + var x=1; + var co=coroutine.create(func(){ + for(var j=0;j<1024;j+=1){ + coroutine.yield(x,i,j); + x+=1; + } + }); + for(var i=0;i<256;i+=1) + println(coroutine.resume(co)); +}(); \ No newline at end of file