Mark Shannon avatar Mark Shannon committed af124c6 Draft

Added value to context to represent block-stack. In order to correctly find a trace it is necessary to differentiate between code run in a finally block that has arrived via an exception being thrown or by falling through. Small changes to DOC.

Comments (0)

Files changed (5)

Include/optimiser.h

 typedef struct _hotpy_instruction_pointer {
     PyCodeObject *code;
     unsigned int offset;
+    unsigned int stack;
 } HotPyIP;
 
 /* Warning: this evaluates its parameter twice */
     HotPyExitEntry entries[HOTPY_POLYMORPHIC_LIMIT];
 } HotPyPolyExitObject;
 
-HotPyContext *HotPyContext_TopLevel(PyCodeObject *code, unsigned int offset);
-
 void HotPyContext_Dump(char *buf, HotPyContext *ctx);
 
 HotPyContext *HotPyContext_New(void);
 
 HotPyOptimiser *HotPyTraceLogger_New(HotPyOptimiser *, FILE* out);
 
-HotPyTraceObject *HotPyTraceManager_FindTrace(PyThreadState *, unsigned char *where);
+HotPyTraceObject *HotPyTraceManager_FindTrace(PyThreadState *, unsigned char *, int);
 
 HotPyTraceObject *HotPyTraceManager_TraceForContext(PyThreadState *, HotPyContext *);
 

Python/recording_interpreter.c

     Py_XDECREF(__top_frame); \
 } while (0)
 
+static int
+block_code(PyFrameObject *f)
+{
+    int i, code = 0;
+    for (i = 0; i < f->f_iblock; i++) {
+        code *= 3;
+        code += 1 + f->f_blockstack[i].b_type == EXCEPT_HANDLER;
+    }
+    return code;
+}
 
 PyObject *
 PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
         if (retval.why == WHY_NOT && py_surrogates_loaded &&
             tstate->trace_recording == 0) {
             /* FindTrace returns a new reference which Execute steals */
-            hot_trace = HotPyTraceManager_FindTrace(tstate, retval.value);
+            int stack = tstate->real_frame->f_stacktop -
+                        tstate->real_frame->f_valuestack;
+            hot_trace = HotPyTraceManager_FindTrace(tstate, retval.value, stack);
             if (hot_trace) {
                 assert(HotPyIP_ToAddr(hot_trace->trace_context->cf_instrs[0]) == retval.value);
                 retval = HotPyTrace_Execute(tstate, hot_trace);
     optimiser->instruction_count++; \
     optimiser->pick(optimiser, n); \
     _ctx = optimiser->require_type(optimiser, t); \
-     if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, _ctx); \
+     if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, block_code(f), _ctx); \
 } while (0)
 
 #define REQUIRE_VALUE(n, v) do { \
     optimiser->instruction_count++; \
     optimiser->pick(optimiser, n); \
     _ctx = optimiser->require_value(optimiser, v); \
-     if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, _ctx); \
+     if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, block_code(f), _ctx); \
 } while (0)
 
 #define REQUIRE_FUNCTION(n, func) do { \
     HotPyContext * _ctx; \
     optimiser->pick(optimiser, n); \
     _ctx = optimiser->require_type(optimiser, &PyFunction_Type); \
-     if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, _ctx); \
+     if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, block_code(f), _ctx); \
     optimiser->instruction_count++; \
     optimiser->pick(optimiser, n); \
     _ctx = optimiser->require_code(optimiser, func); \
-    if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, _ctx); \
+    if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, block_code(f), _ctx); \
 } while (0)
 
 #define REQUIRE_GENERATOR(n, gen) do { \
     optimiser->instruction_count++; \
     optimiser->pick(optimiser, n); \
     _ctx = optimiser->require_type(optimiser, &PyGen_Type); \
-     if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, _ctx); \
+     if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, block_code(f), _ctx); \
     optimiser->pick(optimiser, n); \
     _ctx = optimiser->require_code(optimiser, gen); \
