bpo-39156: Break up COMPARE_OP into four logically distinct opcodes. (GH-17754)

Break up COMPARE_OP into four logically distinct opcodes:
* COMPARE_OP for rich comparisons
* IS_OP for 'is' and 'is not' tests
* CONTAINS_OP for 'in' and 'is not' tests
* JUMP_IF_NOT_EXC_MATCH for checking exceptions in 'try-except' statements.
This commit is contained in:
Mark Shannon 2020-01-14 10:12:45 +00:00 committed by GitHub
parent 62e3973395
commit 9af0e47b17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 4909 additions and 4855 deletions

View File

@ -927,6 +927,20 @@ All of the following opcodes use their arguments.
``cmp_op[opname]``.
.. opcode:: IS_OP (invert)
Performs ``is`` comparison, or ``is not`` if ``invert`` is 1.
.. versionadded:: 3.9
.. opcode:: CONTAINS_OP (invert)
Performs ``in`` comparison, or ``not in`` if ``invert`` is 1.
.. versionadded:: 3.9
.. opcode:: IMPORT_NAME (namei)
Imports the module ``co_names[namei]``. TOS and TOS1 are popped and provide
@ -961,6 +975,13 @@ All of the following opcodes use their arguments.
.. versionadded:: 3.1
.. opcode:: JUMP_IF_NOT_EXC_MATCH (target)
Tests whether the second value on the stack is an exception matching TOS,
and jumps if it is not. Pops two values from the stack.
.. versionadded:: 3.9
.. opcode:: JUMP_IF_TRUE_OR_POP (target)

8
Include/opcode.h generated
View File

@ -93,6 +93,9 @@ extern "C" {
#define POP_JUMP_IF_FALSE 114
#define POP_JUMP_IF_TRUE 115
#define LOAD_GLOBAL 116
#define IS_OP 117
#define CONTAINS_OP 118
#define JUMP_IF_NOT_EXC_MATCH 121
#define SETUP_FINALLY 122
#define LOAD_FAST 124
#define STORE_FAST 125
@ -132,11 +135,6 @@ extern "C" {
remaining private.*/
#define EXCEPT_HANDLER 257
enum cmp_op {PyCmp_LT=Py_LT, PyCmp_LE=Py_LE, PyCmp_EQ=Py_EQ, PyCmp_NE=Py_NE,
PyCmp_GT=Py_GT, PyCmp_GE=Py_GE, PyCmp_IN, PyCmp_NOT_IN,
PyCmp_IS, PyCmp_IS_NOT, PyCmp_EXC_MATCH, PyCmp_BAD};
#define HAS_ARG(op) ((op) >= HAVE_ARGUMENT)
#ifdef __cplusplus

View File

@ -274,6 +274,7 @@ _code_type = type(_write_atomic.__code__)
# Python 3.9a0 3420 (add LOAD_ASSERTION_ERROR #34880)
# Python 3.9a0 3421 (simplified bytecode for with blocks #32949)
# Python 3.9a0 3422 (remove BEGIN_FINALLY, END_FINALLY, CALL_FINALLY, POP_FINALLY bytecodes #33387)
# Python 3.9a2 3423 (add IS_OP, CONTAINS_OP and JUMP_IF_NOT_EXC_MATCH bytecodes #39156)
#
# MAGIC must change whenever the bytecode emitted by the compiler may no
# longer be understood by older implementations of the eval loop (usually
@ -282,7 +283,7 @@ _code_type = type(_write_atomic.__code__)
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
MAGIC_NUMBER = (3422).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3423).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
_PYCACHE = '__pycache__'

View File

@ -21,8 +21,7 @@ try:
except ImportError:
pass
cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is',
'is not', 'exception match', 'BAD')
cmp_op = ('<', '<=', '==', '!=', '>', '>=')
hasconst = []
hasname = []
@ -159,6 +158,10 @@ jabs_op('POP_JUMP_IF_TRUE', 115) # ""
name_op('LOAD_GLOBAL', 116) # Index in name list
def_op('IS_OP', 117)
def_op('CONTAINS_OP', 118)
jabs_op('JUMP_IF_NOT_EXC_MATCH', 121)
jrel_op('SETUP_FINALLY', 122) # Distance to target address
def_op('LOAD_FAST', 124) # Local variable number

View File

@ -278,34 +278,33 @@ dis_traceback = """\
--> 6 BINARY_TRUE_DIVIDE
8 POP_TOP
10 POP_BLOCK
12 JUMP_FORWARD 44 (to 58)
12 JUMP_FORWARD 42 (to 56)
%3d >> 14 DUP_TOP
16 LOAD_GLOBAL 0 (Exception)
18 COMPARE_OP 10 (exception match)
20 POP_JUMP_IF_FALSE 56
22 POP_TOP
24 STORE_FAST 0 (e)
26 POP_TOP
28 SETUP_FINALLY 18 (to 48)
18 JUMP_IF_NOT_EXC_MATCH 54
20 POP_TOP
22 STORE_FAST 0 (e)
24 POP_TOP
26 SETUP_FINALLY 18 (to 46)
%3d 30 LOAD_FAST 0 (e)
32 LOAD_ATTR 1 (__traceback__)
34 STORE_FAST 1 (tb)
36 POP_BLOCK
38 POP_EXCEPT
40 LOAD_CONST 0 (None)
42 STORE_FAST 0 (e)
44 DELETE_FAST 0 (e)
46 JUMP_FORWARD 10 (to 58)
>> 48 LOAD_CONST 0 (None)
50 STORE_FAST 0 (e)
52 DELETE_FAST 0 (e)
54 RERAISE
>> 56 RERAISE
%3d 28 LOAD_FAST 0 (e)
30 LOAD_ATTR 1 (__traceback__)
32 STORE_FAST 1 (tb)
34 POP_BLOCK
36 POP_EXCEPT
38 LOAD_CONST 0 (None)
40 STORE_FAST 0 (e)
42 DELETE_FAST 0 (e)
44 JUMP_FORWARD 10 (to 56)
>> 46 LOAD_CONST 0 (None)
48 STORE_FAST 0 (e)
50 DELETE_FAST 0 (e)
52 RERAISE
>> 54 RERAISE
%3d >> 58 LOAD_FAST 1 (tb)
60 RETURN_VALUE
%3d >> 56 LOAD_FAST 1 (tb)
58 RETURN_VALUE
""" % (TRACEBACK_CODE.co_firstlineno + 1,
TRACEBACK_CODE.co_firstlineno + 2,
TRACEBACK_CODE.co_firstlineno + 3,
@ -506,7 +505,8 @@ class DisTests(unittest.TestCase):
def test_widths(self):
for opcode, opname in enumerate(dis.opname):
if opname in ('BUILD_MAP_UNPACK_WITH_CALL',
'BUILD_TUPLE_UNPACK_WITH_CALL'):
'BUILD_TUPLE_UNPACK_WITH_CALL',
'JUMP_IF_NOT_EXC_MATCH'):
continue
with self.subTest(opname=opname):
width = dis._OPNAME_WIDTH
@ -1045,63 +1045,62 @@ expected_opinfo_jumpy = [
Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=96, starts_line=None, is_jump_target=False),
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=98, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=100, starts_line=None, is_jump_target=False),
Instruction(opname='SETUP_FINALLY', opcode=122, arg=98, argval=202, argrepr='to 202', offset=102, starts_line=20, is_jump_target=True),
Instruction(opname='SETUP_FINALLY', opcode=122, arg=96, argval=200, argrepr='to 200', offset=102, starts_line=20, is_jump_target=True),
Instruction(opname='SETUP_FINALLY', opcode=122, arg=12, argval=118, argrepr='to 118', offset=104, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=106, starts_line=21, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval=0, argrepr='0', offset=108, starts_line=None, is_jump_target=False),
Instruction(opname='BINARY_TRUE_DIVIDE', opcode=27, arg=None, argval=None, argrepr='', offset=110, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=112, starts_line=None, is_jump_target=False),
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=114, starts_line=None, is_jump_target=False),
Instruction(opname='JUMP_FORWARD', opcode=110, arg=28, argval=146, argrepr='to 146', offset=116, starts_line=None, is_jump_target=False),
Instruction(opname='JUMP_FORWARD', opcode=110, arg=26, argval=144, argrepr='to 144', offset=116, starts_line=None, is_jump_target=False),
Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=118, starts_line=22, is_jump_target=True),
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=2, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=120, starts_line=None, is_jump_target=False),
Instruction(opname='COMPARE_OP', opcode=107, arg=10, argval='exception match', argrepr='exception match', offset=122, starts_line=None, is_jump_target=False),
Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=144, argval=144, argrepr='', offset=124, starts_line=None, is_jump_target=False),
Instruction(opname='JUMP_IF_NOT_EXC_MATCH', opcode=121, arg=142, argval=142, argrepr='', offset=122, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=124, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=126, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=128, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=130, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=132, starts_line=23, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=134, starts_line=None, is_jump_target=False),
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=136, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=138, starts_line=None, is_jump_target=False),
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=140, starts_line=None, is_jump_target=False),
Instruction(opname='JUMP_FORWARD', opcode=110, arg=46, argval=190, argrepr='to 190', offset=142, starts_line=None, is_jump_target=False),
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=144, starts_line=None, is_jump_target=True),
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=146, starts_line=25, is_jump_target=True),
Instruction(opname='SETUP_WITH', opcode=143, arg=24, argval=174, argrepr='to 174', offset=148, starts_line=None, is_jump_target=False),
Instruction(opname='STORE_FAST', opcode=125, arg=1, argval='dodgy', argrepr='dodgy', offset=150, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=152, starts_line=26, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Never reach this', argrepr="'Never reach this'", offset=154, starts_line=None, is_jump_target=False),
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=156, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=158, starts_line=None, is_jump_target=False),
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=160, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=162, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=130, starts_line=23, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=132, starts_line=None, is_jump_target=False),
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=134, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=136, starts_line=None, is_jump_target=False),
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=138, starts_line=None, is_jump_target=False),
Instruction(opname='JUMP_FORWARD', opcode=110, arg=46, argval=188, argrepr='to 188', offset=140, starts_line=None, is_jump_target=False),
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=142, starts_line=None, is_jump_target=True),
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=144, starts_line=25, is_jump_target=True),
Instruction(opname='SETUP_WITH', opcode=143, arg=24, argval=172, argrepr='to 172', offset=146, starts_line=None, is_jump_target=False),
Instruction(opname='STORE_FAST', opcode=125, arg=1, argval='dodgy', argrepr='dodgy', offset=148, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=150, starts_line=26, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Never reach this', argrepr="'Never reach this'", offset=152, starts_line=None, is_jump_target=False),
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=154, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=156, starts_line=None, is_jump_target=False),
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=158, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=160, starts_line=None, is_jump_target=False),
Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=162, starts_line=None, is_jump_target=False),
Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=164, starts_line=None, is_jump_target=False),
Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=166, starts_line=None, is_jump_target=False),
Instruction(opname='CALL_FUNCTION', opcode=131, arg=3, argval=3, argrepr='', offset=168, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=170, starts_line=None, is_jump_target=False),
Instruction(opname='JUMP_FORWARD', opcode=110, arg=16, argval=190, argrepr='to 190', offset=172, starts_line=None, is_jump_target=False),
Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=174, starts_line=None, is_jump_target=True),
Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=180, argval=180, argrepr='', offset=176, starts_line=None, is_jump_target=False),
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=178, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=180, starts_line=None, is_jump_target=True),
Instruction(opname='CALL_FUNCTION', opcode=131, arg=3, argval=3, argrepr='', offset=166, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=168, starts_line=None, is_jump_target=False),
Instruction(opname='JUMP_FORWARD', opcode=110, arg=16, argval=188, argrepr='to 188', offset=170, starts_line=None, is_jump_target=False),
Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=172, starts_line=None, is_jump_target=True),
Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=178, argval=178, argrepr='', offset=174, starts_line=None, is_jump_target=False),
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=176, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=178, starts_line=None, is_jump_target=True),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=180, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=182, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=184, starts_line=None, is_jump_target=False),
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=186, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=188, starts_line=None, is_jump_target=False),
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=190, starts_line=None, is_jump_target=True),
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=192, starts_line=28, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=194, starts_line=None, is_jump_target=False),
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=196, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=198, starts_line=None, is_jump_target=False),
Instruction(opname='JUMP_FORWARD', opcode=110, arg=10, argval=212, argrepr='to 212', offset=200, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=202, starts_line=None, is_jump_target=True),
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=204, starts_line=None, is_jump_target=False),
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=206, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=208, starts_line=None, is_jump_target=False),
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=210, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=212, starts_line=None, is_jump_target=True),
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=214, starts_line=None, is_jump_target=False)
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=184, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=186, starts_line=None, is_jump_target=False),
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=188, starts_line=None, is_jump_target=True),
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=190, starts_line=28, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=192, starts_line=None, is_jump_target=False),
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=194, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=196, starts_line=None, is_jump_target=False),
Instruction(opname='JUMP_FORWARD', opcode=110, arg=10, argval=210, argrepr='to 210', offset=198, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=200, starts_line=None, is_jump_target=True),
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=202, starts_line=None, is_jump_target=False),
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=204, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=206, starts_line=None, is_jump_target=False),
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=208, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=210, starts_line=None, is_jump_target=True),
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=212, starts_line=None, is_jump_target=False),
]
# One last piece of inspect fodder to check the default line number handling

