add instruction & changes in codegen

add some instructions that execute const values.
the first symbol called in assignment will use op_load instead of op_meq,op_pop to assign.
This commit is contained in:
ValKmjolnir 2021-06-29 17:18:05 +08:00
parent 706659ba3d
commit 0b2fe61e6e
4 changed files with 375 additions and 106 deletions

View File

@ -32,13 +32,15 @@ You could add your own built-in functions to change this interpreter to a useful
Better choose the latest update of the interpreter.
MUST USE -O2/-O3 if want to optimize the interpreter! pragma gcc optimize(2) seems useless when using g++
MUST USE -O2/-O3 if want to optimize the interpreter!
> g++ -std=c++11 -O2 main.cpp -o nasal.exe
Also remember to use g++ and clang++.
> g++|clang++ -std=c++11 -O2 main.cpp -o nasal.exe
Or use this in linux/macOS/Unix
> g++ -std=c++11 -O2 main.cpp -o nasal
> g++|clang++ -std=c++11 -O2 main.cpp -o nasal
## How to Use?
@ -117,7 +119,7 @@ If i continue saving this interpreter,it will be harder for me to make the bytec
## Byte Code Interpreter
### Version 4.0(last update 2020/12/17)
### Version 4.0 (last update 2020/12/17)
I have just finished the first version of byte-code-interpreter.
@ -150,7 +152,7 @@ for(var i=0;i<4000000;i+=1);
0x0000000b: nop 0x00000000
```
### Version 5.0(last update 2021/3/7)
### Version 5.0 (last update 2021/3/7)
I decide to optimize bytecode vm in this version.
@ -158,7 +160,7 @@ Because it takes more than 1.5s to count i from 0 to 4000000-1.This is not effic
2021/1/23 update: Now it can count from 0 to 4000000-1 in 1.5s.
### Version 6.0(last update 2021/6/1)
### Version 6.0 (last update 2021/6/1)
Use loadg loadl callg calll mcallg mcalll to avoid branches.
@ -199,7 +201,7 @@ for(var i=0;i<4000000;i+=1);
0x0000000c: nop 0x00000000
```
### Version 6.5(last update 2021/6/24)
### Version 6.5 (last update 2021/6/24)
2021/5/31 update: Now gc can collect garbage correctly without re-collecting,which will cause fatal error.
@ -329,16 +331,62 @@ As you could see from the bytecode above,mcall/mcallv/mcallh operands' using fre
And because of the new structure of mcall, addr_stack, a stack used to store the memory address, is deleted from nasal_vm, and now nasal_vm use nasal_val** mem_addr to store the memory address. This will not cause fatal errors because the memory address is used __immediately__ after getting it.
### version 7.0(latest)
### version 7.0 (latest)
2021/6/26 update:
Instruction dispatch is changed from call-threading to computed-goto(with inline function).After changing the way of instruction dispatch,there is a great improvement in nasal_vm.Now vm can run test/bigloop and test/pi in 0.2s!And vm runs test/fib in 0.8s on linux.You could see the time use data below,in Test data section.
This version uses gcc extension "labels as values", which is also supported by clang.(But i don't know if MSVC supports this)
This version uses g++ extension "labels as values", which is also supported by clang++.(But i don't know if MSVC supports this)
There is also a change in nasal_gc: std::vector global is deleted,now the global values are all stored on stack(from val_stack+0 to val_stack+intg-1).
2021/6/29 update:
Add some instructions that execute const values:op_addc,op_subc,op_mulc,op_divc,op_lnkc,op_addeqc,op_subeqc,op_muleqc,op_diveqc,op_lnkeqc.
Now the bytecode of test/bigloop.nas seems like this:
```asm
.number 4e+006
.number 1
0x00000000: intg 0x00000001
0x00000001: pzero 0x00000000
0x00000002: loadg 0x00000000
0x00000003: callg 0x00000000
0x00000004: pnum 0x00000000 (4000000)
0x00000005: less 0x00000000
0x00000006: jf 0x0000000b
0x00000007: mcallg 0x00000000
0x00000008: addeqc 0x00000001 (1)
0x00000009: pop 0x00000000
0x0000000a: jmp 0x00000003
0x0000000b: nop 0x00000000
```
And this test file runs in 0.1s after this update.Most of the calculations are accelerated.
Also, assignment bytecode has changed a lot. Now the first identifier that called in assignment will use op_load to assign, instead of op_meq,op_pop.
```javascript
var (a,b)=(1,2);
a=b=0;
```
```asm
.number 2
0x00000000: intg 0x00000002
0x00000001: pone 0x00000000
0x00000002: loadg 0x00000000
0x00000003: pnum 0x00000000 (2)
0x00000004: loadg 0x00000001
0x00000005: pzero 0x00000000
0x00000006: mcallg 0x00000001
0x00000007: meq 0x00000000 (b=2 use meq,pop->a)
0x00000008: loadg 0x00000000 (a=b use loadg)
0x00000009: nop 0x00000000
```
## Test data
### version 6.5(i5-8250U windows10 2021/6/19)
@ -388,20 +436,20 @@ operands calling total times:
|quick_sort.nas|16226|5561|4144|3524|2833|
|bfs.nas|24707|16297|14606|14269|8672|
### version 7.0(i5-8250U ubuntu-WSL on windows10 2021/6/26)
### version 7.0(i5-8250U ubuntu-WSL on windows10 2021/6/29)
running time:
|file|total time|info|
|:----|:----|:----|
|pi.nas|0.17s|great improvement|
|pi.nas|0.15625s|great improvement|
|fib.nas|0.75s|great improvement|
|bp.nas|0.32s(5467 epoch)|good improvement|
|bigloop.nas|0.11s|great improvement|
|mandelbrot.nas|0.04s|great improvment|
|life.nas|8.80s(windows) 1.34(ubuntu WSL)|little improvement|
|bp.nas|0.4218s(7162 epoch)|good improvement|
|bigloop.nas|0.09375s|great improvement|
|mandelbrot.nas|0.0312s|great improvement|
|life.nas|8.80s(windows) 1.25(ubuntu WSL)|little improvement|
|ascii-art.nas|0.015s|little improvement|
|calc.nas|0.0625s|little improvement|
|calc.nas|0.0468s|little improvement|
|quick_sort.nas|0s|great improvement|
|bfs.nas|0.0156s|great improvement|

View File

@ -28,11 +28,21 @@ enum op_code
op_mul, // *
op_div, // /
op_lnk, // ~
op_addc, // + const
op_subc, // - const
op_mulc, // * const
op_divc, // / const
op_lnkc, // ~ const
op_addeq, // +=
op_subeq, // -=
op_muleq, // *=
op_diveq, // /=
op_lnkeq, // ~=
op_addeqc, // += const
op_subeqc, // -= const
op_muleqc, // *= const
op_diveqc, // /= const
op_lnkeqc, // ~= const
op_meq, // =
op_eq, // ==
op_neq, // !=
@ -98,11 +108,21 @@ struct
{op_mul, "mult "},
{op_div, "div "},
{op_lnk, "link "},
{op_addc, "addc "},
{op_subc, "subc "},
{op_mulc, "multc "},
{op_divc, "divc "},
{op_lnkc, "lnkc "},
{op_addeq, "addeq "},
{op_subeq, "subeq "},
{op_muleq, "muleq "},
{op_diveq, "diveq "},
{op_lnkeq, "lnkeq "},
{op_addeqc, "addeqc"},
{op_subeqc, "subeqc"},
{op_muleqc, "muleqc"},
{op_diveqc, "diveqc"},
{op_lnkeqc, "lnkeqc"},
{op_meq, "meq "},
{op_eq, "eq "},
{op_neq, "neq "},
@ -141,8 +161,8 @@ struct
struct opcode
{
uint8_t op;
uint32_t num;
opcode(uint8_t _op=op_nop,uint32_t _num=0)
int32_t num;
opcode(uint8_t _op=op_nop,int32_t _num=0)
{
op=_op;
num=_num;
@ -178,7 +198,7 @@ private:
void add_sym(std::string&);
int local_find(std::string&);
int global_find(std::string&);
void gen(unsigned char,unsigned int);
void gen(uint8_t,int32_t);
void num_gen(nasal_ast&);
void str_gen(nasal_ast&);
void vec_gen(nasal_ast&);
@ -283,7 +303,7 @@ int nasal_codegen::global_find(std::string& name)
return -1;
}
void nasal_codegen::gen(uint8_t op,uint32_t num)
void nasal_codegen::gen(uint8_t op,int32_t num)
{
exec_code.push_back({op,num});
return;
@ -454,6 +474,7 @@ void nasal_codegen::call_hash(nasal_ast& ast)
void nasal_codegen::call_vec(nasal_ast& ast)
{
// maybe this place can use callv-const if ast's first child is ast_num
if(ast.get_children().size()==1 && ast.get_children()[0].get_type()!=ast_subvec)
{
calc_gen(ast.get_children()[0]);
@ -620,8 +641,17 @@ void nasal_codegen::multi_assign_gen(nasal_ast& ast)
for(int i=0;i<size;++i)
{
mcall(ast.get_children()[0].get_children()[i]);
gen(op_meq,0);
gen(op_pop,0);
// multi assign user loadl and loadg to avoid meq's stack--
// and this operation changes local and global value directly
if(exec_code.back().op==op_mcalll)
exec_code.back().op=op_loadl;
else if(exec_code.back().op==op_mcallg)
exec_code.back().op=op_loadg;
else
{
gen(op_meq,0);
gen(op_pop,0);
}
}
}
else
@ -630,9 +660,18 @@ void nasal_codegen::multi_assign_gen(nasal_ast& ast)
for(int i=0;i<size;++i)
{
gen(op_callvi,i);
// multi assign user loadl and loadg to avoid meq's stack--
// and this operation changes local and global value directly
mcall(ast.get_children()[0].get_children()[i]);
gen(op_meq,0);
gen(op_pop,0);
if(exec_code.back().op==op_mcalll)
exec_code.back().op=op_loadl;
else if(exec_code.back().op==op_mcallg)
exec_code.back().op=op_loadg;
else
{
gen(op_meq,0);
gen(op_pop,0);
}
}
gen(op_pop,0);
}
@ -895,18 +934,57 @@ void nasal_codegen::calc_gen(nasal_ast& ast)
gen(op_meq,0);
break;
// ast_addeq(22)~ast_lnkeq(26) op_addeq(23)~op_lnkeq(27)
case ast_addeq:case ast_subeq:case ast_multeq:case ast_diveq:case ast_lnkeq:
calc_gen(ast.get_children()[1]);
case ast_addeq:case ast_subeq:case ast_multeq:case ast_diveq:
if(ast.get_children()[1].get_type()!=ast_num)
calc_gen(ast.get_children()[1]);
mcall(ast.get_children()[0]);
gen(ast.get_type()-ast_addeq+op_addeq,0);
if(ast.get_children()[1].get_type()!=ast_num)
gen(ast.get_type()-ast_addeq+op_addeq,0);
else
{
regist_number(ast.get_children()[1].get_num());
gen(ast.get_type()-ast_addeq+op_addeqc,number_table[ast.get_children()[1].get_num()]);
}
break;
case ast_lnkeq:
if(ast.get_children()[1].get_type()!=ast_str)
calc_gen(ast.get_children()[1]);
else
regist_string(ast.get_children()[1].get_str());
mcall(ast.get_children()[0]);
if(ast.get_children()[1].get_type()!=ast_str)
gen(op_lnkeq,0);
else
gen(op_lnkeqc,string_table[ast.get_children()[1].get_str()]);
break;
case ast_or:or_gen(ast);break;
case ast_and:and_gen(ast);break;
// ast_add(33)~ast_link(37) op_add(18)~op_lnk(22)
case ast_add:case ast_sub:case ast_mult:case ast_div:case ast_link:
case ast_add:case ast_sub:case ast_mult:case ast_div:
calc_gen(ast.get_children()[0]);
calc_gen(ast.get_children()[1]);
gen(ast.get_type()-ast_add+op_add,0);
if(ast.get_children()[1].get_type()!=ast_num)
{
calc_gen(ast.get_children()[1]);
gen(ast.get_type()-ast_add+op_add,0);
}
else
{
regist_number(ast.get_children()[1].get_num());
gen(ast.get_type()-ast_add+op_addc,number_table[ast.get_children()[1].get_num()]);
}
break;
case ast_link:
calc_gen(ast.get_children()[0]);
if(ast.get_children()[1].get_type()!=ast_str)
{
calc_gen(ast.get_children()[1]);
gen(op_lnk,0);
}
else
{
regist_string(ast.get_children()[1].get_str());
gen(op_lnkc,string_table[ast.get_children()[1].get_str()]);
}
break;
// ast_cmpeq(27)~ast_geq(32) op_eq(29)~op_geq(34)
case ast_cmpeq:case ast_neq:case ast_less:case ast_leq:case ast_grt:case ast_geq:
@ -952,11 +1030,27 @@ void nasal_codegen::block_gen(nasal_ast& ast)
case ast_for:
case ast_forindex:
case ast_foreach:loop_gen(tmp);break;
case ast_equal:
if(tmp.get_children()[0].get_type()==ast_id)
{
calc_gen(tmp.get_children()[1]);
mcall_id(tmp.get_children()[0]);
// only the first mcall_id can use load
if(exec_code.back().op==op_mcalll)
exec_code.back().op=op_loadl;
else
exec_code.back().op=op_loadg;
}
else
{
calc_gen(tmp);
gen(op_pop,0);
}
break;
case ast_id:
case ast_vec:
case ast_hash:
case ast_call:
case ast_equal:
case ast_addeq:
case ast_subeq:
case ast_multeq:
@ -1028,11 +1122,27 @@ void nasal_codegen::main_progress(nasal_ast& ast)
case ast_for:
case ast_forindex:
case ast_foreach:loop_gen(tmp);break;
case ast_equal:
if(tmp.get_children()[0].get_type()==ast_id)
{
calc_gen(tmp.get_children()[1]);
mcall_id(tmp.get_children()[0]);
// only the first mcall_id can use load
if(exec_code.back().op==op_mcalll)
exec_code.back().op=op_loadl;
else
exec_code.back().op=op_loadg;
}
else
{
calc_gen(tmp);
gen(op_pop,0);
}
break;
case ast_id:
case ast_vec:
case ast_hash:
case ast_call:
case ast_equal:
case ast_addeq:
case ast_subeq:
case ast_multeq:
@ -1074,10 +1184,13 @@ void nasal_codegen::print_op(int index)
// print detail info
switch(exec_code[index].op)
{
case op_addc:case op_subc:case op_mulc:case op_divc:
case op_addeqc:case op_subeqc:case op_muleqc:case op_diveqc:
case op_pnum:printf(" (%lf)\n",num_res_table[exec_code[index].num]);break;
case op_callb:printf(" (%s)\n",builtin_func[exec_code[index].num].name);break;
case op_happ:
case op_pstr:
case op_lnkc:case op_lnkeqc:
case op_callh:
case op_mcallh:
case op_para:

View File

@ -684,6 +684,8 @@ nasal_ast nasal_parse::call_scalar()
case tok_lbracket: return callv(); break;
case tok_dot: return callh(); break;
}
// should never run this expression
return nasal_ast(tok_list[ptr].line,ast_nil);
}
nasal_ast nasal_parse::callh()
{

View File

@ -11,7 +11,7 @@ private:
std::stack<int> ret; // ptr stack stores address for function to return
std::stack<int> counter; // iterator stack for forindex/foreach
std::vector<std::string> str_table;// symbols used in process
std::vector<uint32_t> imm; // immediate number
std::vector<int32_t> imm; // immediate number
nasal_val** mem_addr; // used for mem_call
nasal_gc gc; // garbage collector
@ -41,11 +41,21 @@ private:
void opr_mul();
void opr_div();
void opr_lnk();
void opr_addc();
void opr_subc();
void opr_mulc();
void opr_divc();
void opr_lnkc();
void opr_addeq();
void opr_subeq();
void opr_muleq();
void opr_diveq();
void opr_lnkeq();
void opr_addeqc();
void opr_subeqc();
void opr_muleqc();
void opr_diveqc();
void opr_lnkeqc();
void opr_meq();
void opr_eq();
void opr_neq();
@ -298,6 +308,41 @@ inline void nasal_vm::opr_lnk()
(--stack_top)[0]=new_val;
return;
}
inline void nasal_vm::opr_addc()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=stack_top[0]->to_number()+gc.num_addrs[imm[pc]]->ptr.num;
stack_top[0]=new_val;
return;
}
inline void nasal_vm::opr_subc()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=stack_top[0]->to_number()-gc.num_addrs[imm[pc]]->ptr.num;
stack_top[0]=new_val;
return;
}
inline void nasal_vm::opr_mulc()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=stack_top[0]->to_number()*gc.num_addrs[imm[pc]]->ptr.num;
stack_top[0]=new_val;
return;
}
inline void nasal_vm::opr_divc()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=stack_top[0]->to_number()/gc.num_addrs[imm[pc]]->ptr.num;
stack_top[0]=new_val;
return;
}
inline void nasal_vm::opr_lnkc()
{
nasal_val* new_val=gc.gc_alloc(vm_str);
*new_val->ptr.str=stack_top[0]->to_string()+str_table[imm[pc]];
stack_top[0]=new_val;
return;
}
inline void nasal_vm::opr_addeq()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
@ -333,6 +378,41 @@ inline void nasal_vm::opr_lnkeq()
(--stack_top)[0]=mem_addr[0]=new_val;
return;
}
inline void nasal_vm::opr_addeqc()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=mem_addr[0]->to_number()+gc.num_addrs[imm[pc]]->ptr.num;
stack_top[0]=mem_addr[0]=new_val;
return;
}
inline void nasal_vm::opr_subeqc()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=mem_addr[0]->to_number()-gc.num_addrs[imm[pc]]->ptr.num;
stack_top[0]=mem_addr[0]=new_val;
return;
}
inline void nasal_vm::opr_muleqc()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=mem_addr[0]->to_number()*gc.num_addrs[imm[pc]]->ptr.num;
stack_top[0]=mem_addr[0]=new_val;
return;
}
inline void nasal_vm::opr_diveqc()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=mem_addr[0]->to_number()/gc.num_addrs[imm[pc]]->ptr.num;
stack_top[0]=mem_addr[0]=new_val;
return;
}
inline void nasal_vm::opr_lnkeqc()
{
nasal_val* new_val=gc.gc_alloc(vm_str);
*new_val->ptr.str=mem_addr[0]->to_string()+str_table[imm[pc]];
stack_top[0]=mem_addr[0]=new_val;
return;
}
inline void nasal_vm::opr_meq()
{
mem_addr[0]=(--stack_top)[0];
@ -510,7 +590,7 @@ inline void nasal_vm::opr_callvi()
nasal_val* val=stack_top[0];
if(val->type!=vm_vec)
{
die("callvi: multi-definition/multi-assignment must use a vector");
die("callvi: must use a vector");
return;
}
// cannot use operator[],because this may cause overflow
@ -761,6 +841,7 @@ inline void nasal_vm::opr_ret()
}
void nasal_vm::run(std::vector<opcode>& exec)
{
int count[72]={0};
void* opr_table[]=
{
&&nop, &&intg, &&intl, &&offset,
@ -769,16 +850,18 @@ void nasal_vm::run(std::vector<opcode>& exec)
&&newh, &&newf, &&happ, &&para,
&&defpara, &&dynpara, &&unot, &&usub,
&&add, &&sub, &&mul, &&div,
&&lnk, &&addeq, &&subeq, &&muleq,
&&diveq, &&lnkeq, &&meq, &&eq,
&&neq, &&less, &&leq, &&grt,
&&geq, &&pop, &&jmp, &&jt,
&&jf, &&counter, &&cntpop, &&findex,
&&feach, &&callg, &&calll, &&callv,
&&callvi, &&callh, &&callfv, &&callfh,
&&callb, &&slcbegin, &&slcend, &&slc,
&&slc2, &&mcallg, &&mcalll, &&mcallv,
&&mcallh, &&ret
&&lnk, &&addc, &&subc, &&mulc,
&&divc, &&lnkc, &&addeq, &&subeq,
&&muleq, &&diveq, &&lnkeq, &&addeqc,
&&subeqc, &&muleqc, &&diveqc, &&lnkeqc,
&&meq, &&eq, &&neq, &&less,
&&leq, &&grt, &&geq, &&pop,
&&jmp, &&jt, &&jf, &&counter,
&&cntpop, &&findex, &&feach, &&callg,
&&calll, &&callv, &&callvi, &&callh,
&&callfv, &&callfh, &&callb, &&slcbegin,
&&slcend, &&slc, &&slc2, &&mcallg,
&&mcalll, &&mcallv, &&mcallh, &&ret
};
std::vector<void*> code;
for(auto& i:exec)
@ -795,68 +878,91 @@ nop:
if(gc.val_stack[STACK_MAX_DEPTH-1]&&gc.val_stack[STACK_MAX_DEPTH-1]!=(nasal_val*)0xffff)
std::cout<<">> [vm] stack overflow.\n";
std::cout<<">> [vm] process exited after "<<((double)(clock()-begin))/CLOCKS_PER_SEC<<"s.\n";
// debug
// for(int i=0;i<15;++i)
// {
// int maxnum=0,index=0;
// for(int j=0;j<62;++j)
// if(count[j]>maxnum)
// {
// index=j;
// maxnum=count[j];
// }
// std::cout<<code_table[index].name<<": "<<maxnum<<"\n";
// count[index]=0;
// }
return;
#define exec_operand(op) {op();if(!gc.val_stack[STACK_MAX_DEPTH-1])goto *code[++pc];goto nop;}
intg: exec_operand(opr_intg );
intl: exec_operand(opr_intl );
offset: exec_operand(opr_offset );
loadg: exec_operand(opr_loadg );
loadl: exec_operand(opr_loadl );
pnum: exec_operand(opr_pnum );
pone: exec_operand(opr_pone );
pzero: exec_operand(opr_pzero );
pnil: exec_operand(opr_pnil );
pstr: exec_operand(opr_pstr );
newv: exec_operand(opr_newv );
newh: exec_operand(opr_newh );
newf: exec_operand(opr_newf );
happ: exec_operand(opr_happ );
para: exec_operand(opr_para );
defpara: exec_operand(opr_defpara );
dynpara: exec_operand(opr_dynpara );
unot: exec_operand(opr_unot );
usub: exec_operand(opr_usub );
add: exec_operand(opr_add );
sub: exec_operand(opr_sub );
mul: exec_operand(opr_mul );
div: exec_operand(opr_div );
lnk: exec_operand(opr_lnk );
addeq: exec_operand(opr_addeq );
subeq: exec_operand(opr_subeq );
muleq: exec_operand(opr_muleq );
diveq: exec_operand(opr_diveq );
lnkeq: exec_operand(opr_lnkeq );
meq: exec_operand(opr_meq );
eq: exec_operand(opr_eq );
neq: exec_operand(opr_neq );
less: exec_operand(opr_less );
leq: exec_operand(opr_leq );
grt: exec_operand(opr_grt );
geq: exec_operand(opr_geq );
pop: exec_operand(opr_pop );
jmp: exec_operand(opr_jmp );
jt: exec_operand(opr_jt );
jf: exec_operand(opr_jf );
counter: exec_operand(opr_counter );
cntpop: exec_operand(opr_cntpop );
findex: exec_operand(opr_findex );
feach: exec_operand(opr_feach );
callg: exec_operand(opr_callg );
calll: exec_operand(opr_calll );
callv: exec_operand(opr_callv );
callvi: exec_operand(opr_callvi );
callh: exec_operand(opr_callh );
callfv: exec_operand(opr_callfv );
callfh: exec_operand(opr_callfh );
callb: exec_operand(opr_callb );
slcbegin:exec_operand(opr_slcbegin);
slcend: exec_operand(opr_slcend );
slc: exec_operand(opr_slc );
slc2: exec_operand(opr_slc2 );
mcallg: exec_operand(opr_mcallg );
mcalll: exec_operand(opr_mcalll );
mcallv: exec_operand(opr_mcallv );
mcallh: exec_operand(opr_mcallh );
ret: exec_operand(opr_ret );
#define exec_operand(op,num) {op();/*++count[num];*/if(!gc.val_stack[STACK_MAX_DEPTH-1])goto *code[++pc];goto nop;}
intg: exec_operand(opr_intg ,1);
intl: exec_operand(opr_intl ,2);
offset: exec_operand(opr_offset ,3);
loadg: exec_operand(opr_loadg ,4);
loadl: exec_operand(opr_loadl ,5);
pnum: exec_operand(opr_pnum ,6);
pone: exec_operand(opr_pone ,7);
pzero: exec_operand(opr_pzero ,8);
pnil: exec_operand(opr_pnil ,9);
pstr: exec_operand(opr_pstr ,10);
newv: exec_operand(opr_newv ,11);
newh: exec_operand(opr_newh ,12);
newf: exec_operand(opr_newf ,13);
happ: exec_operand(opr_happ ,14);
para: exec_operand(opr_para ,15);
defpara: exec_operand(opr_defpara ,16);
dynpara: exec_operand(opr_dynpara ,17);
unot: exec_operand(opr_unot ,18);
usub: exec_operand(opr_usub ,19);
add: exec_operand(opr_add ,20);
sub: exec_operand(opr_sub ,21);
mul: exec_operand(opr_mul ,22);
div: exec_operand(opr_div ,23);
lnk: exec_operand(opr_lnk ,24);
addc: exec_operand(opr_addc ,25);
subc: exec_operand(opr_subc ,26);
mulc: exec_operand(opr_mulc ,27);
divc: exec_operand(opr_divc ,28);
lnkc: exec_operand(opr_lnkc ,29);
addeq: exec_operand(opr_addeq ,30);
subeq: exec_operand(opr_subeq ,31);
muleq: exec_operand(opr_muleq ,32);
diveq: exec_operand(opr_diveq ,33);
lnkeq: exec_operand(opr_lnkeq ,34);
addeqc: exec_operand(opr_addeqc ,35);
subeqc: exec_operand(opr_subeqc ,36);
muleqc: exec_operand(opr_muleqc ,37);
diveqc: exec_operand(opr_diveqc ,38);
lnkeqc: exec_operand(opr_lnkeqc ,39);
meq: exec_operand(opr_meq ,40);
eq: exec_operand(opr_eq ,41);
neq: exec_operand(opr_neq ,42);
less: exec_operand(opr_less ,43);
leq: exec_operand(opr_leq ,44);
grt: exec_operand(opr_grt ,45);
geq: exec_operand(opr_geq ,46);
pop: exec_operand(opr_pop ,47);
jmp: exec_operand(opr_jmp ,48);
jt: exec_operand(opr_jt ,49);
jf: exec_operand(opr_jf ,50);
counter: exec_operand(opr_counter ,51);
cntpop: exec_operand(opr_cntpop ,52);
findex: exec_operand(opr_findex ,53);
feach: exec_operand(opr_feach ,54);
callg: exec_operand(opr_callg ,55);
calll: exec_operand(opr_calll ,56);
callv: exec_operand(opr_callv ,57);
callvi: exec_operand(opr_callvi ,58);
callh: exec_operand(opr_callh ,59);
callfv: exec_operand(opr_callfv ,60);
callfh: exec_operand(opr_callfh ,61);
callb: exec_operand(opr_callb ,62);
slcbegin:exec_operand(opr_slcbegin,63);
slcend: exec_operand(opr_slcend ,64);
slc: exec_operand(opr_slc ,65);
slc2: exec_operand(opr_slc2 ,66);
mcallg: exec_operand(opr_mcallg ,67);
mcalll: exec_operand(opr_mcalll ,68);
mcallv: exec_operand(opr_mcallv ,69);
mcallh: exec_operand(opr_mcallh ,70);
ret: exec_operand(opr_ret ,71);
}
#endif