mirror of
https://github.com/python/cpython.git
synced 2025-09-27 10:50:04 +00:00
gh-105481: expose opcode metadata via the _opcode module (#106688)
This commit is contained in:
parent
243fdcb40e
commit
6a70edf24c
9 changed files with 558 additions and 21 deletions
|
@ -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);
|
||||||
|
|
32
Include/internal/pycore_opcode_metadata.h
generated
32
Include/internal/pycore_opcode_metadata.h
generated
|
@ -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",
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Expose opcode metadata through :mod:`_opcode`.
|
|
@ -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}
|
||||||
};
|
};
|
||||||
|
|
317
Modules/clinic/_opcode.c.h
generated
317
Modules/clinic/_opcode.c.h
generated
|
@ -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]*/
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue