Skip to content

Commit cf730b5

Browse files
authored
GH-93621: reorder code in with/async-with exception exit path to reduce the size of the exception table (GH-93622)
1 parent 2ba0fd5 commit cf730b5

File tree

3 files changed

+173
-15
lines changed

3 files changed

+173
-15
lines changed

Lib/test/test_dis.py

Lines changed: 159 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ def bug42562():
389389
POP_EXCEPT
390390
RERAISE 1
391391
ExceptionTable:
392+
4 rows
392393
""" % (TRACEBACK_CODE.co_firstlineno,
393394
TRACEBACK_CODE.co_firstlineno + 1,
394395
TRACEBACK_CODE.co_firstlineno + 2,
@@ -421,6 +422,133 @@ def _fstring(a, b, c, d):
421422
RETURN_VALUE
422423
""" % (_fstring.__code__.co_firstlineno, _fstring.__code__.co_firstlineno + 1)
423424

425+
def _with(c):
426+
with c:
427+
x = 1
428+
y = 2
429+
430+
dis_with = """\
431+
%3d RESUME 0
432+
433+
%3d LOAD_FAST 0 (c)
434+
BEFORE_WITH
435+
POP_TOP
436+
437+
%3d LOAD_CONST 1 (1)
438+
STORE_FAST 1 (x)
439+
440+
%3d LOAD_CONST 0 (None)
441+
LOAD_CONST 0 (None)
442+
LOAD_CONST 0 (None)
443+
CALL 2
444+
POP_TOP
445+
446+
%3d LOAD_CONST 2 (2)
447+
STORE_FAST 2 (y)
448+
LOAD_CONST 0 (None)
449+
RETURN_VALUE
450+
451+
%3d >> PUSH_EXC_INFO
452+
WITH_EXCEPT_START
453+
POP_JUMP_FORWARD_IF_TRUE 1 (to 46)
454+
RERAISE 2
455+
>> POP_TOP
456+
POP_EXCEPT
457+
POP_TOP
458+
POP_TOP
459+
460+
%3d LOAD_CONST 2 (2)
461+
STORE_FAST 2 (y)
462+
LOAD_CONST 0 (None)
463+
RETURN_VALUE
464+
>> COPY 3
465+
POP_EXCEPT
466+
RERAISE 1
467+
ExceptionTable:
468+
2 rows
469+
""" % (_with.__code__.co_firstlineno,
470+
_with.__code__.co_firstlineno + 1,
471+
_with.__code__.co_firstlineno + 2,
472+
_with.__code__.co_firstlineno + 1,
473+
_with.__code__.co_firstlineno + 3,
474+
_with.__code__.co_firstlineno + 1,
475+
_with.__code__.co_firstlineno + 3,
476+
)
477+
478+
async def _asyncwith(c):
479+
async with c:
480+
x = 1
481+
y = 2
482+
483+
dis_asyncwith = """\
484+
%3d RETURN_GENERATOR
485+
POP_TOP
486+
RESUME 0
487+
488+
%3d LOAD_FAST 0 (c)
489+
BEFORE_ASYNC_WITH
490+
GET_AWAITABLE 1
491+
LOAD_CONST 0 (None)
492+
>> SEND 3 (to 22)
493+
YIELD_VALUE 3
494+
RESUME 3
495+
JUMP_BACKWARD_NO_INTERRUPT 4 (to 14)
496+
>> POP_TOP
497+
498+
%3d LOAD_CONST 1 (1)
499+
STORE_FAST 1 (x)
500+
501+
%3d LOAD_CONST 0 (None)
502+
LOAD_CONST 0 (None)
503+
LOAD_CONST 0 (None)
504+
CALL 2
505+
GET_AWAITABLE 2
506+
LOAD_CONST 0 (None)
507+
>> SEND 3 (to 56)
508+
YIELD_VALUE 2
509+
RESUME 3
510+
JUMP_BACKWARD_NO_INTERRUPT 4 (to 48)
511+
>> POP_TOP
512+
513+
%3d LOAD_CONST 2 (2)
514+
STORE_FAST 2 (y)
515+
LOAD_CONST 0 (None)
516+
RETURN_VALUE
517+
518+
%3d >> PUSH_EXC_INFO
519+
WITH_EXCEPT_START
520+
GET_AWAITABLE 2
521+
LOAD_CONST 0 (None)
522+
>> SEND 3 (to 82)
523+
YIELD_VALUE 6
524+
RESUME 3
525+
JUMP_BACKWARD_NO_INTERRUPT 4 (to 74)
526+
>> POP_JUMP_FORWARD_IF_TRUE 1 (to 86)
527+
RERAISE 2
528+
>> POP_TOP
529+
POP_EXCEPT
530+
POP_TOP
531+
POP_TOP
532+
533+
%3d LOAD_CONST 2 (2)
534+
STORE_FAST 2 (y)
535+
LOAD_CONST 0 (None)
536+
RETURN_VALUE
537+
>> COPY 3
538+
POP_EXCEPT
539+
RERAISE 1
540+
ExceptionTable:
541+
2 rows
542+
""" % (_asyncwith.__code__.co_firstlineno,
543+
_asyncwith.__code__.co_firstlineno + 1,
544+
_asyncwith.__code__.co_firstlineno + 2,
545+
_asyncwith.__code__.co_firstlineno + 1,
546+
_asyncwith.__code__.co_firstlineno + 3,
547+
_asyncwith.__code__.co_firstlineno + 1,
548+
_asyncwith.__code__.co_firstlineno + 3,
549+
)
550+
551+
424552
def _tryfinally(a, b):
425553
try:
426554
return a
@@ -455,6 +583,7 @@ def _tryfinallyconst(b):
455583
POP_EXCEPT
456584
RERAISE 1
457585
ExceptionTable:
586+
2 rows
458587
""" % (_tryfinally.__code__.co_firstlineno,
459588
_tryfinally.__code__.co_firstlineno + 1,
460589
_tryfinally.__code__.co_firstlineno + 2,
@@ -484,6 +613,7 @@ def _tryfinallyconst(b):
484613
POP_EXCEPT
485614
RERAISE 1
486615
ExceptionTable:
616+
1 row
487617
""" % (_tryfinallyconst.__code__.co_firstlineno,
488618
_tryfinallyconst.__code__.co_firstlineno + 1,
489619
_tryfinallyconst.__code__.co_firstlineno + 2,
@@ -678,6 +808,18 @@ def assert_offsets_increasing(self, text, delta):
678808
self.assertGreaterEqual(offset, expected_offset, line)
679809
expected_offset = offset + delta
680810

811+
def assert_exception_table_increasing(self, lines):
812+
prev_start, prev_end = -1, -1
813+
count = 0
814+
for line in lines:
815+
m = re.match(r' (\d+) to (\d+) -> \d+ \[\d+\]', line)
816+
start, end = [int(g) for g in m.groups()]
817+
self.assertGreaterEqual(end, start)
818+
self.assertGreater(start, prev_end)
819+
prev_start, prev_end = start, end
820+
count += 1
821+
return count
822+
681823
def strip_offsets(self, text):
682824
lines = text.splitlines(True)
683825
start, end = self.find_offset_column(lines)
@@ -691,6 +833,9 @@ def strip_offsets(self, text):
691833
res.append(line)
692834
else:
693835
res.append(line[:start] + line[end:])
836+
num_rows = self.assert_exception_table_increasing(lines)
837+
if num_rows:
838+
res.append(f"{num_rows} row{'s' if num_rows > 1 else ''}\n")
694839
return "".join(res)
695840

696841
def do_disassembly_compare(self, got, expected, with_offsets=False):
@@ -883,6 +1028,12 @@ def test_disassemble_coroutine(self):
8831028
def test_disassemble_fstring(self):
8841029
self.do_disassembly_test(_fstring, dis_fstring)
8851030

1031+
def test_disassemble_with(self):
1032+
self.do_disassembly_test(_with, dis_with)
1033+
1034+
def test_disassemble_asyncwith(self):
1035+
self.do_disassembly_test(_asyncwith, dis_asyncwith)
1036+
8861037
def test_disassemble_try_finally(self):
8871038
self.do_disassembly_test(_tryfinally, dis_tryfinally)
8881039
self.do_disassembly_test(_tryfinallyconst, dis_tryfinallyconst)
@@ -1471,16 +1622,16 @@ def _prepare_test_cases():
14711622
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=302, starts_line=None, is_jump_target=False, positions=None),
14721623
Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=304, starts_line=25, is_jump_target=False, positions=None),
14731624
Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=306, starts_line=None, is_jump_target=False, positions=None),
1474-
Instruction(opname='POP_JUMP_FORWARD_IF_TRUE', opcode=115, arg=4, argval=318, argrepr='to 318', offset=308, starts_line=None, is_jump_target=False, positions=None),
1625+
Instruction(opname='POP_JUMP_FORWARD_IF_TRUE', opcode=115, arg=1, argval=312, argrepr='to 312', offset=308, starts_line=None, is_jump_target=False, positions=None),
14751626
Instruction(opname='RERAISE', opcode=119, arg=2, argval=2, argrepr='', offset=310, starts_line=None, is_jump_target=False, positions=None),
1476-
Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=312, starts_line=None, is_jump_target=False, positions=None),
1627+
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=312, starts_line=None, is_jump_target=True, positions=None),
14771628
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=314, starts_line=None, is_jump_target=False, positions=None),
1478-
Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=316, starts_line=None, is_jump_target=False, positions=None),
1479-
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=318, starts_line=None, is_jump_target=True, positions=None),
1480-
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=320, starts_line=None, is_jump_target=False, positions=None),
1481-
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=322, starts_line=None, is_jump_target=False, positions=None),
1482-
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=324, starts_line=None, is_jump_target=False, positions=None),
1483-
Instruction(opname='JUMP_BACKWARD', opcode=140, arg=27, argval=274, argrepr='to 274', offset=326, starts_line=None, is_jump_target=False, positions=None),
1629+
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=316, starts_line=None, is_jump_target=False, positions=None),
1630+
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=318, starts_line=None, is_jump_target=False, positions=None),
1631+
Instruction(opname='JUMP_BACKWARD', opcode=140, arg=24, argval=274, argrepr='to 274', offset=320, starts_line=None, is_jump_target=False, positions=None),
1632+
Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=322, starts_line=None, is_jump_target=False, positions=None),
1633+
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=324, starts_line=None, is_jump_target=False, positions=None),
1634+
Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=326, starts_line=None, is_jump_target=False, positions=None),
14841635
Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=328, starts_line=None, is_jump_target=False, positions=None),
14851636
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=330, starts_line=22, is_jump_target=False, positions=None),
14861637
Instruction(opname='CHECK_EXC_MATCH', opcode=36, arg=None, argval=None, argrepr='', offset=342, starts_line=None, is_jump_target=False, positions=None),
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Change order of bytecode instructions emitted for :keyword:`with` and :keyword:`async with` to reduce the number of entries in the exception table.

