🔥 change `nasal_gc` to `gc`

This commit is contained in:
ValKmjolnir 2022-10-23 01:29:20 +08:00
parent 3fd1b25f79
commit 3ef8effe9a
8 changed files with 65 additions and 63 deletions

View File

@ -490,7 +490,7 @@ nas_native(builtin_print);
Then complete this function using C++: Then complete this function using C++:
```C++ ```C++
var builtin_print(var* local,nasal_gc& gc) var builtin_print(var* local,gc& ngc)
{ {
// find value with index begin from 1 // find value with index begin from 1
// because local[0] is reserved for value 'me' // because local[0] is reserved for value 'me'
@ -512,7 +512,7 @@ var builtin_print(var* local,nasal_gc& gc)
} }
std::cout<<std::flush; std::cout<<std::flush;
// generate return value, // 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 // or use reserved reference nil/one/zero
return nil; 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: So use `gc::temp` in builtin functions to temprorarily store the gc-managed value that you want to return later. Like this:
```C++ ```C++
var builtin_keys(var* local,nasal_gc& gc) var builtin_keys(var* local,gc& ngc)
{ {
var hash=local[1]; var hash=local[1];
if(hash.type!=vm_hash) if(hash.type!=vm_hash)
return nas_err("keys","\"hash\" must be hash"); return nas_err("keys","\"hash\" must be hash");
// use gc.temp to store the gc-managed-value, to avoid being sweeped // 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; auto& vec=res.vec().elems;
for(auto& iter:hash.hash().elems) for(auto& iter:hash.hash().elems)
vec.push_back(gc.newstr(iter.first)); vec.push_back(ngc.newstr(iter.first));
gc.temp=nil; ngc.temp=nil;
return res; return res;
} }
``` ```
@ -545,7 +545,7 @@ After that, register the built-in function's name(in nasal) and the function's p
struct func struct func
{ {
const char* name; const char* name;
var (*func)(var*,nasal_gc&); var (*func)(var*,gc&);
} builtin[]= } builtin[]=
{ {
{"__print",builtin_print}, {"__print",builtin_print},
@ -617,7 +617,7 @@ double fibonaci(double x){
} }
// remember to use extern "C", // remember to use extern "C",
// so you could search the symbol quickly // 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 // the arguments are generated into a vm_vec: args
// get values from the vector that must be used here // get values from the vector that must be used here
var num=args[0]; var num=args[0];
@ -627,7 +627,7 @@ extern "C" var fib(std::vector<var>& args,nasal_gc& gc){
if(num.type!=vm_num) if(num.type!=vm_num)
return nas_err("extern_fib","\"num\" must be number"); return nas_err("extern_fib","\"num\" must be number");
// ok, you must know that vm_num now is not managed by gc // 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 // usage of gc is the same as adding a native function
return {vm_num,fibonaci(num.tonum())}; return {vm_num,fibonaci(num.tonum())};
} }

View File

