This commit is contained in:
blueloveTH 2023-05-22 13:11:28 +08:00
parent 07be627cb8
commit 429f572c53
3 changed files with 75 additions and 85 deletions

View File

@ -7,8 +7,6 @@ pkpy supports `goto` and `label` just like C. You are allowed to change the cont
## Syntax
Labels are named a dot `.` and an identifier.
```
$goto <identifier>
$label <identifier>

View File

@ -1,5 +1,5 @@
clang++ -pg -O2 -std=c++17 -fno-rtti -stdlib=libc++ -Wall -o pocketpy src/main.cpp
time ./pocketpy benchmarks/primes.py
clang++ -pg -O1 -std=c++17 -fno-rtti -stdlib=libc++ -Wall -o pocketpy src/main.cpp
time ./pocketpy benchmarks/fib.py
mv benchmarks/gmon.out .
gprof pocketpy gmon.out > gprof.txt
rm gmon.out

154
src/vm.h
View File

@ -568,7 +568,6 @@ public:
PyObject* new_module(StrName name);
Str disassemble(CodeObject_ co);
void init_builtin_types();
PyObject* _py_call(PyObject** sp_base, PyObject* callable, ArgsView args, ArgsView kwargs);
PyObject* getattr(PyObject* obj, StrName name, bool throw_err=true);
PyObject* get_unbound_method(PyObject* obj, StrName name, PyObject** self, bool throw_err=true, bool fallback=false);
void parse_int_slice(const Slice& s, int length, int& start, int& stop, int& step);
@ -1110,12 +1109,81 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
ArgsView kwargs(p1, s_data._sp);
if(is_non_tagged_type(callable, tp_function)){
// ret is nullptr or a generator
PyObject* ret = _py_call(p0, callable, args, kwargs);
// stack resetting is handled by _py_call
if(ret != nullptr) return ret;
/*****************_py_call*****************/
// callable must be a `function` object
if(s_data.is_overflow()) StackOverflowError();
const Function& fn = CAST(Function&, callable);
const CodeObject* co = fn.decl->code.get();
int co_nlocals = co->varnames.size();
if(args.size() < fn.argc){
vm->TypeError(fmt(
"expected ",
fn.argc,
" positional arguments, but got ",
args.size(),
" (", fn.decl->code->name, ')'
));
}
// if this function is simple, a.k.a, no kwargs and no *args and not a generator
// we can use a fast path to avoid using buffer copy
if(fn.is_simple){
if(args.size() > fn.argc) TypeError("too many positional arguments");
int spaces = co_nlocals - fn.argc;
for(int j=0; j<spaces; j++) PUSH(PY_NULL);
callstack.emplace(&s_data, p0, co, fn._module, callable, FastLocals(co, args.begin()));
if(op_call) return PY_OP_CALL;
return _run_top_frame();
}
int i = 0;
static THREAD_LOCAL PyObject* buffer[PK_MAX_CO_VARNAMES];
// prepare args
for(int index: fn.decl->args) buffer[index] = args[i++];
// set extra varnames to nullptr
for(int j=i; j<co_nlocals; j++) buffer[j] = PY_NULL;
// prepare kwdefaults
for(auto& kv: fn.decl->kwargs) buffer[kv.key] = kv.value;
// handle *args
if(fn.decl->starred_arg != -1){
ArgsView vargs(args.begin() + i, args.end());
buffer[fn.decl->starred_arg] = VAR(vargs.to_tuple());
i += vargs.size();
}else{
// kwdefaults override
for(auto& kv: fn.decl->kwargs){
if(i >= args.size()) break;
buffer[kv.key] = args[i++];
}
if(i < args.size()) TypeError(fmt("too many arguments", " (", fn.decl->code->name, ')'));
}
for(int i=0; i<kwargs.size(); i+=2){
StrName key = CAST(int, kwargs[i]);
int index = co->varnames_inv.try_get(key);
if(index<0) TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()"));
buffer[index] = kwargs[i+1];
}
if(co->is_generator){
s_data.reset(p0);
return _py_generator(
Frame(&s_data, nullptr, co, fn._module, callable),
ArgsView(buffer, buffer + co_nlocals)
);
}
// copy buffer back to stack
s_data.reset(args.begin());
for(int i=0; i<co_nlocals; i++) PUSH(buffer[i]);
callstack.emplace(&s_data, p0, co, fn._module, callable, FastLocals(co, args.begin()));
if(op_call) return PY_OP_CALL;
return _run_top_frame();
/*****************_py_call*****************/
}
if(is_non_tagged_type(callable, tp_type)){
@ -1172,82 +1240,6 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
return nullptr;
}
inline PyObject* VM::_py_call(PyObject** p0, PyObject* callable, ArgsView args, ArgsView kwargs){
// callable must be a `function` object
if(s_data.is_overflow()) StackOverflowError();
const Function& fn = CAST(Function&, callable);
const CodeObject* co = fn.decl->code.get();
int co_nlocals = co->varnames.size();
if(args.size() < fn.argc){
vm->TypeError(fmt(
"expected ",
fn.argc,
" positional arguments, but got ",
args.size(),
" (", fn.decl->code->name, ')'
));
}
// if this function is simple, a.k.a, no kwargs and no *args and not a generator
// we can use a fast path to avoid using buffer copy
if(fn.is_simple){
if(args.size() > fn.argc) TypeError("too many positional arguments");
int spaces = co_nlocals - fn.argc;
for(int j=0; j<spaces; j++) PUSH(PY_NULL);
callstack.emplace(&s_data, p0, co, fn._module, callable, FastLocals(co, args.begin()));
return nullptr;
}
int i = 0;
static THREAD_LOCAL PyObject* buffer[PK_MAX_CO_VARNAMES];
// prepare args
for(int index: fn.decl->args) buffer[index] = args[i++];
// set extra varnames to nullptr
for(int j=i; j<co_nlocals; j++) buffer[j] = PY_NULL;
// prepare kwdefaults
for(auto& kv: fn.decl->kwargs) buffer[kv.key] = kv.value;
// handle *args
if(fn.decl->starred_arg != -1){
ArgsView vargs(args.begin() + i, args.end());
buffer[fn.decl->starred_arg] = VAR(vargs.to_tuple());
i += vargs.size();
}else{
// kwdefaults override
for(auto& kv: fn.decl->kwargs){
if(i >= args.size()) break;
buffer[kv.key] = args[i++];
}
if(i < args.size()) TypeError(fmt("too many arguments", " (", fn.decl->code->name, ')'));
}
for(int i=0; i<kwargs.size(); i+=2){
StrName key = CAST(int, kwargs[i]);
int index = co->varnames_inv.try_get(key);
if(index<0) TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()"));
buffer[index] = kwargs[i+1];
}
if(co->is_generator){
s_data.reset(p0);
return _py_generator(
Frame(&s_data, nullptr, co, fn._module, callable),
ArgsView(buffer, buffer + co_nlocals)
);
}
// copy buffer back to stack
s_data.reset(args.begin());
for(int i=0; i<co_nlocals; i++) PUSH(buffer[i]);
callstack.emplace(&s_data, p0, co, fn._module, callable, FastLocals(co, args.begin()));
return nullptr;
}
DEF_SNAME(__get__);
DEF_SNAME(__set__);