Skip to content

Commit 680f7d1

Browse files
markshannonkulikjak
authored andcommitted
pythonGH-113860: All executors are now defined in terms of micro ops. Convert counter executor to use uops. (pythonGH-113864)
1 parent 5cf3752 commit 680f7d1

9 files changed

+125
-139
lines changed

Include/cpython/optimizer.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ typedef struct {
3131

3232
typedef struct _PyExecutorObject {
3333
PyObject_VAR_HEAD
34-
/* WARNING: execute consumes a reference to self. This is necessary to allow executors to tail call into each other. */
35-
_Py_CODEUNIT *(*execute)(struct _PyExecutorObject *self, struct _PyInterpreterFrame *frame, PyObject **stack_pointer);
3634
_PyVMData vm_data; /* Used by the VM, but opaque to the optimizer */
3735
/* Data needed by the executor goes here, but is opaque to the VM */
3836
} _PyExecutorObject;
@@ -52,6 +50,12 @@ typedef struct _PyOptimizerObject {
5250
/* Data needed by the optimizer goes here, but is opaque to the VM */
5351
} _PyOptimizerObject;
5452

53+
/** Test support **/
54+
typedef struct {
55+
_PyOptimizerObject base;
56+
int64_t count;
57+
} _PyCounterOptimizerObject;
58+
5559
PyAPI_FUNC(int) PyUnstable_Replace_Executor(PyCodeObject *code, _Py_CODEUNIT *instr, _PyExecutorObject *executor);
5660

5761
PyAPI_FUNC(void) PyUnstable_SetOptimizer(_PyOptimizerObject* optimizer);

Include/internal/pycore_opcode_metadata.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_uop_ids.h

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_uop_metadata.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
203203
[_EXIT_TRACE] = HAS_DEOPT_FLAG,
204204
[_INSERT] = HAS_ARG_FLAG,
205205
[_CHECK_VALIDITY] = HAS_DEOPT_FLAG,
206+
[_LOAD_CONST_INLINE_BORROW] = 0,
207+
[_INTERNAL_INCREMENT_OPT_COUNTER] = 0,
206208
};
207209

208210
const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
@@ -303,6 +305,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
303305
[_INIT_CALL_BOUND_METHOD_EXACT_ARGS] = "_INIT_CALL_BOUND_METHOD_EXACT_ARGS",
304306
[_INIT_CALL_PY_EXACT_ARGS] = "_INIT_CALL_PY_EXACT_ARGS",
305307
[_INSERT] = "_INSERT",
308+
[_INTERNAL_INCREMENT_OPT_COUNTER] = "_INTERNAL_INCREMENT_OPT_COUNTER",
306309
[_IS_NONE] = "_IS_NONE",
307310
[_IS_OP] = "_IS_OP",
308311
[_ITER_CHECK_LIST] = "_ITER_CHECK_LIST",
@@ -328,6 +331,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
328331
[_LOAD_ATTR_WITH_HINT] = "_LOAD_ATTR_WITH_HINT",
329332
[_LOAD_BUILD_CLASS] = "_LOAD_BUILD_CLASS",
330333
[_LOAD_CONST] = "_LOAD_CONST",
334+
[_LOAD_CONST_INLINE_BORROW] = "_LOAD_CONST_INLINE_BORROW",
331335
[_LOAD_DEREF] = "_LOAD_DEREF",
332336
[_LOAD_FAST] = "_LOAD_FAST",
333337
[_LOAD_FAST_AND_CLEAR] = "_LOAD_FAST_AND_CLEAR",

Include/internal/pycore_uops.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,6 @@ typedef struct {
2424
_PyUOpInstruction trace[1];
2525
} _PyUOpExecutorObject;
2626

27-
_Py_CODEUNIT *_PyUOpExecute(
28-
_PyExecutorObject *executor,
29-
_PyInterpreterFrame *frame,
30-
PyObject **stack_pointer);
31-
3227
#ifdef __cplusplus
3328
}
3429
#endif

