add fastpath for simple calls

This commit is contained in:
blueloveTH 2023-10-14 00:29:03 +08:00
parent 2031dadc8e
commit 814ce3e465
5 changed files with 44 additions and 7 deletions

View File

@ -131,6 +131,7 @@ struct FuncDecl {
Str signature; // signature of this function Str signature; // signature of this function
Str docstring; // docstring of this function Str docstring; // docstring of this function
bool is_simple;
void _gc_mark() const; void _gc_mark() const;
}; };

View File

@ -42,6 +42,7 @@ struct Expr{
struct CodeEmitContext{ struct CodeEmitContext{
VM* vm; VM* vm;
FuncDecl_ func; // optional
CodeObject_ co; CodeObject_ co;
// some bugs on MSVC (error C2280) when using std::vector<Expr_> // some bugs on MSVC (error C2280) when using std::vector<Expr_>
// so we use stack_no_copy instead // so we use stack_no_copy instead

View File

@ -1,7 +1,7 @@
python3 prebuild.py python3 prebuild.py
SRC=$(find src/ -name "*.cpp") SRC=$(find src/ -name "*.cpp")
clang++ -pg -O1 -std=c++17 -stdlib=libc++ -Wfatal-errors -o main $SRC src2/main.cpp -Iinclude clang++ -pg -O1 -std=c++17 -stdlib=libc++ -Wfatal-errors -o main $SRC src2/main.cpp -Iinclude
time ./main benchmarks/primes.py time ./main benchmarks/fib.py
mv benchmarks/gmon.out . mv benchmarks/gmon.out .
gprof main gmon.out > gprof.txt gprof main gmon.out > gprof.txt
rm gmon.out rm gmon.out

View File

@ -19,6 +19,7 @@ namespace pkpy{
decl->code = std::make_shared<CodeObject>(lexer->src, name); decl->code = std::make_shared<CodeObject>(lexer->src, name);
decl->nested = name_scope() == NAME_LOCAL; decl->nested = name_scope() == NAME_LOCAL;
contexts.push(CodeEmitContext(vm, decl->code, contexts.size())); contexts.push(CodeEmitContext(vm, decl->code, contexts.size()));
contexts.top().func = decl;
return decl; return decl;
} }
@ -35,11 +36,21 @@ namespace pkpy{
if(ctx()->co->varnames.size() > PK_MAX_CO_VARNAMES){ if(ctx()->co->varnames.size() > PK_MAX_CO_VARNAMES){
SyntaxError("maximum number of local variables exceeded"); SyntaxError("maximum number of local variables exceeded");
} }
FuncDecl_ func = contexts.top().func;
if(func){
func->is_simple = true;
if(func->code->is_generator) func->is_simple = false;
if(func->kwargs.size() > 0) func->is_simple = false;
if(func->starred_arg >= 0) func->is_simple = false;
if(func->starred_kwarg >= 0) func->is_simple = false;
}
contexts.pop(); contexts.pop();
} }
void Compiler::init_pratt_rules(){ void Compiler::init_pratt_rules(){
if(rules[TK(".")].precedence != PREC_NONE) return; PK_LOCAL_STATIC unsigned int count = 0;
if(count > 0) return;
count += 1;
// http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/ // http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
#define METHOD(name) &Compiler::name #define METHOD(name) &Compiler::name
#define NO_INFIX nullptr, PREC_NONE #define NO_INFIX nullptr, PREC_NONE

View File

@ -739,15 +739,15 @@ void VM::_prepare_py_call(PyObject** buffer, ArgsView args, ArgsView kwargs, con
if(args.size() < decl_argc){ if(args.size() < decl_argc){
vm->TypeError(fmt( vm->TypeError(fmt(
"expected ", decl_argc, " positional arguments, got ", args.size(), co->name, "() takes ", decl_argc, " positional arguments but ", args.size(), " were given"
" (", co->name, ')'
)); ));
UNREACHABLE();
} }
int i = 0; int i = 0;
// prepare args // prepare args
for(int index: decl->args) buffer[index] = args[i++]; for(int index: decl->args) buffer[index] = args[i++];
// set extra varnames to nullptr // set extra varnames to PY_NULL
for(int j=i; j<co_nlocals; j++) buffer[j] = PY_NULL; for(int j=i; j<co_nlocals; j++) buffer[j] = PY_NULL;
// prepare kwdefaults // prepare kwdefaults
for(auto& kv: decl->kwargs) buffer[kv.key] = kv.value; for(auto& kv: decl->kwargs) buffer[kv.key] = kv.value;
@ -843,6 +843,28 @@ PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
const CodeObject* co = decl->code.get(); const CodeObject* co = decl->code.get();
int co_nlocals = co->varnames.size(); int co_nlocals = co->varnames.size();
PyObject** _base = args.begin();
if(decl->is_simple){
if(args.size() != decl->args.size()){
TypeError(fmt(
co->name, "() takes ", decl->args.size(), " positional arguments but ", args.size(), " were given"
));
UNREACHABLE();
}
if(!kwargs.empty()){
TypeError(fmt(co->name, "() takes no keyword arguments"));
UNREACHABLE();
}
s_data.reset(_base + co_nlocals);
int i = 0;
// prepare args
for(int index: decl->args) _base[index] = args[i++];
// set extra varnames to PY_NULL
for(int j=i; j<co_nlocals; j++) _base[j] = PY_NULL;
goto __FAST_CALL;
}
_prepare_py_call(buffer, args, kwargs, decl); _prepare_py_call(buffer, args, kwargs, decl);
if(co->is_generator){ if(co->is_generator){
@ -854,8 +876,10 @@ PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
} }
// copy buffer back to stack // copy buffer back to stack
s_data.reset(args.begin()); s_data.reset(_base + co_nlocals);
for(int j=0; j<co_nlocals; j++) PUSH(buffer[j]); for(int j=0; j<co_nlocals; j++) _base[j] = buffer[j];
__FAST_CALL:
callstack.emplace(&s_data, p0, co, fn._module, callable, FastLocals(co, args.begin())); callstack.emplace(&s_data, p0, co, fn._module, callable, FastLocals(co, args.begin()));
if(op_call) return PY_OP_CALL; if(op_call) return PY_OP_CALL;
return _run_top_frame(); return _run_top_frame();