🔥 change `nasal_gc` to `gc`
This commit is contained in:
parent
3fd1b25f79
commit
3ef8effe9a
18
README.md
18
README.md
|
@ -490,7 +490,7 @@ nas_native(builtin_print);
|
|||
Then complete this function using C++:
|
||||
|
||||
```C++
|
||||
var builtin_print(var* local,nasal_gc& gc)
|
||||
var builtin_print(var* local,gc& ngc)
|
||||
{
|
||||
// find value with index begin from 1
|
||||
// because local[0] is reserved for value 'me'
|
||||
|
@ -512,7 +512,7 @@ var builtin_print(var* local,nasal_gc& gc)
|
|||
}
|
||||
std::cout<<std::flush;
|
||||
// generate return value,
|
||||
// use gc::alloc(type) to make a new value
|
||||
// use ngc::alloc(type) to make a new value
|
||||
// or use reserved reference nil/one/zero
|
||||
return nil;
|
||||
}
|
||||
|
@ -524,17 +524,17 @@ The value got before will be collected, but stil in use in this builtin function
|
|||
So use `gc::temp` in builtin functions to temprorarily store the gc-managed value that you want to return later. Like this:
|
||||
|
||||
```C++
|
||||
var builtin_keys(var* local,nasal_gc& gc)
|
||||
var builtin_keys(var* local,gc& ngc)
|
||||
{
|
||||
var hash=local[1];
|
||||
if(hash.type!=vm_hash)
|
||||
return nas_err("keys","\"hash\" must be hash");
|
||||
// use gc.temp to store the gc-managed-value, to avoid being sweeped
|
||||
var res=gc.temp=gc.alloc(vm_vec);
|
||||
var res=ngc.temp=ngc.alloc(vm_vec);
|
||||
auto& vec=res.vec().elems;
|
||||
for(auto& iter:hash.hash().elems)
|
||||
vec.push_back(gc.newstr(iter.first));
|
||||
gc.temp=nil;
|
||||
vec.push_back(ngc.newstr(iter.first));
|
||||
ngc.temp=nil;
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
@ -545,7 +545,7 @@ After that, register the built-in function's name(in nasal) and the function's p
|
|||
struct func
|
||||
{
|
||||
const char* name;
|
||||
var (*func)(var*,nasal_gc&);
|
||||
var (*func)(var*,gc&);
|
||||
} builtin[]=
|
||||
{
|
||||
{"__print",builtin_print},
|
||||
|
@ -617,7 +617,7 @@ double fibonaci(double x){
|
|||
}
|
||||
// remember to use extern "C",
|
||||
// so you could search the symbol quickly
|
||||
extern "C" var fib(std::vector<var>& args,nasal_gc& gc){
|
||||
extern "C" var fib(std::vector<var>& args,gc& ngc){
|
||||
// the arguments are generated into a vm_vec: args
|
||||
// get values from the vector that must be used here
|
||||
var num=args[0];
|
||||
|
@ -627,7 +627,7 @@ extern "C" var fib(std::vector<var>& args,nasal_gc& gc){
|
|||
if(num.type!=vm_num)
|
||||
return nas_err("extern_fib","\"num\" must be number");
|
||||
// ok, you must know that vm_num now is not managed by gc
|
||||
// if want to return a gc object, use gc.alloc(type)
|
||||
// if want to return a gc object, use ngc.alloc(type)
|
||||
// usage of gc is the same as adding a native function
|
||||
return {vm_num,fibonaci(num.tonum())};
|
||||
}
|
||||
|
|
|
@ -469,7 +469,7 @@ nas_native(builtin_print);
|
|||
然后用C++完成这个函数的函数体:
|
||||
|
||||
```C++
|
||||
var builtin_print(var* local,nasal_gc& gc)
|
||||
var builtin_print(var* local,gc& ngc)
|
||||
{
|
||||
// 局部变量的下标其实是从1开始的
|
||||
// 因为local[0]是保留给'me'的空间
|
||||
|
@ -492,7 +492,7 @@ var builtin_print(var* local,nasal_gc& gc)
|
|||
}
|
||||
std::cout<<std::flush;
|
||||
// 最后一定要记得生成返回值,返回值必须是一个内置的类型,
|
||||
// 可以使用gc::alloc(type)来申请一个需要内存管理的复杂数据结构
|
||||
// 可以使用ngc::alloc(type)来申请一个需要内存管理的复杂数据结构
|
||||
// 或者用我们已经定义好的nil/one/zero,这些可以直接使用
|
||||
return nil;
|
||||
}
|
||||
|
@ -504,17 +504,17 @@ var builtin_print(var* local,nasal_gc& gc)
|
|||
可以使用`gc::temp`来暂时存储一个会被返回的需要gc管理的变量,这样可以防止内部所有的申请错误触发垃圾回收。如下所示:
|
||||
|
||||
```C++
|
||||
var builtin_keys(var* local,nasal_gc& gc)
|
||||
var builtin_keys(var* local,gc& ngc)
|
||||
{
|
||||
var hash=local[1];
|
||||
if(hash.type!=vm_hash)
|
||||
return nas_err("keys","\"hash\" must be hash");
|
||||
// 使用gc.temp来存储gc管理的变量,防止错误的回收
|
||||
var res=gc.temp=gc.alloc(vm_vec);
|
||||
var res=ngc.temp=ngc.alloc(vm_vec);
|
||||
auto& vec=res.vec().elems;
|
||||
for(auto& iter:hash.hash().elems)
|
||||
vec.push_back(gc.newstr(iter.first));
|
||||
gc.temp=nil;
|
||||
vec.push_back(ngc.newstr(iter.first));
|
||||
ngc.temp=nil;
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
@ -525,7 +525,7 @@ var builtin_keys(var* local,nasal_gc& gc)
|
|||
struct func
|
||||
{
|
||||
const char* name;
|
||||
var (*func)(var*,nasal_gc&);
|
||||
var (*func)(var*,gc&);
|
||||
} builtin[]=
|
||||
{
|
||||
{"__print",builtin_print},
|
||||
|
@ -592,7 +592,7 @@ double fibonaci(double x){
|
|||
}
|
||||
// 记得用extern "C"
|
||||
// 这样找符号会更加快速便捷,不要在意编译时的warning
|
||||
extern "C" var fib(std::vector<var>& args,nasal_gc& gc){
|
||||
extern "C" var fib(std::vector<var>& args,gc& ngc){
|
||||
// 传参会被送到一个vm_vec类型中送过来,而不是上文中那种指针直接指向局部作用域
|
||||
var num=args[0];
|
||||
// 如果你想让这个函数有更强的稳定性,那么一定要进行合法性检查
|
||||
|
@ -600,7 +600,7 @@ extern "C" var fib(std::vector<var>& args,nasal_gc& gc){
|
|||
if(num.type!=vm_num)
|
||||
return nas_err("extern_fib","\"num\" must be number");
|
||||
// vm_num作为普通的数字类型,不是内存管理的对象,所以无需申请
|
||||
// 如果需要返回内存管理的对象,请使用gc.alloc(type)
|
||||
// 如果需要返回内存管理的对象,请使用ngc.alloc(type)
|
||||
return {vm_num,fibonaci(num.tonum())};
|
||||
}
|
||||
```
|
||||
|
|
12
doc/dev.md
12
doc/dev.md
|
@ -59,7 +59,7 @@ var f=func(x,y,z){return x+y+z};
|
|||
|
||||
### version 1.0 parser (last update 2019/10/14)
|
||||
|
||||
First fully functional version of nasal_parser.
|
||||
First fully functional version of parser.
|
||||
|
||||
Before version 1.0,i tried many times to create a correct parser.
|
||||
|
||||
|
@ -266,8 +266,8 @@ parser will check this left-value and tells that these kinds of left-value are n
|
|||
But now it can work.
|
||||
And you could see its use by reading the code above.
|
||||
To make sure this assignment works correctly,
|
||||
codegen will generate byte code by `nasal_codegen::call_gen()` instead of `nasal_codegen::mcall_gen()`,
|
||||
and the last child of the ast will be generated by `nasal_codegen::mcall_gen()`.
|
||||
codegen will generate byte code by `codegen::call_gen()` instead of `codegen::mcall_gen()`,
|
||||
and the last child of the ast will be generated by `codegen::mcall_gen()`.
|
||||
So the bytecode is totally different now:
|
||||
|
||||
```x86asm
|
||||
|
@ -357,7 +357,7 @@ 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:
|
||||
There is also a change in `gc`:
|
||||
`std::vector` global is deleted,
|
||||
now the global values are all stored on stack(from `val_stack+0` to `val_stack+intg-1`).
|
||||
|
||||
|
@ -415,7 +415,7 @@ a=b=0;
|
|||
|
||||
2021/10/8 update:
|
||||
|
||||
In this version vm_nil and vm_num now is not managed by `nasal_gc`,
|
||||
In this version vm_nil and vm_num now is not managed by `gc`,
|
||||
this will decrease the usage of `gc::alloc` and increase the efficiency of execution.
|
||||
|
||||
New value type is added: `vm_obj`.
|
||||
|
@ -471,7 +471,7 @@ Both of them are meaningless and will be replaced by `op_pnum`.
|
|||
Local values now are __stored on stack__.
|
||||
So function calling will be faster than before.
|
||||
Because in v8.0 when calling a function,
|
||||
new `vm_vec` will be allocated by `nasal_gc`, this makes gc doing mark-sweep too many times and spends a quite lot of time.
|
||||
new `vm_vec` will be allocated by `gc`, this makes gc doing mark-sweep too many times and spends a quite lot of time.
|
||||
In test file `test/bf.nas`, it takes too much time to test the file because this file has too many function calls(see test data below in table `version 8.0 (R9-5900HX ubuntu-WSL 2022/1/23)`).
|
||||
|
||||
Upvalue now is generated when creating first new function in the local scope, using `vm_vec`.
|
||||
|
|
|
@ -238,7 +238,7 @@ m(0)._=m(1)._=10;
|
|||
[0,1,2][1:2][0]=0;
|
||||
```
|
||||
|
||||
在老版本中,语法分析器会检查左值,并且在检测到有特别调用的情况下直接告知用户这种左值是不被接受的(bad lvalue)。但是现在它可以正常运作了。为了保证这种赋值语句能正常执行,codegen模块会优先使用`nasal_codegen::call_gen()`生成前面调用链的字节码而不是全部使用 `nasal_codegen::mcall_gen()`,在最后一个调用处才会使用`nasal_codegen::mcall_gen()`。
|
||||
在老版本中,语法分析器会检查左值,并且在检测到有特别调用的情况下直接告知用户这种左值是不被接受的(bad lvalue)。但是现在它可以正常运作了。为了保证这种赋值语句能正常执行,codegen模块会优先使用`codegen::call_gen()`生成前面调用链的字节码而不是全部使用 `codegen::mcall_gen()`,在最后一个调用处才会使用`codegen::mcall_gen()`。
|
||||
|
||||
所以现在生成的相关字节码也完全不同了:
|
||||
|
||||
|
@ -311,11 +311,11 @@ m(0)._=m(1)._=10;
|
|||
|
||||
2021/6/26 update:
|
||||
|
||||
指令分派方式从call-threading改为了computed-goto。在更改了指令分派方式之后,nasal_vm的执行效率有了非常巨大的提升。现在虚拟机可以在0.2秒内执行完test/bigloop和test/pi!并且在linux平台虚拟机可以在0.8秒内执行完test/fib。你可以在下面的测试数据部分看到测试的结果。
|
||||
指令分派方式从call-threading改为了computed-goto。在更改了指令分派方式之后,`vm`的执行效率有了非常巨大的提升。现在虚拟机可以在0.2秒内执行完`test/bigloop`和`test/pi`!并且在linux平台虚拟机可以在0.8秒内执行完`test/fib`。你可以在下面的测试数据部分看到测试的结果。
|
||||
|
||||
这个分派方式使用了g++扩展"labels as values",clang++目前也支持这种指令分派的实现方式。(不过MSVC支不支持就不得而知了,哈哈)
|
||||
|
||||
nasal_gc中也有部分改动:
|
||||
`gc`中也有部分改动:
|
||||
全局变量不再用`std::vector`存储,而是全部存在操作数栈上(从`val_stack+0`到`val_stack+intg-1`)。
|
||||
|
||||
2021/6/29 update:
|
||||
|
@ -369,7 +369,7 @@ a=b=0;
|
|||
|
||||
2021/10/8 update:
|
||||
|
||||
从这个版本开始`vm_nil`和`vm_num`不再由`nasal_gc`管理,这会大幅度降低`gc::alloc`的调用并且会大幅度提升执行效率。
|
||||
从这个版本开始`vm_nil`和`vm_num`不再由`gc`管理,这会大幅度降低`gc::alloc`的调用并且会大幅度提升执行效率。
|
||||
|
||||
添加了新的数据类型: `vm_obj`。这个类型是留给用户定义他们想要的数据类型的。相关的API会在未来加入。
|
||||
|
||||
|
|
8
main.cpp
8
main.cpp
|
@ -82,14 +82,12 @@ void err()
|
|||
|
||||
void execute(const string& file,const std::vector<string>& argv,const u32 cmd)
|
||||
{
|
||||
// front end use the same error module
|
||||
error err;
|
||||
lexer lex(err);
|
||||
parse parse(err);
|
||||
linker ld(err);
|
||||
codegen gen(err);
|
||||
// back end
|
||||
vm rt;
|
||||
vm ctx;
|
||||
|
||||
// lexer scans file to get tokens
|
||||
lex.scan(file);
|
||||
|
@ -117,12 +115,12 @@ void execute(const string& file,const std::vector<string>& argv,const u32 cmd)
|
|||
else if(cmd&VM_TIME)
|
||||
{
|
||||
auto start=std::chrono::high_resolution_clock::now();
|
||||
rt.run(gen,ld,argv,cmd&VM_DETAIL);
|
||||
ctx.run(gen,ld,argv,cmd&VM_DETAIL);
|
||||
auto end=std::chrono::high_resolution_clock::now();
|
||||
std::clog<<"process exited after "<<(end-start).count()*1.0/std::chrono::high_resolution_clock::duration::period::den<<"s.\n";
|
||||
}
|
||||
else if(cmd&VM_EXEC)
|
||||
rt.run(gen,ld,argv,cmd&VM_DETAIL);
|
||||
ctx.run(gen,ld,argv,cmd&VM_DETAIL);
|
||||
}
|
||||
|
||||
i32 main(i32 argc,const char* argv[])
|
||||
|
|
51
nasal_gc.h
51
nasal_gc.h
|
@ -1,6 +1,7 @@
|
|||
#ifndef __NASAL_GC_H__
|
||||
#define __NASAL_GC_H__
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
|
||||
|
@ -252,6 +253,7 @@ struct nas_val
|
|||
|
||||
nas_val(u8);
|
||||
~nas_val();
|
||||
void clear();
|
||||
};
|
||||
|
||||
var nas_vec::get_val(const i32 n)
|
||||
|
@ -376,6 +378,19 @@ nas_val::~nas_val()
|
|||
}
|
||||
type=vm_nil;
|
||||
}
|
||||
void nas_val::clear()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case vm_str: ptr.str->clear(); break;
|
||||
case vm_vec: ptr.vec->elems.clear(); break;
|
||||
case vm_hash: ptr.hash->elems.clear();break;
|
||||
case vm_func: ptr.func->clear(); break;
|
||||
case vm_upval:ptr.upval->clear(); break;
|
||||
case vm_obj: ptr.obj->clear(); break;
|
||||
case vm_co: ptr.co->clear(); break;
|
||||
}
|
||||
}
|
||||
f64 var::tonum()
|
||||
{
|
||||
return type!=vm_str?val.num:str2num(str().c_str());
|
||||
|
@ -457,20 +472,17 @@ struct gc
|
|||
var temp; // temporary place used in builtin/module functions
|
||||
|
||||
/* constants and memory pool */
|
||||
std::vector<var> strs; // reserved address for const vm_str
|
||||
std::vector<var> env_argv; // command line arguments
|
||||
std::vector<nas_val*> memory; // gc memory
|
||||
std::queue<nas_val*> unused[gc_tsize]; // gc free list
|
||||
std::vector<var> strs; // reserved address for const vm_str
|
||||
std::vector<var> env_argv; // command line arguments
|
||||
std::vector<nas_val*> memory; // gc memory
|
||||
std::queue<nas_val*> unused[gc_tsize]; // gc free list
|
||||
|
||||
/* values for analysis */
|
||||
u64 size[gc_tsize];
|
||||
u64 count[gc_tsize];
|
||||
u64 allocc[gc_tsize];
|
||||
gc(
|
||||
u32& _pc, var*& _localr,
|
||||
var*& _memr, var& _funcr,
|
||||
var& _upvalr, var*& _canary,
|
||||
var*& _top, var* _stk):
|
||||
u64 acnt[gc_tsize];
|
||||
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){}
|
||||
void mark();
|
||||
|
@ -548,16 +560,7 @@ void gc::sweep()
|
|||
{
|
||||
if(i->mark==GC_UNCOLLECTED)
|
||||
{
|
||||
switch(i->type)
|
||||
{
|
||||
case vm_str: i->ptr.str->clear(); break;
|
||||
case vm_vec: i->ptr.vec->elems.clear(); break;
|
||||
case vm_hash: i->ptr.hash->elems.clear();break;
|
||||
case vm_func: i->ptr.func->clear(); break;
|
||||
case vm_upval:i->ptr.upval->clear(); break;
|
||||
case vm_obj: i->ptr.obj->clear(); break;
|
||||
case vm_co: i->ptr.co->clear(); break;
|
||||
}
|
||||
i->clear();
|
||||
unused[i->type-vm_str].push(i);
|
||||
i->mark=GC_COLLECTED;
|
||||
}
|
||||
|
@ -571,7 +574,7 @@ void gc::init(const std::vector<string>& s,const std::vector<string>& argv)
|
|||
funcr=nil;
|
||||
|
||||
for(u8 i=0;i<gc_tsize;++i)
|
||||
size[i]=count[i]=allocc[i]=0;
|
||||
size[i]=count[i]=acnt[i]=0;
|
||||
for(u8 i=0;i<gc_tsize;++i)
|
||||
for(u32 j=0;j<ini[i];++j)
|
||||
{
|
||||
|
@ -615,8 +618,8 @@ void gc::info()
|
|||
const char* name[]={"str ","vec ","hash ","func ","upval","obj ","co "};
|
||||
std::cout<<"\ngarbage collector info(gc/alloc)\n";
|
||||
for(u8 i=0;i<gc_tsize;++i)
|
||||
if(count[i] || allocc[i])
|
||||
std::cout<<" "<<name[i]<<" | "<<count[i]<<","<<allocc[i]<<"\n";
|
||||
if(count[i] || acnt[i])
|
||||
std::cout<<" "<<name[i]<<" | "<<count[i]<<","<<acnt[i]<<"\n";
|
||||
std::cout<<"\nmemory allocator info(max size)\n";
|
||||
for(u8 i=0;i<gc_tsize;++i)
|
||||
if(ini[i] || size[i])
|
||||
|
@ -625,7 +628,7 @@ void gc::info()
|
|||
var gc::alloc(u8 type)
|
||||
{
|
||||
const u8 index=type-vm_str;
|
||||
++allocc[index];
|
||||
++acnt[index];
|
||||
if(unused[index].empty())
|
||||
{
|
||||
++count[index];
|
||||
|
|
|
@ -35,7 +35,7 @@ public:
|
|||
const std::vector<string>& filelist() const {return files;}
|
||||
};
|
||||
|
||||
linker::linker(error& e):lib_loaded(false),err(e){
|
||||
linker::linker(error& e):show_path(false),lib_loaded(false),err(e){
|
||||
#ifdef _WIN32
|
||||
char sep=';';
|
||||
#else
|
||||
|
|
11
nasal_vm.h
11
nasal_vm.h
|
@ -125,11 +125,12 @@ protected:
|
|||
void o_mcallh();
|
||||
void o_ret();
|
||||
public:
|
||||
vm():pc(0),localr(nullptr),memr(nullptr),funcr(nil),
|
||||
upvalr(nil),canary(nullptr),top(stack),
|
||||
num_table(nullptr),str_table(nullptr),
|
||||
ngc(pc,localr,memr,funcr,upvalr,canary,top,stack),
|
||||
files(nullptr),bytecode(nullptr),detail_info(false){}
|
||||
vm():
|
||||
pc(0),localr(nullptr),memr(nullptr),funcr(nil),
|
||||
upvalr(nil),canary(nullptr),top(stack),
|
||||
num_table(nullptr),str_table(nullptr),
|
||||
ngc(pc,localr,memr,funcr,upvalr,canary,top,stack),
|
||||
files(nullptr),bytecode(nullptr),detail_info(false){}
|
||||
void run(
|
||||
const codegen&,
|
||||
const linker&,
|
||||
|
|
Loading…
Reference in New Issue