impl LOAD_FAST

This commit is contained in:
blueloveTH 2023-04-11 17:45:40 +08:00
parent 205fedd053
commit c403252aec
12 changed files with 225 additions and 155 deletions

View File

@ -32,15 +32,6 @@ jobs:
platform: x64 platform: x64
- name: Install libc++ - name: Install libc++
run: sudo apt install -y libc++-15-dev libc++1-15 libc++abi-15-dev libc++abi1-15 run: sudo apt install -y libc++-15-dev libc++1-15 libc++abi-15-dev libc++abi1-15
# libclang-rt-15-dev ...some bugs here
# - name: Coverage Test
# run: |
# python3 preprocess.py
# bash run_tests.sh
# - uses: actions/upload-artifact@v3
# with:
# name: coverage
# path: .coverage
- name: Compile - name: Compile
run: | run: |
python3 build.py linux python3 build.py linux

View File

@ -1,11 +1,12 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
#include "namedict.h"
#include "vm.h" #include "vm.h"
namespace pkpy{ namespace pkpy{
inline PyObject* VM::_run_top_frame(){ inline PyObject* VM::_run_top_frame(bool force_no_pop){
FrameId frame = top_frame(); FrameId frame = top_frame();
const int base_id = frame.index; const int base_id = frame.index;
bool need_raise = false; bool need_raise = false;
@ -28,9 +29,9 @@ inline PyObject* VM::_run_top_frame(){
Bytecode byte = frame->next_bytecode(); Bytecode byte = frame->next_bytecode();
// cache // cache
const CodeObject* co = frame->co; const CodeObject* co = frame->co;
const std::vector<StrName>& co_names = co->names; const auto& co_names = co->names;
const List& co_consts = co->consts; const auto& co_consts = co->consts;
const std::vector<CodeBlock>& co_blocks = co->blocks; const auto& co_blocks = co->blocks;
#if PK_ENABLE_COMPUTED_GOTO #if PK_ENABLE_COMPUTED_GOTO
static void* OP_LABELS[] = { static void* OP_LABELS[] = {
@ -76,16 +77,27 @@ __NEXT_STEP:;
TARGET(LOAD_BUILTIN_EVAL) frame->push(builtins->attr(m_eval)); DISPATCH(); TARGET(LOAD_BUILTIN_EVAL) frame->push(builtins->attr(m_eval)); DISPATCH();
TARGET(LOAD_FUNCTION) { TARGET(LOAD_FUNCTION) {
FuncDecl_ decl = co->func_decls[byte.arg]; FuncDecl_ decl = co->func_decls[byte.arg];
PyObject* obj = VAR(Function({decl, frame->_module, frame->_locals})); PyObject* obj;
if(decl->nested){
obj = VAR(Function({decl, frame->_module, frame->locals_to_namedict()}));
}else{
obj = VAR(Function({decl, frame->_module, nullptr}));
}
frame->push(obj); frame->push(obj);
} DISPATCH(); } DISPATCH();
TARGET(LOAD_NULL) frame->push(_py_null); DISPATCH(); TARGET(LOAD_NULL) frame->push(_py_null); DISPATCH();
/*****************************************/ /*****************************************/
TARGET(LOAD_FAST) {
heap._auto_collect();
PyObject* val = frame->_locals[byte.arg];
if(val == nullptr) vm->NameError(co->varnames[byte.arg]);
frame->push(val);
} DISPATCH();
TARGET(LOAD_NAME) { TARGET(LOAD_NAME) {
heap._auto_collect(); heap._auto_collect();
StrName name = co_names[byte.arg]; StrName name = co_names[byte.arg];
PyObject* val; PyObject* val;
val = frame->f_locals().try_get(name); val = frame->f_locals_try_get(name);
if(val != nullptr) { frame->push(val); DISPATCH(); } if(val != nullptr) { frame->push(val); DISPATCH(); }
val = frame->f_closure_try_get(name); val = frame->f_closure_try_get(name);
if(val != nullptr) { frame->push(val); DISPATCH(); } if(val != nullptr) { frame->push(val); DISPATCH(); }
@ -122,10 +134,9 @@ __NEXT_STEP:;
args[0] = frame->top(); // a args[0] = frame->top(); // a
frame->top() = fast_call(__getitem__, std::move(args)); frame->top() = fast_call(__getitem__, std::move(args));
} DISPATCH(); } DISPATCH();
TARGET(STORE_LOCAL) { TARGET(STORE_FAST)
StrName name = co_names[byte.arg]; frame->_locals[byte.arg] = frame->popx();
frame->f_locals().set(name, frame->popx()); DISPATCH();
} DISPATCH();
TARGET(STORE_GLOBAL) { TARGET(STORE_GLOBAL) {
StrName name = co_names[byte.arg]; StrName name = co_names[byte.arg];
frame->f_globals().set(name, frame->popx()); frame->f_globals().set(name, frame->popx());
@ -144,13 +155,10 @@ __NEXT_STEP:;
args[2] = frame->popx(); // val args[2] = frame->popx(); // val
fast_call(__setitem__, std::move(args)); fast_call(__setitem__, std::move(args));
} DISPATCH(); } DISPATCH();
TARGET(DELETE_LOCAL) { TARGET(DELETE_FAST) {
StrName name = co_names[byte.arg]; PyObject* val = frame->_locals[byte.arg];
if(frame->f_locals().contains(name)){ if(val == nullptr) vm->NameError(co->varnames[byte.arg]);
frame->f_locals().erase(name); frame->_locals[byte.arg] = nullptr;
}else{
NameError(name);
}
} DISPATCH(); } DISPATCH();
TARGET(DELETE_GLOBAL) { TARGET(DELETE_GLOBAL) {
StrName name = co_names[byte.arg]; StrName name = co_names[byte.arg];
@ -515,7 +523,7 @@ __NEXT_STEP:;
/**********************************************************************/ /**********************************************************************/
__PY_SIMPLE_RETURN: __PY_SIMPLE_RETURN:
if(frame.index == base_id){ // [ frameBase<- ] if(frame.index == base_id){ // [ frameBase<- ]
callstack.pop(); if(!force_no_pop) callstack.pop();
return __ret; return __ret;
}else{ }else{
callstack.pop(); callstack.pop();

View File

@ -52,23 +52,20 @@ struct CodeObject {
Str name; Str name;
bool is_generator = false; bool is_generator = false;
CodeObject(shared_ptr<SourceData> src, Str name) { CodeObject(shared_ptr<SourceData> src, const Str& name):
this->src = src; src(src), name(name) {}
this->name = name;
}
std::vector<Bytecode> codes; std::vector<Bytecode> codes;
std::vector<int> lines; // line number for each bytecode std::vector<int> lines; // line number for each bytecode
List consts; List consts;
std::vector<StrName> names; std::vector<StrName> names; // other names
std::vector<StrName> varnames; // local variables
std::map<StrName, int> varnames_inv;
std::set<Str> global_names; std::set<Str> global_names;
std::vector<CodeBlock> blocks = { CodeBlock(NO_BLOCK, -1, 0, 0) }; std::vector<CodeBlock> blocks = { CodeBlock(NO_BLOCK, -1, 0, 0) };
std::map<StrName, int> labels; std::map<StrName, int> labels;
std::vector<FuncDecl_> func_decls; std::vector<FuncDecl_> func_decls;
uint32_t perfect_locals_capacity = NameDict::__Capacity;
uint32_t perfect_hash_seed = kHashSeeds[0];
void optimize(VM* vm); void optimize(VM* vm);
void _gc_mark() const { void _gc_mark() const {

View File

@ -29,13 +29,12 @@
#include <variant> #include <variant>
#include <type_traits> #include <type_traits>
#define PK_VERSION "0.9.7" #define PK_VERSION "0.9.8"
// debug macros // debug macros
#define DEBUG_NO_BUILTIN_MODULES 0 #define DEBUG_NO_BUILTIN_MODULES 0
#define DEBUG_EXTRA_CHECK 0 #define DEBUG_EXTRA_CHECK 0
#define DEBUG_DIS_EXEC 0 #define DEBUG_DIS_EXEC 0
#define DEBUG_DIS_EXEC_MIN 1
#define DEBUG_CEVAL_STEP 0 #define DEBUG_CEVAL_STEP 0
#define DEBUG_FULL_EXCEPTION 0 #define DEBUG_FULL_EXCEPTION 0
#define DEBUG_MEMORY_POOL 0 #define DEBUG_MEMORY_POOL 0
@ -96,7 +95,6 @@ struct Type {
#define FATAL_ERROR() throw std::runtime_error( __FILE__ + std::string(":") + std::to_string(__LINE__) + " FATAL_ERROR()!"); #define FATAL_ERROR() throw std::runtime_error( __FILE__ + std::string(":") + std::to_string(__LINE__) + " FATAL_ERROR()!");
#endif #endif
inline const float kLocalsLoadFactor = 0.67f;
inline const float kInstAttrLoadFactor = 0.67f; inline const float kInstAttrLoadFactor = 0.67f;
inline const float kTypeAttrLoadFactor = 0.5f; inline const float kTypeAttrLoadFactor = 0.5f;

View File

@ -3,6 +3,7 @@
#include "codeobject.h" #include "codeobject.h"
#include "common.h" #include "common.h"
#include "expr.h" #include "expr.h"
#include "obj.h"
namespace pkpy{ namespace pkpy{
@ -38,13 +39,20 @@ class Compiler {
CompileMode mode() const{ return lexer->src->mode; } CompileMode mode() const{ return lexer->src->mode; }
NameScope name_scope() const { return contexts.size()>1 ? NAME_LOCAL : NAME_GLOBAL; } NameScope name_scope() const { return contexts.size()>1 ? NAME_LOCAL : NAME_GLOBAL; }
template<typename... Args> CodeObject_ push_global_context(){
CodeObject_ push_context(Args&&... args){ CodeObject_ co = make_sp<CodeObject>(lexer->src, lexer->src->filename);
CodeObject_ co = make_sp<CodeObject>(std::forward<Args>(args)...);
contexts.push(CodeEmitContext(vm, co)); contexts.push(CodeEmitContext(vm, co));
return co; return co;
} }
FuncDecl_ push_f_context(Str name){
FuncDecl_ decl = make_sp<FuncDecl>();
decl->code = make_sp<CodeObject>(lexer->src, name);
decl->nested = name_scope() == NAME_LOCAL;
contexts.push(CodeEmitContext(vm, decl->code));
return decl;
}
void pop_context(){ void pop_context(){
if(!ctx()->s_expr.empty()){ if(!ctx()->s_expr.empty()){
throw std::runtime_error("!ctx()->s_expr.empty()\n" + ctx()->_log_s_expr()); throw std::runtime_error("!ctx()->s_expr.empty()\n" + ctx()->_log_s_expr());
@ -190,12 +198,12 @@ class Compiler {
void exprLambda(){ void exprLambda(){
auto e = make_expr<LambdaExpr>(name_scope()); FuncDecl_ decl = push_f_context("<lambda>");
auto e = make_expr<LambdaExpr>(decl);
if(!match(TK(":"))){ if(!match(TK(":"))){
_compile_f_args(e->decl, false); _compile_f_args(e->decl, false);
consume(TK(":")); consume(TK(":"));
} }
e->decl->code = push_context(lexer->src, e->decl->name.sv());
// https://github.com/blueloveTH/pocketpy/issues/37 // https://github.com/blueloveTH/pocketpy/issues/37
parse_expression(PREC_LAMBDA + 1, false); parse_expression(PREC_LAMBDA + 1, false);
ctx()->emit(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); ctx()->emit(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE);
@ -528,9 +536,11 @@ __SUBSCR_END:
consume(TK("@id")); consume(TK("@id"));
name = prev().str(); name = prev().str();
} }
int index = ctx()->add_name(name); if(name_scope() == NAME_LOCAL){
auto op = name_scope()==NAME_LOCAL ? OP_STORE_LOCAL : OP_STORE_GLOBAL; ctx()->emit(OP_STORE_FAST, ctx()->add_varname(name), prev().line);
ctx()->emit(op, index, prev().line); }else{
ctx()->emit(OP_STORE_GLOBAL, ctx()->add_name(name), prev().line);
}
} while (match(TK(","))); } while (match(TK(",")));
consume_end_stmt(); consume_end_stmt();
} }
@ -555,9 +565,11 @@ __SUBSCR_END:
consume(TK("@id")); consume(TK("@id"));
name = prev().str(); name = prev().str();
} }
index = ctx()->add_name(name); if(name_scope() == NAME_LOCAL){
auto op = name_scope()==NAME_LOCAL ? OP_STORE_LOCAL : OP_STORE_GLOBAL; ctx()->emit(OP_STORE_FAST, ctx()->add_varname(name), prev().line);
ctx()->emit(op, index, prev().line); }else{
ctx()->emit(OP_STORE_GLOBAL, ctx()->add_name(name), prev().line);
}
} while (match(TK(","))); } while (match(TK(",")));
ctx()->emit(OP_POP_TOP, BC_NOARG, BC_KEEPLINE); ctx()->emit(OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
consume_end_stmt(); consume_end_stmt();
@ -849,27 +861,44 @@ __SUBSCR_END:
else if(match(TK("**"))){ else if(match(TK("**"))){
state = 3; state = 3;
} }
consume(TK("@id")); consume(TK("@id"));
const Str& name = prev().str(); StrName name = prev().str();
if(decl->has_name(name)) SyntaxError("duplicate argument name");
// check duplicate argument name
for(int i: decl->args){
if(decl->code->varnames[i] == name) {
SyntaxError("duplicate argument name");
}
}
if(decl->starred_arg!=-1 && decl->code->varnames[decl->starred_arg] == name){
SyntaxError("duplicate argument name");
}
for(auto& kv: decl->kwargs){
if(decl->code->varnames[kv.key] == name){
SyntaxError("duplicate argument name");
}
}
// eat type hints // eat type hints
if(enable_type_hints && match(TK(":"))) consume(TK("@id")); if(enable_type_hints && match(TK(":"))) consume(TK("@id"));
if(state == 0 && curr().type == TK("=")) state = 2; if(state == 0 && curr().type == TK("=")) state = 2;
int index = ctx()->add_varname(name);
switch (state) switch (state)
{ {
case 0: decl->args.push_back(name); break; case 0:
case 1: decl->starred_arg = name; state+=1; break; decl->args.push_back(index);
break;
case 1:
decl->starred_arg = index;
state+=1;
break;
case 2: { case 2: {
consume(TK("=")); consume(TK("="));
PyObject* value = read_literal(); PyObject* value = read_literal();
if(value == nullptr){ if(value == nullptr){
SyntaxError(Str("expect a literal, not ") + TK_STR(curr().type)); SyntaxError(Str("expect a literal, not ") + TK_STR(curr().type));
} }
decl->kwargs.set(name, value); decl->kwargs.push_back(FuncDecl::KwArg{index, value});
decl->kwargs_order.push_back(name);
} break; } break;
case 3: SyntaxError("**kwargs is not supported yet"); break; case 3: SyntaxError("**kwargs is not supported yet"); break;
} }
@ -877,15 +906,16 @@ __SUBSCR_END:
} }
void compile_function(const std::vector<Expr_>& decorators={}){ void compile_function(const std::vector<Expr_>& decorators={}){
FuncDecl_ decl = make_sp<FuncDecl>(); Str obj_name;
StrName obj_name; Str decl_name;
consume(TK("@id")); consume(TK("@id"));
decl->name = prev().str(); decl_name = prev().str();
if(!ctx()->is_compiling_class && match(TK("::"))){ if(!ctx()->is_compiling_class && match(TK("::"))){
consume(TK("@id")); consume(TK("@id"));
obj_name = decl->name; obj_name = decl_name;
decl->name = prev().str(); decl_name = prev().str();
} }
FuncDecl_ decl = push_f_context(decl_name);
consume(TK("(")); consume(TK("("));
if (!match(TK(")"))) { if (!match(TK(")"))) {
_compile_f_args(decl, true); _compile_f_args(decl, true);
@ -894,7 +924,6 @@ __SUBSCR_END:
if(match(TK("->"))){ if(match(TK("->"))){
if(!match(TK("None"))) consume(TK("@id")); if(!match(TK("None"))) consume(TK("@id"));
} }
decl->code = push_context(lexer->src, decl->name.sv());
compile_block_body(); compile_block_body();
pop_context(); pop_context();
ctx()->emit(OP_LOAD_FUNCTION, ctx()->add_func_decl(decl), prev().line); ctx()->emit(OP_LOAD_FUNCTION, ctx()->add_func_decl(decl), prev().line);
@ -908,15 +937,15 @@ __SUBSCR_END:
} }
if(!ctx()->is_compiling_class){ if(!ctx()->is_compiling_class){
if(obj_name.empty()){ if(obj_name.empty()){
auto e = make_expr<NameExpr>(decl->name, name_scope()); auto e = make_expr<NameExpr>(decl_name, name_scope());
e->emit_store(ctx()); e->emit_store(ctx());
} else { } else {
ctx()->emit(OP_LOAD_GLOBAL, ctx()->add_name(obj_name), prev().line); ctx()->emit(OP_LOAD_GLOBAL, ctx()->add_name(obj_name), prev().line);
int index = ctx()->add_name(decl->name); int index = ctx()->add_name(decl_name);
ctx()->emit(OP_STORE_ATTR, index, prev().line); ctx()->emit(OP_STORE_ATTR, index, prev().line);
} }
}else{ }else{
int index = ctx()->add_name(decl->name); int index = ctx()->add_name(decl_name);
ctx()->emit(OP_STORE_CLASS_ATTR, index, prev().line); ctx()->emit(OP_STORE_CLASS_ATTR, index, prev().line);
} }
} }
@ -963,7 +992,7 @@ public:
// for(auto& t: tokens) std::cout << t.info() << std::endl; // for(auto& t: tokens) std::cout << t.info() << std::endl;
// } // }
CodeObject_ code = push_context(lexer->src, lexer->src->filename); CodeObject_ code = push_global_context();
advance(); // skip @sof, so prev() is always valid advance(); // skip @sof, so prev() is always valid
match_newlines(); // skip possible leading '\n' match_newlines(); // skip possible leading '\n'

View File

@ -105,6 +105,15 @@ struct CodeEmitContext{
return co->names.size() - 1; return co->names.size() - 1;
} }
int add_varname(StrName name){
auto it = co->varnames_inv.find(name);
if(it != co->varnames_inv.end()) return it->second;
co->varnames.push_back(name);
int index = co->varnames.size() - 1;
co->varnames_inv[name] = index;
return index;
}
int add_const(PyObject* v){ int add_const(PyObject* v){
co->consts.push_back(v); co->consts.push_back(v);
return co->consts.size() - 1; return co->consts.size() - 1;
@ -124,18 +133,24 @@ struct NameExpr: Expr{
std::string str() const override { return fmt("Name(", name.escape(), ")"); } std::string str() const override { return fmt("Name(", name.escape(), ")"); }
void emit(CodeEmitContext* ctx) override { void emit(CodeEmitContext* ctx) override {
int index = ctx->add_name(name); switch(scope){
ctx->emit(OP_LOAD_NAME, index, line); case NAME_LOCAL:
ctx->emit(OP_LOAD_FAST, ctx->add_varname(name), line);
break;
case NAME_GLOBAL:
ctx->emit(OP_LOAD_GLOBAL, ctx->add_name(name), line);
break;
default: FATAL_ERROR(); break;
}
} }
bool emit_del(CodeEmitContext* ctx) override { bool emit_del(CodeEmitContext* ctx) override {
int index = ctx->add_name(name);
switch(scope){ switch(scope){
case NAME_LOCAL: case NAME_LOCAL:
ctx->emit(OP_DELETE_LOCAL, index, line); ctx->emit(OP_DELETE_FAST, ctx->add_varname(name), line);
break; break;
case NAME_GLOBAL: case NAME_GLOBAL:
ctx->emit(OP_DELETE_GLOBAL, index, line); ctx->emit(OP_DELETE_GLOBAL, ctx->add_name(name), line);
break; break;
default: FATAL_ERROR(); break; default: FATAL_ERROR(); break;
} }
@ -143,17 +158,17 @@ struct NameExpr: Expr{
} }
bool emit_store(CodeEmitContext* ctx) override { bool emit_store(CodeEmitContext* ctx) override {
int index = ctx->add_name(name);
if(ctx->is_compiling_class){ if(ctx->is_compiling_class){
int index = ctx->add_name(name);
ctx->emit(OP_STORE_CLASS_ATTR, index, line); ctx->emit(OP_STORE_CLASS_ATTR, index, line);
return true; return true;
} }
switch(scope){ switch(scope){
case NAME_LOCAL: case NAME_LOCAL:
ctx->emit(OP_STORE_LOCAL, index, line); ctx->emit(OP_STORE_FAST, ctx->add_varname(name), line);
break; break;
case NAME_GLOBAL: case NAME_GLOBAL:
ctx->emit(OP_STORE_GLOBAL, index, line); ctx->emit(OP_STORE_GLOBAL, ctx->add_name(name), line);
break; break;
default: FATAL_ERROR(); break; default: FATAL_ERROR(); break;
} }
@ -484,14 +499,9 @@ struct SetCompExpr: CompExpr{
struct LambdaExpr: Expr{ struct LambdaExpr: Expr{
FuncDecl_ decl; FuncDecl_ decl;
NameScope scope;
std::string str() const override { return "Lambda()"; } std::string str() const override { return "Lambda()"; }
LambdaExpr(NameScope scope){ LambdaExpr(FuncDecl_ decl): decl(decl) {}
this->decl = make_sp<FuncDecl>();
this->decl->name = "<lambda>";
this->scope = scope;
}
void emit(CodeEmitContext* ctx) override { void emit(CodeEmitContext* ctx) override {
int index = ctx->add_func_decl(decl); int index = ctx->add_func_decl(decl);
@ -624,8 +634,8 @@ struct CallExpr: Expr{
for(auto& item: args) item->emit(ctx); for(auto& item: args) item->emit(ctx);
// emit kwargs // emit kwargs
for(auto& item: kwargs){ for(auto& item: kwargs){
// TODO: optimize this int index = ctx->add_varname(item.first);
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(item.first)), line); ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(index)), line);
item.second->emit(ctx); item.second->emit(ctx);
} }
int KWARGC = (int)kwargs.size(); int KWARGC = (int)kwargs.size();

View File

@ -7,6 +7,7 @@
namespace pkpy{ namespace pkpy{
using ValueStack = pod_vector<PyObject*>; using ValueStack = pod_vector<PyObject*>;
using FastLocals = pod_vector<PyObject*>;
struct Frame { struct Frame {
ValueStack _data; ValueStack _data;
@ -15,18 +16,39 @@ struct Frame {
const CodeObject* co; const CodeObject* co;
PyObject* _module; PyObject* _module;
NameDict_ _locals; FastLocals _locals;
NameDict_ _closure; NameDict_ _closure;
NameDict& f_locals() noexcept { return _locals!=nullptr ? *_locals : _module->attr(); } PyObject* f_locals_try_get(StrName name){
auto it = co->varnames_inv.find(name);
if(it == co->varnames_inv.end()) return nullptr;
return _locals[it->second];
}
NameDict& f_globals() noexcept { return _module->attr(); } NameDict& f_globals() noexcept { return _module->attr(); }
PyObject* f_closure_try_get(StrName name){ PyObject* f_closure_try_get(StrName name){
if(_closure == nullptr) return nullptr; if(_closure == nullptr) return nullptr;
return _closure->try_get(name); return _closure->try_get(name);
} }
Frame(const CodeObject_& co, PyObject* _module, const NameDict_& _locals=nullptr, const NameDict_& _closure=nullptr) NameDict_ locals_to_namedict() const{
: co(co.get()), _module(_module), _locals(_locals), _closure(_closure) { NameDict_ dict = make_sp<NameDict>();
for(int i=0; i<co->varnames.size(); i++){
if(_locals[i] != nullptr) dict->set(co->varnames[i], _locals[i]);
}
return dict;
}
Frame(const CodeObject* co, PyObject* _module, FastLocals&& _locals, const NameDict_& _closure)
: co(co), _module(_module), _locals(std::move(_locals)), _closure(_closure) {
}
Frame(const CodeObject* co, PyObject* _module)
: co(co), _module(_module), _locals(), _closure(nullptr) {
}
Frame(const CodeObject_& co, PyObject* _module)
: co(co.get()), _module(_module), _locals(), _closure(nullptr) {
} }
Frame(const Frame& other) = delete; Frame(const Frame& other) = delete;
@ -155,7 +177,10 @@ struct Frame {
if(_data._data == nullptr) return; if(_data._data == nullptr) return;
for(PyObject* obj : _data) OBJ_MARK(obj); for(PyObject* obj : _data) OBJ_MARK(obj);
OBJ_MARK(_module); OBJ_MARK(_module);
if(_locals != nullptr) _locals->_gc_mark(); // _locals may be move for `eval/exec`
if(_locals._data != nullptr){
for(PyObject* obj: _locals) OBJ_MARK(obj);
}
if(_closure != nullptr) _closure->_gc_mark(); if(_closure != nullptr) _closure->_gc_mark();
co->_gc_mark(); co->_gc_mark();
} }

View File

@ -120,7 +120,7 @@ inline void NameDict::_gc_mark() const{
inline void FuncDecl::_gc_mark() const{ inline void FuncDecl::_gc_mark() const{
code->_gc_mark(); code->_gc_mark();
kwargs._gc_mark(); for(int i=0; i<kwargs.size(); i++) OBJ_MARK(kwargs[i].value);
} }
template<> inline void _gc_mark<List>(List& t){ template<> inline void _gc_mark<List>(List& t){

View File

@ -8,7 +8,6 @@ namespace pkpy {
struct CodeObject; struct CodeObject;
struct Frame; struct Frame;
struct BaseRef;
class VM; class VM;
typedef std::function<PyObject*(VM*, Args&)> NativeFuncRaw; typedef std::function<PyObject*(VM*, Args&)> NativeFuncRaw;
@ -25,20 +24,15 @@ struct NativeFunc {
}; };
struct FuncDecl { struct FuncDecl {
StrName name; struct KwArg {
CodeObject_ code; int key; // index in co->varnames
std::vector<StrName> args; PyObject* value; // default value
StrName starred_arg; // empty if no *arg };
NameDict kwargs; // empty if no k=v CodeObject_ code; // code object of this function
std::vector<StrName> kwargs_order; pod_vector<int> args; // indices in co->varnames
int starred_arg = -1; // index in co->varnames, -1 if no *arg
bool has_name(StrName val) const { pod_vector<KwArg> kwargs; // indices in co->varnames
bool _0 = std::find(args.begin(), args.end(), val) != args.end(); bool nested = false; // whether this function is nested
bool _1 = starred_arg == val;
bool _2 = kwargs.contains(val);
return _0 || _1 || _2;
}
void _gc_mark() const; void _gc_mark() const;
}; };

View File

@ -17,18 +17,19 @@ OPCODE(LOAD_BUILTIN_EVAL)
OPCODE(LOAD_FUNCTION) OPCODE(LOAD_FUNCTION)
OPCODE(LOAD_NULL) OPCODE(LOAD_NULL)
/**************************/ /**************************/
OPCODE(LOAD_FAST)
OPCODE(LOAD_NAME) OPCODE(LOAD_NAME)
OPCODE(LOAD_GLOBAL) OPCODE(LOAD_GLOBAL)
OPCODE(LOAD_ATTR) OPCODE(LOAD_ATTR)
OPCODE(LOAD_METHOD) OPCODE(LOAD_METHOD)
OPCODE(LOAD_SUBSCR) OPCODE(LOAD_SUBSCR)
OPCODE(STORE_LOCAL) OPCODE(STORE_FAST)
OPCODE(STORE_GLOBAL) OPCODE(STORE_GLOBAL)
OPCODE(STORE_ATTR) OPCODE(STORE_ATTR)
OPCODE(STORE_SUBSCR) OPCODE(STORE_SUBSCR)
OPCODE(DELETE_LOCAL) OPCODE(DELETE_FAST)
OPCODE(DELETE_GLOBAL) OPCODE(DELETE_GLOBAL)
OPCODE(DELETE_ATTR) OPCODE(DELETE_ATTR)
OPCODE(DELETE_SUBSCR) OPCODE(DELETE_SUBSCR)

View File

@ -98,12 +98,26 @@ inline void init_builtins(VM* _vm) {
_vm->bind_builtin_func<1>("eval", [](VM* vm, Args& args) { _vm->bind_builtin_func<1>("eval", [](VM* vm, Args& args) {
CodeObject_ code = vm->compile(CAST(Str&, args[0]), "<eval>", EVAL_MODE); CodeObject_ code = vm->compile(CAST(Str&, args[0]), "<eval>", EVAL_MODE);
return vm->_exec(code, vm->top_frame()->_module, vm->top_frame()->_locals); FrameId frame = vm->top_frame();
vm->_push_new_frame(code.get(), frame->_module, std::move(frame->_locals), nullptr);
PyObject* ret = vm->_run_top_frame(true);
frame->_locals = std::move(vm->top_frame()->_locals);
vm->callstack.pop();
return ret;
}); });
_vm->bind_builtin_func<1>("exec", [](VM* vm, Args& args) { _vm->bind_builtin_func<1>("exec", [](VM* vm, Args& args) {
CodeObject_ code = vm->compile(CAST(Str&, args[0]), "<exec>", EXEC_MODE); CodeObject_ code = vm->compile(CAST(Str&, args[0]), "<exec>", EXEC_MODE);
vm->_exec(code, vm->top_frame()->_module, vm->top_frame()->_locals); FrameId frame = vm->top_frame();
// TODO: implementation is not correct
// ...
// moving _locals is dangerous since OP_LOAD_FAST's arg is index of _locals
// the new opcode may not generate the same index, or even not a OP_LOAD_FAST call
// we should do some special handling here
vm->_push_new_frame(code.get(), frame->_module, std::move(frame->_locals), nullptr);
vm->_run_top_frame(true);
frame->_locals = std::move(vm->top_frame()->_locals);
vm->callstack.pop();
return vm->None; return vm->None;
}); });
@ -598,7 +612,7 @@ inline void add_module_json(VM* vm){
vm->bind_func<1>(mod, "loads", [](VM* vm, Args& args) { vm->bind_func<1>(mod, "loads", [](VM* vm, Args& args) {
const Str& expr = CAST(Str&, args[0]); const Str& expr = CAST(Str&, args[0]);
CodeObject_ code = vm->compile(expr, "<json>", JSON_MODE); CodeObject_ code = vm->compile(expr, "<json>", JSON_MODE);
return vm->_exec(code, vm->top_frame()->_module, vm->top_frame()->_locals); return vm->_exec(code, vm->top_frame()->_module);
}); });
vm->bind_func<1>(mod, "dumps", CPP_LAMBDA(vm->fast_call(__json__, Args{args[0]}))); vm->bind_func<1>(mod, "dumps", CPP_LAMBDA(vm->fast_call(__json__, Args{args[0]})));

View File

@ -348,7 +348,7 @@ public:
template<int ARGC> template<int ARGC>
void bind_func(PyObject*, Str, NativeFuncRaw); void bind_func(PyObject*, Str, NativeFuncRaw);
void _error(Exception); void _error(Exception);
PyObject* _run_top_frame(); PyObject* _run_top_frame(bool force_no_pop=false);
void post_init(); void post_init();
}; };
@ -361,11 +361,9 @@ inline PyObject* NativeFunc::operator()(VM* vm, Args& args) const{
} }
inline void CodeObject::optimize(VM* vm){ inline void CodeObject::optimize(VM* vm){
// here we simple pass all names, but only some of them are NAME_LOCAL // uint32_t base_n = (uint32_t)(names.size() / kLocalsLoadFactor + 0.5);
// TODO: ... // perfect_locals_capacity = std::max(find_next_capacity(base_n), NameDict::__Capacity);
uint32_t base_n = (uint32_t)(names.size() / kLocalsLoadFactor + 0.5); // perfect_hash_seed = find_perfect_hash_seed(perfect_locals_capacity, names);
perfect_locals_capacity = std::max(find_next_capacity(base_n), NameDict::__Capacity);
perfect_hash_seed = find_perfect_hash_seed(perfect_locals_capacity, names);
} }
DEF_NATIVE_2(Str, tp_str) DEF_NATIVE_2(Str, tp_str)
@ -596,12 +594,15 @@ inline Str VM::disassemble(CodeObject_ co){
argStr += fmt(" (", CAST(Str, asRepr(co->consts[byte.arg])), ")"); argStr += fmt(" (", CAST(Str, asRepr(co->consts[byte.arg])), ")");
break; break;
case OP_LOAD_NAME: case OP_LOAD_GLOBAL: case OP_LOAD_NAME: case OP_LOAD_GLOBAL:
case OP_STORE_LOCAL: case OP_STORE_GLOBAL: case OP_STORE_GLOBAL:
case OP_LOAD_ATTR: case OP_LOAD_METHOD: case OP_STORE_ATTR: case OP_DELETE_ATTR: case OP_LOAD_ATTR: case OP_LOAD_METHOD: case OP_STORE_ATTR: case OP_DELETE_ATTR:
case OP_IMPORT_NAME: case OP_BEGIN_CLASS: case OP_IMPORT_NAME: case OP_BEGIN_CLASS:
case OP_DELETE_LOCAL: case OP_DELETE_GLOBAL: case OP_DELETE_GLOBAL:
argStr += fmt(" (", co->names[byte.arg].sv(), ")"); argStr += fmt(" (", co->names[byte.arg].sv(), ")");
break; break;
case OP_LOAD_FAST: case OP_STORE_FAST: case OP_DELETE_FAST:
argStr += fmt(" (", co->varnames[byte.arg].sv(), ")");
break;
case OP_BINARY_OP: case OP_BINARY_OP:
argStr += fmt(" (", BINARY_SPECIAL_METHODS[byte.arg], ")"); argStr += fmt(" (", BINARY_SPECIAL_METHODS[byte.arg], ")");
break; break;
@ -611,22 +612,21 @@ inline Str VM::disassemble(CodeObject_ co){
if(i != co->codes.size() - 1) ss << '\n'; if(i != co->codes.size() - 1) ss << '\n';
} }
#if !DEBUG_DIS_EXEC_MIN // std::stringstream consts;
std::stringstream consts; // consts << "co_consts: ";
consts << "co_consts: "; // consts << CAST(Str&, asRepr(VAR(co->consts)));
consts << CAST(Str&, asRepr(VAR(co->consts)));
// std::stringstream names;
// names << "co_names: ";
// List list;
// for(int i=0; i<co->names.size(); i++){
// list.push_back(VAR(co->names[i].sv()));
// }
// names << CAST(Str, asRepr(VAR(list)));
// ss << '\n' << consts.str() << '\n' << names.str();
std::stringstream names;
names << "co_names: ";
List list;
for(int i=0; i<co->names.size(); i++){
list.push_back(VAR(co->names[i].sv()));
}
names << CAST(Str, asRepr(VAR(list)));
ss << '\n' << consts.str() << '\n' << names.str();
#endif
for(auto& decl: co->func_decls){ for(auto& decl: co->func_decls){
ss << "\n\n" << "Disassembly of " << decl->name << ":\n"; ss << "\n\n" << "Disassembly of " << decl->code->name << ":\n";
ss << disassemble(decl->code); ss << disassemble(decl->code);
} }
return Str(ss.str()); return Str(ss.str());
@ -699,33 +699,35 @@ inline PyObject* VM::call(PyObject* callable, Args args, const Args& kwargs, boo
return f(this, args); return f(this, args);
} else if(is_type(callable, tp_function)){ } else if(is_type(callable, tp_function)){
const Function& fn = CAST(Function&, callable); const Function& fn = CAST(Function&, callable);
NameDict_ locals = make_sp<NameDict>( const CodeObject* co = fn.decl->code.get();
kLocalsLoadFactor, // create a FastLocals with the same size as co->varnames
fn.decl->code->perfect_locals_capacity, FastLocals locals(co->varnames.size());
fn.decl->code->perfect_hash_seed // zero init
); for(auto& v: locals) v = nullptr;
int i = 0; int i = 0;
for(StrName name : fn.decl->args){ for(int index: fn.decl->args){
if(i < args.size()){ if(i < args.size()){
locals->set(name, args[i++]); locals[index] = args[i++];
continue; }else{
StrName name = co->varnames[index];
TypeError(fmt("missing positional argument ", name.escape()));
} }
TypeError(fmt("missing positional argument ", name.escape()));
} }
// NameDict.update is of O(capacity) complexity // prepare kwdefaults
// so we try not to call it if possible for(auto& kv: fn.decl->kwargs) locals[kv.key] = kv.value;
if(fn.decl->kwargs.size()!=0) locals->update(fn.decl->kwargs);
// handle *args
if(!fn.decl->starred_arg.empty()){ if(fn.decl->starred_arg != -1){
List vargs; // handle *args List vargs; // handle *args
while(i < args.size()) vargs.push_back(args[i++]); while(i < args.size()) vargs.push_back(args[i++]);
locals->set(fn.decl->starred_arg, VAR(Tuple(std::move(vargs)))); locals[fn.decl->starred_arg] = VAR(Tuple(std::move(vargs)));
}else{ }else{
for(StrName key : fn.decl->kwargs_order){ // kwdefaults override
for(auto& kv: fn.decl->kwargs){
if(i < args.size()){ if(i < args.size()){
locals->set(key, args[i++]); locals[kv.key] = args[i++];
}else{ }else{
break; break;
} }
@ -734,17 +736,18 @@ inline PyObject* VM::call(PyObject* callable, Args args, const Args& kwargs, boo
} }
for(int i=0; i<kwargs.size(); i+=2){ for(int i=0; i<kwargs.size(); i+=2){
const Str& key = CAST(Str&, kwargs[i]); StrName key = CAST(int, kwargs[i]);
if(!fn.decl->kwargs.contains(key)){ auto it = co->varnames_inv.find(key);
TypeError(fmt(key.escape(), " is an invalid keyword argument for ", fn.decl->name, "()")); if(it == co->varnames_inv.end()){
TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()"));
} }
locals->set(key, kwargs[i+1]); locals[it->second] = kwargs[i+1];
} }
PyObject* _module = fn._module != nullptr ? fn._module : top_frame()->_module; PyObject* _module = fn._module != nullptr ? fn._module : top_frame()->_module;
if(fn.decl->code->is_generator){ if(co->is_generator){
return PyIter(Generator(this, Frame(fn.decl->code, _module, locals, fn._closure))); return PyIter(Generator(this, Frame(co, _module, std::move(locals), fn._closure)));
} }
_push_new_frame(fn.decl->code, _module, locals, fn._closure); _push_new_frame(co, _module, std::move(locals), fn._closure);
if(opCall) return _py_op_call; if(opCall) return _py_op_call;
return _run_top_frame(); return _run_top_frame();
} }