-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
GH-113853: Guarantee forward progress in executors #113854
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e7133f1
cd586a6
279b02b
1501bca
4960065
e8300e2
f9e1464
9bfd9e9
93efad4
902fd4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Guarantee that all executors make progress. This then guarantees that tier 2 | ||
execution always makes progress. |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -162,24 +162,22 @@ PyUnstable_SetOptimizer(_PyOptimizerObject *optimizer) | |
} | ||
|
||
int | ||
_PyOptimizer_BackEdge(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer) | ||
_PyOptimizer_Optimize(_PyInterpreterFrame *frame, _Py_CODEUNIT *start, 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)) { | ||
if (!has_space_for_executor(code, start)) { | ||
return 0; | ||
} | ||
_PyOptimizerObject *opt = interp->optimizer; | ||
_PyExecutorObject *executor = NULL; | ||
/* Start optimizing at the destination to guarantee forward progress */ | ||
int err = opt->optimize(opt, code, dest, &executor, (int)(stack_pointer - _PyFrame_Stackbase(frame))); | ||
int err = opt->optimize(opt, code, start, &executor, (int)(stack_pointer - _PyFrame_Stackbase(frame))); | ||
if (err <= 0) { | ||
assert(executor == NULL); | ||
return err; | ||
} | ||
int index = get_index_for_executor(code, src); | ||
int index = get_index_for_executor(code, start); | ||
if (index < 0) { | ||
/* Out of memory. Don't raise and assume that the | ||
* error will show up elsewhere. | ||
|
@@ -190,7 +188,7 @@ _PyOptimizer_BackEdge(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNI | |
Py_DECREF(executor); | ||
return 0; | ||
} | ||
insert_executor(code, src, index, executor); | ||
insert_executor(code, start, index, executor); | ||
Py_DECREF(executor); | ||
return 1; | ||
} | ||
|
@@ -316,38 +314,6 @@ BRANCH_TO_GUARD[4][2] = { | |
#define CONFIDENCE_RANGE 1000 | ||
#define CONFIDENCE_CUTOFF 333 | ||
|
||
/* Returns 1 on success, | ||
* 0 if it failed to produce a worthwhile trace, | ||
* and -1 on an error. | ||
*/ | ||
static int | ||
translate_bytecode_to_trace( | ||
PyCodeObject *code, | ||
_Py_CODEUNIT *instr, | ||
_PyUOpInstruction *trace, | ||
int buffer_size, | ||
_PyBloomFilter *dependencies) | ||
{ | ||
PyCodeObject *initial_code = code; | ||
_Py_BloomFilter_Add(dependencies, initial_code); | ||
_Py_CODEUNIT *initial_instr = instr; | ||
int trace_length = 0; | ||
int max_length = buffer_size; | ||
struct { | ||
PyCodeObject *code; | ||
_Py_CODEUNIT *instr; | ||
} trace_stack[TRACE_STACK_SIZE]; | ||
int trace_stack_depth = 0; | ||
int confidence = CONFIDENCE_RANGE; // Adjusted by branch instructions | ||
|
||
#ifdef Py_DEBUG | ||
char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); | ||
int lltrace = 0; | ||
if (python_lltrace != NULL && *python_lltrace >= '0') { | ||
lltrace = *python_lltrace - '0'; // TODO: Parse an int and all that | ||
} | ||
#endif | ||
|
||
#ifdef Py_DEBUG | ||
#define DPRINTF(level, ...) \ | ||
if (lltrace >= (level)) { printf(__VA_ARGS__); } | ||
|
@@ -403,13 +369,47 @@ translate_bytecode_to_trace( | |
code = trace_stack[trace_stack_depth].code; \ | ||
instr = trace_stack[trace_stack_depth].instr; | ||
|
||
/* Returns 1 on success, | ||
markshannon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* 0 if it failed to produce a worthwhile trace, | ||
* and -1 on an error. | ||
*/ | ||
static int | ||
translate_bytecode_to_trace( | ||
PyCodeObject *code, | ||
_Py_CODEUNIT *instr, | ||
_PyUOpInstruction *trace, | ||
int buffer_size, | ||
_PyBloomFilter *dependencies) | ||
{ | ||
bool progress_needed = true; | ||
PyCodeObject *initial_code = code; | ||
_Py_BloomFilter_Add(dependencies, initial_code); | ||
_Py_CODEUNIT *initial_instr = instr; | ||
int trace_length = 0; | ||
int max_length = buffer_size; | ||
struct { | ||
PyCodeObject *code; | ||
_Py_CODEUNIT *instr; | ||
} trace_stack[TRACE_STACK_SIZE]; | ||
int trace_stack_depth = 0; | ||
int confidence = CONFIDENCE_RANGE; // Adjusted by branch instructions | ||
|
||
#ifdef Py_DEBUG | ||
char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); | ||
int lltrace = 0; | ||
if (python_lltrace != NULL && *python_lltrace >= '0') { | ||
lltrace = *python_lltrace - '0'; // TODO: Parse an int and all that | ||
} | ||
#endif | ||
|
||
DPRINTF(4, | ||
"Optimizing %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)); | ||
uint32_t target = 0; | ||
|
||
top: // Jump here after _PUSH_FRAME or likely branches | ||
for (;;) { | ||
target = INSTR_IP(instr, code); | ||
|
@@ -421,6 +421,15 @@ translate_bytecode_to_trace( | |
uint32_t oparg = instr->op.arg; | ||
uint32_t extended = 0; | ||
|
||
if (opcode == ENTER_EXECUTOR) { | ||
assert(oparg < 256); | ||
_PyExecutorObject *executor = | ||
markshannon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
(_PyExecutorObject *)code->co_executors->executors[oparg]; | ||
opcode = executor->vm_data.opcode; | ||
DPRINTF(2, " * ENTER_EXECUTOR -> %s\n", _PyOpcode_OpName[opcode]); | ||
oparg = executor->vm_data.oparg; | ||
} | ||
|
||
if (opcode == EXTENDED_ARG) { | ||
instr++; | ||
extended = 1; | ||
|
@@ -431,13 +440,23 @@ translate_bytecode_to_trace( | |
goto done; | ||
} | ||
} | ||
|
||
if (opcode == ENTER_EXECUTOR) { | ||
_PyExecutorObject *executor = | ||
(_PyExecutorObject *)code->co_executors->executors[oparg&255]; | ||
opcode = executor->vm_data.opcode; | ||
DPRINTF(2, " * ENTER_EXECUTOR -> %s\n", _PyOpcode_OpName[opcode]); | ||
oparg = (oparg & 0xffffff00) | executor->vm_data.oparg; | ||
assert(opcode != ENTER_EXECUTOR && opcode != EXTENDED_ARG); | ||
|
||
/* Special case the first instruction, | ||
* so that we can guarantee forward progress */ | ||
if (progress_needed) { | ||
progress_needed = false; | ||
if (opcode == JUMP_BACKWARD || opcode == JUMP_BACKWARD_NO_INTERRUPT) { | ||
instr += 1 + _PyOpcode_Caches[opcode] - (int32_t)oparg; | ||
initial_instr = instr; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting. This will affect some of the debug output, notably the "Created a trace for ..." message below at line 676 will mention a different byte offset than the "Optimizing ..." above at line 405. I honestly like what you set it to here better, and debug level 4 is excessively verbose, so I don't care, but some bit of me will miss the correspondence. :-) |
||
continue; | ||
} | ||
else { | ||
if (OPCODE_HAS_DEOPT(opcode)) { | ||
opcode = _PyOpcode_Deopt[opcode]; | ||
} | ||
assert(!OPCODE_HAS_DEOPT(opcode)); | ||
} | ||
} | ||
|
||
switch (opcode) { | ||
|
@@ -480,7 +499,9 @@ translate_bytecode_to_trace( | |
case JUMP_BACKWARD: | ||
case JUMP_BACKWARD_NO_INTERRUPT: | ||
{ | ||
if (instr + 2 - oparg == initial_instr && code == initial_code) { | ||
_Py_CODEUNIT *target = instr + 1 + _PyOpcode_Caches[opcode] - (int)oparg; | ||
markshannon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (target == initial_instr) { | ||
/* We have looped round to the start */ | ||
RESERVE(1); | ||
ADD_TO_TRACE(_JUMP_TO_TOP, 0, 0, 0); | ||
} | ||
|
@@ -641,35 +662,33 @@ translate_bytecode_to_trace( | |
} | ||
assert(code == initial_code); | ||
// Skip short traces like _SET_IP, LOAD_FAST, _SET_IP, _EXIT_TRACE | ||
if (trace_length > 4) { | ||
ADD_TO_TRACE(_EXIT_TRACE, 0, 0, target); | ||
DPRINTF(1, | ||
"Created a trace for %s (%s:%d) at byte offset %d -- length %d\n", | ||
PyUnicode_AsUTF8(code->co_qualname), | ||
PyUnicode_AsUTF8(code->co_filename), | ||
code->co_firstlineno, | ||
2 * INSTR_IP(initial_instr, code), | ||
trace_length); | ||
OPT_HIST(trace_length + buffer_size - max_length, trace_length_hist); | ||
return 1; | ||
} | ||
else { | ||
if (progress_needed || trace_length < 5) { | ||
OPT_STAT_INC(trace_too_short); | ||
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; | ||
} | ||
return 0; | ||
ADD_TO_TRACE(_EXIT_TRACE, 0, 0, target); | ||
DPRINTF(1, | ||
"Created a trace for %s (%s:%d) at byte offset %d -- length %d\n", | ||
PyUnicode_AsUTF8(code->co_qualname), | ||
PyUnicode_AsUTF8(code->co_filename), | ||
code->co_firstlineno, | ||
2 * INSTR_IP(initial_instr, code), | ||
trace_length); | ||
OPT_HIST(trace_length + buffer_size - max_length, trace_length_hist); | ||
return 1; | ||
} | ||
|
||
#undef RESERVE | ||
#undef RESERVE_RAW | ||
#undef INSTR_IP | ||
#undef ADD_TO_TRACE | ||
#undef DPRINTF | ||
} | ||
|
||
#define UNSET_BIT(array, bit) (array[(bit)>>5] &= ~(1<<((bit)&31))) | ||
#define SET_BIT(array, bit) (array[(bit)>>5] |= (1<<((bit)&31))) | ||
|
@@ -854,10 +873,20 @@ counter_optimize( | |
int Py_UNUSED(curr_stackentries) | ||
) | ||
{ | ||
int oparg = instr->op.arg; | ||
while (instr->op.code == EXTENDED_ARG) { | ||
instr++; | ||
oparg = (oparg << 8) | instr->op.arg; | ||
} | ||
if (instr->op.code != JUMP_BACKWARD) { | ||
/* Counter optimizer can only handle backward edges */ | ||
return 0; | ||
} | ||
_Py_CODEUNIT *target = instr + 1 + _PyOpcode_Caches[JUMP_BACKWARD] - oparg; | ||
_PyUOpInstruction buffer[3] = { | ||
{ .opcode = _LOAD_CONST_INLINE_BORROW, .operand = (uintptr_t)self }, | ||
{ .opcode = _INTERNAL_INCREMENT_OPT_COUNTER }, | ||
{ .opcode = _EXIT_TRACE, .target = (uint32_t)(instr - _PyCode_CODE(code)) } | ||
{ .opcode = _EXIT_TRACE, .target = (uint32_t)(target - _PyCode_CODE(code)) } | ||
}; | ||
_PyBloomFilter empty; | ||
_Py_BloomFilter_Init(&empty); | ||
|
Uh oh!
There was an error while loading. Please reload this page.