Python/bytecodes.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2366,16 +2366,8 @@ dummy_func(
23662366
_PyExecutorObject *executor = (_PyExecutorObject *)code->co_executors->executors[oparg&255];
23672367
if (executor->vm_data.valid) {
23682368
Py_INCREF(executor);
2369-
if (executor->execute == _PyUOpExecute) {
2370-
current_executor = (_PyUOpExecutorObject *)executor;
2371-
GOTO_TIER_TWO();
2372-
}
2373-
next_instr = executor->execute(executor, frame, stack_pointer);
2374-
frame = tstate->current_frame;
2375-
if (next_instr == NULL) {
2376-
goto resume_with_error;
2377-
}
2378-
stack_pointer = _PyFrame_GetStackPointer(frame);
2369+
current_executor = (_PyUOpExecutorObject *)executor;
2370+
GOTO_TIER_TWO();
23792371
}
23802372
else {
23812373
code->co_executors->executors[oparg & 255] = NULL;
@@ -4066,6 +4058,16 @@ dummy_func(
40664058
DEOPT_IF(!current_executor->base.vm_data.valid);
40674059
}
40684060

4061+
op(_LOAD_CONST_INLINE_BORROW, (ptr/4 -- value)) {
4062+
value = ptr;
4063+
}
4064+
4065+
/* Internal -- for testing executors */
4066+
op(_INTERNAL_INCREMENT_OPT_COUNTER, (opt --)) {
4067+
_PyCounterOptimizerObject *exe = (_PyCounterOptimizerObject *)opt;
4068+
exe->count++;
4069+
}
4070+
40694071

40704072
// END BYTECODES //
40714073

Python/executor_cases.c.h

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 2 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/optimizer.c

Lines changed: 79 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -212,27 +212,6 @@ PyUnstable_GetExecutor(PyCodeObject *code, int offset)
212212
return NULL;
213213
}
214214

215-
/** Test support **/
216-
217-
218-
typedef struct {
219-
_PyOptimizerObject base;
220-
int64_t count;
221-
} _PyCounterOptimizerObject;
222-
223-
typedef struct {
224-
_PyExecutorObject executor;
225-
_PyCounterOptimizerObject *optimizer;
226-
_Py_CODEUNIT *next_instr;
227-
} _PyCounterExecutorObject;
228-
229-
static void
230-
counter_dealloc(_PyCounterExecutorObject *self) {
231-
_Py_ExecutorClear((_PyExecutorObject *)self);
232-
Py_DECREF(self->optimizer);
233-
PyObject_Free(self);
234-
}
235-
236215
static PyObject *
237216
is_valid(PyObject *self, PyObject *Py_UNUSED(ignored))
238217
{
@@ -244,84 +223,6 @@ static PyMethodDef executor_methods[] = {
244223
{ NULL, NULL },
245224
};
246225

247-
PyTypeObject _PyCounterExecutor_Type = {
248-
PyVarObject_HEAD_INIT(&PyType_Type, 0)
249-
.tp_name = "counting_executor",
250-
.tp_basicsize = sizeof(_PyCounterExecutorObject),
251-
.tp_itemsize = 0,
252-
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
253-
.tp_dealloc = (destructor)counter_dealloc,
254-
.tp_methods = executor_methods,
255-
};
256-
257-
static _Py_CODEUNIT *
258-
counter_execute(_PyExecutorObject *self, _PyInterpreterFrame *frame, PyObject **stack_pointer)
259-
{
260-
((_PyCounterExecutorObject *)self)->optimizer->count++;
261-
_PyFrame_SetStackPointer(frame, stack_pointer);
262-
Py_DECREF(self);
263-
return ((_PyCounterExecutorObject *)self)->next_instr;
264-
}
265-
266-
static int
267-
counter_optimize(
268-
_PyOptimizerObject* self,
269-
PyCodeObject *code,
270-
_Py_CODEUNIT *instr,
271-
_PyExecutorObject **exec_ptr,
272-
int Py_UNUSED(curr_stackentries)
273-
)
274-
{
275-
_PyCounterExecutorObject *executor = (_PyCounterExecutorObject *)_PyObject_New(&_PyCounterExecutor_Type);
276-
if (executor == NULL) {
277-
return -1;
278-
}
279-
executor->executor.execute = counter_execute;
280-
Py_INCREF(self);
281-
executor->optimizer = (_PyCounterOptimizerObject *)self;
282-
executor->next_instr = instr;
283-
*exec_ptr = (_PyExecutorObject *)executor;
284-
_PyBloomFilter empty;
285-
_Py_BloomFilter_Init(&empty);
286-
_Py_ExecutorInit((_PyExecutorObject *)executor, &empty);
287-
return 1;
288-
}
289-
290-
static PyObject *
291-
counter_get_counter(PyObject *self, PyObject *args)
292-
{
293-
return PyLong_FromLongLong(((_PyCounterOptimizerObject *)self)->count);
294-
}
295-
296-
static PyMethodDef counter_optimizer_methods[] = {
297-
{ "get_count", counter_get_counter, METH_NOARGS, NULL },
298-
{ NULL, NULL },
299-
};
300-
301-
PyTypeObject _PyCounterOptimizer_Type = {
302-
PyVarObject_HEAD_INIT(&PyType_Type, 0)
303-
.tp_name = "Counter optimizer",
304-
.tp_basicsize = sizeof(_PyCounterOptimizerObject),
305-
.tp_itemsize = 0,
306-
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
307-
.tp_methods = counter_optimizer_methods,
308-
.tp_dealloc = (destructor)PyObject_Del,
309-
};
310-
311-
PyObject *
312-
PyUnstable_Optimizer_NewCounter(void)
313-
{
314-
_PyCounterOptimizerObject *opt = (_PyCounterOptimizerObject *)_PyObject_New(&_PyCounterOptimizer_Type);
315-
if (opt == NULL) {
316-
return NULL;
317-
}
318-
opt->base.optimize = counter_optimize;
319-
opt->base.resume_threshold = INT16_MAX;
320-
opt->base.backedge_threshold = 0;
321-
opt->count = 0;
322-
return (PyObject *)opt;
323-
}
324-
325226
///////////////////// Experimental UOp Optimizer /////////////////////
326227

327228
static void
@@ -381,7 +282,7 @@ PySequenceMethods uop_as_sequence = {
381282
PyTypeObject _PyUOpExecutor_Type = {
382283
PyVarObject_HEAD_INIT(&PyType_Type, 0)
383284
.tp_name = "uop_executor",
384-
.tp_basicsize = sizeof(_PyUOpExecutorObject) - sizeof(_PyUOpInstruction),
285+
.tp_basicsize = offsetof(_PyUOpExecutorObject, trace),
385286
.tp_itemsize = sizeof(_PyUOpInstruction),
386287
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
387288
.tp_dealloc = (destructor)uop_dealloc,
@@ -843,7 +744,6 @@ make_executor_from_uops(_PyUOpInstruction *buffer, _PyBloomFilter *dependencies)
843744
dest--;
844745
}
845746
assert(dest == -1);
846-
executor->base.execute = _PyUOpExecute;
847747
_Py_ExecutorInit((_PyExecutorObject *)executor, dependencies);
848748
#ifdef Py_DEBUG
849749
char *python_lltrace = Py_GETENV("PYTHON_LLTRACE");
@@ -899,15 +799,6 @@ uop_optimize(
899799
return 1;
900800
}
901801

902-
/* Dummy execute() function for UOp Executor.
903-
* The actual implementation is inlined in ceval.c,
904-
* in _PyEval_EvalFrameDefault(). */
905-
_Py_CODEUNIT *
906-
_PyUOpExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **stack_pointer)
907-
{
908-
Py_FatalError("Tier 2 is now inlined into Tier 1");
909-
}
910-
911802
static void
912803
uop_opt_dealloc(PyObject *self) {
913804
PyObject_Free(self);
@@ -937,6 +828,84 @@ PyUnstable_Optimizer_NewUOpOptimizer(void)
937828
return (PyObject *)opt;
938829
}
939830

831+
static void
832+
counter_dealloc(_PyUOpExecutorObject *self) {
833+
PyObject *opt = (PyObject *)self->trace[0].operand;
834+
Py_DECREF(opt);
835+
uop_dealloc(self);
836+
}
837+
838+
PyTypeObject _PyCounterExecutor_Type = {
839+
PyVarObject_HEAD_INIT(&PyType_Type, 0)
840+
.tp_name = "counting_executor",
841+
.tp_basicsize = offsetof(_PyUOpExecutorObject, trace),
842+
.tp_itemsize = sizeof(_PyUOpInstruction),
843+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
844+
.tp_dealloc = (destructor)counter_dealloc,
845+
.tp_methods = executor_methods,
846+
};
847+
848+
static int
849+
counter_optimize(
850+
_PyOptimizerObject* self,
851+
PyCodeObject *code,
852+
_Py_CODEUNIT *instr,
853+
_PyExecutorObject **exec_ptr,
854+
int Py_UNUSED(curr_stackentries)
855+
)
856+
{
857+
_PyUOpInstruction buffer[3] = {
858+
{ .opcode = _LOAD_CONST_INLINE_BORROW, .operand = (uintptr_t)self },
859+
{ .opcode = _INTERNAL_INCREMENT_OPT_COUNTER },
860+
{ .opcode = _EXIT_TRACE, .target = (uint32_t)(instr - _PyCode_CODE(code)) }
861+
};
862+
_PyBloomFilter empty;
863+
_Py_BloomFilter_Init(&empty);
864+
_PyExecutorObject *executor = make_executor_from_uops(buffer, &empty);
865+
if (executor == NULL) {
866+
return -1;
867+
}
868+
Py_INCREF(self);
869+
Py_SET_TYPE(executor, &_PyCounterExecutor_Type);
870+
*exec_ptr = executor;
871+
return 1;
872+
}
873+
874+
static PyObject *
875+
counter_get_counter(PyObject *self, PyObject *args)
876+
{
877+
return PyLong_FromLongLong(((_PyCounterOptimizerObject *)self)->count);
878+
}
879+
880+
static PyMethodDef counter_optimizer_methods[] = {
881+
{ "get_count", counter_get_counter, METH_NOARGS, NULL },
882+
{ NULL, NULL },
883+
};
884+
885+
PyTypeObject _PyCounterOptimizer_Type = {
886+
PyVarObject_HEAD_INIT(&PyType_Type, 0)
887+
.tp_name = "Counter optimizer",
888+
.tp_basicsize = sizeof(_PyCounterOptimizerObject),
889+
.tp_itemsize = 0,
890+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
891+
.tp_methods = counter_optimizer_methods,
892+
.tp_dealloc = (destructor)PyObject_Del,
893+
};
894+
895+
PyObject *
896+
PyUnstable_Optimizer_NewCounter(void)
897+
{
898+
_PyCounterOptimizerObject *opt = (_PyCounterOptimizerObject *)_PyObject_New(&_PyCounterOptimizer_Type);
899+
if (opt == NULL) {
900+
return NULL;
901+
}
902+
opt->base.optimize = counter_optimize;
903+
opt->base.resume_threshold = INT16_MAX;
904+
opt->base.backedge_threshold = 0;
905+
opt->count = 0;
906+
return (PyObject *)opt;
907+
}
908+
940909

941910
/*****************************************
942911
* Executor management

0 commit comments

Comments
 (0)