View File

@ -65,14 +65,14 @@ class TestTranforms(BytecodeTestCase):
self.check_lnotab(unot)
def test_elim_inversion_of_is_or_in(self):
for line, cmp_op in (
('not a is b', 'is not',),
('not a in b', 'not in',),
('not a is not b', 'is',),
('not a not in b', 'in',),
for line, cmp_op, invert in (
('not a is b', 'IS_OP', 1,),
('not a is not b', 'IS_OP', 0,),
('not a in b', 'CONTAINS_OP', 1,),
('not a not in b', 'CONTAINS_OP', 0,),
):
code = compile(line, '', 'single')
self.assertInBytecode(code, 'COMPARE_OP', cmp_op)
self.assertInBytecode(code, cmp_op, invert)
self.check_lnotab(code)
def test_global_as_constant(self):

View File

@ -434,11 +434,11 @@ class PositionalOnlyTestCase(unittest.TestCase):
def f(x: not (int is int), /): ...
# without constant folding we end up with
# COMPARE_OP(is), UNARY_NOT
# with constant folding we should expect a COMPARE_OP(is not)
# COMPARE_OP(is), IS_OP (0)
# with constant folding we should expect a IS_OP (1)
codes = [(i.opname, i.argval) for i in dis.get_instructions(g)]
self.assertNotIn(('UNARY_NOT', None), codes)
self.assertIn(('COMPARE_OP', 'is not'), codes)
self.assertIn(('IS_OP', 1), codes)
if __name__ == "__main__":

View File

@ -0,0 +1,9 @@
Split the COMPARE_OP bytecode instruction into four distinct instructions.
* COMPARE_OP for rich comparisons
* IS_OP for 'is' and 'is not' tests
* CONTAINS_OP for 'in' and 'is not' tests
* JUMP_IF_NOT_EXC_MATCH for checking exceptions in 'try-except' statements.
This improves the clarity of the interpreter and should provide a modest
speedup.

View File

@ -1247,6 +1247,7 @@ static PYC_MAGIC magic_values[] = {
{ 3360, 3379, L"3.6" },
{ 3390, 3399, L"3.7" },
{ 3400, 3419, L"3.8" },
{ 3420, 3429, L"3.9" },
{ 0 }
};

View File

@ -67,7 +67,6 @@ static void maybe_dtrace_line(PyFrameObject *, int *, int *, int *);
static void dtrace_function_entry(PyFrameObject *);
static void dtrace_function_return(PyFrameObject *);
static PyObject * cmp_outcome(PyThreadState *, int, PyObject *, PyObject *);
static PyObject * import_name(PyThreadState *, PyFrameObject *,
PyObject *, PyObject *, PyObject *);
static PyObject * import_from(PyThreadState *, PyObject *, PyObject *);
@ -2897,12 +2896,13 @@ main_loop:
}
case TARGET(COMPARE_OP): {
assert(oparg <= Py_GE);
PyObject *right = POP();
PyObject *left = TOP();
PyObject *res = cmp_outcome(tstate, oparg, left, right);
PyObject *res = PyObject_RichCompare(left, right, oparg);
SET_TOP(res);
Py_DECREF(left);
Py_DECREF(right);
SET_TOP(res);
if (res == NULL)
goto error;
PREDICT(POP_JUMP_IF_FALSE);
@ -2910,6 +2910,81 @@ main_loop:
DISPATCH();
}
case TARGET(IS_OP): {
PyObject *right = POP();
PyObject *left = TOP();
int res = (left == right)^oparg;
PyObject *b = res ? Py_True : Py_False;
Py_INCREF(b);
SET_TOP(b);
Py_DECREF(left);
Py_DECREF(right);
PREDICT(POP_JUMP_IF_FALSE);
PREDICT(POP_JUMP_IF_TRUE);
FAST_DISPATCH();
}
case TARGET(CONTAINS_OP): {
PyObject *right = POP();
PyObject *left = POP();
int res = PySequence_Contains(right, left);
Py_DECREF(left);
Py_DECREF(right);
if (res < 0) {
goto error;
}
PyObject *b = (res^oparg) ? Py_True : Py_False;
Py_INCREF(b);
PUSH(b);
PREDICT(POP_JUMP_IF_FALSE);
PREDICT(POP_JUMP_IF_TRUE);
FAST_DISPATCH();
}
#define CANNOT_CATCH_MSG "catching classes that do not inherit from "\
"BaseException is not allowed"
case TARGET(JUMP_IF_NOT_EXC_MATCH): {
PyObject *right = POP();
PyObject *left = POP();
if (PyTuple_Check(right)) {
Py_ssize_t i, length;
length = PyTuple_GET_SIZE(right);
for (i = 0; i < length; i++) {
PyObject *exc = PyTuple_GET_ITEM(right, i);
if (!PyExceptionClass_Check(exc)) {
_PyErr_SetString(tstate, PyExc_TypeError,
CANNOT_CATCH_MSG);
Py_DECREF(left);
Py_DECREF(right);
goto error;
}
}
}
else {
if (!PyExceptionClass_Check(right)) {
_PyErr_SetString(tstate, PyExc_TypeError,
CANNOT_CATCH_MSG);
Py_DECREF(left);
Py_DECREF(right);
goto error;
}
}
int res = PyErr_GivenExceptionMatches(left, right);
Py_DECREF(left);
Py_DECREF(right);
if (res > 0) {
/* Exception matches -- Do nothing */;
}
else if (res == 0) {
JUMPTO(oparg);
}
else {
goto error;
}
DISPATCH();
}
case TARGET(IMPORT_NAME): {
PyObject *name = GETITEM(names, oparg);
PyObject *fromlist = POP();
@ -4951,62 +5026,6 @@ _PyEval_SliceIndexNotNone(PyObject *v, Py_ssize_t *pi)
return 1;
}
#define CANNOT_CATCH_MSG "catching classes that do not inherit from "\
"BaseException is not allowed"
static PyObject *
cmp_outcome(PyThreadState *tstate, int op, PyObject *v, PyObject *w)
{
int res = 0;
switch (op) {
case PyCmp_IS:
res = (v == w);
break;
case PyCmp_IS_NOT:
res = (v != w);
break;
case PyCmp_IN:
res = PySequence_Contains(w, v);
if (res < 0)
return NULL;
break;
case PyCmp_NOT_IN:
res = PySequence_Contains(w, v);
if (res < 0)
return NULL;
res = !res;
break;
case PyCmp_EXC_MATCH:
if (PyTuple_Check(w)) {
Py_ssize_t i, length;
length = PyTuple_Size(w);
for (i = 0; i < length; i += 1) {
PyObject *exc = PyTuple_GET_ITEM(w, i);
if (!PyExceptionClass_Check(exc)) {
_PyErr_SetString(tstate, PyExc_TypeError,
CANNOT_CATCH_MSG);
return NULL;
}
}
}
else {
if (!PyExceptionClass_Check(w)) {
_PyErr_SetString(tstate, PyExc_TypeError,
CANNOT_CATCH_MSG);
return NULL;
}
}
res = PyErr_GivenExceptionMatches(v, w);
break;
default:
return PyObject_RichCompare(v, w, op);
}
v = res ? Py_True : Py_False;
Py_INCREF(v);
return v;
}
static PyObject *
import_name(PyThreadState *tstate, PyFrameObject *f,
PyObject *name, PyObject *fromlist, PyObject *level)

View File

@ -1021,7 +1021,11 @@ stack_effect(int opcode, int oparg, int jump)
case LOAD_ATTR:
return 0;
case COMPARE_OP:
case IS_OP:
case CONTAINS_OP:
return -1;
case JUMP_IF_NOT_EXC_MATCH:
return -2;
case IMPORT_NAME:
return -1;
case IMPORT_FROM:
@ -1502,6 +1506,12 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute)
return 0; \
}
#define ADDOP_COMPARE(C, CMP) { \
if (!compiler_addcompare((C), (cmpop_ty)(CMP))) \
return 0; \
}
/* VISIT and VISIT_SEQ takes an ASDL type as their second argument. They use
the ASDL name to synthesize the name of the C type and the visit function.
*/
@ -2433,35 +2443,49 @@ check_compare(struct compiler *c, expr_ty e)
return 1;
}
static int
cmpop(cmpop_ty op)
static int compiler_addcompare(struct compiler *c, cmpop_ty op)
{
int cmp;
switch (op) {
case Eq:
return PyCmp_EQ;
cmp = Py_EQ;
break;
case NotEq:
return PyCmp_NE;
cmp = Py_NE;
break;
case Lt:
return PyCmp_LT;
cmp = Py_LT;
break;
case LtE:
return PyCmp_LE;
cmp = Py_LE;
break;
case Gt:
return PyCmp_GT;
cmp = Py_GT;
break;
case GtE:
return PyCmp_GE;
cmp = Py_GE;
break;
case Is:
return PyCmp_IS;
ADDOP_I(c, IS_OP, 0);
return 1;
case IsNot:
return PyCmp_IS_NOT;
ADDOP_I(c, IS_OP, 1);
return 1;
case In:
return PyCmp_IN;
ADDOP_I(c, CONTAINS_OP, 0);
return 1;
case NotIn:
return PyCmp_NOT_IN;
ADDOP_I(c, CONTAINS_OP, 1);
return 1;
default:
return PyCmp_BAD;
Py_UNREACHABLE();
}
ADDOP_I(c, COMPARE_OP, cmp);
return 1;
}
static int
compiler_jump_if(struct compiler *c, expr_ty e, basicblock *next, int cond)
{
@ -2526,14 +2550,12 @@ compiler_jump_if(struct compiler *c, expr_ty e, basicblock *next, int cond)
(expr_ty)asdl_seq_GET(e->v.Compare.comparators, i));
ADDOP(c, DUP_TOP);
ADDOP(c, ROT_THREE);
ADDOP_I(c, COMPARE_OP,
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, i))));
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, i));
ADDOP_JABS(c, POP_JUMP_IF_FALSE, cleanup);
NEXT_BLOCK(c);
}
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n));
ADDOP_I(c, COMPARE_OP,
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, n))));
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, n));
ADDOP_JABS(c, cond ? POP_JUMP_IF_TRUE : POP_JUMP_IF_FALSE, next);
basicblock *end = compiler_new_block(c);
if (end == NULL)
@ -2976,8 +2998,7 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
[tb, val, exc] L1: DUP )
[tb, val, exc, exc] <evaluate E1> )
[tb, val, exc, exc, E1] COMPARE_OP EXC_MATCH ) only if E1
[tb, val, exc, 1-or-0] POP_JUMP_IF_FALSE L2 )
[tb, val, exc, exc, E1] JUMP_IF_NOT_EXC_MATCH L2 ) only if E1
[tb, val, exc] POP
[tb, val] <assign to V1> (or POP if no V1)
[tb] POP
@ -3029,8 +3050,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
if (handler->v.ExceptHandler.type) {
ADDOP(c, DUP_TOP);
VISIT(c, expr, handler->v.ExceptHandler.type);
ADDOP_I(c, COMPARE_OP, PyCmp_EXC_MATCH);
ADDOP_JABS(c, POP_JUMP_IF_FALSE, except);
ADDOP_JABS(c, JUMP_IF_NOT_EXC_MATCH, except);
}
ADDOP(c, POP_TOP);
if (handler->v.ExceptHandler.name) {
@ -3873,8 +3893,7 @@ compiler_compare(struct compiler *c, expr_ty e)
n = asdl_seq_LEN(e->v.Compare.ops) - 1;
if (n == 0) {
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, 0));
ADDOP_I(c, COMPARE_OP,
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, 0))));
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, 0));
}
else {
basicblock *cleanup = compiler_new_block(c);
@ -3885,14 +3904,12 @@ compiler_compare(struct compiler *c, expr_ty e)
(expr_ty)asdl_seq_GET(e->v.Compare.comparators, i));
ADDOP(c, DUP_TOP);
ADDOP(c, ROT_THREE);
ADDOP_I(c, COMPARE_OP,
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, i))));
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, i));
ADDOP_JABS(c, JUMP_IF_FALSE_OR_POP, cleanup);
NEXT_BLOCK(c);
}
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n));
ADDOP_I(c, COMPARE_OP,
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, n))));
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, n));
basicblock *end = compiler_new_block(c);
if (end == NULL)
return 0;

