Commits

Mark Shannon committed 58a741c Draft

Enable DOC to defer frames even when function is not a constant

  • Participants
  • Parent commits 22c18ce

Comments (0)

Files changed (13)

HotPy/trace_makeopcodetargets.py

               'stop_iteration_or_reraise',  'sub_type', 'kw_dict_merge',
               'make_generator', 'enter_frame', 'new_function', 'new_closure',
               'trace_end', 'get_global_and_builtins', 'save_exc_state', 'swap_exc_state',
-              'restore_clear_exc_state', 'setup_except_handler', 'clear_frame' ]
+              'restore_clear_exc_state', 'setup_except_handler', 'clear_frame',
+              'get_globals_from_function' ]
 
 with_arg_ops = [ 'binary_op', 'ensure_type_tos', 'ensure_type_nos', 'jump_back',
                  'fast_frame', 'set_lasti', 'trace_load_constant', 'pick',

HotPy/trace_opcode_header_gen.py

               'stop_iteration_or_reraise',  'sub_type', 'kw_dict_merge',
               'make_generator', 'enter_frame', 'new_function', 'new_closure',
               'trace_end', 'get_global_and_builtins', 'save_exc_state', 'swap_exc_state',
-              'restore_clear_exc_state', 'setup_except_handler', 'clear_frame' ]
+              'restore_clear_exc_state', 'setup_except_handler', 'clear_frame',
+              'get_globals_from_function' ]
 
 with_arg_ops = [ 'binary_op', 'ensure_type_tos', 'ensure_type_nos', 'jump_back',
                  'fast_frame', 'set_lasti', 'trace_load_constant', 'pick',

Include/optimiser.h

     void (*set_lasti)(HotPyOptimiser*, int);
     void (*fast_frame)(HotPyOptimiser*, int, PyCodeObject*);
     HotPyContext*(*make_frame)(HotPyOptimiser*, PyObject *func);
-    void (*make_frame_const)(HotPyOptimiser*, PyObject *func);
+    HotPyContext*(*make_frame_const)(HotPyOptimiser*, PyObject *func);
     void (*enter_frame)(HotPyOptimiser*, PyCodeObject*);
     void (*gen_enter)(HotPyOptimiser*, PyCodeObject*);
     void (*leave_frame)(HotPyOptimiser*);

Include/trace_opcode.h

 #define RESTORE_CLEAR_EXC_STATE 27
 #define SETUP_EXCEPT_HANDLER 28
 #define CLEAR_FRAME 29
-/* Opcode 30 unused */
+#define GET_GLOBALS_FROM_FUNCTION 30
 /* Opcode 31 unused */
 /* Opcode 32 unused */
 /* Non-trace opcode GET_CLASS_ATTR = 33 */
 "BINARY_TRUE_DIVIDE or RESTORE_CLEAR_EXC_STATE",
 "INPLACE_FLOOR_DIVIDE or SETUP_EXCEPT_HANDLER",
 "INPLACE_TRUE_DIVIDE or CLEAR_FRAME",
-"30",
+"GET_GLOBALS_FROM_FUNCTION",
 "31",
 "32",
 "GET_CLASS_ATTR",

Python/register_interpreter.c

             SET_REGISTER(r1, builtins);
             DISPATCH();
 
+        TARGET(GET_GLOBALS_FROM_FUNCTION)
+            FORMAT_roo;
+            PyObject *globals;
+            PyObject *builtins;
+            PyObject *func = registers[r0];
+            if (!PyFunction_Check(func)) {
+                Py_DECREF(func);
+                PyErr_SetString(PyExc_SystemError,
+                    "function required");
+                goto on_error;
+            }
+            globals = ((PyFunctionObject *)func)->func_globals;
+            builtins = PyDict_GetItemString(globals, "__builtins__");
+            if (PyModule_Check(builtins))
+                builtins = PyObject_GetAttrString(builtins, "__dict__");
+            assert(builtins);
+            Py_INCREF(globals);
+            Py_INCREF(builtins);
+            SET_REGISTER(r1, globals);
+            SET_REGISTER(r2, builtins);
+            DISPATCH();
+
         TARGET(UNARY_NOT)
         /* This differs from ceval's NOT. It is guaranteed that its input
          * will be a boolean or an integer */

Python/register_opcode_targets.h

 &&TARGET_RESTORE_CLEAR_EXC_STATE,
 &&TARGET_SETUP_EXCEPT_HANDLER,
 &&TARGET_CLEAR_FRAME,
-&&_unknown_opcode,
+&&TARGET_GET_GLOBALS_FROM_FUNCTION,
 &&_unknown_opcode,
 &&_unknown_opcode,
 &&TARGET_GET_CLASS_ATTR,

Python/trace_D_O_C.c

     PyObject *code;
     PyObject *globals;   /* global symbol table */
     PyObject *builtins;   /* buitlins symbol table */
-
+    PyObject *function; /* (Deferred) function */
     int lcount;
     int allocated;
     PyObject **locals;
         }
         f->blockcount = 0;
         f->lasti = -1;
