Hi all,
I am the current maintainer of bytecode
(
https://github.com/MatthieuDartiailh/bytecode) which is a library
to perform assembly and disassembly of Python bytecode. The library
was created by V. Stinner.
I started looking in Python 3.11 support in bytecode, I read
Objects/exception_handling_notes.txt and I have a couple of
questions regarding the exception table:
Currently bytecode exposes three level of abstractions:
- the concrete level in which one deals with instruction offset
for jumps and explicit indexing into the known constants and names
- the bytecode level which uses labels for jumps and allow non
integer argument to instructions
- the cfg level which provides basic blocks delineation over the
bytecode level
So my first idea was to directly expose the unpacked exception table
(start, stop, target, stack_depth, last_i) at the concrete level and
use pseudo-instruction and labels at the bytecode level. At this
point of my reflections, I saw
https://github.com/python/cpython/commit/c57aad777afc6c0b382981ee9e4bc94c03bf5f68
about adding pseudo-instructionto dis output in 3.12 and though it
would line up quite nicely. Reading through, I got curious about how
SETUP_WITH handled
popping one extra item from the stack so I went to look at dis
results on a couple of small examples. I tried on 3.10 and
3.11b3 (for some reasons I cannot compile main at a391b74d on
windows).
I looked at simple things and got a bit surprised:
Disassembling:
def f():
try:
a = 1
except:
raise
I get on 3.11:
1 0 RESUME 0
2 2 NOP
3 4 LOAD_CONST 1 (1)
6 STORE_FAST 0 (a)
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
>> 12 PUSH_EXC_INFO
4 14 POP_TOP
5 16 RAISE_VARARGS 0
>> 18 COPY 3
20 POP_EXCEPT
22 RERAISE 1
ExceptionTable:
4 to 6 -> 12 [0]
12 to 16 -> 18 [1] lasti
On 3.10:
2 0 SETUP_FINALLY 5 (to 12)
3 2 LOAD_CONST 1 (1)
4 STORE_FAST 0 (a)
6 POP_BLOCK
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
4 >> 12 POP_TOP
14 POP_TOP
16 POP_TOP
5 18 RAISE_VARARGS 0
This surprised me on two levels:
- first I have never seen the RESUME opcode and it is currently
not documented
- my second surprise comes from the second entry in the
exception table. At first I failed to see why it was needed but
writing this I realize it corresponds to the explicit handling
of exception propagation to the caller. Since I cannot compile
3.12 ATM I am wondering how this plays with pseudo-instruction:
in particular are pseudo-instructions generated for all entries
in the exception table ?
My initial idea was to have a SETUP_FINALLY/SETUP_CLEANUP -
POP_BLOCK pair for each line in the exception table and label
for the jump target. But I realize it means we will have many
such pairs than in 3.10. It is fine by me but I wondered what
choice was made in 3.12 dis and if this approach made sense.
Best regards
Matthieu
_______________________________________________