diff --git a/include/pocketpy/codeobject.h b/include/pocketpy/codeobject.h index d6432b49..23dfd77f 100644 --- a/include/pocketpy/codeobject.h +++ b/include/pocketpy/codeobject.h @@ -63,8 +63,9 @@ struct CodeObject { bool is_generator = false; std::vector codes; - std::vector iblocks; // block index for each bytecode - std::vector lines; // line number for each bytecode + std::vector iblocks; // block index for each bytecode + std::vector lines; // line number for each bytecode + std::vector is_virtual; // whether this bytecode is virtual (not in source code) List consts; std::vector varnames; // local variables NameDictInt varnames_inv; diff --git a/include/pocketpy/expr.h b/include/pocketpy/expr.h index b337289c..78dfa0dd 100644 --- a/include/pocketpy/expr.h +++ b/include/pocketpy/expr.h @@ -60,7 +60,7 @@ struct CodeEmitContext{ CodeBlock* enter_block(CodeBlockType type); void exit_block(); void emit_expr(); // clear the expression stack and generate bytecode - int emit_(Opcode opcode, uint16_t arg, int line); + int emit_(Opcode opcode, uint16_t arg, int line, bool is_virtual=false); void patch_jump(int index); bool add_label(StrName name); int add_varname(StrName name); diff --git a/include/pocketpy/profiler.h b/include/pocketpy/profiler.h index fe2ddb4c..b5f45b0a 100644 --- a/include/pocketpy/profiler.h +++ b/include/pocketpy/profiler.h @@ -17,7 +17,6 @@ struct FrameRecord{ FrameId frame; clock_t prev_time; LineRecord* prev_record; - int prev_line; }; struct LineProfiler{ @@ -28,7 +27,7 @@ struct LineProfiler{ void begin(); void _step(FrameId frame); - void _step_end(FrameId frame); + void _step_end(FrameId frame, int line); void end(); Str stats(); }; diff --git a/src/compiler.cpp b/src/compiler.cpp index 4c97fc9f..bb54f47b 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -32,7 +32,7 @@ namespace pkpy{ // add a `return None` in the end as a guard // previously, we only do this if the last opcode is not a return // however, this is buggy...since there may be a jump to the end (out of bound) even if the last opcode is a return - ctx()->emit_(OP_RETURN_VALUE, 1, BC_KEEPLINE); + ctx()->emit_(OP_RETURN_VALUE, 1, BC_KEEPLINE, true); // find the last valid token int j = i-1; while(tokens[j].type == TK("@eol") || tokens[j].type == TK("@dedent") || tokens[j].type == TK("@eof")) j--; @@ -627,7 +627,7 @@ __EAT_DOTS_END: ctx()->emit_expr(); int patch = ctx()->emit_(OP_POP_JUMP_IF_FALSE, BC_NOARG, prev().line); compile_block_body(); - ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE); + ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE, true); ctx()->patch_jump(patch); ctx()->exit_block(); // optional else clause @@ -647,7 +647,7 @@ __EAT_DOTS_END: bool ok = vars->emit_store(ctx()); if(!ok) SyntaxError(); // this error occurs in `vars` instead of this line, but...nevermind compile_block_body(); - ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE); + ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE, true); ctx()->exit_block(); // optional else clause if (match(TK("else"))) { @@ -805,9 +805,9 @@ __EAT_DOTS_END: ctx()->co->is_generator = true; ctx()->emit_(OP_GET_ITER, BC_NOARG, kw_line); ctx()->enter_block(CodeBlockType::FOR_LOOP); - ctx()->emit_(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE); - ctx()->emit_(OP_YIELD_VALUE, BC_NOARG, BC_KEEPLINE); - ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), BC_KEEPLINE); + ctx()->emit_(OP_FOR_ITER, BC_NOARG, kw_line); + ctx()->emit_(OP_YIELD_VALUE, BC_NOARG, kw_line); + ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), kw_line); ctx()->exit_block(); consume_end_stmt(); break; diff --git a/src/expr.cpp b/src/expr.cpp index 208c78b4..551c6938 100644 --- a/src/expr.cpp +++ b/src/expr.cpp @@ -41,7 +41,7 @@ namespace pkpy{ if(curr_type == CodeBlockType::FOR_LOOP){ // add a no op here to make block check work - emit_(OP_NO_OP, BC_NOARG, BC_KEEPLINE); + emit_(OP_NO_OP, BC_NOARG, BC_KEEPLINE, true); } } @@ -52,13 +52,14 @@ namespace pkpy{ expr->emit_(this); } - int CodeEmitContext::emit_(Opcode opcode, uint16_t arg, int line) { + int CodeEmitContext::emit_(Opcode opcode, uint16_t arg, int line, bool is_virtual) { co->codes.push_back(Bytecode{(uint8_t)opcode, arg}); co->iblocks.push_back(curr_block_i); co->lines.push_back(line); + co->is_virtual.push_back(is_virtual); int i = co->codes.size() - 1; - if(line==BC_KEEPLINE){ - if(i>=1) co->lines[i] = co->lines[i-1]; + if(line == BC_KEEPLINE){ + if(i >= 1) co->lines[i] = co->lines[i-1]; else co->lines[i] = 1; } return i; diff --git a/src/profiler.cpp b/src/profiler.cpp index 7f17098c..3aec2201 100644 --- a/src/profiler.cpp +++ b/src/profiler.cpp @@ -19,13 +19,16 @@ void LineProfiler::begin(){ } void LineProfiler::_step(FrameId frame){ + bool is_virtual = frame->co->is_virtual[frame->_ip]; + if(is_virtual) return; + std::string_view filename = frame->co->src->filename.sv(); int line = frame->co->lines[frame->_ip]; if(frames.empty()){ - frames.push({frame, clock(), nullptr, -1}); + frames.push({frame, clock(), nullptr}); }else{ - _step_end(frame); + _step_end(frame, line); } std::vector& file_records = records[filename]; @@ -41,13 +44,13 @@ void LineProfiler::_step(FrameId frame){ frames.top().prev_record = &file_records.at(line); } -void LineProfiler::_step_end(FrameId frame){ +void LineProfiler::_step_end(FrameId frame, int line){ clock_t now = clock(); FrameRecord& top_frame_record = frames.top(); LineRecord* prev_record = top_frame_record.prev_record; - if(prev_record->line != top_frame_record.prev_line){ - top_frame_record.prev_line = prev_record->line; + // current line is about to change + if(prev_record->line != line){ clock_t delta = now - top_frame_record.prev_time; top_frame_record.prev_time = now; prev_record->hits++; @@ -58,7 +61,7 @@ void LineProfiler::_step_end(FrameId frame){ PK_ASSERT(id_delta >= -1 && id_delta <= 1); if(id_delta == 1){ - frames.push({frame, now, nullptr, -1}); + frames.push({frame, now, nullptr}); }else{ if(id_delta == -1) frames.pop(); } @@ -69,13 +72,10 @@ void LineProfiler::end(){ FrameRecord& top_frame_record = frames.top(); LineRecord* prev_record = top_frame_record.prev_record; - if(prev_record->line != top_frame_record.prev_line){ - top_frame_record.prev_line = prev_record->line; - clock_t delta = now - top_frame_record.prev_time; - top_frame_record.prev_time = now; - prev_record->hits++; - prev_record->time += delta; - } + clock_t delta = now - top_frame_record.prev_time; + top_frame_record.prev_time = now; + prev_record->hits++; + prev_record->time += delta; frames.pop(); PK_ASSERT(frames.empty()); diff --git a/src/vm.cpp b/src/vm.cpp index d6ed685d..d7b86b2d 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -632,7 +632,9 @@ Str VM::disassemble(CodeObject_ co){ pointer = " "; } ss << pad(line, 8) << pointer << pad(std::to_string(i), 3); - ss << " " << pad(OP_NAMES[byte.op], 25) << " "; + std::string bc_name(OP_NAMES[byte.op]); + if(co->is_virtual[i]) bc_name += '*'; + ss << " " << pad(bc_name, 25) << " "; // ss << pad(byte.arg == -1 ? "" : std::to_string(byte.arg), 5); std::string argStr = _opcode_argstr(this, byte, co.get()); ss << argStr; diff --git a/tests/84_line_profiler.py b/tests/84_line_profiler.py index 0d7edd71..b9b7ea00 100644 --- a/tests/84_line_profiler.py +++ b/tests/84_line_profiler.py @@ -3,7 +3,8 @@ from line_profiler import LineProfiler def f2(x): a = 0 for i in range(x): - a += i + if i % 5 == 0: + a += i return a def f1(x):