+        f->function = NULL;
+        f->globals = NULL;
+        f->builtins = NULL;
     }
     return f;
 }
     write_int(buffer, f->lcount);
     write_int(buffer, f->stack_limit);
     write_value(buffer, f->code);
-    write_value(buffer, f->globals);
-    write_value(buffer, f->builtins);
+    if (f->function) {
+        write_int(buffer, 1);
+        write_object(buffer, f->function);
+    }
+    else {
+        write_int(buffer, 0);
+        write_value(buffer, f->globals);
+        write_value(buffer, f->builtins);
+    }
     for (i = 0; i < f->lcount; i++)
         write_object(buffer, f->locals[i]);
     write_int(buffer, f->stack_pointer - f->stack_base);
     f->code = read_value(code, consts);
     Py_INCREF(f->code);
     assert(allocated || f->code != Py_None);
-    f->globals = read_value(code, consts);
-    Py_INCREF(f->globals);
-    f->builtins = read_value(code, consts);
-    Py_INCREF(f->builtins);
-    assert(allocated || f->globals != Py_None);
+    if (read_int(code)) {
+        f->function = read_object(code, consts, opt);
+    }
+    else {
+        f->globals = read_value(code, consts);
+        Py_INCREF(f->globals);
+        f->builtins = read_value(code, consts);
+        Py_INCREF(f->builtins);
+        assert(allocated || f->globals != Py_None);
+    }
     for (i = 0; i < lcount; i++) {
         f->locals[i] = read_object(code, consts, opt);
     }
     lcount = read_int(code);
     read_int(code); /* scount */
     read_int(code); /* Code object */