-    if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, _ctx); \
+    if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, block_code(f), _ctx); \
 } while (0)
 
 #define REQUIRE_LASTI(n, lasti) do { \
     optimiser->instruction_count++; \
     optimiser->pick(optimiser, n); \
     _ctx = optimiser->require_lasti(optimiser, lasti); \
-     if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, _ctx); \
+     if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, block_code(f), _ctx); \
 } while (0)
 
 #define RECORD_MAKE_FRAME(arg) do { \
     HotPyContext * _ctx; \
     optimiser->instruction_count++; \
     _ctx = optimiser->make_frame(optimiser, arg); \
-     if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, _ctx); \
+     if (_ctx) FrameStack_SaveToContext(frame_stack, f->f_lasti, block_code(f), _ctx); \
 } while (0)
 
 #define REQUIRE_BOOLEAN_OR_INT(offset) do { \
     optimiser->instruction_count++; \
     optimiser->opcode(optimiser, DUP_TOP); \
     _ctx = optimiser->require_boolean_or_int(optimiser); \
-     if (_ctx) FrameStack_SaveToContext(frame_stack, offset, _ctx); \
+     if (_ctx) FrameStack_SaveToContext(frame_stack, offset, block_code(f), _ctx); \
 } while (0)
 
 #define RECORD_WITH_EXIT(op, offset) do { \
     HotPyContext * _ctx; \
     optimiser->instruction_count++; \
     _ctx = optimiser->opcode_with_exit(optimiser, op); \
-    if (_ctx) FrameStack_SaveToContext(frame_stack, offset, _ctx); \
+    if (_ctx) FrameStack_SaveToContext(frame_stack, offset, block_code(f), _ctx); \
 } while (0)
 
 #define RECORD_INST_WITH_EXIT(name, offset) do { \
     HotPyContext * _ctx; \
     optimiser->instruction_count++; \
     _ctx = optimiser->name(optimiser); \
-    if (_ctx) FrameStack_SaveToContext(frame_stack, offset, _ctx); \
+    if (_ctx) FrameStack_SaveToContext(frame_stack, offset, block_code(f), _ctx); \
 } while (0)
 
 #define RECORD_PUSH_CONSTANT(obj) do { \
 }
 
 static void
