Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
a414fa6
Add SETUP_CLEANUP bytecode to track lasti for re-raising exceptions i…
markshannon Apr 19, 2021
902690d
Move some handling of exceptions to bytecode from unwind code.
markshannon Apr 21, 2021
21646b1
Move setting up except block out of unwind code into bytecode.
markshannon Apr 21, 2021
9c9575a
Move cleanup code for try-finally and try-except in bytecode.
markshannon Apr 21, 2021
3be89db
Move cleanup code for with and sync with into bytecode.
markshannon Apr 21, 2021
cf664f7
Simplify exit code for async for loops.
markshannon Apr 21, 2021
722b722
Remove SETUP_EXCEPT opcode.
markshannon Apr 21, 2021
caa355f
Remove EXCEPT_HANDLER as a type of exception handling block.
markshannon Apr 21, 2021
4c21ddd
Factor out all block pops and pushes into simple opcodes.
markshannon Apr 21, 2021
6a8d90c
Generate exception handling table
markshannon Apr 22, 2021
9115cea
Parse and verify exception table when unwinding exceptions.
markshannon Apr 23, 2021
350334c
Update test to account for new code object constructor.
markshannon Apr 26, 2021
d6da9cb
Push lasti to stack when cleaning up exception handlers, and use it t…
markshannon Apr 26, 2021
a6b9292
Remove block stack from frames add convert SETUP/POP_BLOCK instructio…
markshannon Apr 26, 2021
9d0a6c1
Make dis understand the exception table.
markshannon Apr 29, 2021
ca482d0
Merge branch 'master' into zero-cost-except
markshannon Apr 29, 2021
4629a97
Set line numbers at start of exception handlers in try-except and try…
markshannon Apr 30, 2021
58b4a5a
Handle exceptions in trace functions correctly.
markshannon Apr 30, 2021
e7a9221
Fix syntax error test.
markshannon Apr 30, 2021
062ffa4
Get frame.setlineno working again.
markshannon Apr 30, 2021
71d95c1
Merge branch 'master' into zero-cost-except
markshannon Apr 30, 2021
e5c2c3e
Remove debugging prints
markshannon Apr 30, 2021
7571e90
Fix test_dis after merge
markshannon Apr 30, 2021
812e78f
Layout frameobject in most compact form.
markshannon Apr 30, 2021
5801089
Add NEWS.
markshannon Apr 30, 2021
8cb6366
Remove SETUP_FINALLY, SETUP_ASYNC_WITH, SETUP_CLEANUP, POP_BLOCK inst…
markshannon Apr 30, 2021
4ec3417
Update dis.rst
markshannon Apr 30, 2021
07cd46d
fix NEWS formatting
markshannon Apr 30, 2021
3b52a3f
Fix typos and clarify text.
markshannon Apr 30, 2021
594a636
Restore dis after discarded experiment.
markshannon Apr 30, 2021
585f306
Bump magic number for 3.11
markshannon Apr 30, 2021
0f11d34
Fix typo
markshannon Apr 30, 2021
63a9b0e
Merge branch 'master' into zero-cost-except
markshannon May 3, 2021
133f74f
Re-order optimization passes to remove more NOPs.
markshannon May 3, 2021
299e051
Update importlib yet again.
markshannon May 3, 2021
43c199d
Assorted clarifications.
markshannon May 6, 2021
630e755
Rename frame.setlineno assistant code to clarify that we now model th…
markshannon May 6, 2021
6333f6c
Revert mistaken change.
markshannon May 6, 2021
e1d6e1e
Update Include/opcode.h
markshannon May 6, 2021
73b600a
Extend notes on exception handling.
markshannon May 6, 2021
f263ee2
Fix refleaks
markshannon May 6, 2021
2f31c84
Merge branch 'main' into zero-cost-except
markshannon May 7, 2021
0c3fd95
Document removal frame push/pop C-API functions.
markshannon May 7, 2021
b92ada2
Update importlib
markshannon May 7, 2021
1878117
Clarify exception handling notes
markshannon May 7, 2021
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
Prev Previous commit
Next Next commit
Assorted clarifications.
  • Loading branch information
markshannon committed May 6, 2021
commit 43c199dd8581cf095462fe47a0a2896103d79775
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"Zero cost" exception handling.

* Uses a lookup table to determine how to handle exceptions
* Uses a lookup table to determine how to handle exceptions.
* Removes SETUP_FINALLY and POP_TOP block instructions, eliminating the runtime overhead of try statements.
* Reduces the size of the fram object by about 60%
* Reduces the size of the frame object by about 60%.

Patch by Mark Shannon
26 changes: 15 additions & 11 deletions Objects/exception_table_notes.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
Description of the internal format of the exception table.
Comment thread
ericsnowcurrently marked this conversation as resolved.
Outdated

Conceptually, the exception table consists of a sequence of 5-tuples:
start-offset (inclusive), end-offset (exclusive), target, stack-depth, push-lasti (boolean)
1. start-offset (inclusive)
2. end-offset (exclusive)
3. target
4. stack-depth
5. push-lasti (boolean)

