blueloveTH пре 2 година
родитељ
комит
1558e5b40f
8 измењених фајлова са 34 додато и 30 уклоњено
  1. 3 2
      include/pocketpy/codeobject.h
  2. 1 1
      include/pocketpy/expr.h
  3. 1 2
      include/pocketpy/profiler.h
  4. 6 6
      src/compiler.cpp
  5. 5 4
      src/expr.cpp
  6. 13 13
      src/profiler.cpp
  7. 3 1
      src/vm.cpp
  8. 2 1
      tests/84_line_profiler.py

+ 3 - 2
include/pocketpy/codeobject.h

@@ -63,8 +63,9 @@ struct CodeObject {
     bool is_generator = false;
 
     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> iblocks;       // block index for each bytecode
+    std::vector<int> lines;         // line number for each bytecode
+    std::vector<char> is_virtual;   // whether this bytecode is virtual (not in source code)
     List consts;
     std::vector<StrName> varnames;      // local variables
     NameDictInt varnames_inv;

+ 1 - 1
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);

+ 1 - 2
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();
 };

+ 6 - 6
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;

+ 5 - 4
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;

+ 13 - 13
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<LineRecord>& 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());

+ 3 - 1
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;

+ 2 - 1
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):