-FrameStack_SaveToContext(FrameStack *stack, unsigned int offset, HotPyContext *ctx)
+FrameStack_SaveToContext(FrameStack *stack, unsigned int offset,
+                         int block_stack, HotPyContext *ctx)
 {
     int i;
     assert(ctx->cf_instrs[0].code == NULL);
     assert(stack->count);
     ctx->cf_instrs[0].offset = offset;
+    ctx->cf_instrs[0].stack = block_stack;
     Py_INCREF(stack->insts[stack->count-1].code);
     ctx->cf_instrs[0].code = stack->insts[stack->count-1].code;
     for (i = 1; i < stack->count; i++) {
             w = GETITEM(names, oparg);
             RECORD_INST(get_globals);
             RECORD_OPCODE(POP_TOP);
-            REQUIRE_VALUE(1, f->f_globals);
             RECORD_INST_WITH_NAME(store_to_globals, w);
             v = POP();
             err = PyDict_SetItem(f->f_globals, w, v);
             RECORD_SET_LASTI(f->f_lasti);
             w = GETITEM(names, oparg);
             RECORD_INST(get_globals);
-            REQUIRE_VALUE(1, f->f_builtins);
-            REQUIRE_VALUE(2, f->f_globals);
             RECORD_INST_WITH_NAME(load_from_globals, w);
             if (PyDict_CheckExact(f->f_globals)
                 && PyDict_CheckExact(f->f_builtins)) {

Python/trace_D_O_C.c

 
 #define LOCAL(n) (opt->top_frame->locals[n])
 
+#ifdef VERBOSE
+#define COMMENT(txt) x->next->comment(x->next, txt)
+#else
+#define COMMENT(txt) ((void)0)
+#endif
+
 static void free_register(HotPyOptimiser *x, int n)
 {
     HotPy_DOC* opt = (HotPy_DOC*)x;
     /* Whoops run out of registers! */
     /* Presume an infinite number of registers,
      * and expect next stage to clean up? */
-    assert("Run out of registers" && 0);
-    abort();
+    COMMENT("Ran out of registers");
+    return -1;
 }
 
 static void
 {
     HotPy_DOC* opt = (HotPy_DOC*)x;
     int r = next_register_index(x, HOTPY_REGISTER_COUNT * 3/8, HOTPY_REGISTER_COUNT * 5/8);
-    set_constant_register(opt, constant, r);
-    x->next->push_constant(x->next, VALUE(constant));
-    x->next->store_register(x->next, r);
+    if (r >= 0) {
+        set_constant_register(opt, constant, r);
+        x->next->push_constant(x->next, VALUE(constant));
+        x->next->store_register(x->next, r);
+    }
 }
 
 static PyObject *next_register(HotPyOptimiser *x)
     int r;
     HotPy_DOC* opt = (HotPy_DOC*)x;
     r = next_register_index(x, HOTPY_REGISTER_COUNT * 5/8, HOTPY_REGISTER_COUNT);
+    if (r < 0) {
+        assert("Run out of registers" && 0);
+        abort();
+    }
     return new_register(opt, r);
 }
 
     out += sprintf(buf, "Stack: depth=%d", d);
     if (d > 0) {
         out += sprintf(out, " [ ");
+        if (d > 4) {
+            out += sprintf(out, "... ");
+            d = 4;
+        }
         for (i = d; i > 0; i--) {
             out = show_item(f->stack_pointer[-i], out);
         }
-        if (d > 3)
-            out += sprintf(out, "... ");
         out += sprintf(out, " ] ");
     }
     x->next->comment(x->next, buf);
 }
 
-#define COMMENT(txt) x->next->comment(x->next, txt)
 #else
 #define SHOW_STACK() ((void)0)
 #define SHOW_FRAME() ((void)0)
-#define COMMENT(txt) ((void)0)
 #endif
 
 #define CHECK_OK if (opt->err_occurred) return
 }
 
 static void
-materialise_frame(HotPyOptimiser *x, DeferredFrame *f, int depth);
+materialise_stack_frame(HotPyOptimiser *x, DeferredFrame *f, int depth);
+
+static void
+materialise_frame(HotPyOptimiser *x, DeferredFrame *f);
 
 /* Consumes a reference to obj */
 static void
     case DK_CONSTANT:
         if (INDEX(obj) == 0)
             assign_constant_to_register(x, obj);
-        assert(INDEX(obj) != 0);
-        x->next->load_register(x->next, INDEX(obj));
+        if (INDEX(obj) == 0)
+            x->next->push_constant(x->next, VALUE(obj));
+        else
+            x->next->load_register(x->next, INDEX(obj));
         break;
     case DK_TUPLE:
         for (i = 0; i < Py_SIZE(VALUE(obj)); i++) {
             break;
         }
     case DK_FRAME:
-        assert(0 && "Should not be materialising frames");
+        assert(Py_TYPE(VALUE(obj)) == &DeferredFrame_Type);
+        materialise_frame(x, (DeferredFrame *)VALUE(obj));
         break;
     case DK_BOUND:
         Py_INCREF(PyTuple_GET_ITEM(VALUE(obj), 0));
     write_int(buffer, f->blockcount);
     for (i = 0; i < f->blockcount; i++) {
         write_int(buffer, f->blockstack[i].b_type);
-        write_int(buffer, f->blockstack[i].b_handler);
+        /* b_handler can be -1 for SETUP_EXCEPT_HANDLER */
+        write_int(buffer, (unsigned short)f->blockstack[i].b_handler);
         write_int(buffer, f->blockstack[i].b_level);
     }
     write_int(buffer, f->lasti);
     f->blockcount = read_int(code);
     for (i = 0; i < f->blockcount; i++) {
          f->blockstack[i].b_type = read_int(code);
-         f->blockstack[i].b_handler = read_int(code);
+         f->blockstack[i].b_handler = (signed short)read_int(code);
          f->blockstack[i].b_level = read_int(code);
     }
     f->lasti = read_int(code);
 #endif
 
 static void
-materialise_frame(HotPyOptimiser *x, DeferredFrame *f, int real_depth)
+materialise_stack_frame(HotPyOptimiser *x, DeferredFrame *f, int real_depth)
 {
     ByteBuffer buffer;
     PyObject *bytes;
 }
 
 static void
+materialise_frame(HotPyOptimiser *x, DeferredFrame *f)
+{
+    int i, lcount = f->lcount;
+    while (f->locals[lcount-1] == NULL || f->locals[lcount-1] == REAL_VALUE)
+        lcount--;
+    assert(f->code != Py_None);
+    assert(f->globals != Py_None);
+    assert(f->builtins != Py_None);
+    assert(f->stack_base == f->stack_pointer);
+    for (i = 0; i < lcount; i++) {
+        Py_INCREF(f->locals[i]);
+        materialise(x, f->locals[i]);
+    }
+    x->next->push_constant(x->next, f->code);
+    x->next->push_constant(x->next, f->globals);
+    x->next->push_constant(x->next, f->builtins);
+    x->next->fast_frame(x->next, lcount, (PyCodeObject *)f->code);
+}
+
+static void
 create_state_from_frozen(HotPyOptimiser *x, HotPyContext *context)
 {
     int i;
 {
     int i;
     PyObject *x;
-    buf += sprintf(buf, "%sLocals: ", f->allocated ? "Allocated " : "Deferred ");
+    buf += sprintf(buf, f->allocated ? "Allocated" : "Deferred");
+    buf += sprintf(buf, ". Locals: ");
     for (i = 0; i < f->lcount; i++) {
         x = f->locals[i];
         buf += sprintf(buf, "%s %d, ", kind_names[KIND(x)], INDEX(x));
         x = f->stack_base[i];
         buf += sprintf(buf, "%s %d, ", kind_names[KIND(x)], INDEX(x));
     }
+    if (f->blockcount) {
+        int i;
+        PyTryBlock *b;
+        buf += sprintf(buf, "Blocks: ");
+        for (i = 0; i < f->blockcount; i++) {
+            b = &f->blockstack[i];
+            buf += sprintf(buf, "%s %d %d, ",
+                           _HotPy_Instruction_Names[b->b_type],
+                           b->b_level, b->b_handler);
+        }
+    }
     buf[0] = '\n';
     buf[1] = 0;
     return buf + 1;
     blocks = read_int(code);
     for (i = 0; i < blocks; i++) {
         int type = read_int(code);
-        int handler = read_int(code);
+        int handler = (signed short)read_int(code);
         int level = read_int(code);
         PyFrame_BlockSetup(f, type, handler, level);
     }
     int reg_index;
     SHOW_FRAME();
     if (index >= opt->top_frame->lcount) {
+        assert(opt->top_frame->allocated);
         v = POP();
         materialise(x, v);
         x->next->store_fast(x->next, index);
         assert(opt->top_frame->locals[index] && opt->top_frame->locals[index] != REAL_VALUE);
         Py_INCREF(opt->top_frame->locals[index]);
         materialise(x, opt->top_frame->locals[index]);
-        x->next->store_fast(x->next, i);
+        x->next->store_fast(x->next, index);
     }
     SHOW_STACK();
     SHOW_FRAME();
         int real_depth = 0;
         for (i = 1; i < HOTPY_CONTEXT_DEPTH; i++)
             real_depth += opt->frame_stack[i]->allocated;
-        materialise_frame(x, opt->frame_stack[0], real_depth);
+        materialise_stack_frame(x, opt->frame_stack[0], real_depth);
         Py_CLEAR(opt->frame_stack[0]);
         for (i = 1; i < HOTPY_CONTEXT_DEPTH; i++)
             opt->frame_stack[i-1] = opt->frame_stack[i];
                 pop_block(x, caller, POP_BLOCK);
         }
         return;
+    case POP_EXCEPT:
+        /* This should be quite rare, so don't bother optimising it */
+        materialise_stack_top(x, STACK_DEPTH());
+        x->next->opcode(x->next, op);
+        return;
     case SUB_TYPE: case DESCRIPTOR_GET: case NEW_FUNCTION:
     case GET_CLASS_ATTR: case HAS_CLASS_ATTR:
         materialise_stack_top(x, 2);
 {
     HotPy_DOC *opt = (HotPy_DOC *)x;
     DeferredFrame *f = opt->top_frame;
-    PyTryBlock *b = &f->blockstack[f->blockcount];
+    PyTryBlock *b;
+    if (op == SETUP_EXCEPT_HANDLER) {
+        /* Don't optimise this for now */
+        /* XXX -- This could be optimised but it is a bit tricky */
+        int i;
+        for (i = 0; i < f->blockcount; i++) {
+             b = &f->blockstack[i];
+             if (b->b_type == FOR_ITER)
+                 b->b_type = SETUP_FOR_ITER;
+             x->next->setup_block(x->next, b->b_type, b->b_handler, b->b_level);
+        }
+        f->blockcount = 0;
+        materialise_stack_top(x, STACK_DEPTH());
+        x->next->setup_block(x->next, op, handler, level);
+        return;
+    }
+    b = &f->blockstack[f->blockcount];
     if (op == SETUP_FOR_ITER)
         b->b_type = FOR_ITER;
     else
         SHOW_STACK();
         return;
     case BUILD_MAP:
-        PUSH(new_map());
+        /* Do not defer {}s for now
+        PUSH(new_map()); */
+        x->next->opcode_with_arg(x->next, op, oparg);
+        PUSH(real_value());
         return;
     case GEN_CHECK_OR_EXIT:
         v = POP();
             context = x->next->opcode_with_exit(x->next, op);
             if (context)
                 save_state_to_context(x, context);
-        } else
+        }
+        else {
             Py_DECREF(v);
-        Py_DECREF(u);
+            Py_DECREF(u);
+        }
         break;
     case CALL_CFUNC_0:
         defer_stack_below(x, 1);

Python/trace_interpreter.c

     Py_DECREF(trace); \
     trace = the_trace; \
     HOTPY_TICK(); \
+    LOG("Trace transfer"); \
     goto fast_next_opcode; \
 } while (0)
 
             if (tstate->execution_context == NULL) {
                 if (x)
                     PUSH_TO_REAL(x);
+                else
+                    assert(PyErr_Occurred());
                 goto materialised;
             }
             if (x == NULL) {

Python/trace_manager.c

     for (i = 0; i < HOTPY_CONTEXT_DEPTH && cf->cf_instrs[i].code; i++) {
         h ^= PyObject_Hash((PyObject *)cf->cf_instrs[i].code);
         h ^= (Py_uhash_t)cf->cf_instrs[i].offset;
+        h ^= (Py_uhash_t)(cf->cf_instrs[i].stack << 12);
     }
     return (Py_hash_t)h;
 }
     assert(Py_TYPE(b) == &Context_Type);
     for (i = 0; i < HOTPY_CONTEXT_DEPTH; i++)
         if (a->cf_instrs[i].code != b->cf_instrs[i].code ||
-            a->cf_instrs[i].offset != b->cf_instrs[i].offset)
+            a->cf_instrs[i].offset != b->cf_instrs[i].offset ||
+            a->cf_instrs[i].stack != b->cf_instrs[i].stack)
             return 0;
     if (PyObject_RichCompareBool(a->cf_type_info, b->cf_type_info, Py_EQ) == 0)
         return 0;
         for (i = 0; i < HOTPY_CONTEXT_DEPTH; i++) {
             top->cf_instrs[i].code = NULL;
             top->cf_instrs[i].offset = 0;
+            top->cf_instrs[i].stack = 0;
         }
         Py_INCREF(Py_None);
         top->cf_type_info = Py_None;
 }
 
 HotPyContext *
-HotPyContext_TopLevel(PyCodeObject *code, unsigned int offset)
+HotPyContext_TopLevel(PyCodeObject *code, unsigned int offset, unsigned int stack)
 {
     int i;
     HotPyContext *top = PyObject_New(HotPyContext, &Context_Type);
         Py_INCREF(code);
         top->cf_instrs[0].code = code;
         top->cf_instrs[0].offset = offset;
+        top->cf_instrs[0].stack = stack;
         for (i = 1; i < HOTPY_CONTEXT_DEPTH; i++) {
             top->cf_instrs[i].code = NULL;
             top->cf_instrs[i].offset = 0;
+            top->cf_instrs[i].stack = 0;
         }
         Py_INCREF(Py_None);
         top->cf_type_info = Py_None;
     x = _Py_HashPointer(op->ip.code);
     if (x == -1)
         return -1;
-    x = x ^ op->ip.offset;
+    x ^= (op->ip.offset + (op->ip.stack << 12));
     if (x == -1)
         x = -2;
     return x;
         Py_RETURN_NOTIMPLEMENTED;
     a = (IPKey *)v;
     b = (IPKey *)w;
-    res = a->ip.offset == b->ip.offset && a->ip.code == b->ip.code;
+    res = a->ip.offset == b->ip.offset && a->ip.code == b->ip.code &&
+          a->ip.stack == b->ip.stack;
     if (op == Py_NE)
         res = 1-res;
     if (res) {
 }
 
 static PyObject *
-make_ip_key(PyCodeObject *code, unsigned int offset)
+make_ip_key(PyCodeObject *code, unsigned int offset, int stack)
 {
     IPKey *key = PyObject_New(IPKey, &IPKey_Type);
     if (key == NULL)
     Py_INCREF(code);
     key->ip.code = code;
     key->ip.offset = offset;
+    key->ip.stack = stack;
     return (PyObject *)key;
 }
 
 /* Returns a new reference
  * XXX - To do. In event of an error. Call unraisable and clean up */
 HotPyTraceObject *
-HotPyTraceManager_FindTrace(PyThreadState *tstate, unsigned char *where)
+HotPyTraceManager_FindTrace(PyThreadState *tstate, unsigned char *where, int stack)
 {
     PyObject *value;
     HotPyTraceObject *trace;
     PyObject *trace_cache = tstate->trace_cache;
     PyCodeObject *code = tstate->real_frame->f_code;
     unsigned char *first_instr;
-    Py_ssize_t offset;
+    unsigned int offset;
     if (_HotPy_Options.tracing == 0)
         return NULL;
     first_instr = (unsigned char *)(PyBytes_AS_STRING(code->co_code));
     offset = where - first_instr;
     assert(offset >= 0 && offset < PyBytes_Size(code->co_code));
-    PyObject *key = make_ip_key(code, offset);
+    assert(stack >= 0);
+    PyObject *key = make_ip_key(code, offset, stack);
     if (key == NULL)
         return NULL;
     assert(where);
     }
     else if (Py_TYPE(value) == &Counter_Type) {
         if (++((CounterObject *)value)->count >= _HotPy_Thresholds.warm) {
-            HotPyContext *context = HotPyContext_TopLevel(code, offset);
+            HotPyContext *context = HotPyContext_TopLevel(code, offset, stack);
             trace = new_trace_object(context);
             if (trace != NULL)
                 PyDict_SetItem(trace_cache, key, (PyObject *)trace);
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.