2930
Python/importlib.h generated

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -116,11 +116,11 @@ static void *opcode_targets[256] = {
&&TARGET_POP_JUMP_IF_FALSE,
&&TARGET_POP_JUMP_IF_TRUE,
&&TARGET_LOAD_GLOBAL,
&&TARGET_IS_OP,
&&TARGET_CONTAINS_OP,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&TARGET_JUMP_IF_NOT_EXC_MATCH,
&&TARGET_SETUP_FINALLY,
&&_unknown_opcode,
&&TARGET_LOAD_FAST,

View File

@ -12,10 +12,10 @@
#define UNCONDITIONAL_JUMP(op) (op==JUMP_ABSOLUTE || op==JUMP_FORWARD)
#define CONDITIONAL_JUMP(op) (op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP || op==JUMP_IF_NOT_EXC_MATCH)
#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE \
|| op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP || op==JUMP_IF_NOT_EXC_MATCH)
#define JUMPS_ON_TRUE(op) (op==POP_JUMP_IF_TRUE || op==JUMP_IF_TRUE_OR_POP)
#define GETJUMPTGT(arr, i) (get_arg(arr, i) / sizeof(_Py_CODEUNIT) + \
(ABSOLUTE_JUMP(_Py_OPCODE(arr[i])) ? 0 : i+1))
@ -194,6 +194,7 @@ markblocks(_Py_CODEUNIT *code, Py_ssize_t len)
case JUMP_IF_TRUE_OR_POP:
case POP_JUMP_IF_FALSE:
case POP_JUMP_IF_TRUE:
case JUMP_IF_NOT_EXC_MATCH:
case JUMP_ABSOLUTE:
case SETUP_FINALLY:
case SETUP_WITH:
@ -493,6 +494,7 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
case POP_JUMP_IF_TRUE:
case JUMP_IF_FALSE_OR_POP:
case JUMP_IF_TRUE_OR_POP:
case JUMP_IF_NOT_EXC_MATCH:
j = blocks[j / sizeof(_Py_CODEUNIT)] * sizeof(_Py_CODEUNIT);
break;

View File

@ -22,11 +22,6 @@ footer = """
remaining private.*/
#define EXCEPT_HANDLER 257
enum cmp_op {PyCmp_LT=Py_LT, PyCmp_LE=Py_LE, PyCmp_EQ=Py_EQ, PyCmp_NE=Py_NE,
PyCmp_GT=Py_GT, PyCmp_GE=Py_GE, PyCmp_IN, PyCmp_NOT_IN,
PyCmp_IS, PyCmp_IS_NOT, PyCmp_EXC_MATCH, PyCmp_BAD};
#define HAS_ARG(op) ((op) >= HAVE_ARGUMENT)
#ifdef __cplusplus