-    read_int(code); /* Globals */
-    read_int(code); /* Builtins */
+    if (read_int(code)) {
+        object_registers(code, registers); /* Function */
+    }
+    else {
+        read_int(code); /* Globals */
+        read_int(code); /* Builtins */
+    }
     for (i = 0; i < lcount; i++) {
         object_registers(code, registers);
     }
               PyObject *consts, PyFrameObject **next_frame)
 {
     PyFrameObject *f;
+    PyObject *func = NULL;
     PyObject *globals, *builtins;
     PyCodeObject *code_obj;
     Py_ssize_t i, allocated, lcount, stack, blocks;
     lcount = read_int(code);
     read_int(code); /* stack-limit */
     code_obj = (PyCodeObject *)read_value(code, consts);
-    globals = read_value(code, consts);
-    builtins = read_value(code, consts);
+    if (read_int(code)) {
+        func = create_object(tstate, code, consts);
+        globals = ((PyFunctionObject *)func)->func_globals;
+        builtins = PyDict_GetItemString(globals, "__builtins__");
+        if (PyModule_Check(builtins))
+            builtins = PyObject_GetAttrString(builtins, "__dict__");
+        assert(builtins);
+    }
+    else {
+        globals = read_value(code, consts);
+        builtins = read_value(code, consts);
+    }
     if (allocated) {
         f = *next_frame;
         Py_INCREF(f);
         f = HotPyFrame_New(tstate, code_obj, globals, builtins, NULL);
         tstate->recursion_depth++;
     }
+    Py_XDECREF(func);
     for (i = 0; i < lcount; i++) {
         PyObject *obj = create_object(tstate, code, consts);
         if (obj) {
     else {
         PyObject *builtins;
         PyObject *globals = opt->top_frame->globals;
-        assert(globals);
-        /* XXX - Need to properly define when builtins are fixed */
-        builtins = PyDict_GetItemString(globals, "__builtins__");
-        if (PyModule_Check(builtins))
-            builtins = PyObject_GetAttrString(builtins, "__dict__");
-        assert(builtins);
-        PUSH(get_constant(opt, globals));
-        PUSH(get_constant(opt, builtins));
+        if (globals) {
+            /* XXX - Need to properly define when builtins are fixed */
+            builtins = PyDict_GetItemString(globals, "__builtins__");
+            if (PyModule_Check(builtins))
+                builtins = PyObject_GetAttrString(builtins, "__dict__");
+            assert(builtins);
+            PUSH(get_constant(opt, globals));
+            PUSH(get_constant(opt, builtins));
+        }
+        else {
+            assert(opt->top_frame->function);
+            Py_INCREF(opt->top_frame->function);
+            materialise(x, opt->top_frame->function);
+            x->next->opcode(x->next, GET_GLOBALS_FROM_FUNCTION);
+            PUSH(real_value());
+            PUSH(real_value());
+        }
     }
 }
 
 }
 
 static HotPyContext *
-make_frame(HotPyOptimiser *x, PyObject *function)
+make_frame(HotPyOptimiser *x, PyObject *func)
 {
+    /* Create a new frame */
+    int i;
     HotPy_DOC *opt = (HotPy_DOC *)x;
     HotPyContext *context;
+    DeferredFrame *f = NULL;
+    PyCodeObject *co;
+    PyObject *w = POP();
+    PyObject *u = POP();
+    PyObject *v = POP();
+    PyObject *kws = as_map(u);
+    PyObject *args = as_tuple(v);
+    assert(PyFunction_Check(func));
+    co = (PyCodeObject *)PyFunction_GET_CODE(func);
+    if (kws == NULL || args == NULL || Py_SIZE(args) != co->co_argcount)
+        goto fail;
+    if (co->co_cellvars && Py_SIZE(co->co_cellvars)) {
+        COMMENT("Cell variables not yet supported");
+        goto fail;
+    }
+    if (co->co_freevars && Py_SIZE(co->co_freevars)) {
+        COMMENT("Free variables not yet supported");
+        goto fail;
+    }
+    f = new_frame(co->co_nlocals, co->co_stacksize + HOTPY_STACK_OVERHEAD);
+    if (f) {
+        for (i = 0; i < co->co_argcount; i++) {
+            PyObject *tmp = PyTuple_GET_ITEM(args, i);
+            Py_INCREF(tmp);
+            f->locals[i] = tmp;
+        }
+        f->function = w;
+        Py_INCREF(co);
+        f->code = (PyObject *)co;
+        f->locals_base_register = next_base_register(opt);
+        PUSH(new_deferred(DK_FRAME, 0, (PyObject *)f));
+        SHOW_STACK();
+        Py_DECREF(u);
+        Py_DECREF(v);
+        return NULL;
+    }
+fail:
+    PUSH(v);
+    PUSH(u);
+    PUSH(w);
     defer_stack_below(x, 3);
     materialise_stack_top(x, 3);
-    context = x->next->make_frame(x->next, function);
+    context = x->next->make_frame(x->next, func);
     PUSH(real_value());
     if (context)
         save_state_to_context(x, context);
     return f;
 }
 
-static void
+static HotPyContext*
 make_frame_const(HotPyOptimiser *x, PyObject *func)
 {
     /* Create a new frame */
     HotPy_DOC *opt = (HotPy_DOC *)x;
-    CHECK_OK;
+    HotPyContext *context;
     DeferredFrame *f = NULL;
     PyFrameObject *frame;
     PyCodeObject *co;
         Py_DECREF(u);
         Py_DECREF(v);
         Py_DECREF(w);
-        return;
+        return NULL;
     }
 fail:
     PUSH(v);
     PUSH(u);
     PUSH(w);
-    make_frame(x, func);
+    defer_stack_below(x, 3);
+    materialise_stack_top(x, 3);
+    context = x->next->make_frame(x->next, func);
+    PUSH(real_value());
+    if (context)
+        save_state_to_context(x, context);
+    return context;
 }
 
 static void

Python/trace_interpreter.c

             PUSH(builtins);
             DISPATCH();
 
