reduce bytecode size

This commit is contained in:
blueloveTH 2023-10-14 14:30:15 +08:00
parent ffde186dfa
commit 1afef9dcb9
7 changed files with 33 additions and 30 deletions

View File

@ -21,8 +21,7 @@ inline const char* OP_NAMES[] = {
struct Bytecode{ struct Bytecode{
uint8_t op; uint8_t op;
uint16_t block; uint16_t arg;
int arg;
}; };
enum CodeBlockType { enum CodeBlockType {
@ -33,7 +32,7 @@ enum CodeBlockType {
TRY_EXCEPT, TRY_EXCEPT,
}; };
inline const int BC_NOARG = -1; inline const uint8_t BC_NOARG = 0;
inline const int BC_KEEPLINE = -1; inline const int BC_KEEPLINE = -1;
struct CodeBlock { struct CodeBlock {
@ -103,6 +102,7 @@ struct CodeObject {
bool is_generator = false; bool is_generator = false;
std::vector<Bytecode> codes; std::vector<Bytecode> codes;
std::vector<int> iblocks; // block index for each bytecode
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> varnames; // local variables std::vector<StrName> varnames; // local variables
@ -111,6 +111,10 @@ struct CodeObject {
NameDictInt labels; NameDictInt labels;
std::vector<FuncDecl_> func_decls; std::vector<FuncDecl_> func_decls;
const CodeBlock& _get_block_codei(int codei) const{
return blocks[iblocks[codei]];
}
CodeObject(std::shared_ptr<SourceData> src, const Str& name); CodeObject(std::shared_ptr<SourceData> src, const Str& name);
void _gc_mark() const; void _gc_mark() const;
void write(VM* vm, CodeObjectSerializer& ss) const; void write(VM* vm, CodeObjectSerializer& ss) const;

View File

@ -52,7 +52,7 @@
// This is the maximum number of arguments in a function declaration // This is the maximum number of arguments in a function declaration
// including positional arguments, keyword-only arguments, and varargs // including positional arguments, keyword-only arguments, and varargs
// (not recommended to change this) // (not recommended to change this / it should be less than 200)
#define PK_MAX_CO_VARNAMES 32 #define PK_MAX_CO_VARNAMES 32
namespace pkpy{ namespace pkpy{

View File

@ -60,7 +60,7 @@ struct CodeEmitContext{
void exit_block(); void exit_block();
void emit_expr(); // clear the expression stack and generate bytecode void emit_expr(); // clear the expression stack and generate bytecode
std::string _log_s_expr(); std::string _log_s_expr();
int emit(Opcode opcode, int arg, int line); int emit(Opcode opcode, uint16_t arg, int line);
void patch_jump(int index); void patch_jump(int index);
bool add_label(StrName name); bool add_label(StrName name);
int add_varname(StrName name); int add_varname(StrName name);

View File

@ -88,7 +88,7 @@ __NEXT_STEP:;
TARGET(LOAD_NONE) PUSH(None); DISPATCH(); TARGET(LOAD_NONE) PUSH(None); DISPATCH();
TARGET(LOAD_TRUE) PUSH(True); DISPATCH(); TARGET(LOAD_TRUE) PUSH(True); DISPATCH();
TARGET(LOAD_FALSE) PUSH(False); DISPATCH(); TARGET(LOAD_FALSE) PUSH(False); DISPATCH();
TARGET(LOAD_INTEGER) PUSH(VAR(byte.arg)); DISPATCH(); TARGET(LOAD_INTEGER) PUSH(VAR((int16_t)byte.arg)); DISPATCH();
TARGET(LOAD_ELLIPSIS) PUSH(Ellipsis); DISPATCH(); TARGET(LOAD_ELLIPSIS) PUSH(Ellipsis); DISPATCH();
TARGET(LOAD_FUNCTION) { TARGET(LOAD_FUNCTION) {
FuncDecl_ decl = co->func_decls[byte.arg]; FuncDecl_ decl = co->func_decls[byte.arg];
@ -511,8 +511,8 @@ __NEXT_STEP:;
DISPATCH(); DISPATCH();
TARGET(CALL) TARGET(CALL)
_0 = vectorcall( _0 = vectorcall(
byte.arg & 0xFFFF, // ARGC byte.arg & 0xFF, // ARGC
(byte.arg>>16) & 0xFFFF, // KWARGC (byte.arg>>8) & 0xFF, // KWARGC
true true
); );
if(_0 == PY_OP_CALL) DISPATCH_OP_CALL(); if(_0 == PY_OP_CALL) DISPATCH_OP_CALL();
@ -600,7 +600,8 @@ __NEXT_STEP:;
if(_0 != StopIteration){ if(_0 != StopIteration){
PUSH(_0); PUSH(_0);
}else{ }else{
frame->jump_abs_break(co_blocks[byte.block].end); // TODO: optimize this
frame->jump_abs_break(co->_get_block_codei(frame->_ip).end);
} }
DISPATCH(); DISPATCH();
/*****************************************/ /*****************************************/

View File

@ -50,10 +50,9 @@ namespace pkpy{
return ss.str(); return ss.str();
} }
int CodeEmitContext::emit(Opcode opcode, int arg, int line) { int CodeEmitContext::emit(Opcode opcode, uint16_t arg, int line) {
co->codes.push_back( co->codes.push_back(Bytecode{(uint8_t)opcode, arg});
Bytecode{(uint8_t)opcode, (uint16_t)curr_block_i, arg} co->iblocks.push_back(curr_block_i);
);
co->lines.push_back(line); co->lines.push_back(line);
int i = co->codes.size() - 1; int i = co->codes.size() - 1;
if(line==BC_KEEPLINE){ if(line==BC_KEEPLINE){
@ -75,6 +74,7 @@ namespace pkpy{
} }
int CodeEmitContext::add_varname(StrName name){ int CodeEmitContext::add_varname(StrName name){
// PK_MAX_CO_VARNAMES will be checked when pop_context(), not here
int index = co->varnames_inv.try_get(name); int index = co->varnames_inv.try_get(name);
if(index >= 0) return index; if(index >= 0) return index;
co->varnames.push_back(name); co->varnames.push_back(name);
@ -143,8 +143,7 @@ namespace pkpy{
bool NameExpr::emit_store(CodeEmitContext* ctx) { bool NameExpr::emit_store(CodeEmitContext* ctx) {
if(ctx->is_compiling_class){ if(ctx->is_compiling_class){
int index = name.index; ctx->emit(OP_STORE_CLASS_ATTR, name.index, line);
ctx->emit(OP_STORE_CLASS_ATTR, index, line);
return true; return true;
} }
ctx->emit_store_name(scope, name, line); ctx->emit_store_name(scope, name, line);
@ -214,7 +213,7 @@ namespace pkpy{
if(std::holds_alternative<i64>(value)){ if(std::holds_alternative<i64>(value)){
i64 _val = std::get<i64>(value); i64 _val = std::get<i64>(value);
if(_val >= INT16_MIN && _val <= INT16_MAX){ if(_val >= INT16_MIN && _val <= INT16_MAX){
ctx->emit(OP_LOAD_INTEGER, (int)_val, line); ctx->emit(OP_LOAD_INTEGER, (uint16_t)_val, line);
return; return;
} }
obj = VAR(_val); obj = VAR(_val);
@ -237,7 +236,7 @@ namespace pkpy{
if(std::holds_alternative<i64>(lit->value)){ if(std::holds_alternative<i64>(lit->value)){
i64 _val = -std::get<i64>(lit->value); i64 _val = -std::get<i64>(lit->value);
if(_val >= INT16_MIN && _val <= INT16_MAX){ if(_val >= INT16_MIN && _val <= INT16_MAX){
ctx->emit(OP_LOAD_INTEGER, (int)_val, line); ctx->emit(OP_LOAD_INTEGER, (uint16_t)_val, line);
}else{ }else{
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(_val)), line); ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(_val)), line);
} }
@ -484,7 +483,7 @@ namespace pkpy{
if(vargs || vkwargs){ if(vargs || vkwargs){
for(auto& item: args) item->emit(ctx); for(auto& item: args) item->emit(ctx);
ctx->emit(OP_BUILD_TUPLE_UNPACK, (int)args.size(), line); ctx->emit(OP_BUILD_TUPLE_UNPACK, (uint16_t)args.size(), line);
if(!kwargs.empty()){ if(!kwargs.empty()){
for(auto& item: kwargs){ for(auto& item: kwargs){
@ -508,13 +507,13 @@ namespace pkpy{
// vectorcall protocal // vectorcall protocal
for(auto& item: args) item->emit(ctx); for(auto& item: args) item->emit(ctx);
for(auto& item: kwargs){ for(auto& item: kwargs){
int index = StrName(item.first.sv()).index; uint16_t index = StrName(item.first.sv()).index;
ctx->emit(OP_LOAD_INTEGER, index, line); ctx->emit(OP_LOAD_INTEGER, index, line);
item.second->emit(ctx); item.second->emit(ctx);
} }
int KWARGC = (int)kwargs.size(); int KWARGC = kwargs.size();
int ARGC = (int)args.size(); int ARGC = args.size();
ctx->emit(OP_CALL, (KWARGC<<16)|ARGC, line); ctx->emit(OP_CALL, (KWARGC<<8)|ARGC, line);
} }
} }

View File

@ -25,7 +25,7 @@ namespace pkpy{
bool Frame::jump_to_exception_handler(){ bool Frame::jump_to_exception_handler(){
// try to find a parent try block // try to find a parent try block
int block = co->codes[_ip].block; int block = co->iblocks[_ip];
while(block >= 0){ while(block >= 0){
if(co->blocks[block].type == TRY_EXCEPT) break; if(co->blocks[block].type == TRY_EXCEPT) break;
block = co->blocks[block].parent; block = co->blocks[block].parent;
@ -47,8 +47,7 @@ namespace pkpy{
} }
void Frame::jump_abs_break(int target){ void Frame::jump_abs_break(int target){
const Bytecode& prev = co->codes[_ip]; int i = co->iblocks[_ip];
int i = prev.block;
_next_ip = target; _next_ip = target;
if(_next_ip >= co->codes.size()){ if(_next_ip >= co->codes.size()){
while(i>=0) i = _exit_block(i); while(i>=0) i = _exit_block(i);
@ -58,9 +57,9 @@ namespace pkpy{
// _ = 0 // _ = 0
// # if there is no op here, the block check will fail // # if there is no op here, the block check will fail
// while i: --i // while i: --i
const Bytecode& next = co->codes[target]; int next_block = co->iblocks[target];
while(i>=0 && i!=next.block) i = _exit_block(i); while(i>=0 && i!=next_block) i = _exit_block(i);
if(i!=next.block) throw std::runtime_error("invalid jump"); if(i!=next_block) throw std::runtime_error("invalid jump");
} }
} }

View File

@ -513,7 +513,7 @@ PyObject* VM::new_module(Str name, Str package) {
} }
static std::string _opcode_argstr(VM* vm, Bytecode byte, const CodeObject* co){ static std::string _opcode_argstr(VM* vm, Bytecode byte, const CodeObject* co){
std::string argStr = byte.arg == -1 ? "" : std::to_string(byte.arg); std::string argStr = byte.arg == BC_NOARG ? "" : std::to_string(byte.arg);
switch(byte.op){ switch(byte.op){
case OP_LOAD_CONST: case OP_FORMAT_STRING: case OP_IMPORT_PATH: case OP_LOAD_CONST: case OP_FORMAT_STRING: case OP_IMPORT_PATH:
if(vm != nullptr){ if(vm != nullptr){