From 6a70edf24ca217c5ed4a556d0df5748fc775c762 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Fri, 14 Jul 2023 18:41:52 +0100 Subject: gh-105481: expose opcode metadata via the _opcode module (#106688) --- Include/cpython/compile.h | 6 + Include/internal/pycore_opcode_metadata.h | 32 ++- Lib/test/test__opcode.py | 64 +++++ .../2023-07-13-16-04-15.gh-issue-105481.pYSwMj.rst | 1 + Modules/_opcode.c | 91 ++++++ Modules/clinic/_opcode.c.h | 317 ++++++++++++++++++++- Python/compile.c | 32 +++ Python/flowgraph.c | 4 +- Tools/cases_generator/generate_cases.py | 32 ++- 9 files changed, 558 insertions(+), 21 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-07-13-16-04-15.gh-issue-105481.pYSwMj.rst diff --git a/Include/cpython/compile.h b/Include/cpython/compile.h index f5a62a8..cd7fd7b 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 e94732b..8373f56 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 + #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 3e08492..7d9553d 100644 --- a/Lib/test/test__opcode.py +++ b/Lib/test/test__opcode.py @@ -9,6 +9,70 @@ from _opcode import stack_effect 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 0000000..bc2ba51 --- /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 3c0fce4..b3b9873 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 3bd3ba0..3eb050e 100644 --- a/Modules/clinic/_opcode.c.h +++ b/Modules/clinic/_opcode.c.h @@ -86,6 +86,321 @@ exit: 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 d9e38cf..9e86e06 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 04f269c..e485ed1 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 ce16271..6589289 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -308,7 +308,7 @@ class InstructionFlags: 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 @@ class Analyzer: self.write_provenance_header() + self.out.emit("\n#include ") + self.write_pseudo_instrs() self.out.emit("") @@ -1202,8 +1204,16 @@ class Analyzer: # 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 @@ class Analyzer: 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 @@ class Analyzer: 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 @@ class Analyzer: 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") -- cgit v0.12