gh-94216: add pseudo instructions to the dis/opcodes modules (GH-94241)

This commit is contained in:
Irit Katriel 2022-07-01 15:33:35 +01:00 committed by GitHub
parent be80db14c4
commit c57aad777a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 245 additions and 83 deletions

View File

@ -1351,13 +1351,74 @@ iterations of the loop.
.. opcode:: HAVE_ARGUMENT .. opcode:: HAVE_ARGUMENT
This is not really an opcode. It identifies the dividing line between This is not really an opcode. It identifies the dividing line between
opcodes which don't use their argument and those that do opcodes in the range [0,255] which don't use their argument and those
(``< HAVE_ARGUMENT`` and ``>= HAVE_ARGUMENT``, respectively). that do (``< HAVE_ARGUMENT`` and ``>= HAVE_ARGUMENT``, respectively).
If your application uses pseudo instructions, use the :data:`hasarg`
collection instead.
.. versionchanged:: 3.6 .. versionchanged:: 3.6
Now every instruction has an argument, but opcodes ``< HAVE_ARGUMENT`` Now every instruction has an argument, but opcodes ``< HAVE_ARGUMENT``
ignore it. Before, only opcodes ``>= HAVE_ARGUMENT`` had an argument. ignore it. Before, only opcodes ``>= HAVE_ARGUMENT`` had an argument.
.. versionchanged:: 3.12
Pseudo instructions were added to the :mod:`dis` module, and for them
it is not true that comparison with ``HAVE_ARGUMENT`` indicates whether
they use their arg.
**Pseudo-instructions**
These opcodes do not appear in python bytecode, they are used by the compiler
but are replaced by real opcodes or removed before bytecode is generated.
.. opcode:: SETUP_FINALLY (target)
Set up an exception handler for the following code block. If an exception
occurs, the value stack level is restored to its current state and control
is transferred to the exception handler at ``target``.
.. opcode:: SETUP_CLEANUP (target)
Like ``SETUP_FINALLY``, but in case of exception also pushes the last
instruction (``lasti``) to the stack so that ``RERAISE`` can restore it.
If an exception occurs, the value stack level and the last instruction on
the frame are restored to their current state, and control is transferred
to the exception handler at ``target``.
.. opcode:: SETUP_WITH (target)
Like ``SETUP_CLEANUP``, but in case of exception one more item is popped
from the stack before control is transferred to the exception handler at
``target``.
This variant is used in :keyword:`with` and :keyword:`async with`
constructs, which push the return value of the context manager's
:meth:`~object.__enter__` or :meth:`~object.__aenter__` to the stack.
.. opcode:: POP_BLOCK
Marks the end of the code block associated with the last ``SETUP_FINALLY``,
``SETUP_CLEANUP`` or ``SETUP_WITH``.
.. opcode:: JUMP
.. opcode:: JUMP_NO_INTERRUPT
.. opcode:: POP_JUMP_IF_FALSE
.. opcode:: POP_JUMP_IF_TRUE
.. opcode:: POP_JUMP_IF_NONE
.. opcode:: POP_JUMP_IF_NOT_NONE
Undirected relative jump instructions which are replaced by their
directed (forward/backward) counterparts by the assembler.
.. opcode:: LOAD_METHOD
Optimized unbound method lookup. Emitted as a ``LOAD_ATTR`` opcode
with a flag set in the arg.
.. _opcode_collections: .. _opcode_collections:
@ -1367,6 +1428,10 @@ Opcode collections
These collections are provided for automatic introspection of bytecode These collections are provided for automatic introspection of bytecode
instructions: instructions:
.. versionchanged:: 3.12
The collections now contain pseudo instructions as well. These are
opcodes with values ``>= MIN_PSEUDO_OPCODE``.
.. data:: opname .. data:: opname
Sequence of operation names, indexable using the bytecode. Sequence of operation names, indexable using the bytecode.
@ -1382,6 +1447,13 @@ instructions:
Sequence of all compare operation names. Sequence of all compare operation names.
.. data:: hasarg
Sequence of bytecodes that use their argument.
.. versionadded:: 3.12
.. data:: hasconst .. data:: hasconst
Sequence of bytecodes that access a constant. Sequence of bytecodes that access a constant.
@ -1418,3 +1490,9 @@ instructions:
.. data:: hascompare .. data:: hascompare
Sequence of bytecodes of Boolean operations. Sequence of bytecodes of Boolean operations.
.. data:: hasexc
Sequence of bytecodes that set an exception handler.
.. versionadded:: 3.12