+        TARGET(GET_GLOBALS_FROM_FUNCTION)
+            PyObject *globals;
+            PyObject *builtins;
+            PyObject *func = POP();
+            if (!PyFunction_Check(func)) {
+                Py_DECREF(func);
+                PyErr_SetString(PyExc_SystemError,
+                    "function required");
+                goto on_error;
+            }
+            globals = ((PyFunctionObject *)func)->func_globals;
+            builtins = PyDict_GetItemString(globals, "__builtins__");
+            if (PyModule_Check(builtins))
+                builtins = PyObject_GetAttrString(builtins, "__dict__");
+            assert(builtins);
+            Py_INCREF(globals);
+            Py_INCREF(builtins);
+            PUSH(globals);
+            PUSH(builtins);
+            DISPATCH();
+
         TARGET(UNARY_NOT)
         /* This differs from ceval's NOT. It is guaranteed that its input
          * will be a boolean or an integer */

Python/trace_logger.c

     return x->next->make_frame(x->next, function);
 }
 
-static void
+static HotPyContext*
 make_frame_const(HotPyOptimiser *x, PyObject *function)
 {
     fprintf(OUT, "%smake_frame_const\n", INDENT);
-    x->next->make_frame_const(x->next, function);
+    return x->next->make_frame_const(x->next, function);
 }
 
 static void

Python/trace_opcode_targets.h

 &&TARGET_RESTORE_CLEAR_EXC_STATE,
 &&TARGET_SETUP_EXCEPT_HANDLER,
 &&TARGET_CLEAR_FRAME,
-&&_unknown_opcode,
+&&TARGET_GET_GLOBALS_FROM_FUNCTION,
 &&_unknown_opcode,
 &&_unknown_opcode,
 &&TARGET_GET_CLASS_ATTR,

Python/trace_recorder_register.c

     int used = 0;
     HotPyTraceRecorder *opt = (HotPyTraceRecorder *)x;
     for (i = 0; i < opt->stack_items; i++) {
-        if (opt->used_regs[i] == 0)
+        if (opt->used_regs[opt->stack[i]] == 0)
             return 0;
     }
     for (i = 0; i < HOTPY_STACK_REGISTERS; i++)
         case TWO_MOVE:
             print_rroo(out, &code);
             break;
-        case COPY_MOVE:
+        case COPY_MOVE: case GET_GLOBALS_FROM_FUNCTION:
             print_roo(out, &code);
             break;
         case FAST_LOAD_GLOBAL:
         case STOP_ITERATION_OR_RERAISE: case RERAISE:
         case SAVE_EXC_STATE: case SWAP_EXC_STATE: case RESTORE_CLEAR_EXC_STATE:
         case DELETE_FAST: case DELETE_DEREF:
-        case GET_GLOBAL_AND_BUILTINS:
+        case GET_GLOBAL_AND_BUILTINS: case GET_GLOBALS_FROM_FUNCTION:
         case UNARY_NOT: case AS_TUPLE: case MAKE_GENERATOR: case GEN_STARTED:
         case HAS_DICT: case GET_TYPE: case OVERRIDES_GENERIC_GETATTRIBUTE:
         case OVERRIDES_GENERIC_SETATTR: case ALLOCATE: case SIZE:
         case STOP_ITERATION_OR_RERAISE: case RERAISE:
         case SAVE_EXC_STATE: case SWAP_EXC_STATE: case RESTORE_CLEAR_EXC_STATE:
         case DELETE_FAST: case DELETE_DEREF:
-        case GET_GLOBAL_AND_BUILTINS:
+        case GET_GLOBAL_AND_BUILTINS: case GET_GLOBALS_FROM_FUNCTION:
         case UNARY_NOT: case AS_TUPLE: case MAKE_GENERATOR: case GEN_STARTED:
         case HAS_DICT: case GET_TYPE: case OVERRIDES_GENERIC_GETATTRIBUTE:
         case OVERRIDES_GENERIC_SETATTR: case ALLOCATE: case SIZE:
     case NEW_CLOSURE: case OVERRIDES: case KW_DICT_MERGE:
         write_rrro(x, op);
         return;
