optimize vm

This commit is contained in:
ValKmjolnir 2022-07-04 00:16:04 +08:00
parent 452bb4a5d8
commit 92d68b357c
6 changed files with 90 additions and 63 deletions

View File

@ -551,31 +551,48 @@ And `get` has the same process.
And we must remind you that if you do this: And we must remind you that if you do this:
```javascript ```javascript
var b=a.get; var trait={
println(b()); get:func{return me.val;},
println(b()); set:func(x){me.val=x;}
};
var class={
new:func(){
return {
val:nil,
parents:[trait]
};
}
};
var a=class.new();
var b=class.new();
a.set(114);
b.set(514);
println(a.get());
println(b.get());
var c=a.get;
var d=b.get;
println(c());
println(c());
println(d());
println(d());
``` ```
You will find the vm crashes: You will get this result now:
```bash ```bash
114514 114
114514 514
[vm] callh: must call a hash 514
trace back: 514
0x0000050f: 3d 00 00 00 08 callh 0x8 ("val") (a.nas:2) 514
0x00000544: 3e 00 00 00 00 callfv 0x0 (a.nas:19) 514
vm stack(0x7fffd1b38250<sp+83>, limit 10, total 7):
0x00000059 | nil |
0x00000058 | pc | 0x544
0x00000057 | addr | 0x0
0x00000056 | nil |
0x00000055 | nil |
0x00000054 | nil |
0x00000053 | func | <0x1a3b250> entry:0x125
``` ```
Because `a.get` will set `me=a` in the `trait.get`. First time we running it, it works. But when the function returns, `me` is set to `nil`, so the second time, we failed to call the function. Because `a.get` will set `me=a` in the `trait.get`. Then `b.get` do the `me=b`. So in fact c is `b.get` too after running `var d=b.get`.
If you want to use this trick to make the program running more efficiently, you must know this special mechanism.
### __native functions__ ### __native functions__

View File

@ -518,31 +518,48 @@ println(a.get());
不过我们必须提醒你一点如果你在这个地方使用该优化来减少hash的搜索开销: 不过我们必须提醒你一点如果你在这个地方使用该优化来减少hash的搜索开销:
```javascript ```javascript
var b=a.get; var trait={
println(b()); get:func{return me.val;},
println(b()); set:func(x){me.val=x;}
};
var class={
new:func(){
return {
val:nil,
parents:[trait]
};
}
};
var a=class.new();
var b=class.new();
a.set(114);
b.set(514);
println(a.get());
println(b.get());
var c=a.get;
var d=b.get;
println(c());
println(c());
println(d());
println(d());
``` ```
那么你会发现虚拟机崩溃了: 那么你会发现现在虚拟机会输出这个结果:
```bash ```bash
114514 114
114514 514
[vm] callh: must call a hash 514
trace back: 514
0x0000050f: 3d 00 00 00 08 callh 0x8 ("val") (a.nas:2) 514
0x00000544: 3e 00 00 00 00 callfv 0x0 (a.nas:19) 514
vm stack(0x7fffd1b38250<sp+83>, limit 10, total 7):
0x00000059 | nil |
0x00000058 | pc | 0x544
0x00000057 | addr | 0x0
0x00000056 | nil |
0x00000055 | nil |
0x00000054 | nil |
0x00000053 | func | <0x1a3b250> entry:0x125
``` ```
因为执行`a.get`时在`trait.get`函数的属性中进行了`me=a`的操作。所以接下来第一次运行的时候它确实成功运行了。但是当函数返回时,`me`会被自动设置为`nil`以保证安全,所以第二次调用的时候,`me`不再是个hash类型故虚拟机抛出了错误。这不意味着这种优化方法是不可行的只要你理解了它的运行机制这种优化方式仍然可以使用。 因为执行`a.get`时在`trait.get`函数的属性中进行了`me=a`的操作。而`b.get`则执行了`me=b`的操作。所以在运行`var d=b.get`后实际上c也变成`b.get`了。
如果你想要用这种小技巧来让程序运行更高效的话,最好是要知道这里存在这样一个机制。
### __内置函数__ ### __内置函数__

View File