@ -469,7 +469,7 @@ nas_native(builtin_print);
然后用C++完成这个函数的函数体: 然后用C++完成这个函数的函数体:
```C++ ```C++
var builtin_print(var* local,nasal_gc& gc) var builtin_print(var* local,gc& ngc)
{ {
// 局部变量的下标其实是从1开始的 // 局部变量的下标其实是从1开始的
// 因为local[0]是保留给'me'的空间 // 因为local[0]是保留给'me'的空间
@ -492,7 +492,7 @@ var builtin_print(var* local,nasal_gc& gc)
} }
std::cout<<std::flush; std::cout<<std::flush;
// 最后一定要记得生成返回值,返回值必须是一个内置的类型, // 最后一定要记得生成返回值,返回值必须是一个内置的类型,
// 可以使用gc::alloc(type)来申请一个需要内存管理的复杂数据结构 // 可以使用ngc::alloc(type)来申请一个需要内存管理的复杂数据结构
// 或者用我们已经定义好的nil/one/zero这些可以直接使用 // 或者用我们已经定义好的nil/one/zero这些可以直接使用
return nil; return nil;
} }
@ -504,17 +504,17 @@ var builtin_print(var* local,nasal_gc& gc)
可以使用`gc::temp`来暂时存储一个会被返回的需要gc管理的变量这样可以防止内部所有的申请错误触发垃圾回收。如下所示 可以使用`gc::temp`来暂时存储一个会被返回的需要gc管理的变量这样可以防止内部所有的申请错误触发垃圾回收。如下所示
```C++ ```C++
var builtin_keys(var* local,nasal_gc& gc) var builtin_keys(var* local,gc& ngc)
{ {
var hash=local[1]; var hash=local[1];
if(hash.type!=vm_hash) if(hash.type!=vm_hash)
return nas_err("keys","\"hash\" must be hash"); return nas_err("keys","\"hash\" must be hash");
// 使用gc.temp来存储gc管理的变量防止错误的回收 // 使用gc.temp来存储gc管理的变量防止错误的回收
var res=gc.temp=gc.alloc(vm_vec); var res=ngc.temp=ngc.alloc(vm_vec);
auto& vec=res.vec().elems; auto& vec=res.vec().elems;
for(auto& iter:hash.hash().elems) for(auto& iter:hash.hash().elems)
vec.push_back(gc.newstr(iter.first)); vec.push_back(ngc.newstr(iter.first));
gc.temp=nil; ngc.temp=nil;
return res; return res;
} }
``` ```
@ -525,7 +525,7 @@ var builtin_keys(var* local,nasal_gc& gc)
struct func struct func
{ {
const char* name; const char* name;
var (*func)(var*,nasal_gc&); var (*func)(var*,gc&);
} builtin[]= } builtin[]=
{ {
{"__print",builtin_print}, {"__print",builtin_print},
@ -592,7 +592,7 @@ double fibonaci(double x){
} }
// 记得用extern "C" // 记得用extern "C"
// 这样找符号会更加快速便捷不要在意编译时的warning // 这样找符号会更加快速便捷不要在意编译时的warning
extern "C" var fib(std::vector<var>& args,nasal_gc& gc){ extern "C" var fib(std::vector<var>& args,gc& ngc){
// 传参会被送到一个vm_vec类型中送过来而不是上文中那种指针直接指向局部作用域 // 传参会被送到一个vm_vec类型中送过来而不是上文中那种指针直接指向局部作用域
var num=args[0]; var num=args[0];
// 如果你想让这个函数有更强的稳定性,那么一定要进行合法性检查 // 如果你想让这个函数有更强的稳定性,那么一定要进行合法性检查
@ -600,7 +600,7 @@ extern "C" var fib(std::vector<var>& args,nasal_gc& gc){
if(num.type!=vm_num) if(num.type!=vm_num)
return nas_err("extern_fib","\"num\" must be number"); return nas_err("extern_fib","\"num\" must be number");
// vm_num作为普通的数字类型不是内存管理的对象所以无需申请 // vm_num作为普通的数字类型不是内存管理的对象所以无需申请
// 如果需要返回内存管理的对象请使用gc.alloc(type) // 如果需要返回内存管理的对象,请使用ngc.alloc(type)
return {vm_num,fibonaci(num.tonum())}; return {vm_num,fibonaci(num.tonum())};
} }
``` ```

View File

@ -59,7 +59,7 @@ var f=func(x,y,z){return x+y+z};
### version 1.0 parser (last update 2019/10/14) ### 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. 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. But now it can work.
And you could see its use by reading the code above. And you could see its use by reading the code above.
To make sure this assignment works correctly, To make sure this assignment works correctly,
codegen will generate byte code by `nasal_codegen::call_gen()` instead of `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 `nasal_codegen::mcall_gen()`. and the last child of the ast will be generated by `codegen::mcall_gen()`.
So the bytecode is totally different now: So the bytecode is totally different now:
```x86asm ```x86asm
@ -357,7 +357,7 @@ This version uses g++ extension "labels as values",
which is also supported by clang++. which is also supported by clang++.
(But i don't know if MSVC supports this) (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, `std::vector` global is deleted,
now the global values are all stored on stack(from `val_stack+0` to `val_stack+intg-1`). 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: 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. this will decrease the usage of `gc::alloc` and increase the efficiency of execution.
New value type is added: `vm_obj`. 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__. Local values now are __stored on stack__.
So function calling will be faster than before. So function calling will be faster than before.
Because in v8.0 when calling a function, 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)`). 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`. Upvalue now is generated when creating first new function in the local scope, using `vm_vec`.

View File

@ -238,7 +238,7 @@ m(0)._=m(1)._=10;
[0,1,2][1:2][0]=0; [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: 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支不支持就不得而知了哈哈) 这个分派方式使用了g++扩展"labels as values"clang++目前也支持这种指令分派的实现方式。(不过MSVC支不支持就不得而知了哈哈)
nasal_gc中也有部分改动: `gc`中也有部分改动:
全局变量不再用`std::vector`存储,而是全部存在操作数栈上(从`val_stack+0`到`val_stack+intg-1`)。 全局变量不再用`std::vector`存储,而是全部存在操作数栈上(从`val_stack+0`到`val_stack+intg-1`)。
2021/6/29 update: 2021/6/29 update:
@ -369,7 +369,7 @@ a=b=0;
2021/10/8 update: 2021/10/8 update:
从这个版本开始`vm_nil`和`vm_num`不再由`nasal_gc`管理,这会大幅度降低`gc::alloc`的调用并且会大幅度提升执行效率。 从这个版本开始`vm_nil`和`vm_num`不再由`gc`管理,这会大幅度降低`gc::alloc`的调用并且会大幅度提升执行效率。
添加了新的数据类型: `vm_obj`。这个类型是留给用户定义他们想要的数据类型的。相关的API会在未来加入。 添加了新的数据类型: `vm_obj`。这个类型是留给用户定义他们想要的数据类型的。相关的API会在未来加入。

View File

@ -82,14 +82,12 @@ void err()
void execute(const string& file,const std::vector<string>& argv,const u32 cmd) void execute(const string& file,const std::vector<string>& argv,const u32 cmd)
{ {
// front end use the same error module
error err; error err;
lexer lex(err); lexer lex(err);
parse parse(err); parse parse(err);
linker ld(err); linker ld(err);
codegen gen(err); codegen gen(err);
// back end vm ctx;
vm rt;
// lexer scans file to get tokens // lexer scans file to get tokens
lex.scan(file); 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) else if(cmd&VM_TIME)
{ {
auto start=std::chrono::high_resolution_clock::now(); 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(); 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"; 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) 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[]) i32 main(i32 argc,const char* argv[])

View File

@ -1,6 +1,7 @@
#ifndef __NASAL_GC_H__ #ifndef __NASAL_GC_H__
#define __NASAL_GC_H__ #define __NASAL_GC_H__
#include <vector>
#include <queue> #include <queue>
#include <unordered_map> #include <unordered_map>
@ -252,6 +253,7 @@ struct nas_val
nas_val(u8); nas_val(u8);
~nas_val(); ~nas_val();
void clear();
}; };
var nas_vec::get_val(const i32 n) var nas_vec::get_val(const i32 n)
@ -376,6 +378,19 @@ nas_val::~nas_val()
} }
type=vm_nil; 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() f64 var::tonum()
{ {
return type!=vm_str?val.num:str2num(str().c_str()); 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 var temp; // temporary place used in builtin/module functions
/* constants and memory pool */ /* constants and memory pool */
std::vector<var> strs; // reserved address for const vm_str std::vector<var> strs; // reserved address for const vm_str
std::vector<var> env_argv; // command line arguments std::vector<var> env_argv; // command line arguments
std::vector<nas_val*> memory; // gc memory std::vector<nas_val*> memory; // gc memory
std::queue<nas_val*> unused[gc_tsize]; // gc free list std::queue<nas_val*> unused[gc_tsize]; // gc free list
/* values for analysis */ /* values for analysis */
u64 size[gc_tsize]; u64 size[gc_tsize];
u64 count[gc_tsize]; u64 count[gc_tsize];
u64 allocc[gc_tsize]; u64 acnt[gc_tsize];
gc( gc(u32& _pc, var*& _localr, var*& _memr, var& _funcr,
u32& _pc, var*& _localr, var& _upvalr, var*& _canary, var*& _top, var* _stk):
var*& _memr, var& _funcr,
var& _upvalr, var*& _canary,
var*& _top, var* _stk):
pc(_pc),localr(_localr),memr(_memr),funcr(_funcr),upvalr(_upvalr), pc(_pc),localr(_localr),memr(_memr),funcr(_funcr),upvalr(_upvalr),
canary(_canary),top(_top),stack(_stk),cort(nullptr),temp(nil){} canary(_canary),top(_top),stack(_stk),cort(nullptr),temp(nil){}
void mark(); void mark();
@ -548,16 +560,7 @@ void gc::sweep()
{ {
if(i->mark==GC_UNCOLLECTED) if(i->mark==GC_UNCOLLECTED)
{ {
switch(i->type) i->clear();
{
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;
}
unused[i->type-vm_str].push(i); unused[i->type-vm_str].push(i);
i->mark=GC_COLLECTED; i->mark=GC_COLLECTED;
} }
@ -571,7 +574,7 @@ void gc::init(const std::vector<string>& s,const std::vector<string>& argv)
funcr=nil; funcr=nil;
for(u8 i=0;i<gc_tsize;++i) 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(u8 i=0;i<gc_tsize;++i)
for(u32 j=0;j<ini[i];++j) 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 "}; const char* name[]={"str ","vec ","hash ","func ","upval","obj ","co "};
std::cout<<"\ngarbage collector info(gc/alloc)\n"; std::cout<<"\ngarbage collector info(gc/alloc)\n";
for(u8 i=0;i<gc_tsize;++i) for(u8 i=0;i<gc_tsize;++i)
if(count[i] || allocc[i]) if(count[i] || acnt[i])
std::cout<<" "<<name[i]<<" | "<<count[i]<<","<<allocc[i]<<"\n"; std::cout<<" "<<name[i]<<" | "<<count[i]<<","<<acnt[i]<<"\n";
std::cout<<"\nmemory allocator info(max size)\n"; std::cout<<"\nmemory allocator info(max size)\n";
for(u8 i=0;i<gc_tsize;++i) for(u8 i=0;i<gc_tsize;++i)
if(ini[i] || size[i]) if(ini[i] || size[i])
@ -625,7 +628,7 @@ void gc::info()
var gc::alloc(u8 type) var gc::alloc(u8 type)
{ {
const u8 index=type-vm_str; const u8 index=type-vm_str;
++allocc[index]; ++acnt[index];
if(unused[index].empty()) if(unused[index].empty())
{ {
++count[index]; ++count[index];

View File

@ -35,7 +35,7 @@ public:
const std::vector<string>& filelist() const {return files;} 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 #ifdef _WIN32
char sep=';'; char sep=';';
#else #else

View File

@ -125,11 +125,12 @@ protected:
void o_mcallh(); void o_mcallh();
void o_ret(); void o_ret();
public: public:
vm():pc(0),localr(nullptr),memr(nullptr),funcr(nil), vm():
upvalr(nil),canary(nullptr),top(stack), pc(0),localr(nullptr),memr(nullptr),funcr(nil),
num_table(nullptr),str_table(nullptr), upvalr(nil),canary(nullptr),top(stack),
ngc(pc,localr,memr,funcr,upvalr,canary,top,stack), num_table(nullptr),str_table(nullptr),
files(nullptr),bytecode(nullptr),detail_info(false){} ngc(pc,localr,memr,funcr,upvalr,canary,top,stack),
files(nullptr),bytecode(nullptr),detail_info(false){}
void run( void run(
const codegen&, const codegen&,
const linker&, const linker&,