+    case GET_GLOBALS_FROM_FUNCTION:
+        write_roo(x, op);
+        return;
     default:
         fprintf(stderr, "Trace Recorder (register) not implemented: %s\n", _HotPy_Instruction_Names[op]);
         exit(-1);
     return NULL;
 }
 
-static void
+static HotPyContext*
 make_frame_const(HotPyOptimiser *x, PyObject *func)
 {
     write_rrro(x, MAKE_FRAME);
     CONSISTENT(x);
+    return NULL;
 }
 
 static void
     case DESCRIPTOR_GET: case GET_CLASS_ATTR: case HAS_CLASS_ATTR:
     case SUB_TYPE: case NEW_FUNCTION:
         return defuses_for_rro(instr, defs, uses);
+    case GET_GLOBALS_FROM_FUNCTION:
+        return defuses_for_roo(instr, defs, uses);
     case VALUE_FROM_OBJECT_DICT_OR_EXIT_CONST:
         return defuses_for_rokkkE(instr, defs, uses);
     case POP_STACK_TO_REGISTER:
         return relabel_uses_rrro(instr, relabel_uses_table);
     case GET_GLOBAL_AND_BUILTINS:
         return relabel_uses_oo(instr, relabel_uses_table);
+    case GET_GLOBALS_FROM_FUNCTION:
+        return relabel_uses_roo(instr, relabel_uses_table);
     case UNARY_NOT: case AS_TUPLE: case MAKE_GENERATOR: case GEN_STARTED:
     case HAS_DICT: case GET_TYPE: case OVERRIDES_GENERIC_GETATTRIBUTE:
     case OVERRIDES_GENERIC_SETATTR: case ALLOCATE: case SIZE: case MOVE:
         return relabel_defs_rrro(instr, relabel_defs_table);
     case GET_GLOBAL_AND_BUILTINS:
         return relabel_defs_oo(instr, relabel_defs_table);
+    case GET_GLOBALS_FROM_FUNCTION:
+        return relabel_defs_roo(instr, relabel_defs_table);
     case UNARY_NOT: case AS_TUPLE: case MAKE_GENERATOR: case GEN_STARTED:
     case HAS_DICT: case GET_TYPE: case OVERRIDES_GENERIC_GETATTRIBUTE:
     case OVERRIDES_GENERIC_SETATTR: case ALLOCATE: case SIZE: case MOVE:
         return get_exit_context_rrro(instr, exits);
     case GET_GLOBAL_AND_BUILTINS:
         return get_exit_context_oo(instr, exits);
+    case GET_GLOBALS_FROM_FUNCTION:
+        return get_exit_context_roo(instr, exits);
     case UNARY_NOT: case AS_TUPLE: case MAKE_GENERATOR: case GEN_STARTED:
     case HAS_DICT: case GET_TYPE: case OVERRIDES_GENERIC_GETATTRIBUTE:
     case OVERRIDES_GENERIC_SETATTR: case ALLOCATE: case SIZE: case MOVE:

Python/trace_recorder_stack.c

     return NULL;
 }
 
-static void
+static HotPyContext *
 make_frame_const(HotPyOptimiser *x, PyObject *func)
 {
     opcode(x, MAKE_FRAME);
+    return NULL;
 }
 
 static void

Python/trace_specialiser.c

                 Py_DECREF(frame);
             }
             PyErr_Restore(ty, ex, tb);
-            x->next->make_frame_const(x->next, function);
+            context = x->next->make_frame_const(x->next, function);
         }
         else
-            x->next->make_frame(x->next, function);
+            context = x->next->make_frame(x->next, function);
     }
     else
-        x->next->make_frame(x->next, function);
+        context = x->next->make_frame(x->next, function);
     if (f)
         PUSH(frame_info(f));
     else
     Py_DECREF(args_info);
     Py_XDECREF(args);
     Py_DECREF(func_info);
+    if (context)
+        save_state_to_context(x, context);
     return context;
 }
 
-static void
+static HotPyContext *
 make_frame_const(HotPyOptimiser *x, PyObject *function)
 {
     HotPySpecialiser *opt = (HotPySpecialiser *)x;
     PyObject *func_info = POP();
     fix_value(func_info, function);
     PUSH(func_info);
-    context = make_frame(x, function);
-    assert (context == NULL);
+    return make_frame(x, function);
 }
 
 /* Consumes a reference to f */