View File

@ -93,6 +93,17 @@ New Modules
Improved Modules Improved Modules
================ ================
dis
---
* Pseudo instruction opcodes (which are used by the compiler but
do not appear in executable bytecode) are now exposed in the
:mod:`dis` module.
:data:`~dis.HAVE_ARGUMENT` is still relevant to real opcodes,
but it is not useful for pseudo instrcutions. Use the new
:data:`~dis.hasarg` collection instead.
(Contributed by Irit Katriel in :gh:`94216`.)
os os
-- --

View File

@ -19,7 +19,7 @@ extern const uint8_t _PyOpcode_Deopt[256];
extern const uint8_t _PyOpcode_Original[256]; extern const uint8_t _PyOpcode_Original[256];
#ifdef NEED_OPCODE_TABLES #ifdef NEED_OPCODE_TABLES
static const uint32_t _PyOpcode_RelativeJump[8] = { static const uint32_t _PyOpcode_RelativeJump[9] = {
0U, 0U,
0U, 0U,
536870912U, 536870912U,
@ -28,8 +28,9 @@ static const uint32_t _PyOpcode_RelativeJump[8] = {
122880U, 122880U,
0U, 0U,
0U, 0U,
1008U,
}; };
static const uint32_t _PyOpcode_Jump[8] = { static const uint32_t _PyOpcode_Jump[9] = {
0U, 0U,
0U, 0U,
536870912U, 536870912U,
@ -38,6 +39,7 @@ static const uint32_t _PyOpcode_Jump[8] = {
122880U, 122880U,
0U, 0U,
0U, 0U,
1008U,
}; };
const uint8_t _PyOpcode_Caches[256] = { const uint8_t _PyOpcode_Caches[256] = {
@ -427,7 +429,7 @@ const uint8_t _PyOpcode_Original[256] = {
#endif // NEED_OPCODE_TABLES #endif // NEED_OPCODE_TABLES
#ifdef Py_DEBUG #ifdef Py_DEBUG
static const char *const _PyOpcode_OpName[256] = { static const char *const _PyOpcode_OpName[267] = {
[CACHE] = "CACHE", [CACHE] = "CACHE",
[POP_TOP] = "POP_TOP", [POP_TOP] = "POP_TOP",
[PUSH_NULL] = "PUSH_NULL", [PUSH_NULL] = "PUSH_NULL",
@ -684,6 +686,17 @@ static const char *const _PyOpcode_OpName[256] = {
[253] = "<253>", [253] = "<253>",
[254] = "<254>", [254] = "<254>",
[DO_TRACING] = "DO_TRACING", [DO_TRACING] = "DO_TRACING",
[SETUP_FINALLY] = "SETUP_FINALLY",
[SETUP_CLEANUP] = "SETUP_CLEANUP",
[SETUP_WITH] = "SETUP_WITH",
[POP_BLOCK] = "POP_BLOCK",
[JUMP] = "JUMP",
[JUMP_NO_INTERRUPT] = "JUMP_NO_INTERRUPT",
[POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE",
[POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE",
[POP_JUMP_IF_NONE] = "POP_JUMP_IF_NONE",
[POP_JUMP_IF_NOT_NONE] = "POP_JUMP_IF_NOT_NONE",
[LOAD_METHOD] = "LOAD_METHOD",
}; };
#endif #endif

32
Include/opcode.h generated
View File

@ -120,6 +120,19 @@ extern "C" {
#define POP_JUMP_BACKWARD_IF_NONE 174 #define POP_JUMP_BACKWARD_IF_NONE 174
#define POP_JUMP_BACKWARD_IF_FALSE 175 #define POP_JUMP_BACKWARD_IF_FALSE 175
#define POP_JUMP_BACKWARD_IF_TRUE 176 #define POP_JUMP_BACKWARD_IF_TRUE 176
#define MIN_PSEUDO_OPCODE 256
#define SETUP_FINALLY 256
#define SETUP_CLEANUP 257
#define SETUP_WITH 258
#define POP_BLOCK 259
#define JUMP 260
#define JUMP_NO_INTERRUPT 261
#define POP_JUMP_IF_FALSE 262
#define POP_JUMP_IF_TRUE 263
#define POP_JUMP_IF_NONE 264
#define POP_JUMP_IF_NOT_NONE 265
#define LOAD_METHOD 266
#define MAX_PSEUDO_OPCODE 266
#define BINARY_OP_ADAPTIVE 3 #define BINARY_OP_ADAPTIVE 3
#define BINARY_OP_ADD_FLOAT 4 #define BINARY_OP_ADD_FLOAT 4
#define BINARY_OP_ADD_INT 5 #define BINARY_OP_ADD_INT 5
@ -194,9 +207,19 @@ extern "C" {
#define UNPACK_SEQUENCE_TWO_TUPLE 182 #define UNPACK_SEQUENCE_TWO_TUPLE 182
#define DO_TRACING 255 #define DO_TRACING 255
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
|| ((op) == JUMP_NO_INTERRUPT) \
|| ((op) == POP_JUMP_IF_FALSE) \
|| ((op) == POP_JUMP_IF_TRUE) \
|| ((op) == POP_JUMP_IF_NONE) \
|| ((op) == POP_JUMP_IF_NOT_NONE) \
|| ((op) == LOAD_METHOD) \
)
#define HAS_CONST(op) (false\ #define HAS_CONST(op) (false\
|| ((op) == 100) \ || ((op) == LOAD_CONST) \
|| ((op) == 172) \ || ((op) == KW_NAMES) \
) )
#define NB_ADD 0 #define NB_ADD 0
@ -226,11 +249,8 @@ extern "C" {
#define NB_INPLACE_TRUE_DIVIDE 24 #define NB_INPLACE_TRUE_DIVIDE 24
#define NB_INPLACE_XOR 25 #define NB_INPLACE_XOR 25
#define HAS_ARG(op) ((op) >= HAVE_ARGUMENT)
/* Reserve some bytecodes for internal use in the compiler. #define IS_PSEUDO_OPCODE(op) (((op) >= MIN_PSEUDO_OPCODE) && ((op) <= MAX_PSEUDO_OPCODE))
* The value of 240 is arbitrary. */
#define IS_ARTIFICIAL(op) ((op) > 240)
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -610,7 +610,7 @@ def _unpack_opargs(code):
op = code[i] op = code[i]
deop = _deoptop(op) deop = _deoptop(op)
caches = _inline_cache_entries[deop] caches = _inline_cache_entries[deop]
if deop >= HAVE_ARGUMENT: if deop in hasarg:
arg = code[i+1] | extended_arg arg = code[i+1] | extended_arg
extended_arg = (arg << 8) if deop == EXTENDED_ARG else 0 extended_arg = (arg << 8) if deop == EXTENDED_ARG else 0
# The oparg is stored as a signed integer # The oparg is stored as a signed integer

View File

@ -4,9 +4,9 @@ opcode module - potentially shared between dis and other modules which
operate on bytecodes (e.g. peephole optimizers). operate on bytecodes (e.g. peephole optimizers).
""" """
__all__ = ["cmp_op", "hasconst", "hasname", "hasjrel", "hasjabs", __all__ = ["cmp_op", "hasarg", "hasconst", "hasname", "hasjrel", "hasjabs",
"haslocal", "hascompare", "hasfree", "opname", "opmap", "haslocal", "hascompare", "hasfree", "hasexc", "opname", "opmap",
"HAVE_ARGUMENT", "EXTENDED_ARG", "hasnargs"] "HAVE_ARGUMENT", "EXTENDED_ARG"]
# It's a chicken-and-egg I'm afraid: # It's a chicken-and-egg I'm afraid:
# We're imported before _opcode's made. # We're imported before _opcode's made.
@ -23,6 +23,7 @@ except ImportError:
cmp_op = ('<', '<=', '==', '!=', '>', '>=') cmp_op = ('<', '<=', '==', '!=', '>', '>=')
hasarg = []
hasconst = [] hasconst = []
hasname = [] hasname = []
hasjrel = [] hasjrel = []
@ -30,13 +31,21 @@ hasjabs = []
haslocal = [] haslocal = []
hascompare = [] hascompare = []
hasfree = [] hasfree = []
hasnargs = [] # unused hasexc = []
def is_pseudo(op):
return op >= MIN_PSEUDO_OPCODE and op <= MAX_PSEUDO_OPCODE
oplists = [hasarg, hasconst, hasname, hasjrel, hasjabs,
haslocal, hascompare, hasfree, hasexc]
opmap = {} opmap = {}
opname = ['<%r>' % (op,) for op in range(256)]
## pseudo opcodes (used in the compiler) mapped to the values
## they can become in the actual code.
_pseudo_ops = {}
def def_op(name, op): def def_op(name, op):
opname[op] = name
opmap[name] = op opmap[name] = op
def name_op(name, op): def name_op(name, op):
@ -51,6 +60,17 @@ def jabs_op(name, op):
def_op(name, op) def_op(name, op)
hasjabs.append(op) hasjabs.append(op)
def pseudo_op(name, op, real_ops):
def_op(name, op)
_pseudo_ops[name] = real_ops
# add the pseudo opcode to the lists its targets are in
for oplist in oplists:
res = [opmap[rop] in oplist for rop in real_ops]
if any(res):
assert all(res)
oplist.append(op)
# Instruction opcodes for compiled code # Instruction opcodes for compiled code
# Blank lines correspond to available opcodes # Blank lines correspond to available opcodes
@ -105,7 +125,7 @@ def_op('ASYNC_GEN_WRAP', 87)
def_op('PREP_RERAISE_STAR', 88) def_op('PREP_RERAISE_STAR', 88)
def_op('POP_EXCEPT', 89) def_op('POP_EXCEPT', 89)
HAVE_ARGUMENT = 90 # Opcodes from here have an argument: HAVE_ARGUMENT = 90 # real opcodes from here have an argument:
name_op('STORE_NAME', 90) # Index in name list name_op('STORE_NAME', 90) # Index in name list
name_op('DELETE_NAME', 91) # "" name_op('DELETE_NAME', 91) # ""
@ -200,8 +220,34 @@ jrel_op('POP_JUMP_BACKWARD_IF_NONE', 174)
jrel_op('POP_JUMP_BACKWARD_IF_FALSE', 175) jrel_op('POP_JUMP_BACKWARD_IF_FALSE', 175)
jrel_op('POP_JUMP_BACKWARD_IF_TRUE', 176) jrel_op('POP_JUMP_BACKWARD_IF_TRUE', 176)
hasarg.extend([op for op in opmap.values() if op >= HAVE_ARGUMENT])
MIN_PSEUDO_OPCODE = 256
pseudo_op('SETUP_FINALLY', 256, ['NOP'])
hasexc.append(256)
pseudo_op('SETUP_CLEANUP', 257, ['NOP'])
hasexc.append(257)
pseudo_op('SETUP_WITH', 258, ['NOP'])
hasexc.append(258)
pseudo_op('POP_BLOCK', 259, ['NOP'])
pseudo_op('JUMP', 260, ['JUMP_FORWARD', 'JUMP_BACKWARD'])
pseudo_op('JUMP_NO_INTERRUPT', 261, ['JUMP_FORWARD', 'JUMP_BACKWARD_NO_INTERRUPT'])
pseudo_op('POP_JUMP_IF_FALSE', 262, ['POP_JUMP_FORWARD_IF_FALSE', 'POP_JUMP_BACKWARD_IF_FALSE'])
pseudo_op('POP_JUMP_IF_TRUE', 263, ['POP_JUMP_FORWARD_IF_TRUE', 'POP_JUMP_BACKWARD_IF_TRUE'])
pseudo_op('POP_JUMP_IF_NONE', 264, ['POP_JUMP_FORWARD_IF_NONE', 'POP_JUMP_BACKWARD_IF_NONE'])
pseudo_op('POP_JUMP_IF_NOT_NONE', 265, ['POP_JUMP_FORWARD_IF_NOT_NONE', 'POP_JUMP_BACKWARD_IF_NOT_NONE'])
pseudo_op('LOAD_METHOD', 266, ['LOAD_ATTR'])
MAX_PSEUDO_OPCODE = MIN_PSEUDO_OPCODE + len(_pseudo_ops) - 1
del def_op, name_op, jrel_op, jabs_op, pseudo_op
opname = ['<%r>' % (op,) for op in range(MAX_PSEUDO_OPCODE + 1)]
for op, i in opmap.items():
opname[i] = op
del def_op, name_op, jrel_op, jabs_op
_nb_ops = [ _nb_ops = [
("NB_ADD", "+"), ("NB_ADD", "+"),

View File

@ -18,9 +18,10 @@ class OpcodeTests(unittest.TestCase):
self.assertRaises(ValueError, stack_effect, dis.opmap['BUILD_SLICE']) self.assertRaises(ValueError, stack_effect, dis.opmap['BUILD_SLICE'])
self.assertRaises(ValueError, stack_effect, dis.opmap['POP_TOP'], 0) self.assertRaises(ValueError, stack_effect, dis.opmap['POP_TOP'], 0)
# All defined opcodes # All defined opcodes
has_arg = dis.hasarg
for name, code in filter(lambda item: item[0] not in dis.deoptmap, dis.opmap.items()): for name, code in filter(lambda item: item[0] not in dis.deoptmap, dis.opmap.items()):
with self.subTest(opname=name): with self.subTest(opname=name):
if code < dis.HAVE_ARGUMENT: if code not in has_arg:
stack_effect(code) stack_effect(code)
self.assertRaises(ValueError, stack_effect, code, 0) self.assertRaises(ValueError, stack_effect, code, 0)
else: else:
@ -46,10 +47,12 @@ class OpcodeTests(unittest.TestCase):
self.assertEqual(stack_effect(JUMP_FORWARD, 0, jump=True), 0) self.assertEqual(stack_effect(JUMP_FORWARD, 0, jump=True), 0)
self.assertEqual(stack_effect(JUMP_FORWARD, 0, jump=False), 0) self.assertEqual(stack_effect(JUMP_FORWARD, 0, jump=False), 0)
# All defined opcodes # All defined opcodes
has_arg = dis.hasarg
has_exc = dis.hasexc
has_jump = dis.hasjabs + dis.hasjrel has_jump = dis.hasjabs + dis.hasjrel
for name, code in filter(lambda item: item[0] not in dis.deoptmap, dis.opmap.items()): for name, code in filter(lambda item: item[0] not in dis.deoptmap, dis.opmap.items()):
with self.subTest(opname=name): with self.subTest(opname=name):
if code < dis.HAVE_ARGUMENT: if code not in has_arg:
common = stack_effect(code) common = stack_effect(code)
jump = stack_effect(code, jump=True) jump = stack_effect(code, jump=True)
nojump = stack_effect(code, jump=False) nojump = stack_effect(code, jump=False)
@ -57,7 +60,7 @@ class OpcodeTests(unittest.TestCase):
common = stack_effect(code, 0) common = stack_effect(code, 0)
jump = stack_effect(code, 0, jump=True) jump = stack_effect(code, 0, jump=True)
nojump = stack_effect(code, 0, jump=False) nojump = stack_effect(code, 0, jump=False)
if code in has_jump: if code in has_jump or code in has_exc:
self.assertEqual(common, max(jump, nojump)) self.assertEqual(common, max(jump, nojump))
else: else:
self.assertEqual(jump, common) self.assertEqual(jump, common)

View File

@ -897,7 +897,7 @@ class DisTests(DisTestBase):
continue continue
with self.subTest(opname=opname): with self.subTest(opname=opname):
width = dis._OPNAME_WIDTH width = dis._OPNAME_WIDTH
if opcode < dis.HAVE_ARGUMENT: if opcode in dis.hasarg:
width += 1 + dis._OPARG_WIDTH width += 1 + dis._OPARG_WIDTH
self.assertLessEqual(len(opname), width) self.assertLessEqual(len(opname), width)

View File

@ -0,0 +1 @@
The :mod:`dis` module now has the opcodes for pseudo instructions (those which are used by the compiler during code generation but then removed or replaced by real opcodes before the final bytecode is emitted).

View File

@ -60,12 +60,7 @@ _opcode_stack_effect_impl(PyObject *module, int opcode, PyObject *oparg,
"stack_effect: jump must be False, True or None"); "stack_effect: jump must be False, True or None");
return -1; return -1;
} }
if (IS_ARTIFICIAL(opcode)) { effect = PyCompile_OpcodeStackEffectWithJump(opcode, oparg_int, jump_int);
effect = PY_INVALID_STACK_EFFECT;
}
else {
effect = PyCompile_OpcodeStackEffectWithJump(opcode, oparg_int, jump_int);
}
if (effect == PY_INVALID_STACK_EFFECT) { if (effect == PY_INVALID_STACK_EFFECT) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"invalid opcode or oparg"); "invalid opcode or oparg");

View File

@ -71,40 +71,14 @@
#define MAX_ALLOWED_STACK_USE (STACK_USE_GUIDELINE * 100) #define MAX_ALLOWED_STACK_USE (STACK_USE_GUIDELINE * 100)
/* Pseudo-instructions used in the compiler, #define MAX_REAL_OPCODE 254
* but turned into NOPs or other instructions
* by the assembler. */
#define SETUP_FINALLY -1
#define SETUP_CLEANUP -2
#define SETUP_WITH -3
#define POP_BLOCK -4
#define JUMP -5
#define JUMP_NO_INTERRUPT -6
#define POP_JUMP_IF_FALSE -7
#define POP_JUMP_IF_TRUE -8
#define POP_JUMP_IF_NONE -9
#define POP_JUMP_IF_NOT_NONE -10
#define LOAD_METHOD -11
#define MIN_VIRTUAL_OPCODE -11
#define MAX_ALLOWED_OPCODE 254
#define IS_WITHIN_OPCODE_RANGE(opcode) \ #define IS_WITHIN_OPCODE_RANGE(opcode) \
((opcode) >= MIN_VIRTUAL_OPCODE && (opcode) <= MAX_ALLOWED_OPCODE) (((opcode) >= 0 && (opcode) <= MAX_REAL_OPCODE) || \
IS_PSEUDO_OPCODE(opcode))
#define IS_VIRTUAL_OPCODE(opcode) ((opcode) < 0)
#define IS_VIRTUAL_JUMP_OPCODE(opcode) \
((opcode) == JUMP || \
(opcode) == JUMP_NO_INTERRUPT || \
(opcode) == POP_JUMP_IF_NONE || \
(opcode) == POP_JUMP_IF_NOT_NONE || \
(opcode) == POP_JUMP_IF_FALSE || \
(opcode) == POP_JUMP_IF_TRUE)
#define IS_JUMP_OPCODE(opcode) \ #define IS_JUMP_OPCODE(opcode) \
(IS_VIRTUAL_JUMP_OPCODE(opcode) || \ is_bit_set_in_table(_PyOpcode_Jump, opcode)
is_bit_set_in_table(_PyOpcode_Jump, opcode))
#define IS_BLOCK_PUSH_OPCODE(opcode) \ #define IS_BLOCK_PUSH_OPCODE(opcode) \
((opcode) == SETUP_FINALLY || \ ((opcode) == SETUP_FINALLY || \
@ -125,7 +99,6 @@
(opcode) == POP_JUMP_FORWARD_IF_FALSE || \ (opcode) == POP_JUMP_FORWARD_IF_FALSE || \
(opcode) == POP_JUMP_BACKWARD_IF_FALSE) (opcode) == POP_JUMP_BACKWARD_IF_FALSE)
#define IS_BACKWARDS_JUMP_OPCODE(opcode) \ #define IS_BACKWARDS_JUMP_OPCODE(opcode) \
((opcode) == JUMP_BACKWARD || \ ((opcode) == JUMP_BACKWARD || \
(opcode) == JUMP_BACKWARD_NO_INTERRUPT || \ (opcode) == JUMP_BACKWARD_NO_INTERRUPT || \
@ -183,11 +156,11 @@ typedef struct exceptstack {
static inline int static inline int
is_bit_set_in_table(const uint32_t *table, int bitindex) { is_bit_set_in_table(const uint32_t *table, int bitindex) {
/* Is the relevant bit set in the relevant word? */ /* Is the relevant bit set in the relevant word? */
/* 256 bits fit into 8 32-bits words. /* 512 bits fit into 9 32-bits words.
* Word is indexed by (bitindex>>ln(size of int in bits)). * Word is indexed by (bitindex>>ln(size of int in bits)).
* Bit within word is the low bits of bitindex. * Bit within word is the low bits of bitindex.
*/ */
if (bitindex >= 0 && bitindex < 256) { if (bitindex >= 0 && bitindex < 512) {
uint32_t word = table[bitindex >> LOG_BITS_PER_INT]; uint32_t word = table[bitindex >> LOG_BITS_PER_INT];
return (word >> (bitindex & MASK_LOW_LOG_BITS)) & 1; return (word >> (bitindex & MASK_LOW_LOG_BITS)) & 1;
} }
@ -218,7 +191,7 @@ static int
instr_size(struct instr *instruction) instr_size(struct instr *instruction)
{ {
int opcode = instruction->i_opcode; int opcode = instruction->i_opcode;
assert(!IS_VIRTUAL_OPCODE(opcode)); assert(!IS_PSEUDO_OPCODE(opcode));
int oparg = HAS_ARG(opcode) ? instruction->i_oparg : 0; int oparg = HAS_ARG(opcode) ? instruction->i_oparg : 0;
int extended_args = (0xFFFFFF < oparg) + (0xFFFF < oparg) + (0xFF < oparg); int extended_args = (0xFFFFFF < oparg) + (0xFFFF < oparg) + (0xFF < oparg);
int caches = _PyOpcode_Caches[opcode]; int caches = _PyOpcode_Caches[opcode];
@ -229,7 +202,7 @@ static void
write_instr(_Py_CODEUNIT *codestr, struct instr *instruction, int ilen) write_instr(_Py_CODEUNIT *codestr, struct instr *instruction, int ilen)
{ {
int opcode = instruction->i_opcode; int opcode = instruction->i_opcode;
assert(!IS_VIRTUAL_OPCODE(opcode)); assert(!IS_PSEUDO_OPCODE(opcode));
int oparg = HAS_ARG(opcode) ? instruction->i_oparg : 0; int oparg = HAS_ARG(opcode) ? instruction->i_oparg : 0;
int caches = _PyOpcode_Caches[opcode]; int caches = _PyOpcode_Caches[opcode];
switch (ilen - caches) { switch (ilen - caches) {
@ -1274,7 +1247,7 @@ static int
is_end_of_basic_block(struct instr *instr) is_end_of_basic_block(struct instr *instr)
{ {
int opcode = instr->i_opcode; int opcode = instr->i_opcode;
return is_jump(instr) || IS_SCOPE_EXIT_OPCODE(opcode); return IS_JUMP_OPCODE(opcode) || IS_SCOPE_EXIT_OPCODE(opcode);
} }
static int static int
@ -1324,7 +1297,7 @@ basicblock_addop(basicblock *b, int opcode, int oparg,
static int static int
compiler_addop(struct compiler *c, int opcode, bool line) compiler_addop(struct compiler *c, int opcode, bool line)
{ {
assert(!HAS_ARG(opcode) || IS_ARTIFICIAL(opcode)); assert(!HAS_ARG(opcode));
if (compiler_use_new_implicit_block_if_needed(c) < 0) { if (compiler_use_new_implicit_block_if_needed(c) < 0) {
return -1; return -1;
} }
@ -8990,7 +8963,6 @@ apply_static_swaps(basicblock *block, int i)
static bool static bool
jump_thread(struct instr *inst, struct instr *target, int opcode) jump_thread(struct instr *inst, struct instr *target, int opcode)
{ {
assert(!IS_VIRTUAL_OPCODE(opcode) || IS_VIRTUAL_JUMP_OPCODE(opcode));
assert(is_jump(inst)); assert(is_jump(inst));
assert(is_jump(target)); assert(is_jump(target));
// bpo-45773: If inst->i_target == target->i_target, then nothing actually // bpo-45773: If inst->i_target == target->i_target, then nothing actually

View File

@ -34,7 +34,8 @@ def write_contents(f):
targets = ['_unknown_opcode'] * 256 targets = ['_unknown_opcode'] * 256
targets[255] = "TARGET_DO_TRACING" targets[255] = "TARGET_DO_TRACING"
for opname, op in opcode.opmap.items(): for opname, op in opcode.opmap.items():
targets[op] = "TARGET_%s" % opname if not opcode.is_pseudo(op):
targets[op] = "TARGET_%s" % opname
next_op = 1 next_op = 1
for opname in opcode._specialized_instructions: for opname in opcode._specialized_instructions:
while targets[next_op] != '_unknown_opcode': while targets[next_op] != '_unknown_opcode':

View File

@ -20,11 +20,8 @@ extern "C" {{
""".lstrip() """.lstrip()
footer = """ footer = """
#define HAS_ARG(op) ((op) >= HAVE_ARGUMENT)
/* Reserve some bytecodes for internal use in the compiler. #define IS_PSEUDO_OPCODE(op) (((op) >= MIN_PSEUDO_OPCODE) && ((op) <= MAX_PSEUDO_OPCODE))
* The value of 240 is arbitrary. */
#define IS_ARTIFICIAL(op) ((op) > 240)
#ifdef __cplusplus #ifdef __cplusplus
} }
@ -63,8 +60,8 @@ def write_int_array_from_ops(name, ops, out):
bits = 0 bits = 0
for op in ops: for op in ops:
bits |= 1<<op bits |= 1<<op
out.write(f"static const uint32_t {name}[8] = {{\n") out.write(f"static const uint32_t {name}[9] = {{\n")
for i in range(8): for i in range(9):
out.write(f" {bits & UINT32_MASK}U,\n") out.write(f" {bits & UINT32_MASK}U,\n")
bits >>= 32 bits >>= 32
assert bits == 0 assert bits == 0
@ -81,10 +78,19 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
exec(code, opcode) exec(code, opcode)
opmap = opcode['opmap'] opmap = opcode['opmap']
opname = opcode['opname'] opname = opcode['opname']
hasarg = opcode['hasarg']
hasconst = opcode['hasconst'] hasconst = opcode['hasconst']
hasjrel = opcode['hasjrel'] hasjrel = opcode['hasjrel']
hasjabs = opcode['hasjabs'] hasjabs = opcode['hasjabs']
used = [ False ] * 256 is_pseudo = opcode['is_pseudo']
_pseudo_ops = opcode['_pseudo_ops']
HAVE_ARGUMENT = opcode["HAVE_ARGUMENT"]
MIN_PSEUDO_OPCODE = opcode["MIN_PSEUDO_OPCODE"]
MAX_PSEUDO_OPCODE = opcode["MAX_PSEUDO_OPCODE"]
NUM_OPCODES = len(opname)
used = [ False ] * len(opname)
next_op = 1 next_op = 1
for name, op in opmap.items(): for name, op in opmap.items():
@ -108,9 +114,17 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
for name in opname: for name in opname:
if name in opmap: if name in opmap:
fobj.write(DEFINE.format(name, opmap[name])) op = opmap[name]
if name == 'POP_EXCEPT': # Special entry for HAVE_ARGUMENT if op == HAVE_ARGUMENT:
fobj.write(DEFINE.format("HAVE_ARGUMENT", opcode["HAVE_ARGUMENT"])) fobj.write(DEFINE.format("HAVE_ARGUMENT", HAVE_ARGUMENT))
if op == MIN_PSEUDO_OPCODE:
fobj.write(DEFINE.format("MIN_PSEUDO_OPCODE", MIN_PSEUDO_OPCODE))
fobj.write(DEFINE.format(name, op))
if op == MAX_PSEUDO_OPCODE:
fobj.write(DEFINE.format("MAX_PSEUDO_OPCODE", MAX_PSEUDO_OPCODE))
for name, op in specialized_opmap.items(): for name, op in specialized_opmap.items():
fobj.write(DEFINE.format(name, op)) fobj.write(DEFINE.format(name, op))
@ -129,8 +143,9 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
iobj.write("};\n") iobj.write("};\n")
deoptcodes = {} deoptcodes = {}
for basic in opmap: for basic, op in opmap.items():
deoptcodes[basic] = basic if not is_pseudo(op):
deoptcodes[basic] = basic
for basic, family in opcode["_specializations"].items(): for basic, family in opcode["_specializations"].items():
for specialized in family: for specialized in family:
deoptcodes[specialized] = basic deoptcodes[specialized] = basic
@ -146,10 +161,17 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
iobj.write("};\n") iobj.write("};\n")
iobj.write("#endif // NEED_OPCODE_TABLES\n") iobj.write("#endif // NEED_OPCODE_TABLES\n")
fobj.write("\n")
fobj.write("#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\\")
for op in _pseudo_ops:
if opmap[op] in hasarg:
fobj.write(f"\n || ((op) == {op}) \\")
fobj.write("\n )\n")
fobj.write("\n") fobj.write("\n")
fobj.write("#define HAS_CONST(op) (false\\") fobj.write("#define HAS_CONST(op) (false\\")
for op in hasconst: for op in hasconst:
fobj.write(f"\n || ((op) == {op}) \\") fobj.write(f"\n || ((op) == {opname[op]}) \\")
fobj.write("\n )\n") fobj.write("\n )\n")
fobj.write("\n") fobj.write("\n")
@ -158,7 +180,7 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
iobj.write("\n") iobj.write("\n")
iobj.write("#ifdef Py_DEBUG\n") iobj.write("#ifdef Py_DEBUG\n")
iobj.write("static const char *const _PyOpcode_OpName[256] = {\n") iobj.write(f"static const char *const _PyOpcode_OpName[{NUM_OPCODES}] = {{\n")
for op, name in enumerate(opname_including_specialized): for op, name in enumerate(opname_including_specialized):
if name[0] != "<": if name[0] != "<":
op = name op = name