gh-105481: expose opcode metadata via the _opcode module (#106688)

This commit is contained in:
Irit Katriel 2023-07-14 18:41:52 +01:00 committed by GitHub
parent 243fdcb40e
commit 6a70edf24c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 558 additions and 21 deletions

View file

@ -67,3 +67,9 @@ typedef struct {
#define PY_INVALID_STACK_EFFECT INT_MAX #define PY_INVALID_STACK_EFFECT INT_MAX
PyAPI_FUNC(int) PyCompile_OpcodeStackEffect(int opcode, int oparg); PyAPI_FUNC(int) PyCompile_OpcodeStackEffect(int opcode, int oparg);
PyAPI_FUNC(int) PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int jump); 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);

View file

@ -3,6 +3,8 @@
// Python/bytecodes.c // Python/bytecodes.c
// Do not edit! // Do not edit!
#include <stdbool.h>
#define IS_PSEUDO_INSTR(OP) ( \ #define IS_PSEUDO_INSTR(OP) ( \
((OP) == LOAD_CLOSURE) || \ ((OP) == LOAD_CLOSURE) || \
@ -941,14 +943,20 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
#endif #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 }; 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_ARG_FLAG (1)
#define HAS_CONST_FLAG (2) #define HAS_CONST_FLAG (2)
#define HAS_NAME_FLAG (4) #define HAS_NAME_FLAG (4)
#define HAS_JUMP_FLAG (8) #define HAS_JUMP_FLAG (8)
#define OPCODE_HAS_ARG(OP) (_PyOpcode_opcode_metadata[(OP)].flags & (HAS_ARG_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_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_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_JUMP(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_JUMP_FLAG))
struct opcode_metadata { struct opcode_metadata {
bool valid_entry; bool valid_entry;
enum InstructionFormat instr_format; enum InstructionFormat instr_format;
@ -971,12 +979,16 @@ struct opcode_macro_expansion {
#define SAME_OPCODE_METADATA(OP1, OP2) \ #define SAME_OPCODE_METADATA(OP1, OP2) \
(OPCODE_METADATA_FMT(OP1) == OPCODE_METADATA_FMT(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 #ifndef NEED_OPCODE_METADATA
extern const struct opcode_metadata _PyOpcode_opcode_metadata[512]; extern const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE];
extern const struct opcode_macro_expansion _PyOpcode_macro_expansion[256]; extern const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPANSION_SIZE];
extern const char * const _PyOpcode_uop_name[512]; extern const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE];
#else // if NEED_OPCODE_METADATA #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 }, [NOP] = { true, INSTR_FMT_IX, 0 },
[RESUME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [RESUME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG },
[INSTRUMENTED_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 }, [CACHE] = { true, INSTR_FMT_IX, 0 },
[RESERVED] = { 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 } } }, [NOP] = { .nuops = 1, .uops = { { NOP, 0, 0 } } },
[LOAD_FAST_CHECK] = { .nuops = 1, .uops = { { LOAD_FAST_CHECK, 0, 0 } } }, [LOAD_FAST_CHECK] = { .nuops = 1, .uops = { { LOAD_FAST_CHECK, 0, 0 } } },
[LOAD_FAST] = { .nuops = 1, .uops = { { LOAD_FAST, 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 } } }, [BINARY_OP] = { .nuops = 1, .uops = { { BINARY_OP, 0, 0 } } },
[SWAP] = { .nuops = 1, .uops = { { SWAP, 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", [EXIT_TRACE] = "EXIT_TRACE",
[SAVE_IP] = "SAVE_IP", [SAVE_IP] = "SAVE_IP",
[_GUARD_BOTH_INT] = "_GUARD_BOTH_INT", [_GUARD_BOTH_INT] = "_GUARD_BOTH_INT",

View file

@ -9,6 +9,70 @@ from _opcode import stack_effect
class OpcodeTests(unittest.TestCase): 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): def test_stack_effect(self):
self.assertEqual(stack_effect(dis.opmap['POP_TOP']), -1) self.assertEqual(stack_effect(dis.opmap['POP_TOP']), -1)
self.assertEqual(stack_effect(dis.opmap['BUILD_SLICE'], 0), -1) self.assertEqual(stack_effect(dis.opmap['BUILD_SLICE'], 0), -1)

View file

@ -0,0 +1 @@
Expose opcode metadata through :mod:`_opcode`.

View file

@ -1,4 +1,5 @@
#include "Python.h" #include "Python.h"
#include "compile.h"
#include "opcode.h" #include "opcode.h"
#include "internal/pycore_code.h" #include "internal/pycore_code.h"
@ -61,6 +62,91 @@ _opcode_stack_effect_impl(PyObject *module, int opcode, PyObject *oparg,
/*[clinic input] /*[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 _opcode.get_specialization_stats
Return the specialization stats Return the specialization stats
@ -80,6 +166,11 @@ _opcode_get_specialization_stats_impl(PyObject *module)
static PyMethodDef static PyMethodDef
opcode_functions[] = { opcode_functions[] = {
_OPCODE_STACK_EFFECT_METHODDEF _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 _OPCODE_GET_SPECIALIZATION_STATS_METHODDEF
{NULL, NULL, 0, NULL} {NULL, NULL, 0, NULL}
}; };

View file

@ -86,6 +86,321 @@ exit:
return return_value; 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__, PyDoc_STRVAR(_opcode_get_specialization_stats__doc__,
"get_specialization_stats($module, /)\n" "get_specialization_stats($module, /)\n"
"--\n" "--\n"
@ -103,4 +418,4 @@ _opcode_get_specialization_stats(PyObject *module, PyObject *Py_UNUSED(ignored))
{ {
return _opcode_get_specialization_stats_impl(module); return _opcode_get_specialization_stats_impl(module);
} }
/*[clinic end generated code: output=21e3d53a659c651a input=a9049054013a1b77]*/ /*[clinic end generated code: output=ae2b2ef56d582180 input=a9049054013a1b77]*/

View file

@ -36,7 +36,9 @@
#include "pycore_pystate.h" // _Py_GetConfig() #include "pycore_pystate.h" // _Py_GetConfig()
#include "pycore_symtable.h" // PySTEntryObject, _PyFuture_FromAST() #include "pycore_symtable.h" // PySTEntryObject, _PyFuture_FromAST()
#define NEED_OPCODE_METADATA
#include "pycore_opcode_metadata.h" // _PyOpcode_opcode_metadata, _PyOpcode_num_popped/pushed #include "pycore_opcode_metadata.h" // _PyOpcode_opcode_metadata, _PyOpcode_num_popped/pushed
#undef NEED_OPCODE_METADATA
#define COMP_GENEXP 0 #define COMP_GENEXP 0
#define COMP_LISTCOMP 1 #define COMP_LISTCOMP 1
@ -864,6 +866,36 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
return stack_effect(opcode, oparg, -1); 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 static int
codegen_addop_noarg(instr_sequence *seq, int opcode, location loc) codegen_addop_noarg(instr_sequence *seq, int opcode, location loc)
{ {

View file

@ -7,9 +7,7 @@
#include "pycore_pymem.h" // _PyMem_IsPtrFreed() #include "pycore_pymem.h" // _PyMem_IsPtrFreed()
#include "pycore_opcode_utils.h" #include "pycore_opcode_utils.h"
#define NEED_OPCODE_METADATA #include "pycore_opcode_metadata.h" // OPCODE_HAS_ARG, etc
#include "pycore_opcode_metadata.h" // _PyOpcode_opcode_metadata, _PyOpcode_num_popped/pushed
#undef NEED_OPCODE_METADATA
#undef SUCCESS #undef SUCCESS

View file

@ -308,7 +308,7 @@ class InstructionFlags:
for name, value in flags.bitmask.items(): for name, value in flags.bitmask.items():
out.emit( out.emit(
f"#define OPCODE_{name[:-len('_FLAG')]}(OP) " f"#define OPCODE_{name[:-len('_FLAG')]}(OP) "
f"(_PyOpcode_opcode_metadata[(OP)].flags & ({name}))") f"(_PyOpcode_opcode_metadata[OP].flags & ({name}))")
@dataclasses.dataclass @dataclasses.dataclass
@ -1192,6 +1192,8 @@ class Analyzer:
self.write_provenance_header() self.write_provenance_header()
self.out.emit("\n#include <stdbool.h>")
self.write_pseudo_instrs() self.write_pseudo_instrs()
self.out.emit("") self.out.emit("")
@ -1202,8 +1204,16 @@ class Analyzer:
# Write type definitions # Write type definitions
self.out.emit(f"enum InstructionFormat {{ {', '.join(format_enums)} }};") 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) InstructionFlags.emit_macros(self.out)
self.out.emit("")
with self.out.block("struct opcode_metadata", ";"): with self.out.block("struct opcode_metadata", ";"):
self.out.emit("bool valid_entry;") self.out.emit("bool valid_entry;")
self.out.emit("enum InstructionFormat instr_format;") self.out.emit("enum InstructionFormat instr_format;")
@ -1226,13 +1236,20 @@ class Analyzer:
self.out.emit("") self.out.emit("")
# Write metadata array declaration # 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("#ifndef NEED_OPCODE_METADATA")
self.out.emit("extern const struct opcode_metadata _PyOpcode_opcode_metadata[512];") self.out.emit("extern const struct opcode_metadata "
self.out.emit("extern const struct opcode_macro_expansion _PyOpcode_macro_expansion[256];") "_PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE];")
self.out.emit("extern const char * const _PyOpcode_uop_name[512];") 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("#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 # Write metadata for each instruction
for thing in self.everything: for thing in self.everything:
@ -1253,7 +1270,8 @@ class Analyzer:
self.out.emit("};") self.out.emit("};")
with self.out.block( 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 # Write macro expansion for each non-pseudo instruction
@ -1279,7 +1297,7 @@ class Analyzer:
case _: case _:
typing.assert_never(thing) 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.write_uop_items(lambda name, counter: f"[{name}] = \"{name}\",")
self.out.emit("#endif // NEED_OPCODE_METADATA") self.out.emit("#endif // NEED_OPCODE_METADATA")