Skip to content

bpo-17611: Move unwinding of stack from interpreter to compiler #4682

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

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
50dfe22
bpo-17611: Move unwinding of stack from interpreter to compiler
pitrou Jul 23, 2017
f65e3e6
Cleanup
pitrou Jul 23, 2017
e9fc614
Improve stack size calculation for SETUP_{EXCEPT,FINALLY}
pitrou Jul 23, 2017
4788ea1
Update frozen importlib
pitrou Nov 6, 2017
a42e21e
Remove useless code
pitrou Jul 23, 2017
071f986
Don't predict FOR_ITER as it breaks tracing without computed gotos
pitrou Jul 23, 2017
33bd715
Remove dead code and fix tracing tests
pitrou Nov 6, 2017
710e2f1
Fix test_dis
pitrou Jul 23, 2017
129798d
Add JUMP_FINALLY to avoid finally block duplication
pitrou Jul 24, 2017
b34c641
Remove finally block duplication
pitrou Jul 24, 2017
72087e6
More precise computation of code object stack size
pitrou Jul 24, 2017
fe0519f
JUMP_FINALLY pushes 6 values to be consistent with the effect of an e…
pitrou Jul 24, 2017
67d8382
Exact stack size computation by popping stale exception state in RERAISE
pitrou Jul 24, 2017
9560ad4
Remove the now pointless POP_MANY
pitrou Jul 24, 2017
456b35d
Add comment for RERAISE
pitrou Jul 24, 2017
ec4f127
Remove JUMP_FINALLY
pitrou Jul 24, 2017
8437c30
Remove last block duplication
pitrou Jul 24, 2017
b33101e
Get rid of END_ITER
pitrou Jul 24, 2017
ea6e3f9
Fix comments in frameobject.c
pitrou Jul 24, 2017
9dee9ff
Add stack size tests
pitrou Jul 29, 2017
1346a9c
Fix tests in test_dis to match new bytecode.
nascheme Nov 28, 2017
ef5e4d6
Slightly more logical order for fblocktype enum.
nascheme Nov 28, 2017
7b4ef3d
Add news file.
nascheme Dec 2, 2017
92448dc
Revert this change to test_peepholer.py.
nascheme Dec 3, 2017
1cd8850
Change test_peepholer check back to assertEqual().
nascheme Dec 3, 2017
3794016
Simplify handling of 'for' loops in frame_setlineno() and fix it for …
serhiy-storchaka Aug 24, 2017
442d30e
Pass 'final' block to compiler_push_finally_try().
nascheme Dec 4, 2017
0610860
Bugfix for fblock_unwind_finally_try(), don't unwind current block.
nascheme Dec 4, 2017
c65e9a6
Make fblock stack into a singly linked list.
nascheme Dec 4, 2017
51746f6
Introduce POP_NO_EXCEPT opcode.
nascheme Dec 23, 2017
3234776
Make PUSH_NO_EXCEPT push a single NULL.
nascheme Dec 28, 2017
40d835b
Restore SETUP_WITH and SETUP_ASYNC_WITH.
nascheme Dec 29, 2017
992b824
Refactor test_dis to make regen of expected instructions cleaner.
nascheme Dec 29, 2017
25db5df
Fix test_dis.
nascheme Dec 29, 2017
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
103 changes: 53 additions & 50 deletions Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,14 @@ The Python compiler currently generates the following bytecode instructions.
three.


.. opcode:: ROT_FOUR

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

.. versionadded:: 3.7


.. opcode:: DUP_TOP

Duplicates the reference on top of the stack.
Expand Down Expand Up @@ -576,11 +584,10 @@ the original TOS1.
Resolves ``__aenter__`` and ``__aexit__`` from the object on top of the
stack. Pushes ``__aexit__`` and result of ``__aenter__()`` to the stack.


.. opcode:: SETUP_ASYNC_WITH

Creates a new frame object.

Pushes a new frame block on the block stack. The top of stack value
is moved to after the new block.


**Miscellaneous opcodes**
Expand All @@ -592,17 +599,6 @@ the original TOS1.
is terminated with :opcode:`POP_TOP`.


.. opcode:: BREAK_LOOP

Terminates a loop due to a :keyword:`break` statement.


.. opcode:: CONTINUE_LOOP (target)

Continues a loop due to a :keyword:`continue` statement. *target* is the
address to jump to (which should be a :opcode:`FOR_ITER` instruction).


.. opcode:: SET_ADD (i)

Calls ``set.add(TOS1[-i], TOS)``. Used to implement set comprehensions.
Expand Down Expand Up @@ -649,6 +645,7 @@ iterations of the loop.

.. versionadded:: 3.6


.. opcode:: IMPORT_STAR

Loads all symbols not starting with ``'_'`` directly from the module TOS to
Expand All @@ -659,7 +656,7 @@ iterations of the loop.
.. opcode:: POP_BLOCK

Removes one block from the block stack. Per frame, there is a stack of
blocks, denoting nested loops, try statements, and such.
blocks, denoting :keyword:`try` statements, and such.


.. opcode:: POP_EXCEPT
Expand All @@ -670,11 +667,28 @@ iterations of the loop.
popped values are used to restore the exception state.


.. opcode:: END_FINALLY
.. opcode:: PUSH_NO_EXCEPT

Push NULL on the stack. This signals to exception handling opcodes that no
exception occurred.

.. versionadded:: 3.7


Terminates a :keyword:`finally` clause. The interpreter recalls whether the
exception has to be re-raised, or whether the function returns, and continues
with the outer-next block.
.. opcode:: POP_NO_EXCEPT

Clear the current exception. Pop exception info from the stack and the
block stack.

.. versionadded:: 3.7


.. opcode:: RERAISE

If a finally block was entered with an exception, re-raise the exception.
Otherwise, exit the finally block normally.

.. versionadded:: 3.7


.. opcode:: LOAD_BUILD_CLASS
Expand All @@ -694,33 +708,34 @@ iterations of the loop.
store it in (a) variable(s) (:opcode:`STORE_FAST`, :opcode:`STORE_NAME`, or
:opcode:`UNPACK_SEQUENCE`).


.. opcode:: WITH_CLEANUP_START

Cleans up the stack when a :keyword:`with` statement block exits. TOS is the
context manager's :meth:`__exit__` bound method. Below TOS are 1--3 values
indicating how/why the finally clause was entered:
Starts cleaning up the stack when a :keyword:`with` statement block exits.

* SECOND = ``None``
* (SECOND, THIRD) = (``WHY_{RETURN,CONTINUE}``), retval
* SECOND = ``WHY_*``; no retval below it
* (SECOND, THIRD, FOURTH) = exc_info()
At the top of the stack are either ``NULL`` (pushed by
:opcode:`BEGIN_FINALLY`), or 6 values pushed if an exception has been
raised in the with block. Below is the context manager's
:meth:`~object.__exit__` bound method.

In the last case, ``TOS(SECOND, THIRD, FOURTH)`` is called, otherwise
``TOS(None, None, None)``. Pushes SECOND and result of the call
to the stack.
If TOS is ``NULL``, calls ``SECOND(None, None, None)``, otherwise calls
``SEVENTH(TOP, SECOND, THIRD)``. Pushes result of the call and TOS to the
stack.


.. opcode:: WITH_CLEANUP_FINISH

Pops exception type and result of 'exit' function call from the stack.
Finishes cleaning up the stack when a :keyword:`with` statement block exits.

If the stack represents an exception, *and* the function call returns a
'true' value, this information is "zapped" and replaced with a single
``WHY_SILENCED`` to prevent :opcode:`END_FINALLY` from re-raising the
exception. (But non-local gotos will still be resumed.)
TOS is result of ``__exit__()`` function call pushed by
:opcode:`WITH_CLEANUP_START`. SECOND indicates the action.

.. XXX explain the WHY stuff!
* If SECOND is ``NULL`` (pushed by :opcode:`BEGIN_FINALLY`) continue from
the next instruction. 4 values are popped from the stack.
* If SECOND is an exception type (pushed when an exception has been raised)
re-raise the exception and restore ``sys.exc_info()``. 9 values are
popped from the stack.

.. versionchanged:: 3.7


All of the following opcodes use their arguments.
Expand Down Expand Up @@ -960,22 +975,10 @@ All of the following opcodes use their arguments.
Loads the global named ``co_names[namei]`` onto the stack.


.. opcode:: SETUP_LOOP (delta)

Pushes a block for a loop onto the block stack. The block spans from the
current instruction with a size of *delta* bytes.


.. opcode:: SETUP_EXCEPT (delta)

Pushes a try block from a try-except clause onto the block stack. *delta*
points to the first except block.


.. opcode:: SETUP_FINALLY (delta)

Pushes a try block from a try-except clause onto the block stack. *delta*
points to the finally block.
Pushes a try block from a try-finally or try-except clause onto the block
stack. *delta* points to the finally block or the first except block.


