[Python-checkins] gh-105481: expose opcode metadata via the _opcode module (#106688)
iritkatriel
webhook-mailer at python.org
Fri Jul 14 13:41:56 EDT 2023
https://github.com/python/cpython/commit/6a70edf24ca217c5ed4a556d0df5748fc775c762
commit: 6a70edf24ca217c5ed4a556d0df5748fc775c762
branch: main
author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com>
committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com>
date: 2023-07-14T18:41:52+01:00
summary:
gh-105481: expose opcode metadata via the _opcode module (#106688)
files:
A Misc/NEWS.d/next/Library/2023-07-13-16-04-15.gh-issue-105481.pYSwMj.rst
M Include/cpython/compile.h
M Include/internal/pycore_opcode_metadata.h
M Lib/test/test__opcode.py
M Modules/_opcode.c
M Modules/clinic/_opcode.c.h
M Python/compile.c
M Python/flowgraph.c
M Tools/cases_generator/generate_cases.py
diff --git a/Include/cpython/compile.h b/Include/cpython/compile.h
index f5a62a8ec6dd0..cd7fd7bd37766 100644
--- a/Include/cpython/compile.h
+++ b/Include/cpython/compile.h
@@ -67,3 +67,9 @@ typedef struct {
#define PY_INVALID_STACK_EFFECT INT_MAX
PyAPI_FUNC(int) PyCompile_OpcodeStackEffect(int opcode, int oparg);
PyAPI_FUNC(int) PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int jump);
+
+PyAPI_FUNC(int) PyUnstable_OpcodeIsValid(int opcode);
+PyAPI_FUNC(int) PyUnstable_OpcodeHasArg(int opcode);
+PyAPI_FUNC(int) PyUnstable_OpcodeHasConst(int opcode);
+PyAPI_FUNC(int) PyUnstable_OpcodeHasName(int opcode);
+PyAPI_FUNC(int) PyUnstable_OpcodeHasJump(int opcode);
diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h
index e94732b64384b..8373f56653b1c 100644
--- a/Include/internal/pycore_opcode_metadata.h
+++ b/Include/internal/pycore_opcode_metadata.h
@@ -3,6 +3,8 @@
// Python/bytecodes.c
// Do not edit!
+#include <stdbool.h>
+
#define IS_PSEUDO_INSTR(OP) ( \
((OP) == LOAD_CLOSURE) || \
@@ -941,14 +943,20 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
#endif
enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC00, INSTR_FMT_IBC000, INSTR_FMT_IBC00000, INSTR_FMT_IBC00000000, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC0, INSTR_FMT_IXC00, INSTR_FMT_IXC000 };
+
+#define IS_VALID_OPCODE(OP) \
+ (((OP) >= 0) && ((OP) < OPCODE_METADATA_SIZE) && \
+ (_PyOpcode_opcode_metadata[(OP)].valid_entry))
+
#define HAS_ARG_FLAG (1)
#define HAS_CONST_FLAG (2)
#define HAS_NAME_FLAG (4)
#define HAS_JUMP_FLAG (8)
-#define OPCODE_HAS_ARG(OP) (_PyOpcode_opcode_metadata[(OP)].flags & (HAS_ARG_FLAG))
-#define OPCODE_HAS_CONST(OP) (_PyOpcode_opcode_metadata[(OP)].flags & (HAS_CONST_FLAG))
-#define OPCODE_HAS_NAME(OP) (_PyOpcode_opcode_metadata[(OP)].flags & (HAS_NAME_FLAG))
-#define OPCODE_HAS_JUMP(OP) (_PyOpcode_opcode_metadata[(OP)].flags & (HAS_JUMP_FLAG))
+#define OPCODE_HAS_ARG(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_ARG_FLAG))
+#define OPCODE_HAS_CONST(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_CONST_FLAG))
+#define OPCODE_HAS_NAME(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_NAME_FLAG))
+#define OPCODE_HAS_JUMP(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_JUMP_FLAG))
+
struct opcode_metadata {
bool valid_entry;
enum InstructionFormat instr_format;
@@ -971,12 +979,16 @@ struct opcode_macro_expansion {
#define SAME_OPCODE_METADATA(OP1, OP2) \
(OPCODE_METADATA_FMT(OP1) == OPCODE_METADATA_FMT(OP2))
+#define OPCODE_METADATA_SIZE 512
+#define OPCODE_UOP_NAME_SIZE 512
+#define OPCODE_MACRO_EXPANSION_SIZE 256
+
#ifndef NEED_OPCODE_METADATA
-extern const struct opcode_metadata _PyOpcode_opcode_metadata[512];
-extern const struct opcode_macro_expansion _PyOpcode_macro_expansion[256];
-extern const char * const _PyOpcode_uop_name[512];
+extern const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE];
+extern const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPANSION_SIZE];
+extern const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE];
#else // if NEED_OPCODE_METADATA
-const struct opcode_metadata _PyOpcode_opcode_metadata[512] = {
+const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = {
[NOP] = { true, INSTR_FMT_IX, 0 },
[RESUME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG },
[INSTRUMENTED_RESUME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG },
@@ -1194,7 +1206,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[512] = {
[CACHE] = { true, INSTR_FMT_IX, 0 },
[RESERVED] = { true, INSTR_FMT_IX, 0 },
};
-const struct opcode_macro_expansion _PyOpcode_macro_expansion[256] = {
+const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPANSION_SIZE] = {
[NOP] = { .nuops = 1, .uops = { { NOP, 0, 0 } } },
[LOAD_FAST_CHECK] = { .nuops = 1, .uops = { { LOAD_FAST_CHECK, 0, 0 } } },
[LOAD_FAST] = { .nuops = 1, .uops = { { LOAD_FAST, 0, 0 } } },
@@ -1308,7 +1320,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[256] = {
[BINARY_OP] = { .nuops = 1, .uops = { { BINARY_OP, 0, 0 } } },
[SWAP] = { .nuops = 1, .uops = { { SWAP, 0, 0 } } },
};
-const char * const _PyOpcode_uop_name[512] = {
+const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE] = {
[EXIT_TRACE] = "EXIT_TRACE",
[SAVE_IP] = "SAVE_IP",
[_GUARD_BOTH_INT] = "_GUARD_BOTH_INT",
diff --git a/Lib/test/test__opcode.py b/Lib/test/test__opcode.py
index 3e084928844d2..7d9553d9e383a 100644
--- a/Lib/test/test__opcode.py
+++ b/Lib/test/test__opcode.py
@@ -9,6 +9,70 @@
class OpcodeTests(unittest.TestCase):
+ def check_bool_function_result(self, func, ops, expected):
+ for op in ops:
+ if isinstance(op, str):
+ op = dis.opmap[op]
+ with self.subTest(opcode=op, func=func):
+ self.assertIsInstance(func(op), bool)
+ self.assertEqual(func(op), expected)
+
+ def test_invalid_opcodes(self):
+ invalid = [-100, -1, 255, 512, 513, 1000]
+ self.check_bool_function_result(_opcode.is_valid, invalid, False)
+ self.check_bool_function_result(_opcode.has_arg, invalid, False)
+ self.check_bool_function_result(_opcode.has_const, invalid, False)
+ self.check_bool_function_result(_opcode.has_name, invalid, False)
+ self.check_bool_function_result(_opcode.has_jump, invalid, False)
+
+ def test_is_valid(self):
+ names = [
+ 'CACHE',
+ 'POP_TOP',
+ 'IMPORT_NAME',
+ 'JUMP',
+ 'INSTRUMENTED_RETURN_VALUE',
+ ]
+ opcodes = [dis.opmap[opname] for opname in names]
+ self.check_bool_function_result(_opcode.is_valid, opcodes, True)
+
+ def test_has_arg(self):
+ has_arg = ['SWAP', 'LOAD_FAST', 'INSTRUMENTED_POP_JUMP_IF_TRUE', 'JUMP']
+ no_arg = ['SETUP_WITH', 'POP_TOP', 'NOP', 'CACHE']
+ self.check_bool_function_result(_opcode.has_arg, has_arg, True)
+ self.check_bool_function_result(_opcode.has_arg, no_arg, False)
+
+ def test_has_const(self):
+ has_const = ['LOAD_CONST', 'RETURN_CONST', 'KW_NAMES']
+ no_const = ['SETUP_WITH', 'POP_TOP', 'NOP', 'CACHE']
+ self.check_bool_function_result(_opcode.has_const, has_const, True)
+ self.check_bool_function_result(_opcode.has_const, no_const, False)
+
+ def test_has_name(self):
+ has_name = ['STORE_NAME', 'DELETE_ATTR', 'STORE_GLOBAL', 'IMPORT_FROM',
+ 'LOAD_FROM_DICT_OR_GLOBALS']
+ no_name = ['SETUP_WITH', 'POP_TOP', 'NOP', 'CACHE']
+ self.check_bool_function_result(_opcode.has_name, has_name, True)
+ self.check_bool_function_result(_opcode.has_name, no_name, False)
+
+ def test_has_jump(self):
+ has_jump = ['FOR_ITER', 'JUMP_FORWARD', 'JUMP', 'POP_JUMP_IF_TRUE', 'SEND']
+ no_jump = ['SETUP_WITH', 'POP_TOP', 'NOP', 'CACHE']
+ self.check_bool_function_result(_opcode.has_jump, has_jump, True)
+ self.check_bool_function_result(_opcode.has_jump, no_jump, False)
+
+ # the following test is part of the refactor, it will be removed soon
+ def test_against_legacy_bool_values(self):
+ # limiting to ops up to ENTER_EXECUTOR, because everything after that
+ # is not currently categorized correctly in opcode.py.
+ for op in range(0, opcode.opmap['ENTER_EXECUTOR']):
+ with self.subTest(op=op):
+ if opcode.opname[op] != f'<{op}>':
+ self.assertEqual(op in dis.hasarg, _opcode.has_arg(op))
+ self.assertEqual(op in dis.hasconst, _opcode.has_const(op))
+ self.assertEqual(op in dis.hasname, _opcode.has_name(op))
+ self.assertEqual(op in dis.hasjrel, _opcode.has_jump(op))
+
def test_stack_effect(self):
self.assertEqual(stack_effect(dis.opmap['POP_TOP']), -1)
self.assertEqual(stack_effect(dis.opmap['BUILD_SLICE'], 0), -1)
diff --git a/Misc/NEWS.d/next/Library/2023-07-13-16-04-15.gh-issue-105481.pYSwMj.rst b/Misc/NEWS.d/next/Library/2023-07-13-16-04-15.gh-issue-105481.pYSwMj.rst
new file mode 100644
index 0000000000000..bc2ba51d31aa9
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-07-13-16-04-15.gh-issue-105481.pYSwMj.rst
@@ -0,0 +1 @@
+Expose opcode metadata through :mod:`_opcode`.
diff --git a/Modules/_opcode.c b/Modules/_opcode.c
index 3c0fce48770ac..b3b9873d21a5b 100644
--- a/Modules/_opcode.c
+++ b/Modules/_opcode.c
@@ -1,4 +1,5 @@
#include "Python.h"
+#include "compile.h"
#include "opcode.h"
#include "internal/pycore_code.h"
@@ -61,6 +62,91 @@ _opcode_stack_effect_impl(PyObject *module, int opcode, PyObject *oparg,
/*[clinic input]
+_opcode.is_valid -> bool
+
+ opcode: int
+
+Return True if opcode is valid, False otherwise.
+[clinic start generated code]*/
+
+static int
+_opcode_is_valid_impl(PyObject *module, int opcode)
+/*[clinic end generated code: output=b0d918ea1d073f65 input=fe23e0aa194ddae0]*/
+{
+ return PyUnstable_OpcodeIsValid(opcode);
+}
+
+/*[clinic input]
+
+_opcode.has_arg -> bool
+
+ opcode: int
+
+Return True if the opcode uses its oparg, False otherwise.
+[clinic start generated code]*/
+
+static int
+_opcode_has_arg_impl(PyObject *module, int opcode)
+/*[clinic end generated code: output=7a062d3b2dcc0815 input=93d878ba6361db5f]*/
+{
+ return PyUnstable_OpcodeIsValid(opcode) &&
+ PyUnstable_OpcodeHasArg(opcode);
+}
+
+/*[clinic input]
+
+_opcode.has_const -> bool
+
+ opcode: int
+
+Return True if the opcode accesses a constant, False otherwise.
+[clinic start generated code]*/
+
+static int
+_opcode_has_const_impl(PyObject *module, int opcode)
+/*[clinic end generated code: output=c646d5027c634120 input=a6999e4cf13f9410]*/
+{
+ return PyUnstable_OpcodeIsValid(opcode) &&
+ PyUnstable_OpcodeHasConst(opcode);
+}
+
+/*[clinic input]
+
+_opcode.has_name -> bool
+
+ opcode: int
+
+Return True if the opcode accesses an attribute by name, False otherwise.
+[clinic start generated code]*/
+
+static int
+_opcode_has_name_impl(PyObject *module, int opcode)
+/*[clinic end generated code: output=b49a83555c2fa517 input=448aa5e4bcc947ba]*/
+{
+ return PyUnstable_OpcodeIsValid(opcode) &&
+ PyUnstable_OpcodeHasName(opcode);
+}
+
+/*[clinic input]
+
+_opcode.has_jump -> bool
+
+ opcode: int
+
+Return True if the opcode has a jump target, False otherwise.
+[clinic start generated code]*/
+
+static int
+_opcode_has_jump_impl(PyObject *module, int opcode)
+/*[clinic end generated code: output=e9c583c669f1c46a input=35f711274357a0c3]*/
+{
+ return PyUnstable_OpcodeIsValid(opcode) &&
+ PyUnstable_OpcodeHasJump(opcode);
+
+}
+
+/*[clinic input]
+
_opcode.get_specialization_stats
Return the specialization stats
@@ -80,6 +166,11 @@ _opcode_get_specialization_stats_impl(PyObject *module)
static PyMethodDef
opcode_functions[] = {
_OPCODE_STACK_EFFECT_METHODDEF
+ _OPCODE_IS_VALID_METHODDEF
+ _OPCODE_HAS_ARG_METHODDEF
+ _OPCODE_HAS_CONST_METHODDEF
+ _OPCODE_HAS_NAME_METHODDEF
+ _OPCODE_HAS_JUMP_METHODDEF
_OPCODE_GET_SPECIALIZATION_STATS_METHODDEF
{NULL, NULL, 0, NULL}
};
diff --git a/Modules/clinic/_opcode.c.h b/Modules/clinic/_opcode.c.h
index 3bd3ba0238743..3eb050e470c34 100644
--- a/Modules/clinic/_opcode.c.h
+++ b/Modules/clinic/_opcode.c.h
@@ -86,6 +86,321 @@ _opcode_stack_effect(PyObject *module, PyObject *const *args, Py_ssize_t nargs,
return return_value;
}
+PyDoc_STRVAR(_opcode_is_valid__doc__,
+"is_valid($module, /, opcode)\n"
+"--\n"
+"\n"
+"Return True if opcode is valid, False otherwise.");
+
+#define _OPCODE_IS_VALID_METHODDEF \
+ {"is_valid", _PyCFunction_CAST(_opcode_is_valid), METH_FASTCALL|METH_KEYWORDS, _opcode_is_valid__doc__},
+
+static int
+_opcode_is_valid_impl(PyObject *module, int opcode);
+
+static PyObject *
+_opcode_is_valid(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+ #define NUM_KEYWORDS 1
+ static struct {
+ PyGC_Head _this_is_not_used;
+ PyObject_VAR_HEAD
+ PyObject *ob_item[NUM_KEYWORDS];
+ } _kwtuple = {
+ .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+ .ob_item = { &_Py_ID(opcode), },
+ };
+ #undef NUM_KEYWORDS
+ #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+ #else // !Py_BUILD_CORE
+ # define KWTUPLE NULL
+ #endif // !Py_BUILD_CORE
+
+ static const char * const _keywords[] = {"opcode", NULL};
+ static _PyArg_Parser _parser = {
+ .keywords = _keywords,
+ .fname = "is_valid",
+ .kwtuple = KWTUPLE,
+ };
+ #undef KWTUPLE
+ PyObject *argsbuf[1];
+ int opcode;
+ int _return_value;
+
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ opcode = _PyLong_AsInt(args[0]);
+ if (opcode == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ _return_value = _opcode_is_valid_impl(module, opcode);
+ if ((_return_value == -1) && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = PyBool_FromLong((long)_return_value);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_opcode_has_arg__doc__,
+"has_arg($module, /, opcode)\n"
+"--\n"
+"\n"
+"Return True if the opcode uses its oparg, False otherwise.");
+
+#define _OPCODE_HAS_ARG_METHODDEF \
+ {"has_arg", _PyCFunction_CAST(_opcode_has_arg), METH_FASTCALL|METH_KEYWORDS, _opcode_has_arg__doc__},
+
+static int
+_opcode_has_arg_impl(PyObject *module, int opcode);
+
+static PyObject *
+_opcode_has_arg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+ #define NUM_KEYWORDS 1
+ static struct {
+ PyGC_Head _this_is_not_used;
+ PyObject_VAR_HEAD
+ PyObject *ob_item[NUM_KEYWORDS];
+ } _kwtuple = {
+ .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+ .ob_item = { &_Py_ID(opcode), },
+ };
+ #undef NUM_KEYWORDS
+ #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+ #else // !Py_BUILD_CORE
+ # define KWTUPLE NULL
+ #endif // !Py_BUILD_CORE
+
+ static const char * const _keywords[] = {"opcode", NULL};
+ static _PyArg_Parser _parser = {
+ .keywords = _keywords,
+ .fname = "has_arg",
+ .kwtuple = KWTUPLE,
+ };
+ #undef KWTUPLE
+ PyObject *argsbuf[1];
+ int opcode;
+ int _return_value;
+
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ opcode = _PyLong_AsInt(args[0]);
+ if (opcode == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ _return_value = _opcode_has_arg_impl(module, opcode);
+ if ((_return_value == -1) && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = PyBool_FromLong((long)_return_value);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_opcode_has_const__doc__,
+"has_const($module, /, opcode)\n"
+"--\n"
+"\n"
+"Return True if the opcode accesses a constant, False otherwise.");
+
+#define _OPCODE_HAS_CONST_METHODDEF \
+ {"has_const", _PyCFunction_CAST(_opcode_has_const), METH_FASTCALL|METH_KEYWORDS, _opcode_has_const__doc__},
+
+static int
+_opcode_has_const_impl(PyObject *module, int opcode);
+
+static PyObject *
+_opcode_has_const(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+ #define NUM_KEYWORDS 1
+ static struct {
+ PyGC_Head _this_is_not_used;
+ PyObject_VAR_HEAD
+ PyObject *ob_item[NUM_KEYWORDS];
+ } _kwtuple = {
+ .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+ .ob_item = { &_Py_ID(opcode), },
+ };
+ #undef NUM_KEYWORDS
+ #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+ #else // !Py_BUILD_CORE
+ # define KWTUPLE NULL
+ #endif // !Py_BUILD_CORE
+
+ static const char * const _keywords[] = {"opcode", NULL};
+ static _PyArg_Parser _parser = {
+ .keywords = _keywords,
+ .fname = "has_const",
+ .kwtuple = KWTUPLE,
+ };
+ #undef KWTUPLE
+ PyObject *argsbuf[1];
+ int opcode;
+ int _return_value;
+
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ opcode = _PyLong_AsInt(args[0]);
+ if (opcode == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ _return_value = _opcode_has_const_impl(module, opcode);
+ if ((_return_value == -1) && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = PyBool_FromLong((long)_return_value);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_opcode_has_name__doc__,
+"has_name($module, /, opcode)\n"
+"--\n"
+"\n"
+"Return True if the opcode accesses an attribute by name, False otherwise.");
+
+#define _OPCODE_HAS_NAME_METHODDEF \
+ {"has_name", _PyCFunction_CAST(_opcode_has_name), METH_FASTCALL|METH_KEYWORDS, _opcode_has_name__doc__},
+
+static int
+_opcode_has_name_impl(PyObject *module, int opcode);
+
+static PyObject *
+_opcode_has_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+ #define NUM_KEYWORDS 1
+ static struct {
+ PyGC_Head _this_is_not_used;
+ PyObject_VAR_HEAD
+ PyObject *ob_item[NUM_KEYWORDS];
+ } _kwtuple = {
+ .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+ .ob_item = { &_Py_ID(opcode), },
+ };
+ #undef NUM_KEYWORDS
+ #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+ #else // !Py_BUILD_CORE
+ # define KWTUPLE NULL
+ #endif // !Py_BUILD_CORE
+
+ static const char * const _keywords[] = {"opcode", NULL};
+ static _PyArg_Parser _parser = {
+ .keywords = _keywords,
+ .fname = "has_name",
+ .kwtuple = KWTUPLE,
+ };
+ #undef KWTUPLE
+ PyObject *argsbuf[1];
+ int opcode;
+ int _return_value;
+
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ opcode = _PyLong_AsInt(args[0]);
+ if (opcode == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ _return_value = _opcode_has_name_impl(module, opcode);
+ if ((_return_value == -1) && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = PyBool_FromLong((long)_return_value);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_opcode_has_jump__doc__,
+"has_jump($module, /, opcode)\n"
+"--\n"
+"\n"
+"Return True if the opcode has a jump target, False otherwise.");
+
+#define _OPCODE_HAS_JUMP_METHODDEF \
+ {"has_jump", _PyCFunction_CAST(_opcode_has_jump), METH_FASTCALL|METH_KEYWORDS, _opcode_has_jump__doc__},
+
+static int
+_opcode_has_jump_impl(PyObject *module, int opcode);
+
+static PyObject *
+_opcode_has_jump(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+ #define NUM_KEYWORDS 1
+ static struct {
+ PyGC_Head _this_is_not_used;
+ PyObject_VAR_HEAD
+ PyObject *ob_item[NUM_KEYWORDS];
+ } _kwtuple = {
+ .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+ .ob_item = { &_Py_ID(opcode), },
+ };
+ #undef NUM_KEYWORDS
+ #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+ #else // !Py_BUILD_CORE
+ # define KWTUPLE NULL
+ #endif // !Py_BUILD_CORE
+
+ static const char * const _keywords[] = {"opcode", NULL};
+ static _PyArg_Parser _parser = {
+ .keywords = _keywords,
+ .fname = "has_jump",
+ .kwtuple = KWTUPLE,
+ };
+ #undef KWTUPLE
+ PyObject *argsbuf[1];
+ int opcode;
+ int _return_value;
+
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ opcode = _PyLong_AsInt(args[0]);
+ if (opcode == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ _return_value = _opcode_has_jump_impl(module, opcode);
+ if ((_return_value == -1) && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = PyBool_FromLong((long)_return_value);
+
+exit:
+ return return_value;
+}
+
PyDoc_STRVAR(_opcode_get_specialization_stats__doc__,
"get_specialization_stats($module, /)\n"
"--\n"
@@ -103,4 +418,4 @@ _opcode_get_specialization_stats(PyObject *module, PyObject *Py_UNUSED(ignored))
{
return _opcode_get_specialization_stats_impl(module);
}
-/*[clinic end generated code: output=21e3d53a659c651a input=a9049054013a1b77]*/
+/*[clinic end generated code: output=ae2b2ef56d582180 input=a9049054013a1b77]*/
diff --git a/Python/compile.c b/Python/compile.c
index d9e38cfdefaf2..9e86e06777ffa 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -36,7 +36,9 @@
#include "pycore_pystate.h" // _Py_GetConfig()
#include "pycore_symtable.h" // PySTEntryObject, _PyFuture_FromAST()
+#define NEED_OPCODE_METADATA
#include "pycore_opcode_metadata.h" // _PyOpcode_opcode_metadata, _PyOpcode_num_popped/pushed
+#undef NEED_OPCODE_METADATA
#define COMP_GENEXP 0
#define COMP_LISTCOMP 1
@@ -864,6 +866,36 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
return stack_effect(opcode, oparg, -1);
}
+int
+PyUnstable_OpcodeIsValid(int opcode)
+{
+ return IS_VALID_OPCODE(opcode);
+}
+
+int
+PyUnstable_OpcodeHasArg(int opcode)
+{
+ return OPCODE_HAS_ARG(opcode);
+}
+
+int
+PyUnstable_OpcodeHasConst(int opcode)
+{
+ return OPCODE_HAS_CONST(opcode);
+}
+
+int
+PyUnstable_OpcodeHasName(int opcode)
+{
+ return OPCODE_HAS_NAME(opcode);
+}
+
+int
+PyUnstable_OpcodeHasJump(int opcode)
+{
+ return OPCODE_HAS_JUMP(opcode);
+}
+
static int
codegen_addop_noarg(instr_sequence *seq, int opcode, location loc)
{
diff --git a/Python/flowgraph.c b/Python/flowgraph.c
index 04f269c585383..e485ed103147a 100644
--- a/Python/flowgraph.c
+++ b/Python/flowgraph.c
@@ -7,9 +7,7 @@
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
#include "pycore_opcode_utils.h"
-#define NEED_OPCODE_METADATA
-#include "pycore_opcode_metadata.h" // _PyOpcode_opcode_metadata, _PyOpcode_num_popped/pushed
-#undef NEED_OPCODE_METADATA
+#include "pycore_opcode_metadata.h" // OPCODE_HAS_ARG, etc
#undef SUCCESS
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index ce16271097b95..6589289121863 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -308,7 +308,7 @@ def emit_macros(cls, out: Formatter):
for name, value in flags.bitmask.items():
out.emit(
f"#define OPCODE_{name[:-len('_FLAG')]}(OP) "
- f"(_PyOpcode_opcode_metadata[(OP)].flags & ({name}))")
+ f"(_PyOpcode_opcode_metadata[OP].flags & ({name}))")
@dataclasses.dataclass
@@ -1192,6 +1192,8 @@ def write_metadata(self) -> None:
self.write_provenance_header()
+ self.out.emit("\n#include <stdbool.h>")
+
self.write_pseudo_instrs()
self.out.emit("")
@@ -1202,8 +1204,16 @@ def write_metadata(self) -> None:
# Write type definitions
self.out.emit(f"enum InstructionFormat {{ {', '.join(format_enums)} }};")
+ self.out.emit("")
+ self.out.emit(
+ "#define IS_VALID_OPCODE(OP) \\\n"
+ " (((OP) >= 0) && ((OP) < OPCODE_METADATA_SIZE) && \\\n"
+ " (_PyOpcode_opcode_metadata[(OP)].valid_entry))")
+
+ self.out.emit("")
InstructionFlags.emit_macros(self.out)
+ self.out.emit("")
with self.out.block("struct opcode_metadata", ";"):
self.out.emit("bool valid_entry;")
self.out.emit("enum InstructionFormat instr_format;")
@@ -1226,13 +1236,20 @@ def write_metadata(self) -> None:
self.out.emit("")
# Write metadata array declaration
+ self.out.emit("#define OPCODE_METADATA_SIZE 512")
+ self.out.emit("#define OPCODE_UOP_NAME_SIZE 512")
+ self.out.emit("#define OPCODE_MACRO_EXPANSION_SIZE 256")
+ self.out.emit("")
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("extern const char * const _PyOpcode_uop_name[512];")
+ self.out.emit("extern const struct opcode_metadata "
+ "_PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE];")
+ self.out.emit("extern const struct opcode_macro_expansion "
+ "_PyOpcode_macro_expansion[OPCODE_MACRO_EXPANSION_SIZE];")
+ self.out.emit("extern const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE];")
self.out.emit("#else // if NEED_OPCODE_METADATA")
- self.out.emit("const struct opcode_metadata _PyOpcode_opcode_metadata[512] = {")
+ self.out.emit("const struct opcode_metadata "
+ "_PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = {")
# Write metadata for each instruction
for thing in self.everything:
@@ -1253,7 +1270,8 @@ def write_metadata(self) -> None:
self.out.emit("};")
with self.out.block(
- "const struct opcode_macro_expansion _PyOpcode_macro_expansion[256] =",
+ "const struct opcode_macro_expansion "
+ "_PyOpcode_macro_expansion[OPCODE_MACRO_EXPANSION_SIZE] =",
";",
):
# Write macro expansion for each non-pseudo instruction
@@ -1279,7 +1297,7 @@ def write_metadata(self) -> None:
case _:
typing.assert_never(thing)
- with self.out.block("const char * const _PyOpcode_uop_name[512] =", ";"):
+ with self.out.block("const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE] =", ";"):
self.write_uop_items(lambda name, counter: f"[{name}] = \"{name}\",")
self.out.emit("#endif // NEED_OPCODE_METADATA")
More information about the Python-checkins
mailing list