Skip to content

Commit 864d68d

Browse files
committed
Jump directly to side-exit executors
1 parent 65c41cb commit 864d68d

File tree

3 files changed

+52
-22
lines changed

3 files changed

+52
-22
lines changed

Include/cpython/optimizer.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ PyAPI_FUNC(_PyOptimizerObject *) PyUnstable_GetOptimizer(void);
6161
PyAPI_FUNC(_PyExecutorObject *) PyUnstable_GetExecutor(PyCodeObject *code, int offset);
6262

6363
int _PyOptimizer_BackEdge(struct _PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer);
64-
int _PyOptimizer_Anywhere(struct _PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer);
64+
int _PyOptimizer_Unanchored(struct _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, _PyExecutorObject **pexecutor, PyObject **stack_pointer);
6565

6666
extern _PyOptimizerObject _PyOptimizer_Default;
6767

Python/ceval.c

+33-2
Original file line numberDiff line numberDiff line change
@@ -1108,9 +1108,22 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
11081108
_PyFrame_SetStackPointer(frame, stack_pointer);
11091109
// Increment side exit counter for this uop
11101110
int pc = next_uop - 1 - current_executor->trace;
1111+
_PyExecutorObject **pexecutor = current_executor->executors + pc;
1112+
if (*pexecutor != NULL) {
1113+
PyCodeObject *code = _PyFrame_GetCode(frame);
1114+
DPRINTF(2, "Jumping to new executor for %s (%s:%d) at byte offset %d\n",
1115+
PyUnicode_AsUTF8(code->co_qualname),
1116+
PyUnicode_AsUTF8(code->co_filename),
1117+
code->co_firstlineno,
1118+
2 * (int)(frame->instr_ptr - _PyCode_CODE(_PyFrame_GetCode(frame))));
1119+
Py_DECREF(current_executor);
1120+
current_executor = (_PyUOpExecutorObject *)*pexecutor;
1121+
Py_INCREF(current_executor);
1122+
goto enter_tier_two;
1123+
}
11111124
uint16_t *pcounter = current_executor->counters + pc;
11121125
*pcounter += 1;
1113-
if (*pcounter == 16 && // TODO: use resume_threshold
1126+
if (*pcounter == 32 && // TODO: use resume_threshold
11141127
tstate->interp->optimizer != &_PyOptimizer_Default &&
11151128
(opcode == POP_JUMP_IF_FALSE ||
11161129
opcode == POP_JUMP_IF_TRUE ||
@@ -1121,14 +1134,32 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
11211134
_PyUOpName(uopcode), pc, current_executor, (int)(*pcounter));
11221135
DPRINTF(2, " T1: %s\n", _PyOpcode_OpName[opcode]);
11231136
// The counter will cycle around once the 16 bits overflow
1124-
int optimized = _PyOptimizer_Anywhere(frame, src, dest, stack_pointer);
1137+
int optimized = _PyOptimizer_Unanchored(frame, dest, pexecutor, stack_pointer);
11251138
if (optimized < 0) {
11261139
goto error_tier_two;
11271140
}
11281141
if (optimized) {
11291142
DPRINTF(1, "--> Optimized %s @ %d in %p\n",
11301143
_PyUOpName(uopcode), pc, current_executor);
11311144
DPRINTF(1, " T1: %s\n", _PyOpcode_OpName[src->op.code]);
1145+
PyCodeObject *code = _PyFrame_GetCode(frame);
1146+
DPRINTF(2, "Jumping to fresh executor for %s (%s:%d) at byte offset %d\n",
1147+
PyUnicode_AsUTF8(code->co_qualname),
1148+
PyUnicode_AsUTF8(code->co_filename),
1149+
code->co_firstlineno,
1150+
2 * (int)(frame->instr_ptr - _PyCode_CODE(_PyFrame_GetCode(frame))));
1151+
Py_DECREF(current_executor);
1152+
current_executor = (_PyUOpExecutorObject *)*pexecutor;
1153+
if (current_executor->trace[0].opcode != uopcode) {
1154+
Py_INCREF(current_executor);
1155+
goto enter_tier_two;
1156+
}
1157+
// This is guaranteed to deopt again; forget about it
1158+
DPRINTF(2, "It's not an improvement -- discarding trace\n");
1159+
*pexecutor = NULL;
1160+
Py_DECREF(current_executor);
1161+
next_instr = frame->instr_ptr;
1162+
goto resume_frame;
11321163
}
11331164
else {
11341165
DPRINTF(2, "--> Failed to optimize %s @ %d in %p\n",

Python/optimizer.c

+18-19
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,10 @@ PyUnstable_SetOptimizer(_PyOptimizerObject *optimizer)
155155
Py_DECREF(old);
156156
}
157157

158-
// src is where to insert ENTER_EXECUTOR
159-
// dest is where to start tracing
160-
static int
161-
optimizer_wherever(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer)
158+
int
159+
_PyOptimizer_BackEdge(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer)
162160
{
161+
assert(src->op.code == JUMP_BACKWARD);
163162
PyCodeObject *code = _PyFrame_GetCode(frame);
164163
assert(PyCode_Check(code));
165164
PyInterpreterState *interp = _PyInterpreterState_GET();
@@ -189,27 +188,27 @@ optimizer_wherever(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *
189188
return 1;
190189
}
191190

191+
// Return an unanchored executor. The caller owns the executor when returning 1.
192+
// No ENTER_EXECUTOR is inserted, nor is the executor added to the code object.
192193
int
193-
_PyOptimizer_BackEdge(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer)
194-
{
195-
assert(src->op.code == JUMP_BACKWARD);
196-
return optimizer_wherever(frame, src, dest, stack_pointer);
197-
}
198-
199-
// Start tracing and insert ENTER_EXECUTOR at the same place.
200-
// Normally src == dest, but when there's an EXTENDED_ARG involved,
201-
// dest points at the preceding EXTENDED_ARG.
202-
// Do not use at JUMP_BACKWARD. Won't replace ENTER_EXECUTOR.
203-
int
204-
_PyOptimizer_Anywhere(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer)
194+
_PyOptimizer_Unanchored(
195+
_PyInterpreterFrame *frame,
196+
_Py_CODEUNIT *instr,
197+
_PyExecutorObject **pexecutor,
198+
PyObject **stack_pointer)
205199
{
206-
if (src->op.code == JUMP_BACKWARD) {
200+
if (instr->op.code == JUMP_BACKWARD || instr->op.code == ENTER_EXECUTOR) {
207201
return 0;
208202
}
209-
if (src->op.code == ENTER_EXECUTOR) {
203+
PyCodeObject *code = _PyFrame_GetCode(frame);
204+
assert(PyCode_Check(code));
205+
PyInterpreterState *interp = _PyInterpreterState_GET();
206+
_PyOptimizerObject *opt = interp->optimizer;
207+
if (strcmp(opt->ob_base.ob_type->tp_name, "uop_optimizer") != 0) {
210208
return 0;
211209
}
212-
return optimizer_wherever(frame, src, dest, stack_pointer);
210+
*pexecutor = NULL;
211+
return opt->optimize(opt, code, instr, pexecutor, (int)(stack_pointer - _PyFrame_Stackbase(frame)));
213212
}
214213

215214
_PyExecutorObject *

0 commit comments

Comments
 (0)