bug fixed & more efficient callfv

I changed callfv's way of calling a function with arguments in vm_vec.
now callfv fetches arguments from val_stack directly,so it runs test/fib.nas from 2.4s to 1.9s.

delete operand callf,add operands callfv & callfh.

also,i check val_stack's top to make sure there is not a stack overflow.
This commit is contained in:
Valk Richard Li 2021-06-03 21:49:31 +08:00
parent a68bf85f04
commit 8e29a3ec5b
9 changed files with 164 additions and 135 deletions

View File

@ -26,8 +26,7 @@ var setsize=func(vector,size)
} }
var system=func(str) var system=func(str)
{ {
__builtin_system(str); return __builtin_system(str);
return;
} }
var input=func() var input=func()
{ {

View File

@ -1,10 +1,5 @@
#include "nasal.h" #include "nasal.h"
nasal_lexer lexer;
nasal_parse parse;
nasal_import import;
std::string file="null";
nasal_codegen codegen;
nasal_vm vm; nasal_vm vm;
void help() void help()
@ -12,11 +7,11 @@ void help()
std::cout std::cout
<<">> [\"file\"] input file name. \n" <<">> [\"file\"] input file name. \n"
<<">> [help ] show help. \n" <<">> [help ] show help. \n"
<<">> [clear ] clear the screen. \n" <<">> [clear] clear the screen. \n"
<<">> [lex ] view tokens. \n" <<">> [lex ] view tokens. \n"
<<">> [ast ] view abstract syntax tree. \n" <<">> [ast ] view abstract syntax tree. \n"
<<">> [code ] view byte code. \n" <<">> [code ] view byte code. \n"
<<">> [exec ] execute program on bytecode vm. \n" <<">> [exec ] execute program on bytecode vm.\n"
<<">> [logo ] print logo of nasal . \n" <<">> [logo ] print logo of nasal . \n"
<<">> [exit ] quit nasal interpreter. \n"; <<">> [exit ] quit nasal interpreter. \n";
return; return;
@ -39,8 +34,12 @@ void die(std::string stage,std::string filename)
return; return;
} }
void execute(std::string& command) void execute(std::string& file,std::string& command)
{ {
nasal_lexer lexer;
nasal_parse parse;
nasal_import import;
nasal_codegen codegen;
lexer.openfile(file); lexer.openfile(file);
lexer.scanner(); lexer.scanner();
if(lexer.get_error()) if(lexer.get_error())
@ -95,12 +94,13 @@ void execute(std::string& command)
int main() int main()
{ {
std::string command; std::string command;
std::string file="null";
#ifdef _WIN32 #ifdef _WIN32
// use chcp 65001 to use unicode io // use chcp 65001 to use unicode io
system("chcp 65001"); system("chcp 65001");
system("cls"); system("cls");
#else #else
system("clear"); int rs=system("clear");// avoid annoying warning of high version gcc/g++
#endif #endif
logo(); logo();
std::cout<<">> Nasal interpreter ver 6.5 efficient gc test .\n"; std::cout<<">> Nasal interpreter ver 6.5 efficient gc test .\n";
@ -120,7 +120,7 @@ int main()
#ifdef _WIN32 #ifdef _WIN32
system("cls"); system("cls");
#else #else
system("clear"); int rs=system("clear");
#endif #endif
} }
else if(command=="logo") else if(command=="logo")
@ -128,7 +128,7 @@ int main()
else if(command=="exit") else if(command=="exit")
break; break;
else if(command=="lex" || command=="ast" || command=="code" || command=="exec") else if(command=="lex" || command=="ast" || command=="code" || command=="exec")
execute(command); execute(file,command);
else else
file=command; file=command;
} }

View File

