split coroutine lib from builtin

This commit is contained in:
ValKmjolnir 2023-07-31 00:25:36 +08:00
parent 654a37ab88
commit 29b15b89f7
8 changed files with 141 additions and 117 deletions

View File

@ -20,6 +20,7 @@ set(NASAL_OBJECT_SOURCE_FILE
${CMAKE_SOURCE_DIR}/src/ast_visitor.cpp
${CMAKE_SOURCE_DIR}/src/nasal_ast.cpp
${CMAKE_SOURCE_DIR}/src/nasal_builtin.cpp
${CMAKE_SOURCE_DIR}/src/coroutine.cpp
${CMAKE_SOURCE_DIR}/src/fg_props.cpp
${CMAKE_SOURCE_DIR}/src/math_lib.cpp
${CMAKE_SOURCE_DIR}/src/nasal_codegen.cpp

View File

@ -18,7 +18,8 @@ NASAL_HEADER=\
src/optimizer.h\
src/symbol_finder.h\
src/fg_props.h\
src/math_lib.h
src/math_lib.h\
src/coroutine.h
NASAL_OBJECT=\
build/nasal_err.o\
@ -37,6 +38,7 @@ NASAL_OBJECT=\
build/nasal_builtin.o\
build/fg_props.o\
build/math_lib.o\
build/coroutine.o\
build/nasal_vm.o\
build/nasal_dbg.o\
build/main.o
@ -89,6 +91,12 @@ build/nasal_builtin.o: \
src/nasal_builtin.h src/nasal_builtin.cpp | build
$(CXX) -std=$(STD) -c -O3 src/nasal_builtin.cpp -fno-exceptions -fPIC -o build/nasal_builtin.o -I .
build/coroutine.o: \
src/nasal.h\
src/nasal_gc.h\
src/coroutine.h src/coroutine.cpp | build
$(CXX) -std=$(STD) -c -O3 src/coroutine.cpp -fno-exceptions -fPIC -o build/coroutine.o -I .
build/math_lib.o: \
src/nasal.h\
src/nasal_gc.h\

116
src/coroutine.cpp Normal file
View File

@ -0,0 +1,116 @@
#include "coroutine.h"
var builtin_cocreate(var* local, gc& ngc) {
// +-------------+
// | old pc | <- top[0]
// +-------------+
// | old localr | <- top[-1]
// +-------------+
// | old upvalr | <- top[-2]
// +-------------+
// | local scope |
// | ... |
// +-------------+ <- local pointer stored in localr
// | old funcr | <- old function stored in funcr
// +-------------+
var func = local[1];
if (func.type!=vm_func) {
return nas_err("coroutine::create", "must use a function to create coroutine");
}
if (ngc.cort) {
return nas_err("coroutine::create", "cannot create another coroutine in a coroutine");
}
var co = ngc.alloc(vm_co);
nas_co& cort = co.co();
cort.ctx.pc = func.func().entry-1;
cort.ctx.top[0] = nil;
cort.ctx.localr = cort.ctx.top+1;
cort.ctx.top = cort.ctx.localr+func.func().lsize;
cort.ctx.localr[0] = func.func().local[0];
cort.ctx.top[0] = nil; // old upvalr
cort.ctx.top++;
cort.ctx.top[0] = var::addr((var*)nullptr); // old localr
cort.ctx.top++;
cort.ctx.top[0] = var::ret(0); // old pc, set to zero to make op_ret recognizing this as coroutine function
cort.ctx.funcr = func; // make sure the coroutine function can use correct upvalues
cort.status = nas_co::status::suspended;
return co;
}
var builtin_coresume(var* local, gc& ngc) {
if (ngc.cort) {
return nas_err("coroutine::resume", "cannot start another coroutine when one is running");
}
var co = local[1];
// return nil if is not a coroutine object
if (co.type!=vm_co) {
return nil;
}
// cannot resume a dead coroutine
if (co.co().status==nas_co::status::dead) {
return nil;
}
// change to coroutine context
ngc.ctxchg(co.co());
// fetch coroutine's stack top and return
// so the coroutine's stack top in fact is not changed
if (ngc.rctx->top[0].type==vm_ret) {
// when first calling this coroutine, the stack top must be vm_ret
return ngc.rctx->top[0];
}
// after first calling the coroutine, each time coroutine.yield triggered
// a new space will be reserved on stack with value nil
// so we could fill this place with args
// the coroutine seems like coroutine.yield returns the value
// but in fact coroutine.yield stop the coroutine
// until main context calls the coroutine.resume
return local[2];
}
var builtin_coyield(var* local, gc& ngc) {
if (!ngc.cort) {
return nas_err("coroutine::yield", "no coroutine is running");
}
// this will set to main stack top
ngc.ctxreserve();
// then this will return value to main's stack top[0]
// the procedure seems like coroutine.resume returns the value
// but in fact coroutine.resume stop the main context
// until coroutine calls the coroutine.yield
return local[1];
}
var builtin_costatus(var* local, gc& ngc) {
var co = local[1];
if (co.type!=vm_co) {
return ngc.newstr("error");
}
switch(co.co().status) {
case nas_co::status::suspended: return ngc.newstr("suspended");
case nas_co::status::running: return ngc.newstr("running");
case nas_co::status::dead: return ngc.newstr("dead");
}
return nil;
}
var builtin_corun(var* local, gc& ngc) {
return ngc.cort? one:zero;
}
nasal_builtin_table coroutine_native[] = {
{"__cocreate", builtin_cocreate},
{"__coresume", builtin_coresume},
{"__coyield", builtin_coyield},
{"__costatus", builtin_costatus},
{"__corun", builtin_corun},
{nullptr, nullptr}
};

13
src/coroutine.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
var builtin_cocreate(var*, gc&);
var builtin_coresume(var*, gc&);
var builtin_coyield(var*, gc&);
var builtin_costatus(var*, gc&);
var builtin_corun(var*, gc&);
extern nasal_builtin_table coroutine_native[];

View File

@ -1040,112 +1040,6 @@ var builtin_md5(var* local, gc& ngc) {
return ngc.newstr(md5(str.str()));
}
var builtin_cocreate(var* local, gc& ngc) {
// +-------------+
// | old pc | <- top[0]
// +-------------+
// | old localr | <- top[-1]
// +-------------+
// | old upvalr | <- top[-2]
// +-------------+
// | local scope |
// | ... |
// +-------------+ <- local pointer stored in localr
// | old funcr | <- old function stored in funcr
// +-------------+
var func = local[1];
if (func.type!=vm_func) {
return nas_err("coroutine::create", "must use a function to create coroutine");
}
if (ngc.cort) {
return nas_err("coroutine::create", "cannot create another coroutine in a coroutine");
}
var co = ngc.alloc(vm_co);
nas_co& cort = co.co();
cort.ctx.pc = func.func().entry-1;
cort.ctx.top[0] = nil;
cort.ctx.localr = cort.ctx.top+1;
cort.ctx.top = cort.ctx.localr+func.func().lsize;
cort.ctx.localr[0] = func.func().local[0];
cort.ctx.top[0] = nil; // old upvalr
cort.ctx.top++;
cort.ctx.top[0] = var::addr((var*)nullptr); // old localr
cort.ctx.top++;
cort.ctx.top[0] = var::ret(0); // old pc, set to zero to make op_ret recognizing this as coroutine function
cort.ctx.funcr = func; // make sure the coroutine function can use correct upvalues
cort.status = nas_co::status::suspended;
return co;
}
var builtin_coresume(var* local, gc& ngc) {
if (ngc.cort) {
return nas_err("coroutine::resume", "cannot start another coroutine when one is running");
}
var co = local[1];
// return nil if is not a coroutine object
if (co.type!=vm_co) {
return nil;
}
// cannot resume a dead coroutine
if (co.co().status==nas_co::status::dead) {
return nil;
}
// change to coroutine context
ngc.ctxchg(co.co());
// fetch coroutine's stack top and return
// so the coroutine's stack top in fact is not changed
if (ngc.rctx->top[0].type==vm_ret) {
// when first calling this coroutine, the stack top must be vm_ret
return ngc.rctx->top[0];
}
// after first calling the coroutine, each time coroutine.yield triggered
// a new space will be reserved on stack with value nil
// so we could fill this place with args
// the coroutine seems like coroutine.yield returns the value
// but in fact coroutine.yield stop the coroutine
// until main context calls the coroutine.resume
return local[2];
}
var builtin_coyield(var* local, gc& ngc) {
if (!ngc.cort) {
return nas_err("coroutine::yield", "no coroutine is running");
}
// this will set to main stack top
ngc.ctxreserve();
// then this will return value to main's stack top[0]
// the procedure seems like coroutine.resume returns the value
// but in fact coroutine.resume stop the main context
// until coroutine calls the coroutine.yield
return local[1];
}
var builtin_costatus(var* local, gc& ngc) {
var co = local[1];
if (co.type!=vm_co) {
return ngc.newstr("error");
}
switch(co.co().status) {
case nas_co::status::suspended: return ngc.newstr("suspended");
case nas_co::status::running: return ngc.newstr("running");
case nas_co::status::dead: return ngc.newstr("dead");
}
return nil;
}
var builtin_corun(var* local, gc& ngc) {
return ngc.cort? one:zero;
}
var builtin_millisec(var* local, gc& ngc) {
f64 res = std::chrono::duration_cast<std::chrono::milliseconds>
(std::chrono::high_resolution_clock::now().time_since_epoch())
@ -1295,11 +1189,6 @@ nasal_builtin_table builtin[] = {
{"__platform", builtin_platform},
{"__arch", builtin_arch},
{"__md5", builtin_md5},
{"__cocreate", builtin_cocreate},
{"__coresume", builtin_coresume},
{"__coyield", builtin_coyield},
{"__costatus", builtin_costatus},
{"__corun", builtin_corun},
{"__millisec", builtin_millisec},
{"__gcextd", builtin_gcextend},
{"__gcinfo", builtin_gcinfo},

View File

@ -109,11 +109,6 @@ var builtin_arch(var*, gc&);
std::string tohex(u32);
std::string md5(const std::string&);
var builtin_md5(var*, gc&);
var builtin_cocreate(var*, gc&);
var builtin_coresume(var*, gc&);
var builtin_coyield(var*, gc&);
var builtin_costatus(var*, gc&);
var builtin_corun(var*, gc&);
var builtin_millisec(var*, gc&);
var builtin_gcextend(var*, gc&);
var builtin_gcinfo(var*, gc&);

View File

@ -15,6 +15,7 @@ void codegen::load_native_function_table(nasal_builtin_table* table) {
void codegen::init_native_function() {
load_native_function_table(builtin);
load_native_function_table(math_lib_native);
load_native_function_table(coroutine_native);
load_native_function_table(flight_gear_native);
}

View File

@ -9,6 +9,7 @@
#include "nasal_import.h"
#include "nasal_builtin.h"
#include "coroutine.h"
#include "math_lib.h"
#include "fg_props.h"