.. opcode:: LOAD_FAST (var_num)
Expand Down
12 changes: 6 additions & 6 deletions Include/opcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ extern "C" {
#define ROT_THREE 3
#define DUP_TOP 4
#define DUP_TOP_TWO 5
#define ROT_FOUR 6
#define NOP 9
#define UNARY_POSITIVE 10
#define UNARY_NEGATIVE 11
Expand All @@ -29,6 +30,8 @@ extern "C" {
#define BINARY_TRUE_DIVIDE 27
#define INPLACE_FLOOR_DIVIDE 28
#define INPLACE_TRUE_DIVIDE 29
#define WITH_CLEANUP_START 40
#define WITH_CLEANUP_FINISH 41
#define GET_AITER 50
#define GET_ANEXT 51
#define BEFORE_ASYNC_WITH 52
Expand All @@ -55,15 +58,14 @@ extern "C" {
#define INPLACE_AND 77
#define INPLACE_XOR 78
#define INPLACE_OR 79
#define BREAK_LOOP 80
#define WITH_CLEANUP_START 81
#define WITH_CLEANUP_FINISH 82
#define RERAISE 80
#define POP_NO_EXCEPT 81
#define RETURN_VALUE 83
#define IMPORT_STAR 84
#define SETUP_ANNOTATIONS 85
#define YIELD_VALUE 86
#define POP_BLOCK 87
#define END_FINALLY 88
#define PUSH_NO_EXCEPT 88
#define POP_EXCEPT 89
#define HAVE_ARGUMENT 90
#define STORE_NAME 90
Expand Down Expand Up @@ -92,8 +94,6 @@ extern "C" {
#define POP_JUMP_IF_FALSE 114
#define POP_JUMP_IF_TRUE 115
#define LOAD_GLOBAL 116
#define CONTINUE_LOOP 119
#define SETUP_LOOP 120
#define SETUP_EXCEPT 121
#define SETUP_FINALLY 122
#define LOAD_FAST 124
Expand Down
3 changes: 2 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.6rc1 3379 (more thorough __class__ validation #23722)
# Python 3.7a0 3390 (add LOAD_METHOD and CALL_METHOD opcodes)
# Python 3.7a0 3391 (update GET_AITER #31709)
# Python 3.7a0 3392 (bpo-17611: move frame block handling to compiler)
#
# MAGIC must change whenever the bytecode emitted by the compiler may no
# longer be understood by older implementations of the eval loop (usually
Expand All @@ -250,7 +251,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 = (3391).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3392).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

_PYCACHE = '__pycache__'
Expand Down
21 changes: 11 additions & 10 deletions Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def jabs_op(name, op):
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 All @@ -83,6 +84,9 @@ def jabs_op(name, op):
def_op('INPLACE_FLOOR_DIVIDE', 28)
def_op('INPLACE_TRUE_DIVIDE', 29)

def_op('WITH_CLEANUP_START', 40)
def_op('WITH_CLEANUP_FINISH', 41)

def_op('GET_AITER', 50)
def_op('GET_ANEXT', 51)
def_op('BEFORE_ASYNC_WITH', 52)
Expand Down Expand Up @@ -113,16 +117,15 @@ def jabs_op(name, op):
def_op('INPLACE_AND', 77)
def_op('INPLACE_XOR', 78)
def_op('INPLACE_OR', 79)
def_op('BREAK_LOOP', 80)
def_op('WITH_CLEANUP_START', 81)
def_op('WITH_CLEANUP_FINISH', 82)
def_op('RERAISE', 80)
def_op('POP_NO_EXCEPT', 81)

def_op('RETURN_VALUE', 83)
def_op('IMPORT_STAR', 84)
def_op('SETUP_ANNOTATIONS', 85)
def_op('YIELD_VALUE', 86)
def_op('POP_BLOCK', 87)
def_op('END_FINALLY', 88)
def_op('PUSH_NO_EXCEPT', 88)
def_op('POP_EXCEPT', 89)

HAVE_ARGUMENT = 90 # Opcodes from here have an argument:
Expand Down Expand Up @@ -158,9 +161,7 @@ def jabs_op(name, op):

name_op('LOAD_GLOBAL', 116) # Index in name list

jabs_op('CONTINUE_LOOP', 119) # Target address
jrel_op('SETUP_LOOP', 120) # Distance to target address
jrel_op('SETUP_EXCEPT', 121) # ""
jrel_op('SETUP_EXCEPT', 121) # Distance to target address
jrel_op('SETUP_FINALLY', 122) # ""

def_op('LOAD_FAST', 124) # Local variable number
Expand Down Expand Up @@ -189,16 +190,16 @@ def jabs_op(name, op):

jrel_op('SETUP_WITH', 143)

def_op('EXTENDED_ARG', 144)
EXTENDED_ARG = 144

def_op('LIST_APPEND', 145)
def_op('SET_ADD', 146)
def_op('MAP_ADD', 147)

def_op('LOAD_CLASSDEREF', 148)
hasfree.append(148)

def_op('EXTENDED_ARG', 144)
EXTENDED_ARG = 144

def_op('BUILD_LIST_UNPACK', 149)
def_op('BUILD_MAP_UNPACK', 150)
def_op('BUILD_MAP_UNPACK_WITH_CALL', 151)
Expand Down
Loading