@ -172,14 +172,15 @@ nasal_val* builtin_setsize(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
nasal_val* builtin_system(std::vector<nasal_val*>& local_scope,nasal_gc& gc) nasal_val* builtin_system(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{ {
nasal_val* ret_addr=gc.gc_alloc(vm_num);
nasal_val* str_addr=local_scope[1]; nasal_val* str_addr=local_scope[1];
if(str_addr->type!=vm_str) if(str_addr->type!=vm_str)
{ {
builtin_err("system","\"str\" must be string"); builtin_err("system","\"str\" must be string");
return nullptr; return nullptr;
} }
system(str_addr->ptr.str->data()); ret_addr->ptr.num=(double)system(str_addr->ptr.str->data());
return gc.nil_addr; return ret_addr;
} }
nasal_val* builtin_input(std::vector<nasal_val*>& local_scope,nasal_gc& gc) nasal_val* builtin_input(std::vector<nasal_val*>& local_scope,nasal_gc& gc)

View File

@ -54,7 +54,8 @@ enum op_code
op_callv, // call vec[index] op_callv, // call vec[index]
op_callvi, // call vec[immediate] (used in multi-assign/multi-define) op_callvi, // call vec[immediate] (used in multi-assign/multi-define)
op_callh, // call hash.label op_callh, // call hash.label
op_callf, // call function(parameters) op_callfv, // call function(vector as parameters)
op_callfh, // call function(hash as parameters)
op_callb, // call builtin-function op_callb, // call builtin-function
op_slcbegin, // begin of slice like: vec[1,2,3:6,0,-1] op_slcbegin, // begin of slice like: vec[1,2,3:6,0,-1]
op_slcend, // end of slice op_slcend, // end of slice
@ -124,7 +125,8 @@ struct
{op_callv, "callv "}, {op_callv, "callv "},
{op_callvi, "callvi"}, {op_callvi, "callvi"},
{op_callh, "callh "}, {op_callh, "callh "},
{op_callf, "callf "}, {op_callfv, "callfv"},
{op_callfh, "callfh"},
{op_callb, "callb "}, {op_callb, "callb "},
{op_slcbegin, "slcbeg"}, {op_slcbegin, "slcbeg"},
{op_slcend, "slcend"}, {op_slcend, "slcend"},
@ -509,12 +511,18 @@ void nasal_codegen::call_vec(nasal_ast& ast)
void nasal_codegen::call_func(nasal_ast& ast) void nasal_codegen::call_func(nasal_ast& ast)
{ {
if(!ast.get_children().size()) if(!ast.get_children().size())
gen(op_newv,0); gen(op_callfv,0);
else if(ast.get_children()[0].get_type()==ast_hashmember) else if(ast.get_children()[0].get_type()==ast_hashmember)
{
hash_gen(ast); hash_gen(ast);
gen(op_callfh,0);
}
else else
vec_gen(ast); {
gen(op_callf,0); for(auto& node:ast.get_children())
calc_gen(node);
gen(op_callfv,ast.get_children().size());
}
return; return;
} }

View File

@ -12,21 +12,22 @@ enum nasal_type
vm_type_size vm_type_size
}; };
// change parameters here to make your own efficient gc
// better set bigger number on vm_num and vm_vec
const int increment[vm_type_size]= const int increment[vm_type_size]=
{ {
8, // vm_nil,in fact it is not in use 0, // vm_nil,in fact it is not in use
65536,// vm_num 65536,// vm_num
512, // vm_str 256, // vm_str
16, // vm_func 16, // vm_func
128, // vm_vec 2048, // vm_vec
16 // vm_hash 8 // vm_hash
}; };
struct nasal_vec; struct nasal_vec; //24 bytes
struct nasal_hash; struct nasal_hash;//56 bytes
struct nasal_func; struct nasal_func;//120 bytes
struct nasal_scop; struct nasal_val; // 16 bytes
struct nasal_val;
struct nasal_vec struct nasal_vec
{ {
@ -61,8 +62,10 @@ struct nasal_func
struct nasal_val struct nasal_val
{ {
bool collected; #define GC_UNCOLLECTED 0
bool mark; #define GC_FOUND 1
#define GC_COLLECTED 2
uint8_t mark;
uint16_t type; uint16_t type;
union union
{ {
@ -202,15 +205,13 @@ void nasal_func::clear()
/*functions of nasal_val*/ /*functions of nasal_val*/
nasal_val::nasal_val() nasal_val::nasal_val()
{ {
collected=true; mark=GC_COLLECTED;
mark=false;
type=vm_nil; type=vm_nil;
return; return;
} }
nasal_val::nasal_val(int val_type) nasal_val::nasal_val(int val_type)
{ {
collected=true; mark=GC_COLLECTED;
mark=false;
type=val_type; type=val_type;
switch(type) switch(type)
{ {
@ -290,7 +291,7 @@ void nasal_gc::mark()
nasal_val* tmp=bfs.front(); nasal_val* tmp=bfs.front();
bfs.pop(); bfs.pop();
if(!tmp || tmp->mark) continue; if(!tmp || tmp->mark) continue;
tmp->mark=true; tmp->mark=GC_FOUND;
if(tmp->type==vm_vec) if(tmp->type==vm_vec)
for(auto i:tmp->ptr.vec->elems) for(auto i:tmp->ptr.vec->elems)
bfs.push(i); bfs.push(i);
@ -311,21 +312,20 @@ void nasal_gc::sweep()
{ {
for(auto i:memory) for(auto i:memory)
{ {
if(!i->collected && !i->mark) if(i->mark==GC_UNCOLLECTED)
{ {
switch(i->type) switch(i->type)
{ {
case vm_nil: break;
case vm_num: i->ptr.num=0; break;
case vm_str: i->ptr.str->clear(); break; case vm_str: i->ptr.str->clear(); break;
case vm_vec: i->ptr.vec->elems.clear(); break; case vm_vec: i->ptr.vec->elems.clear(); break;
case vm_hash:i->ptr.hash->elems.clear();break; case vm_hash:i->ptr.hash->elems.clear();break;
case vm_func:i->ptr.func->clear(); break; case vm_func:i->ptr.func->clear(); break;
} }
free_list[i->type].push(i); free_list[i->type].push(i);
i->collected=true; i->mark=GC_COLLECTED;
} }
i->mark=false; else if(i->mark==GC_FOUND)
i->mark=GC_UNCOLLECTED;
} }
return; return;
} }
@ -338,6 +338,7 @@ void nasal_gc::gc_init(std::vector<double>& nums,std::vector<std::string>& strs)
memory.push_back(tmp); memory.push_back(tmp);
free_list[i].push(tmp); free_list[i].push(tmp);
} }
stack_top=val_stack; // set stack_top to val_stack stack_top=val_stack; // set stack_top to val_stack
zero_addr=new nasal_val(vm_num); // init constant 0 zero_addr=new nasal_val(vm_num); // init constant 0
@ -349,21 +350,21 @@ void nasal_gc::gc_init(std::vector<double>& nums,std::vector<std::string>& strs)
nil_addr=new nasal_val(vm_nil); // init nil nil_addr=new nasal_val(vm_nil); // init nil
*val_stack=nil_addr; // the first space will not store any values,but gc checks *val_stack=nil_addr; // the first space will not store any values,but gc checks
// init constant numbers // init constant numbers
num_addrs.resize(nums.size());
for(int i=0;i<nums.size();++i) for(int i=0;i<nums.size();++i)
{ {
nasal_val* tmp=new nasal_val(vm_num); nasal_val* tmp=new nasal_val(vm_num);
tmp->ptr.num=nums[i]; tmp->ptr.num=nums[i];
num_addrs.push_back(tmp); num_addrs[i]=tmp;
} }
// init constant strings // init constant strings
str_addrs.resize(strs.size());
for(int i=0;i<strs.size();++i) for(int i=0;i<strs.size();++i)
{ {
nasal_val* tmp=new nasal_val(vm_str); nasal_val* tmp=new nasal_val(vm_str);
*tmp->ptr.str=strs[i]; *tmp->ptr.str=strs[i];
str_addrs.push_back(tmp); str_addrs[i]=tmp;
} }
return; return;
} }
@ -405,7 +406,7 @@ nasal_val* nasal_gc::gc_alloc(int type)
free_list[type].push(tmp); free_list[type].push(tmp);
} }
nasal_val* ret=free_list[type].front(); nasal_val* ret=free_list[type].front();
ret->collected=false; ret->mark=GC_UNCOLLECTED;
free_list[type].pop(); free_list[type].pop();
return ret; return ret;
} }
@ -423,7 +424,7 @@ nasal_val* nasal_gc::builtin_alloc(int type)
free_list[type].push(tmp); free_list[type].push(tmp);
} }
nasal_val* ret=free_list[type].front(); nasal_val* ret=free_list[type].front();
ret->collected=false; ret->mark=GC_UNCOLLECTED;
free_list[type].pop(); free_list[type].pop();
return ret; return ret;
} }