Python/compile.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5551,20 +5551,26 @@ compiler_visit_keyword(struct compiler *c, keyword_ty k)
55515551
static int
55525552
compiler_with_except_finish(struct compiler *c, basicblock * cleanup) {
55535553
UNSET_LOC(c);
5554-
basicblock *exit;
5555-
exit = compiler_new_block(c);
5556-
if (exit == NULL)
5554+
basicblock *suppress = compiler_new_block(c);
5555+
if (suppress == NULL) {
55575556
return 0;
5558-
ADDOP_JUMP(c, POP_JUMP_IF_TRUE, exit);
5557+
}
5558+
ADDOP_JUMP(c, POP_JUMP_IF_TRUE, suppress);
55595559
ADDOP_I(c, RERAISE, 2);
5560-
compiler_use_next_block(c, cleanup);
5561-
POP_EXCEPT_AND_RERAISE(c);
5562-
compiler_use_next_block(c, exit);
5560+
compiler_use_next_block(c, suppress);
55635561
ADDOP(c, POP_TOP); /* exc_value */
55645562
ADDOP(c, POP_BLOCK);
55655563
ADDOP(c, POP_EXCEPT);
55665564
ADDOP(c, POP_TOP);
55675565
ADDOP(c, POP_TOP);
5566+
basicblock *exit = compiler_new_block(c);
5567+
if (exit == NULL) {
5568+
return 0;
5569+
}
5570+
ADDOP_JUMP(c, JUMP, exit);
5571+
compiler_use_next_block(c, cleanup);
5572+
POP_EXCEPT_AND_RERAISE(c);
5573+
compiler_use_next_block(c, exit);
55685574
return 1;
55695575
}
55705576

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy