Commits

Mark Shannon committed 13984a2 Draft

Refactor tailcall logic and inform optimisers if locals are dead

  • Participants
  • Parent commits c1f74c7

Comments (0)

Files changed (7)

Include/optimiser.h

     HotPyContext*(*from_object_dict_fast)(HotPyOptimiser*, int, PyDictKeysObject*, PyObject*);
     void (*set_in_object_dict_fast)(HotPyOptimiser*, int, PyDictKeysObject*, PyObject*);
     HotPyContext*(*current_context)(HotPyOptimiser*);
+    void (*dead_locals)(HotPyOptimiser*);
     int instruction_count;
     HotPyContext *start_context;
 };

Python/recording_interpreter.c

     return recording_eval_top_level(tstate, next_instr, optimiser, &fs);
 }
 
+static int is_tailcall(PyFrameObject *f, PyCodeObject *co,
+                       unsigned char* next_instr)
+{
+    if (_HotPy_Options.tailcall == 0 ||
+        (co->co_flags & CO_GENERATOR) != 0 ||
+        (co->co_flags & CO_INVISIBLE) == 0 ||
+        f->f_iblock != 0)
+        return 0;
+    if (next_instr[0] == RETURN_VALUE)
+        return 1;
+    if (next_instr[0] == VALUE_NOP &&
+        next_instr[1] == RETURN_VALUE)
+        return 1;
+    return 0;
+}
+
+static int is_framedead(PyCodeObject *co, unsigned char* next_instr)
+{
+    if (_HotPy_Options.tailcall == 0 ||
+        (co->co_flags & CO_GENERATOR) != 0 ||
+        (co->co_flags & CO_INVISIBLE) == 0)
+        return 0;
+    if (next_instr[0] == RETURN_VALUE)
+        return 1;
+    if (next_instr[0] == VALUE_NOP &&
+        next_instr[1] == RETURN_VALUE)
+        return 1;
+    if (next_instr[0] == POP_TOP &&
+        next_instr[1] == LOAD_CONST &&
+        next_instr[4] == RETURN_VALUE)
+        return 1;
+    return 0;
+}
+
 static HotPyReturnValue
 recording_eval(PyThreadState *tstate, unsigned char* next_instr,
                HotPyOptimiser* optimiser, FrameStack *frame_stack)
                         x = NULL;
                         break;
                     }
-                    if (next_instr[0] == RETURN_VALUE &&
-                        _HotPy_Options.tailcall &&
-                        f->f_iblock == 0 &&
-                        (co->co_flags & CO_GENERATOR) == 0 &&
-                        (co->co_flags & CO_INVISIBLE) != 0) {
+                    if (is_tailcall(f, co, next_instr)) {
                         /* Tail call here */
 
                         /* pop frame */
                         }
                         return ret;
                     }
+                    else if (is_framedead(co, next_instr)) {
+                        optimiser->dead_locals(optimiser);
+                    }
                     else {
                         FrameStack_PushFrame(frame_stack, callee_frame->f_code,
                                              next_instr-first_instr);
                 }
             }
             else {
-                if (next_instr[0] == RETURN_VALUE &&
-                    _HotPy_Options.tailcall &&
-                    f->f_iblock == 0 &&
-                    (co->co_flags & CO_GENERATOR) == 0 &&
-                    (co->co_flags & CO_INVISIBLE) != 0) {
+                if (is_tailcall(f, co, next_instr)) {
                     /* Tail call here */
                     /* pop frame */
                     Py_XINCREF(f->f_back);
                         return (HotPyReturnValue){ x, WHY_RETURN };
                     }
                 }
+                else if (is_framedead(co, next_instr)) {
+                    optimiser->dead_locals(optimiser);
+                }
                 else {
                     /* Optimisers can (in theory) insert up to 3 exits for a call */
                     RECORD_INST_WITH_EXIT(prepare_call1, f->f_lasti);
             break;
 
         TARGET(DESCRIPTOR_GET)
+            if (is_framedead(co, next_instr)) {
+                optimiser->dead_locals(optimiser);
+            }
             w = POP();
             v = POP();
             x = Py_TYPE(v)->tp_descr_get(v, w, (PyObject *)Py_TYPE(w));
             break;
 
         TARGET(DESCRIPTOR_SET)
+            if (is_framedead(co, next_instr)) {
+                optimiser->dead_locals(optimiser);
+            }
             u = POP();
             w = POP();
             v = POP();

Python/trace_D_O_C.c

 }
 
 static void
+dead_locals(HotPyOptimiser *x)
+{
+    HotPy_DOC *opt = (HotPy_DOC *)x;
+    int i;
+    for (i = 0; i < opt->top_frame->lcount; i++) {
+        Py_CLEAR(opt->top_frame->locals[i]);
+    }
+}
+
+static void
 bind_callable(HotPyOptimiser *x, PyTypeObject *type)
 {
     HotPy_DOC *opt = (HotPy_DOC *)x;
     x->opt_base.prepare_call1 = prepare_call1;
     x->opt_base.prepare_call2 = prepare_call2;
     x->opt_base.current_context = current_context;
+    x->opt_base.dead_locals = dead_locals;
     x->opt_base.from_object_dict_fast = from_object_dict_fast;
     x->opt_base.set_in_object_dict_fast = set_in_object_dict_fast;
     x->top_frame = NULL;

Python/trace_logger.c

 }
 
 static void
+dead_locals(HotPyOptimiser *x)
+{
+    fprintf(OUT, "#Dead locals\n", INDENT);
+    x->next->dead_locals(x->next);
+}
+
+static void
 unrolling(HotPyOptimiser *x)
 {
     fprintf(OUT, "%sLoop Header <--\n", INDENT);
     x->opt_base.set_in_object_dict_fast = set_in_object_dict_fast;
     x->opt_base.opcode_with_arg = opcode_with_arg;
     x->opt_base.current_context = current_context;
+    x->opt_base.dead_locals = dead_locals;
     x->out = out;
     return (HotPyOptimiser *)x;
 }

Python/trace_recorder_register.c

     return HotPyContext_New();
 }
 
+static void
+dead_locals(HotPyOptimiser *x)
+{
+    /* No code to emit */
+}
+
 static HotPyContext *
 require_type(HotPyOptimiser *x, PyTypeObject* type)
 {

Python/trace_recorder_stack.c

     return context;
 }
 
+static void
+dead_locals(HotPyOptimiser *x)
+{
+    /* No code to emit */
+}
+
 static HotPyContext *
 require_type(HotPyOptimiser *x, PyTypeObject* type)
 {
     x->opt_base.opcode = opcode;
     x->opt_base.opcode_with_arg = opcode_with_arg;
     x->opt_base.current_context = current_context;
+    x->opt_base.dead_locals = dead_locals;
     x->opt_base.call_object = call_object;
     x->opt_base.call_cfunction = call_cfunction;
     x->opt_base.call_cmethod = call_cmethod;

Python/trace_specialiser.c

     return context;
 }
 
+static void
+dead_locals(HotPyOptimiser *x)
+{
+    x->next->dead_locals(x->next);
+}
+
 /* These are already specialised, so we don't expect to see these.
  * Just pass them on */
 static void
     x->opt_base.prepare_call2 = prepare_call2;
     x->opt_base.opcode_with_arg = opcode_with_arg;
     x->opt_base.current_context = current_context;
+    x->opt_base.dead_locals = dead_locals;
     x->opt_base.set_in_object_dict_fast = set_in_object_dict_fast;
     x->opt_base.from_object_dict_fast = from_object_dict_fast;
     x->top_frame = NULL;