View File

@ -14,7 +14,7 @@ private:
std::vector<std::string> str_table; // symbols used in process std::vector<std::string> str_table; // symbols used in process
std::vector<opcode> exec_code; // byte codes store here std::vector<opcode> exec_code; // byte codes store here
std::stack<nasal_val**> addr_stack; // stack for mem_call std::stack<nasal_val**> addr_stack; // stack for mem_call
nasal_gc gc; //garbage collector nasal_gc gc; // garbage collector
void die(std::string); void die(std::string);
bool condition(nasal_val*); bool condition(nasal_val*);
@ -69,7 +69,8 @@ private:
void opr_callv(); void opr_callv();
void opr_callvi(); void opr_callvi();
void opr_callh(); void opr_callh();
void opr_callf(); void opr_callfv();
void opr_callfh();
void opr_callb(); void opr_callb();
void opr_slcbegin(); void opr_slcbegin();
void opr_slcend(); void opr_slcend();
@ -101,6 +102,7 @@ void nasal_vm::init(
std::vector<opcode>& exec) std::vector<opcode>& exec)
{ {
gc.gc_init(nums,strs); gc.gc_init(nums,strs);
gc.val_stack[STACK_MAX_DEPTH-1]=nullptr;
str_table=strs; // get constant strings & symbols str_table=strs; // get constant strings & symbols
exec_code=exec; // get bytecodes exec_code=exec; // get bytecodes
loop_mark=true; // set loop mark to true loop_mark=true; // set loop mark to true
@ -135,7 +137,7 @@ bool nasal_vm::condition(nasal_val* val_addr)
std::string& str=*val_addr->ptr.str; std::string& str=*val_addr->ptr.str;
double number=str2num(str); double number=str2num(str);
if(std::isnan(number)) if(std::isnan(number))
return str.length(); return !str.empty();
return number; return number;
} }
return false; return false;
@ -211,19 +213,21 @@ void nasal_vm::opr_newf()
{ {
nasal_val* val=gc.gc_alloc(vm_func); nasal_val* val=gc.gc_alloc(vm_func);
val->ptr.func->entry=exec_code[pc].num; val->ptr.func->entry=exec_code[pc].num;
if(!gc.local.empty()) if(gc.local.empty())
val->ptr.func->closure=gc.local.back();// local contains 'me'
else
val->ptr.func->closure.push_back(gc.nil_addr);// me val->ptr.func->closure.push_back(gc.nil_addr);// me
else
val->ptr.func->closure=gc.local.back();// local contains 'me'
*(++stack_top)=val; *(++stack_top)=val;
return; return;
} }
void nasal_vm::opr_vapp() void nasal_vm::opr_vapp()
{ {
nasal_val* vec=*(stack_top-exec_code[pc].num); nasal_val** begin=stack_top-exec_code[pc].num+1;
for(nasal_val** i=stack_top-exec_code[pc].num+1;i<=stack_top;++i) auto& vec=begin[-1]->ptr.vec->elems;// stack_top-exec_code[pc].num stores the vector
vec->ptr.vec->elems.push_back(*i); vec.resize(exec_code[pc].num);
stack_top-=exec_code[pc].num; for(int i=0;i<exec_code[pc].num;++i)
vec[i]=begin[i];
stack_top=begin-1;
return; return;
} }
void nasal_vm::opr_happ() void nasal_vm::opr_happ()
@ -265,7 +269,7 @@ void nasal_vm::opr_unot()
{ {
double number=str2num(*val->ptr.str); double number=str2num(*val->ptr.str);
if(std::isnan(number)) if(std::isnan(number))
new_val=val->ptr.str->length()?gc.zero_addr:gc.one_addr; new_val=val->ptr.str->empty()?gc.one_addr:gc.zero_addr;
else else
new_val=number?gc.zero_addr:gc.one_addr; new_val=number?gc.zero_addr:gc.one_addr;
} }
@ -526,8 +530,7 @@ void nasal_vm::opr_findex()
void nasal_vm::opr_feach() void nasal_vm::opr_feach()
{ {
std::vector<nasal_val*>& ref=(*stack_top)->ptr.vec->elems; std::vector<nasal_val*>& ref=(*stack_top)->ptr.vec->elems;
++counter.top(); if(++counter.top()>=ref.size())
if(counter.top()>=ref.size())
{ {
pc=exec_code[pc].num-1; pc=exec_code[pc].num-1;
return; return;
@ -635,73 +638,89 @@ void nasal_vm::opr_callh()
*stack_top=res; *stack_top=res;
return; return;
} }
void nasal_vm::opr_callf() void nasal_vm::opr_callfv()
{ {
// get parameter list and function value // get parameter list and function value
nasal_val* para_addr=*stack_top; int args_size=exec_code[pc].num;
nasal_val* func_addr=*(stack_top-1); nasal_val** vec=stack_top-args_size+1;
nasal_val* func_addr=*(vec-1);
if(func_addr->type!=vm_func) if(func_addr->type!=vm_func)
{ {
die("callf: called a value that is not a function"); die("callfv: called a value that is not a function");
return; return;
} }
// push new local scope // push new local scope
gc.local.push_back(func_addr->ptr.func->closure); auto& ref_func=*func_addr->ptr.func;
gc.local.push_back(ref_func.closure);
// load parameters // load parameters
nasal_func& ref_func=*func_addr->ptr.func; auto& ref_default=ref_func.default_para;
std::vector<nasal_val*>& ref_default=ref_func.default_para; auto& ref_closure=gc.local.back();
std::vector<nasal_val*>& ref_closure=gc.local.back(); auto& ref_keys=ref_func.key_table;
std::unordered_map<std::string,int>& ref_keys=ref_func.key_table;
if(para_addr->type==vm_vec) int offset=ref_func.offset;
{
std::vector<nasal_val*>& args=para_addr->ptr.vec->elems;
int args_size=args.size();
int para_size=ref_keys.size(); int para_size=ref_keys.size();
for(int i=0;i<para_size;++i) // load arguments
if(args_size<para_size && !ref_default[args_size])
{ {
if(i>=args_size) // if the first default value is not nullptr,then values after it are not nullptr
{ die("callfv: lack argument(s)");
if(!ref_default[i])
{
die("callf: lack argument(s)");
return; return;
} }
ref_closure[i+ref_func.offset]=ref_default[i]; // if args_size>para_size,for 0 to args_size will cause corruption
} for(int i=0;i<para_size;++i)
else ref_closure[i+offset]=(i>=args_size)?ref_default[i]:vec[i];
ref_closure[i+ref_func.offset]=args[i]; // load dynamic argument if args_size>=para_size
}
if(ref_func.dynpara>=0) if(ref_func.dynpara>=0)
{ {
nasal_val* vec_addr=gc.gc_alloc(vm_vec); nasal_val* vec_addr=gc.gc_alloc(vm_vec);
for(int i=para_size;i<args_size;++i) for(int i=para_size;i<args_size;++i)
vec_addr->ptr.vec->elems.push_back(args[i]); vec_addr->ptr.vec->elems.push_back(vec[i]);
ref_closure[para_size+ref_func.offset]=vec_addr; ref_closure[para_size+offset]=vec_addr;
} }
}
else stack_top-=args_size;// pop arguments
ret.push(pc);
pc=ref_func.entry-1;
return;
}
void nasal_vm::opr_callfh()
{
// get parameter list and function value
std::unordered_map<std::string,nasal_val*>& ref_hash=(*stack_top)->ptr.hash->elems;
nasal_val* func_addr=*(stack_top-1);
if(func_addr->type!=vm_func)
{ {
std::unordered_map<std::string,nasal_val*>& ref_hash=para_addr->ptr.hash->elems; die("callfh: called a value that is not a function");
if(ref_func.dynpara>=0)
{
die("callf: special call cannot use dynamic argument");
return; return;
} }
// push new local scope
auto& ref_func=*func_addr->ptr.func;
gc.local.push_back(ref_func.closure);
// load parameters
auto& ref_default=ref_func.default_para;
auto& ref_closure=gc.local.back();
auto& ref_keys=ref_func.key_table;
if(ref_func.dynpara>=0)
{
die("callfh: special call cannot use dynamic argument");
return;
}
int offset=ref_func.offset;
for(auto& i:ref_keys) for(auto& i:ref_keys)
{ {
if(ref_hash.count(i.first)) if(ref_hash.count(i.first))
ref_closure[i.second+ref_func.offset]=ref_hash[i.first]; ref_closure[i.second+offset]=ref_hash[i.first];
else if(ref_default[i.second]) else if(ref_default[i.second])
ref_closure[i.second+ref_func.offset]=ref_default[i.second]; ref_closure[i.second+offset]=ref_default[i.second];
else else
{ {
die("callf: lack argument(s): \""+i.first+"\""); die("callfh: lack argument(s): \""+i.first+"\"");
return; return;
} }
} }
}
--stack_top; --stack_top;// pop hash
ret.push(pc); ret.push(pc);
pc=ref_func.entry-1; pc=ref_func.entry-1;
return; return;
@ -920,7 +939,8 @@ void nasal_vm::run()
&nasal_vm::opr_callv, &nasal_vm::opr_callv,
&nasal_vm::opr_callvi, &nasal_vm::opr_callvi,
&nasal_vm::opr_callh, &nasal_vm::opr_callh,
&nasal_vm::opr_callf, &nasal_vm::opr_callfv,
&nasal_vm::opr_callfh,
&nasal_vm::opr_callb, &nasal_vm::opr_callb,
&nasal_vm::opr_slcbegin, &nasal_vm::opr_slcbegin,
&nasal_vm::opr_slcend, &nasal_vm::opr_slcend,
@ -934,7 +954,7 @@ void nasal_vm::run()
}; };
clock_t begin_time=clock(); clock_t begin_time=clock();
// main loop // main loop
for(pc=0;loop_mark;++pc) for(pc=0;loop_mark&&!gc.val_stack[STACK_MAX_DEPTH-1];++pc)
(this->*opr_table[exec_code[pc].op])(); (this->*opr_table[exec_code[pc].op])();
float total_time=((double)(clock()-begin_time))/CLOCKS_PER_SEC; float total_time=((double)(clock()-begin_time))/CLOCKS_PER_SEC;
std::cout<<">> [vm] process exited after "<<total_time<<"s.\n"; std::cout<<">> [vm] process exited after "<<total_time<<"s.\n";

View File

@ -26,8 +26,7 @@ var setsize=func(vector,size)
} }
var system=func(str) var system=func(str)
{ {
__builtin_system(str); return __builtin_system(str);
return;
} }
var input=func() var input=func()
{ {

View File

@ -137,7 +137,7 @@ while(error>0.0005)
} }
cnt+=1; cnt+=1;
show+=1; show+=1;
if(show==300) if(show==350)
{ {
show=0; show=0;
print('epoch ',cnt,':',error,'\r'); print('epoch ',cnt,':',error,'\r');

View File

@ -6,3 +6,4 @@ var fib=func(x)
} }
for(var i=0;i<31;i+=1) for(var i=0;i<31;i+=1)
print(fib(i),'\n'); print(fib(i),'\n');