FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

GitHub Viewer

#include "Python.h" #include "opcode.h" #include "pycore_interp.h" #include "pycore_opcode_metadata.h" #include "pycore_opcode_utils.h" #include "pycore_optimizer.h" #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_uops.h" #include "cpython/optimizer.h" #include #include #include #define MAX_EXECUTORS_SIZE 256 static bool has_space_for_executor(PyCodeObject *code, _Py_CODEUNIT *instr) { if (instr->op.code == ENTER_EXECUTOR) { return true; } if (code->co_executors == NULL) { return true; } return code->co_executors->size < MAX_EXECUTORS_SIZE; } static int32_t get_index_for_executor(PyCodeObject *code, _Py_CODEUNIT *instr) { if (instr->op.code == ENTER_EXECUTOR) { return instr->op.arg; } _PyExecutorArray *old = code->co_executors; int size = 0; int capacity = 0; if (old != NULL) { size = old->size; capacity = old->capacity; assert(size < MAX_EXECUTORS_SIZE); } assert(size capacity = new_capacity; new->size = size; code->co_executors = new; } assert(size < code->co_executors->capacity); return size; } static void insert_executor(PyCodeObject *code, _Py_CODEUNIT *instr, int index, _PyExecutorObject *executor) { Py_INCREF(executor); if (instr->op.code == ENTER_EXECUTOR) { assert(index == instr->op.arg); _PyExecutorObject *old = code->co_executors->executors[index]; executor->vm_data.opcode = old->vm_data.opcode; executor->vm_data.oparg = old->vm_data.oparg; old->vm_data.opcode = 0; code->co_executors->executors[index] = executor; Py_DECREF(old); } else { assert(code->co_executors->size == index); assert(code->co_executors->capacity > index); executor->vm_data.opcode = instr->op.code; executor->vm_data.oparg = instr->op.arg; code->co_executors->executors[index] = executor; assert(index < MAX_EXECUTORS_SIZE); instr->op.code = ENTER_EXECUTOR; instr->op.arg = index; code->co_executors->size++; } return; } int PyUnstable_Replace_Executor(PyCodeObject *code, _Py_CODEUNIT *instr, _PyExecutorObject *new) { if (instr->op.code != ENTER_EXECUTOR) { PyErr_Format(PyExc_ValueError, "No executor to replace"); return -1; } int index = instr->op.arg; assert(index >= 0); insert_executor(code, instr, index, new); return 0; } static int error_optimize( _PyOptimizerObject* self, PyCodeObject *code, _Py_CODEUNIT *instr, _PyExecutorObject **exec, int Py_UNUSED(stack_entries)) { PyErr_Format(PyExc_SystemError, "Should never call error_optimize"); return -1; } static PyTypeObject DefaultOptimizer_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "noop_optimizer", .tp_basicsize = sizeof(_PyOptimizerObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, }; _PyOptimizerObject _PyOptimizer_Default = { PyObject_HEAD_INIT(&DefaultOptimizer_Type) .optimize = error_optimize, .resume_threshold = UINT16_MAX, .backedge_threshold = UINT16_MAX, }; _PyOptimizerObject * PyUnstable_GetOptimizer(void) { PyInterpreterState *interp = _PyInterpreterState_GET(); if (interp->optimizer == &_PyOptimizer_Default) { return NULL; } assert(interp->optimizer_backedge_threshold == interp->optimizer->backedge_threshold); assert(interp->optimizer_resume_threshold == interp->optimizer->resume_threshold); Py_INCREF(interp->optimizer); return interp->optimizer; } void PyUnstable_SetOptimizer(_PyOptimizerObject *optimizer) { PyInterpreterState *interp = _PyInterpreterState_GET(); if (optimizer == NULL) { optimizer = &_PyOptimizer_Default; } _PyOptimizerObject *old = interp->optimizer; Py_INCREF(optimizer); interp->optimizer = optimizer; interp->optimizer_backedge_threshold = optimizer->backedge_threshold; interp->optimizer_resume_threshold = optimizer->resume_threshold; Py_DECREF(old); } _PyInterpreterFrame * _PyOptimizer_BackEdge(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer) { assert(src->op.code == JUMP_BACKWARD); PyCodeObject *code = (PyCodeObject *)frame->f_executable; assert(PyCode_Check(code)); PyInterpreterState *interp = _PyInterpreterState_GET(); if (!has_space_for_executor(code, src)) { goto jump_to_destination; } _PyOptimizerObject *opt = interp->optimizer; _PyExecutorObject *executor = NULL; int err = opt->optimize(opt, code, dest, &executor, (int)(stack_pointer - _PyFrame_Stackbase(frame))); if (err uops[i].offset + 1; switch (expansion->uops[i].size) { case OPARG_FULL: if (extras && OPCODE_HAS_JUMP(opcode)) { if (opcode == JUMP_BACKWARD_NO_INTERRUPT) { oparg -= extras; } else { assert(opcode != JUMP_BACKWARD); oparg += extras; } } break; case OPARG_CACHE_1: operand = read_u16(&instr[offset].cache); break; case OPARG_CACHE_2: operand = read_u32(&instr[offset].cache); break; case OPARG_CACHE_4: operand = read_u64(&instr[offset].cache); break; case OPARG_TOP: // First half of super-instr oparg = orig_oparg >> 4; break; case OPARG_BOTTOM: // Second half of super-instr oparg = orig_oparg & 0xF; break; case OPARG_SAVE_IP: // op==SAVE_IP; oparg=next instr oparg = INSTR_IP(instr + offset, code); break; default: fprintf(stderr, "opcode=%d, oparg=%d; nuops=%d, i=%d; size=%d, offset=%d\n", opcode, oparg, nuops, i, expansion->uops[i].size, expansion->uops[i].offset); Py_FatalError("garbled expansion"); } ADD_TO_TRACE(expansion->uops[i].uop, oparg, operand); if (expansion->uops[i].uop == _POP_FRAME) { TRACE_STACK_POP(); DPRINTF(2, "Returning to %s (%s:%d) at byte offset %d\n", PyUnicode_AsUTF8(code->co_qualname), PyUnicode_AsUTF8(code->co_filename), code->co_firstlineno, 2 * INSTR_IP(instr, code)); goto top; } if (expansion->uops[i].uop == _PUSH_FRAME) { assert(i + 1 == nuops); int func_version_offset = offsetof(_PyCallCache, func_version)/sizeof(_Py_CODEUNIT) // Add one to account for the actual opcode/oparg pair: + 1; uint32_t func_version = read_u32(&instr[func_version_offset].cache); PyFunctionObject *func = _PyFunction_LookupByVersion(func_version); DPRINTF(3, "Function object: %p\n", func); if (func != NULL) { PyCodeObject *new_code = (PyCodeObject *)PyFunction_GET_CODE(func); if (new_code == code) { // Recursive call, bail (we could be here forever). DPRINTF(2, "Bailing on recursive call to %s (%s:%d)\n", PyUnicode_AsUTF8(new_code->co_qualname), PyUnicode_AsUTF8(new_code->co_filename), new_code->co_firstlineno); ADD_TO_TRACE(SAVE_IP, 0, 0); goto done; } if (new_code->co_version != func_version) { // func.__code__ was updated. // Perhaps it may happen again, so don't bother tracing. // TODO: Reason about this -- is it better to bail or not? DPRINTF(2, "Bailing because co_version != func_version\n"); ADD_TO_TRACE(SAVE_IP, 0, 0); goto done; } // Increment IP to the return address instr += _PyOpcode_Caches[_PyOpcode_Deopt[opcode]] + 1; TRACE_STACK_PUSH(); code = new_code; instr = _PyCode_CODE(code); DPRINTF(2, "Continuing in %s (%s:%d) at byte offset %d\n", PyUnicode_AsUTF8(code->co_qualname), PyUnicode_AsUTF8(code->co_filename), code->co_firstlineno, 2 * INSTR_IP(instr, code)); goto top; } ADD_TO_TRACE(SAVE_IP, 0, 0); goto done; } } break; } DPRINTF(2, "Unsupported opcode %s\n", uop_name(opcode)); goto done; // Break out of loop } // End default } // End switch (opcode) instr++; // Add cache size for opcode instr += _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]; } // End for (;;) done: while (trace_stack_depth > 0) { TRACE_STACK_POP(); } assert(code == initial_code); // Skip short traces like SAVE_IP, LOAD_FAST, SAVE_IP, EXIT_TRACE if (trace_length > 3) { ADD_TO_TRACE(EXIT_TRACE, 0, 0); DPRINTF(1, "Created a trace for %s (%s:%d) at byte offset %d -- length %d+%d\n", PyUnicode_AsUTF8(code->co_qualname), PyUnicode_AsUTF8(code->co_filename), code->co_firstlineno, 2 * INSTR_IP(initial_instr, code), trace_length, buffer_size - max_length); if (max_length < buffer_size) { // There are stubs if (trace_length < max_length) { // There's a gap before the stubs // Move the stubs back to be immediately after the main trace // (which ends at trace_length) DPRINTF(2, "Moving %d stub uops back by %d\n", buffer_size - max_length, max_length - trace_length); trace_length = move_stubs(trace, trace_length, max_length, buffer_size); } else { assert(trace_length == max_length); // There's no gap trace_length = buffer_size; } } return trace_length; } else { DPRINTF(4, "No trace for %s (%s:%d) at byte offset %d\n", PyUnicode_AsUTF8(code->co_qualname), PyUnicode_AsUTF8(code->co_filename), code->co_firstlineno, 2 * INSTR_IP(initial_instr, code)); } return 0; #undef RESERVE #undef RESERVE_RAW #undef INSTR_IP #undef ADD_TO_TRACE #undef DPRINTF } static int remove_unneeded_uops(_PyUOpInstruction *trace, int trace_length) { // Stage 1: Replace unneeded SAVE_IP uops with NOP. // Note that we don't enter stubs, those SAVE_IPs are needed. int last_save_ip = -1; int last_instr = 0; bool need_ip = true; for (int pc = 0; pc < trace_length; pc++) { int opcode = trace[pc].opcode; if (opcode == SAVE_CURRENT_IP) { // Special case: never remove preceding SAVE_IP last_save_ip = -1; } else if (opcode == SAVE_IP) { if (!need_ip && last_save_ip >= 0) { trace[last_save_ip].opcode = NOP; } need_ip = false; last_save_ip = pc; } else if (opcode == JUMP_TO_TOP || opcode == EXIT_TRACE) { last_instr = pc + 1; break; } else { // If opcode has ERROR or DEOPT, set need_up to true if (_PyOpcode_opcode_metadata[opcode].flags & (HAS_ERROR_FLAG | HAS_DEOPT_FLAG)) { need_ip = true; } } } // Stage 2: Squash NOP opcodes (pre-existing or set above). int dest = 0; for (int pc = 0; pc < last_instr; pc++) { int opcode = trace[pc].opcode; if (opcode != NOP) { if (pc != dest) { trace[dest] = trace[pc]; } dest++; } } // Stage 3: Move the stubs back. if (dest < last_instr) { int new_trace_length = move_stubs(trace, dest, last_instr, trace_length); #ifdef Py_DEBUG char *uop_debug = Py_GETENV("PYTHONUOPSDEBUG"); int lltrace = 0; if (uop_debug != NULL && *uop_debug >= '0') { lltrace = *uop_debug - '0'; // TODO: Parse an int and all that } if (lltrace >= 2) { printf("Optimized trace (length %d+%d = %d, saved %d):\n", dest, trace_length - last_instr, new_trace_length, trace_length - new_trace_length); for (int pc = 0; pc < new_trace_length; pc++) { printf("%4d: (%s, %d, %" PRIu64 ")\n", pc, uop_name(trace[pc].opcode), (trace[pc].oparg), (uint64_t)(trace[pc].operand)); } } #endif trace_length = new_trace_length; } return trace_length; } static int uop_optimize( _PyOptimizerObject *self, PyCodeObject *code, _Py_CODEUNIT *instr, _PyExecutorObject **exec_ptr, int curr_stackentries) { _PyUOpInstruction trace[_Py_UOP_MAX_TRACE_LENGTH]; int trace_length = translate_bytecode_to_trace(code, instr, trace, _Py_UOP_MAX_TRACE_LENGTH); if (trace_length '0') { trace_length = _Py_uop_analyze_and_optimize(code, trace, trace_length, curr_stackentries); } trace_length = remove_unneeded_uops(trace, trace_length); _PyUOpExecutorObject *executor = PyObject_NewVar(_PyUOpExecutorObject, &UOpExecutor_Type, trace_length); if (executor == NULL) { return -1; } executor->base.execute = _PyUopExecute; memcpy(executor->trace, trace, trace_length * sizeof(_PyUOpInstruction)); *exec_ptr = (_PyExecutorObject *)executor; return 1; } static void uop_opt_dealloc(PyObject *self) { PyObject_Free(self); } static PyTypeObject UOpOptimizer_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "uop_optimizer", .tp_basicsize = sizeof(_PyOptimizerObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, .tp_dealloc = uop_opt_dealloc, }; PyObject * PyUnstable_Optimizer_NewUOpOptimizer(void) { _PyOptimizerObject *opt = PyObject_New(_PyOptimizerObject, &UOpOptimizer_Type); if (opt == NULL) { return NULL; } opt->optimize = uop_optimize; opt->resume_threshold = UINT16_MAX; // Need at least 3 iterations to settle specializations. // A few lower bits of the counter are reserved for other flags. opt->backedge_threshold = 3

Back | FazBrowse Home | New Git URL