summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/cpython/optimizer.h2
-rw-r--r--Include/internal/pycore_opcode.h9
-rw-r--r--Lib/test/test_capi/test_misc.py23
-rw-r--r--Modules/_testinternalcapi.c21
-rw-r--r--Python/opcode_metadata.h6
-rw-r--r--Python/optimizer.c70
-rw-r--r--Tools/build/generate_opcode_h.py7
-rw-r--r--Tools/cases_generator/generate_cases.py6
8 files changed, 130 insertions, 14 deletions
diff --git a/Include/cpython/optimizer.h b/Include/cpython/optimizer.h
index 2664f5b..2260501 100644
--- a/Include/cpython/optimizer.h
+++ b/Include/cpython/optimizer.h
@@ -38,6 +38,8 @@ PyAPI_FUNC(void) PyUnstable_SetOptimizer(_PyOptimizerObject* optimizer);
PyAPI_FUNC(_PyOptimizerObject *) PyUnstable_GetOptimizer(void);
+PyAPI_FUNC(_PyExecutorObject *)PyUnstable_GetExecutor(PyCodeObject *code, int offset);
+
struct _PyInterpreterFrame *
_PyOptimizer_BackEdge(struct _PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer);
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 428df4c..c7c2fdc 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -242,8 +242,11 @@ const uint8_t _PyOpcode_Deopt[256] = {
};
#endif // NEED_OPCODE_TABLES
-#ifdef Py_DEBUG
-static const char *const _PyOpcode_OpName[268] = {
+
+extern const char *const _PyOpcode_OpName[268];
+
+#ifdef NEED_OPCODE_TABLES
+const char *const _PyOpcode_OpName[268] = {
[CACHE] = "CACHE",
[POP_TOP] = "POP_TOP",
[PUSH_NULL] = "PUSH_NULL",
@@ -513,7 +516,7 @@ static const char *const _PyOpcode_OpName[268] = {
[STORE_FAST_MAYBE_NULL] = "STORE_FAST_MAYBE_NULL",
[LOAD_CLOSURE] = "LOAD_CLOSURE",
};
-#endif
+#endif // NEED_OPCODE_TABLES
#define EXTRA_CASES \
case 184: \
diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py
index 9e825a3..de9f00a 100644
--- a/Lib/test/test_capi/test_misc.py
+++ b/Lib/test/test_capi/test_misc.py
@@ -2415,5 +2415,28 @@ class TestOptimizerAPI(unittest.TestCase):
self.assertEqual(opt.get_count(), 10)
+class TestUops(unittest.TestCase):
+
+ def test_basic_loop(self):
+
+ def testfunc(x):
+ i = 0
+ while i < x:
+ i += 1
+
+ testfunc(1000)
+
+ ex = None
+ for offset in range(0, 100, 2):
+ try:
+ ex = _testinternalcapi.get_executor(testfunc.__code__, offset)
+ break
+ except ValueError:
+ pass
+ if ex is None:
+ return
+ self.assertIn("SAVE_IP", str(ex))
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c
index 84511d2..52e524a 100644
--- a/Modules/_testinternalcapi.c
+++ b/Modules/_testinternalcapi.c
@@ -858,6 +858,26 @@ get_optimizer(PyObject *self, PyObject *Py_UNUSED(ignored))
return opt;
}
+static PyObject *
+get_executor(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+
+ if (!_PyArg_CheckPositional("get_executor", nargs, 2, 2)) {
+ return NULL;
+ }
+ PyObject *code = args[0];
+ PyObject *offset = args[1];
+ long ioffset = PyLong_AsLong(offset);
+ if (ioffset == -1 && PyErr_Occurred()) {
+ return NULL;
+ }
+ if (!PyCode_Check(code)) {
+ PyErr_SetString(PyExc_TypeError, "first argument must be a code object");
+ return NULL;
+ }
+ return (PyObject *)PyUnstable_GetExecutor((PyCodeObject *)code, ioffset);
+}
+
static int _pending_callback(void *arg)
{
/* we assume the argument is callable object to which we own a reference */
@@ -1326,6 +1346,7 @@ static PyMethodDef module_functions[] = {
{"iframe_getlasti", iframe_getlasti, METH_O, NULL},
{"get_optimizer", get_optimizer, METH_NOARGS, NULL},
{"set_optimizer", set_optimizer, METH_O, NULL},
+ {"get_executor", _PyCFunction_CAST(get_executor), METH_FASTCALL, NULL},
{"get_counter_optimizer", get_counter_optimizer, METH_NOARGS, NULL},
{"get_uop_optimizer", get_uop_optimizer, METH_NOARGS, NULL},
{"pending_threadfunc", _PyCFunction_CAST(pending_threadfunc),
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 82c9823..ac86a4a 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -942,9 +942,7 @@ struct opcode_macro_expansion {
#ifndef NEED_OPCODE_METADATA
extern const struct opcode_metadata _PyOpcode_opcode_metadata[512];
extern const struct opcode_macro_expansion _PyOpcode_macro_expansion[256];
-#ifdef Py_DEBUG
extern const char * const _PyOpcode_uop_name[512];
-#endif
#else
const struct opcode_metadata _PyOpcode_opcode_metadata[512] = {
[NOP] = { true, INSTR_FMT_IX, 0 },
@@ -1265,7 +1263,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[256] = {
[COPY] = { .nuops = 1, .uops = { { COPY, 0, 0 } } },
[SWAP] = { .nuops = 1, .uops = { { SWAP, 0, 0 } } },
};
-#ifdef Py_DEBUG
+#ifdef NEED_OPCODE_METADATA
const char * const _PyOpcode_uop_name[512] = {
[300] = "EXIT_TRACE",
[301] = "SAVE_IP",
@@ -1282,5 +1280,5 @@ const char * const _PyOpcode_uop_name[512] = {
[312] = "_LOAD_LOCALS",
[313] = "_LOAD_FROM_DICT_OR_GLOBALS",
};
-#endif
+#endif // NEED_OPCODE_METADATA
#endif
diff --git a/Python/optimizer.c b/Python/optimizer.c
index 32f0b14..c3ab649 100644
--- a/Python/optimizer.c
+++ b/Python/optimizer.c
@@ -188,6 +188,23 @@ jump_to_destination:
return frame;
}
+_PyExecutorObject *
+PyUnstable_GetExecutor(PyCodeObject *code, int offset)
+{
+ int code_len = (int)Py_SIZE(code);
+ for (int i = 0 ; i < code_len;) {
+ if (_PyCode_CODE(code)[i].op.code == ENTER_EXECUTOR && i*2 == offset) {
+ int oparg = _PyCode_CODE(code)[i].op.arg;
+ _PyExecutorObject *res = code->co_executors->executors[oparg];
+ Py_INCREF(res);
+ return res;
+ }
+ i += _PyInstruction_GetLength(code, i);
+ }
+ PyErr_SetString(PyExc_ValueError, "no executor at given offset");
+ return NULL;
+}
+
/** Test support **/
@@ -287,6 +304,58 @@ uop_dealloc(_PyUOpExecutorObject *self) {
PyObject_Free(self);
}
+static const char *
+uop_name(int index) {
+ if (index < EXIT_TRACE) {
+ return _PyOpcode_OpName[index];
+ }
+ return _PyOpcode_uop_name[index];
+}
+
+static Py_ssize_t
+uop_len(_PyUOpExecutorObject *self)
+{
+ int count = 1;
+ for (; count < _Py_UOP_MAX_TRACE_LENGTH; count++) {
+ if (self->trace[count-1].opcode == EXIT_TRACE) {
+ break;
+ }
+ }
+ return count;
+}
+
+static PyObject *
+uop_item(_PyUOpExecutorObject *self, Py_ssize_t index)
+{
+ for (int i = 0; i < _Py_UOP_MAX_TRACE_LENGTH; i++) {
+ if (self->trace[i].opcode == EXIT_TRACE) {
+ break;
+ }
+ if (i != index) {
+ continue;
+ }
+ const char *name = uop_name(self->trace[i].opcode);
+ PyObject *oname = _PyUnicode_FromASCII(name, strlen(name));
+ if (oname == NULL) {
+ return NULL;
+ }
+ PyObject *operand = PyLong_FromUnsignedLongLong(self->trace[i].operand);
+ if (operand == NULL) {
+ Py_DECREF(oname);
+ return NULL;
+ }
+ PyObject *args[2] = { oname, operand };
+ return _PyTuple_FromArraySteal(args, 2);
+ }
+ PyErr_SetNone(PyExc_IndexError);
+ return NULL;
+}
+
+PySequenceMethods uop_as_sequence = {
+ .sq_length = (lenfunc)uop_len,
+ .sq_item = (ssizeargfunc)uop_item,
+};
+
static PyTypeObject UOpExecutor_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "uop_executor",
@@ -294,6 +363,7 @@ static PyTypeObject UOpExecutor_Type = {
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
.tp_dealloc = (destructor)uop_dealloc,
+ .tp_as_sequence = &uop_as_sequence,
};
static int
diff --git a/Tools/build/generate_opcode_h.py b/Tools/build/generate_opcode_h.py
index 4711fbb..2e841e6 100644
--- a/Tools/build/generate_opcode_h.py
+++ b/Tools/build/generate_opcode_h.py
@@ -184,14 +184,15 @@ def main(opcode_py,
fobj.write(f"#define ENABLE_SPECIALIZATION {int(ENABLE_SPECIALIZATION)}")
iobj.write("\n")
- iobj.write("#ifdef Py_DEBUG\n")
- iobj.write(f"static const char *const _PyOpcode_OpName[{NUM_OPCODES}] = {{\n")
+ iobj.write(f"\nextern const char *const _PyOpcode_OpName[{NUM_OPCODES}];\n")
+ iobj.write("\n#ifdef NEED_OPCODE_TABLES\n")
+ iobj.write(f"const char *const _PyOpcode_OpName[{NUM_OPCODES}] = {{\n")
for op, name in enumerate(opname_including_specialized):
if name[0] != "<":
op = name
iobj.write(f''' [{op}] = "{name}",\n''')
iobj.write("};\n")
- iobj.write("#endif\n")
+ iobj.write("#endif // NEED_OPCODE_TABLES\n")
iobj.write("\n")
iobj.write("#define EXTRA_CASES \\\n")
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 657dfa9..a90abfe 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -1224,9 +1224,7 @@ class Analyzer:
self.out.emit("#ifndef NEED_OPCODE_METADATA")
self.out.emit("extern const struct opcode_metadata _PyOpcode_opcode_metadata[512];")
self.out.emit("extern const struct opcode_macro_expansion _PyOpcode_macro_expansion[256];")
- self.out.emit("#ifdef Py_DEBUG")
self.out.emit("extern const char * const _PyOpcode_uop_name[512];")
- self.out.emit("#endif")
self.out.emit("#else")
self.out.emit("const struct opcode_metadata _PyOpcode_opcode_metadata[512] = {")
@@ -1273,10 +1271,10 @@ class Analyzer:
case _:
typing.assert_never(thing)
- self.out.emit("#ifdef Py_DEBUG")
+ self.out.emit("#ifdef NEED_OPCODE_METADATA")
with self.out.block("const char * const _PyOpcode_uop_name[512] =", ";"):
self.write_uop_items(lambda name, counter: f"[{counter}] = \"{name}\",")
- self.out.emit("#endif")
+ self.out.emit("#endif // NEED_OPCODE_METADATA")
self.out.emit("#endif")