@ -43,7 +43,7 @@
#define PRTINT64 "%lld" #define PRTINT64 "%lld"
#endif #endif
const uint32_t STACK_DEPTH=8192; const uint32_t STACK_DEPTH=4096;
inline double hex_to_double(const char* str) inline double hex_to_double(const char* str)
{ {

View File

@ -182,7 +182,7 @@ void nasal_dbg::run(
{ {
detail_info=true; detail_info=true;
init(gen.get_strs(),gen.get_nums(),gen.get_code(),linker.get_file(),argv); init(gen.get_strs(),gen.get_nums(),gen.get_code(),linker.get_file(),argv);
const void* opr_table[]= const void* oprs[]=
{ {
&&vmexit, &&intg, &&intl, &&loadg, &&vmexit, &&intg, &&intl, &&loadg,
&&loadl, &&loadu, &&pnum, &&pnil, &&loadl, &&loadu, &&pnum, &&pnil,
@ -207,7 +207,7 @@ void nasal_dbg::run(
std::vector<const void*> code; std::vector<const void*> code;
for(auto& i:gen.get_code()) for(auto& i:gen.get_code())
{ {
code.push_back(opr_table[i.op]); code.push_back(oprs[i.op]);
imm.push_back(i.num); imm.push_back(i.num);
} }
// goto the first operand // goto the first operand

View File

@ -624,8 +624,8 @@ void nasal_gc::info()
const char* name[]={"str ","func ","vec ","hash ","upval","obj ","co "}; const char* name[]={"str ","func ","vec ","hash ","upval","obj ","co "};
std::cout<<"\ngarbage collector info\n"; std::cout<<"\ngarbage collector info\n";
for(uint8_t i=0;i<gc_obj_size;++i) for(uint8_t i=0;i<gc_obj_size;++i)
std::cout<<" "<<name[i]<<" | mark-sweep | "<<count[i]<<"\n" std::cout<<" "<<name[i]<<" | gc | "<<count[i]<<"\n"
<<" | mem-alloc | "<<allocc[i]<<"\n"; <<" | new | "<<allocc[i]<<"\n";
std::cout<<"\nmemory allocator info(max size)\n"; std::cout<<"\nmemory allocator info(max size)\n";
for(uint8_t i=0;i<gc_obj_size;++i) for(uint8_t i=0;i<gc_obj_size;++i)
std::cout<<" "<<name[i]<<" | "<<ini[i]+size[i]*incr[i]<<" (+"<<size[i]<<")\n"; std::cout<<" "<<name[i]<<" | "<<ini[i]+size[i]*incr[i]<<" (+"<<size[i]<<")\n";

View File

@ -4,22 +4,19 @@
class nasal_vm class nasal_vm
{ {
protected: protected:
/* values of nasal_vm */ /* registers and constants of nasal_vm */
nasal_ref stack[STACK_DEPTH];
uint32_t pc; // program counter uint32_t pc; // program counter
nasal_ref* global; // global scope register
nasal_ref* localr; // local scope register nasal_ref* localr; // local scope register
nasal_ref* memr; // used for mem_call nasal_ref* memr; // used for mem_call
nasal_ref funcr; // function register nasal_ref funcr; // function register
nasal_ref upvalr; // upvalue register nasal_ref upvalr; // upvalue register
nasal_ref* canary; // avoid stackoverflow nasal_ref* canary; // avoid stackoverflow
nasal_ref* top; // stack top nasal_ref* top; // stack top
/* constant */
const double* num_table;// const numbers, ref from nasal_codegen const double* num_table;// const numbers, ref from nasal_codegen
const std::string* str_table;// const symbols, ref from nasal_codegen const std::string* str_table;// const symbols, ref from nasal_codegen
std::vector<uint32_t> imm; // immediate number std::vector<uint32_t> imm; // immediate number
/* main stack */
nasal_ref stack[STACK_DEPTH];
/* garbage collector */ /* garbage collector */
nasal_gc gc; nasal_gc gc;
@ -125,9 +122,7 @@ protected:
void opr_mcallh(); void opr_mcallh();
void opr_ret(); void opr_ret();
public: public:
nasal_vm(): nasal_vm():gc(pc,localr,memr,funcr,upvalr,canary,top,stack){}
global(stack),
gc(pc,localr,memr,funcr,upvalr,canary,top,stack){}
void run( void run(
const nasal_codegen&, const nasal_codegen&,
const nasal_import&, const nasal_import&,
@ -152,7 +147,6 @@ void nasal_vm::init(
/* set canary and program counter */ /* set canary and program counter */
pc=0; pc=0;
global=stack;
localr=nullptr; localr=nullptr;
memr=nullptr; memr=nullptr;
funcr=nil; funcr=nil;
@ -290,7 +284,7 @@ void nasal_vm::register_info()
{ {
printf("registers(%s):\n",gc.coroutine?"coroutine":"main"); printf("registers(%s):\n",gc.coroutine?"coroutine":"main");
printf(" [ pc ] | pc | 0x%x\n",pc); printf(" [ pc ] | pc | 0x%x\n",pc);
printf(" [ global ] | addr | 0x" PRTHEX64 "\n",(uint64_t)global); printf(" [ global ] | addr | 0x" PRTHEX64 "\n",(uint64_t)stack);
printf(" [ localr ] | addr | 0x" PRTHEX64 "\n",(uint64_t)localr); printf(" [ localr ] | addr | 0x" PRTHEX64 "\n",(uint64_t)localr);
printf(" [ memr ] | addr | 0x" PRTHEX64 "\n",(uint64_t)memr); printf(" [ memr ] | addr | 0x" PRTHEX64 "\n",(uint64_t)memr);
if(funcr.type==vm_nil) if(funcr.type==vm_nil)
@ -312,11 +306,11 @@ void nasal_vm::global_state()
{ {
if(!bytecode[0].num || stack[0].type==vm_none) // bytecode[0].op is op_intg if(!bytecode[0].num || stack[0].type==vm_none) // bytecode[0].op is op_intg
return; return;
printf("global(0x" PRTHEX64 "<sp+0>):\n",(uint64_t)global); printf("global(0x" PRTHEX64 "<sp+0>):\n",(uint64_t)stack);
for(uint32_t i=0;i<bytecode[0].num;++i) for(uint32_t i=0;i<bytecode[0].num;++i)
{ {
printf(" 0x%.8x",i); printf(" 0x%.8x",i);
valinfo(global[i]); valinfo(stack[i]);
} }
} }
void nasal_vm::local_state() void nasal_vm::local_state()
@ -413,7 +407,7 @@ inline void nasal_vm::opr_intl()
} }
inline void nasal_vm::opr_loadg() inline void nasal_vm::opr_loadg()
{ {
global[imm[pc]]=(top--)[0]; stack[imm[pc]]=(top--)[0];
} }
inline void nasal_vm::opr_loadl() inline void nasal_vm::opr_loadl()
{ {
@ -687,7 +681,7 @@ inline void nasal_vm::opr_feach()
} }
inline void nasal_vm::opr_callg() inline void nasal_vm::opr_callg()
{ {
(++top)[0]=global[imm[pc]]; (++top)[0]=stack[imm[pc]];
} }
inline void nasal_vm::opr_calll() inline void nasal_vm::opr_calll()
{ {
@ -898,7 +892,7 @@ inline void nasal_vm::opr_slc2()
} }
inline void nasal_vm::opr_mcallg() inline void nasal_vm::opr_mcallg()
{ {
memr=global+imm[pc]; memr=stack+imm[pc];
(++top)[0]=memr[0]; (++top)[0]=memr[0];
// push value in this memory space on stack // push value in this memory space on stack
// to avoid being garbage collected // to avoid being garbage collected
@ -984,7 +978,6 @@ inline void nasal_vm::opr_ret()
upvalr=top[-3]; upvalr=top[-3];
top=local-1; top=local-1;
func.func().local[0]=nil;// get func and set 'me' to nil
funcr=top[0]; funcr=top[0];
top[0]=ret; // rewrite func with returned value top[0]=ret; // rewrite func with returned value
@ -1012,7 +1005,7 @@ void nasal_vm::run(
detail_info=detail; detail_info=detail;
init(gen.get_strs(),gen.get_nums(),gen.get_code(),linker.get_file(),argv); init(gen.get_strs(),gen.get_nums(),gen.get_code(),linker.get_file(),argv);
uint64_t count[op_ret+1]={0}; uint64_t count[op_ret+1]={0};
const void* opr_table[]= const void* oprs[]=
{ {
&&vmexit, &&intg, &&intl, &&loadg, &&vmexit, &&intg, &&intl, &&loadg,
&&loadl, &&loadu, &&pnum, &&pnil, &&loadl, &&loadu, &&pnum, &&pnil,
@ -1037,7 +1030,7 @@ void nasal_vm::run(
std::vector<const void*> code; std::vector<const void*> code;
for(auto& i:gen.get_code()) for(auto& i:gen.get_code())
{ {
code.push_back(opr_table[i.op]); code.push_back(oprs[i.op]);
imm.push_back(i.num); imm.push_back(i.num);
} }
// goto the first operand // goto the first operand
@ -1048,7 +1041,7 @@ vmexit:
die("stack overflow"); die("stack overflow");
if(opcnt) if(opcnt)
opcallsort(count); opcallsort(count);
if(detail_info) if(detail)
gc.info(); gc.info();
gc.clear(); gc.clear();
imm.clear(); imm.clear();