Skip to content

bpo-46528: Simplify the VM's stack manipulations #30902

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jan 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 9 additions & 45 deletions Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -348,46 +348,28 @@ The Python compiler currently generates the following bytecode instructions.

.. opcode:: NOP

Do nothing code. Used as a placeholder by the bytecode optimizer.
Do nothing code. Used as a placeholder by the bytecode optimizer, and to
generate line tracing events.


.. opcode:: POP_TOP

Removes the top-of-stack (TOS) item.


.. opcode:: ROT_TWO

Swaps the two top-most stack items.


.. opcode:: ROT_THREE

Lifts second and third stack item one position up, moves top down to position
three.


.. opcode:: ROT_FOUR

Lifts second, third and fourth stack items one position up, moves top down
to position four.

.. versionadded:: 3.8


.. opcode:: DUP_TOP
.. opcode:: COPY (i)

Duplicates the reference on top of the stack.
Push the *i*-th item to the top of the stack. The item is not removed from its
original location.

.. versionadded:: 3.2
.. versionadded:: 3.11


.. opcode:: DUP_TOP_TWO
.. opcode:: SWAP (i)

Duplicates the two references on top of the stack, leaving them in the
same order.
Swap TOS with the item at position *i*.

.. versionadded:: 3.2
.. versionadded:: 3.11


**Unary operations**
Expand Down Expand Up @@ -689,8 +671,6 @@ iterations of the loop.
success (``True``) or failure (``False``).


All of the following opcodes use their arguments.

.. opcode:: STORE_NAME (namei)

Implements ``name = TOS``. *namei* is the index of *name* in the attribute
Expand Down Expand Up @@ -1217,22 +1197,6 @@ All of the following opcodes use their arguments.
success (``True``) or failure (``False``).


.. opcode:: ROT_N (count)

Lift the top *count* stack items one position up, and move TOS down to
position *count*.

.. versionadded:: 3.10


.. opcode:: COPY (i)

Push the *i*-th item to the top of the stack. The item is not removed from its
original location.

.. versionadded:: 3.11


.. opcode:: RESUME (where)

A no-op. Performs internal tracing, debugging and optimization checks.
Expand Down
5 changes: 3 additions & 2 deletions Doc/whatsnew/3.11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -411,8 +411,9 @@ CPython bytecode changes
indicate failure with :const:`None` (where a tuple of extracted values would
otherwise be).

* Added :opcode:`COPY`, which pushes the *i*-th item to the top of the stack.
The item is not removed from its original location.
* Replace several stack manipulation instructions (``DUP_TOP``, ``DUP_TOP_TWO``,
``ROT_TWO``, ``ROT_THREE``, ``ROT_FOUR``, and ``ROT_N``) with new
:opcode:`COPY` and :opcode:`SWAP` instructions.

* Add :opcode:`POP_JUMP_IF_NOT_NONE` and :opcode:`POP_JUMP_IF_NONE` opcodes to
speed up conditional jumps.
Expand Down
117 changes: 56 additions & 61 deletions Include/opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.11a4 3474 (Add RESUME opcode)
# Python 3.11a5 3475 (Add RETURN_GENERATOR opcode)
# Python 3.11a5 3476 (Add ASYNC_GEN_WRAP opcode)
# Python 3.11a5 3477 (Replace DUP_TOP/DUP_TOP_TWO with COPY and
# ROT_TWO/ROT_THREE/ROT_FOUR/ROT_N with SWAP)

# Python 3.12 will start with magic number 3500

Expand All @@ -395,7 +397,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.

MAGIC_NUMBER = (3476).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3477).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

_PYCACHE = '__pycache__'
Expand Down
7 changes: 1 addition & 6 deletions Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@ def jabs_op(name, op):
# Blank lines correspond to available opcodes

def_op('POP_TOP', 1)
def_op('ROT_TWO', 2)
def_op('ROT_THREE', 3)
def_op('DUP_TOP', 4)
def_op('DUP_TOP_TWO', 5)
def_op('ROT_FOUR', 6)

def_op('NOP', 9)
def_op('UNARY_POSITIVE', 10)
Expand Down Expand Up @@ -116,7 +111,7 @@ def jabs_op(name, op):
name_op('DELETE_ATTR', 96) # ""
name_op('STORE_GLOBAL', 97) # ""
name_op('DELETE_GLOBAL', 98) # ""
def_op('ROT_N', 99)
def_op('SWAP', 99)
def_op('LOAD_CONST', 100) # Index in const list
hasconst.append(100)
name_op('LOAD_NAME', 101) # Index in name list
Expand Down
1 change: 0 additions & 1 deletion Lib/test/test__opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ class OpcodeTests(unittest.TestCase):

def test_stack_effect(self):
self.assertEqual(stack_effect(dis.opmap['POP_TOP']), -1)
self.assertEqual(stack_effect(dis.opmap['DUP_TOP_TWO']), 2)
self.assertEqual(stack_effect(dis.opmap['BUILD_SLICE'], 0), -1)
self.assertEqual(stack_effect(dis.opmap['BUILD_SLICE'], 1), -1)
self.assertEqual(stack_effect(dis.opmap['BUILD_SLICE'], 3), -2)
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1195,8 +1195,8 @@ def _prepare_test_cases():
Instruction(opname='CALL_NO_KW', opcode=169, arg=1, argval=1, argrepr='', offset=156, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=158, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=160, starts_line=25, is_jump_target=False, positions=None),
Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=162, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=164, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=162, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=164, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='CALL_NO_KW', opcode=169, arg=3, argval=3, argrepr='', offset=166, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=168, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='JUMP_FORWARD', opcode=110, arg=25, argval=222, argrepr='to 222', offset=170, starts_line=None, is_jump_target=False, positions=None),
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_peepholer.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ def f():
def test_pack_unpack(self):
for line, elem in (
('a, = a,', 'LOAD_CONST',),
('a, b = a, b', 'ROT_TWO',),
('a, b, c = a, b, c', 'ROT_THREE',),
('a, b = a, b', 'SWAP',),
('a, b, c = a, b, c', 'SWAP',),
):
with self.subTest(line=line):
code = compile(line,'','single')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Replace several stack manipulation instructions (``DUP_TOP``,
``DUP_TOP_TWO``, ``ROT_TWO``, ``ROT_THREE``, ``ROT_FOUR``, and ``ROT_N``)
with new :opcode:`COPY` and :opcode:`SWAP` instructions.
Loading