All offsets and lengths are in instructions, not bytes.

Expand Down Expand Up @@ -31,19 +35,19 @@ The 8 bits of a bit are (msb left) SXdddddd where S is the start bit. X is the e
In addition, we will combine depth and lasti into a single value, ((depth<<1)+lasti), before encoding.

For example, the exception entry:
start: 20
end: 28
target 100
depth 3
lasti False
start: 20
end: 28
target: 100
depth: 3
lasti: False

is encoded first by converting to the more compact four value form:
start: 20
size: 8
target 100
depth<<1+lasti 6
start: 20
size: 8
target: 100
depth<<1+lasti: 6

Where is then encoded as:
which is then encoded as:
148 (MSB + 20 for start)
8 (size)
65 (Extend bit + 1)
Expand Down
2 changes: 1 addition & 1 deletion Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ explain_incompatible_block_stack(int64_t to_stack)
return "stack is too deep to analyze";
}
if (to_stack == UNINITIALIZED) {
return "can't jump into an exception handler, or code may be unreachable.";
return "can't jump into an exception handler, or code may be unreachable";
}
Kind target_kind = top_block(to_stack);
switch(target_kind) {
Expand Down
10 changes: 8 additions & 2 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -4070,7 +4070,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
- (FOURTH, FIFTH, SIXTH) = previous exception
- SEVENTH: lasti of exception in exc_info()
- EIGHTH: the context.__exit__ bound method
We call SEVENTH(TOP, SECOND, THIRD).
We call EIGHTH(TOP, SECOND, THIRD).
Then we push again the TOP exception and the __exit__
return value.
*/
Expand Down Expand Up @@ -4772,9 +4772,13 @@ positional_only_passed_as_keyword(PyThreadState *tstate, PyCodeObject *co,

}

/* Exception table parsing code.
* See Objects/exception_table_notes.txt for details.
*/

static inline unsigned char *
Comment thread
ericsnowcurrently marked this conversation as resolved.
parse_varint(unsigned char *p, int *result) {
int val = p[0]&63;
int val = p[0] & 63;
while (p[0] & 64) {
p++;
val = (val << 6) | (p[0] & 63);
Expand All @@ -4788,11 +4792,13 @@ scan_back_to_entry_start(unsigned char *p) {
for (; (p[0]&128) == 0; p--);
return p;
}

static inline unsigned char *
skip_to_next_entry(unsigned char *p) {
for (; (p[0]&128) == 0; p++);
return p;
}

static inline unsigned char *
parse_range(unsigned char *p, int *start, int*end)
{
Expand Down
18 changes: 9 additions & 9 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ is_relative_jump(struct instr *i)
static inline int
is_jump(struct instr *i)
{

return i->i_opcode >= SETUP_WITH || is_bit_set_in_table(_PyOpcode_Jump, i->i_opcode);
}

Expand Down Expand Up @@ -1138,14 +1137,21 @@ stack_effect(int opcode, int oparg, int jump)
case LOAD_GLOBAL:
return 1;

/* Exception handling */
/* Exception handling pseudo-instructions */
case SETUP_FINALLY:
/* 0 in the normal flow.
* Restore the stack position and push 6 values before jumping to
* Restore the stack position and push 3 values before jumping to
* the handler if an exception be raised. */
return jump ? 3 : 0;
case SETUP_CLEANUP:
/* As SETUP_FINALLY, but pushes lasti as well */
return jump ? 4 : 0;
Comment thread
ericsnowcurrently marked this conversation as resolved.
case SETUP_WITH:
/* 0 in the normal flow.
* Restore the stack position to the position before the result
* of __(a)enter__ and push 4 values before jumping to the handler
* if an exception be raised. */
return jump ? -1 + 4 : 0;

case RERAISE:
return -3;
Expand Down Expand Up @@ -1197,12 +1203,6 @@ stack_effect(int opcode, int oparg, int jump)
/* Iterators and generators */
case GET_AWAITABLE:
return 0;
case SETUP_WITH:
/* 0 in the normal flow.
* Restore the stack position to the position before the result
* of __aenter__ and push 7 values before jumping to the handler
* if an exception be raised. */
return jump ? -1 + 4 : 0;

case BEFORE_ASYNC_WITH:
case BEFORE_WITH:
Expand Down
2 changes: 2 additions & 0 deletions Tools/scripts/generate_opcode_h.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
footer = """
#define HAS_ARG(op) ((op) >= HAVE_ARGUMENT)

/* Reserve some bytecodes for internal use in the compiler.
* The value of 240 is arbitrary. */
#define IS_ARTIFICIAL(op) ((op) > 240)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is special about 240? A note here would go along way toward making this line less mysterious. It may be more useful to put such a note (and the specific value) in opcode.py instead, since the relationship with the data there is much more obvious.


#ifdef __cplusplus
Expand Down