From acf066c3a0202f8bada87e03a56a01047d6ab79e Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Thu, 9 Jun 2022 00:02:10 +0100 Subject: [PATCH 1/5] reorder code in with/async-with exception exit path to reduce the size of the exception table --- Lib/test/test_dis.py | 16 ++++++++-------- Python/compile.c | 20 +++++++++++++------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index a94cd6432e1232..84aaeef3febefc 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -1471,16 +1471,16 @@ def _prepare_test_cases(): Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=302, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=304, starts_line=25, is_jump_target=False, positions=None), Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=306, starts_line=None, is_jump_target=False, positions=None), - 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), + 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), Instruction(opname='RERAISE', opcode=119, arg=2, argval=2, argrepr='', offset=310, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=312, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=312, starts_line=None, is_jump_target=True, positions=None), Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=314, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=316, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=318, starts_line=None, is_jump_target=True, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=320, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=322, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=324, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=140, arg=27, argval=274, argrepr='to 274', offset=326, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=316, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=318, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=140, arg=24, argval=274, argrepr='to 274', offset=320, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=322, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=324, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=326, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=328, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=330, starts_line=22, is_jump_target=False, positions=None), Instruction(opname='CHECK_EXC_MATCH', opcode=36, arg=None, argval=None, argrepr='', offset=342, starts_line=None, is_jump_target=False, positions=None), diff --git a/Python/compile.c b/Python/compile.c index bbd71936cf3468..b922e0145451e5 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -5551,20 +5551,26 @@ compiler_visit_keyword(struct compiler *c, keyword_ty k) static int compiler_with_except_finish(struct compiler *c, basicblock * cleanup) { UNSET_LOC(c); - basicblock *exit; - exit = compiler_new_block(c); - if (exit == NULL) + basicblock *suppress = compiler_new_block(c); + if (suppress == NULL) { return 0; - ADDOP_JUMP(c, POP_JUMP_IF_TRUE, exit); + } + ADDOP_JUMP(c, POP_JUMP_IF_TRUE, suppress); ADDOP_I(c, RERAISE, 2); - compiler_use_next_block(c, cleanup); - POP_EXCEPT_AND_RERAISE(c); - compiler_use_next_block(c, exit); + compiler_use_next_block(c, suppress); ADDOP(c, POP_TOP); /* exc_value */ ADDOP(c, POP_BLOCK); ADDOP(c, POP_EXCEPT); ADDOP(c, POP_TOP); ADDOP(c, POP_TOP); + basicblock *exit = compiler_new_block(c); + if (exit == NULL) { + return 0; + } + ADDOP_JUMP(c, JUMP, exit); + compiler_use_next_block(c, cleanup); + POP_EXCEPT_AND_RERAISE(c); + compiler_use_next_block(c, exit); return 1; } From 4966724069dfe865c075676b0e7fe69611692199 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 09:08:31 +0000 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2022-06-09-09-08-29.gh-issue-93621.-_Pn1d.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-06-09-09-08-29.gh-issue-93621.-_Pn1d.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-09-09-08-29.gh-issue-93621.-_Pn1d.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-09-09-08-29.gh-issue-93621.-_Pn1d.rst new file mode 100644 index 00000000000000..388540982fd1ff --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-06-09-09-08-29.gh-issue-93621.-_Pn1d.rst @@ -0,0 +1 @@ +Change order of bytecode instructions emitted for :keyword:`with` and :keyword:`async with` to reduce the number of entries in the exception table. From dc96fbec8cc6f18f0d0dec720cbbca83afb700f9 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Thu, 9 Jun 2022 10:45:53 +0100 Subject: [PATCH 3/5] add direct dis tests for with and async-with --- Lib/test/test_dis.py | 135 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 84aaeef3febefc..0dda03d143291e 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -421,6 +421,131 @@ def _fstring(a, b, c, d): RETURN_VALUE """ % (_fstring.__code__.co_firstlineno, _fstring.__code__.co_firstlineno + 1) +def _with(c): + with c: + x = 1 + y = 2 + +dis_with = """\ +%3d RESUME 0 + +%3d LOAD_FAST 0 (c) + BEFORE_WITH + POP_TOP + +%3d LOAD_CONST 1 (1) + STORE_FAST 1 (x) + +%3d LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + CALL 2 + POP_TOP + +%3d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + LOAD_CONST 0 (None) + RETURN_VALUE + +%3d >> PUSH_EXC_INFO + WITH_EXCEPT_START + POP_JUMP_FORWARD_IF_TRUE 1 (to 46) + RERAISE 2 + >> POP_TOP + POP_EXCEPT + POP_TOP + POP_TOP + +%3d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + LOAD_CONST 0 (None) + RETURN_VALUE + >> COPY 3 + POP_EXCEPT + RERAISE 1 +ExceptionTable: +""" % (_with.__code__.co_firstlineno, + _with.__code__.co_firstlineno + 1, + _with.__code__.co_firstlineno + 2, + _with.__code__.co_firstlineno + 1, + _with.__code__.co_firstlineno + 3, + _with.__code__.co_firstlineno + 1, + _with.__code__.co_firstlineno + 3, + ) + +async def _asyncwith(c): + async with c: + x = 1 + y = 2 + +dis_asyncwith = """\ +%3d RETURN_GENERATOR + POP_TOP + RESUME 0 + +%3d LOAD_FAST 0 (c) + BEFORE_ASYNC_WITH + GET_AWAITABLE 1 + LOAD_CONST 0 (None) + >> SEND 3 (to 22) + YIELD_VALUE 3 + RESUME 3 + JUMP_BACKWARD_NO_INTERRUPT 4 (to 14) + >> POP_TOP + +%3d LOAD_CONST 1 (1) + STORE_FAST 1 (x) + +%3d LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + CALL 2 + GET_AWAITABLE 2 + LOAD_CONST 0 (None) + >> SEND 3 (to 56) + YIELD_VALUE 2 + RESUME 3 + JUMP_BACKWARD_NO_INTERRUPT 4 (to 48) + >> POP_TOP + +%3d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + LOAD_CONST 0 (None) + RETURN_VALUE + +%3d >> PUSH_EXC_INFO + WITH_EXCEPT_START + GET_AWAITABLE 2 + LOAD_CONST 0 (None) + >> SEND 3 (to 82) + YIELD_VALUE 6 + RESUME 3 + JUMP_BACKWARD_NO_INTERRUPT 4 (to 74) + >> POP_JUMP_FORWARD_IF_TRUE 1 (to 86) + RERAISE 2 + >> POP_TOP + POP_EXCEPT + POP_TOP + POP_TOP + +%3d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + LOAD_CONST 0 (None) + RETURN_VALUE + >> COPY 3 + POP_EXCEPT + RERAISE 1 +ExceptionTable: +""" % (_asyncwith.__code__.co_firstlineno, + _asyncwith.__code__.co_firstlineno + 1, + _asyncwith.__code__.co_firstlineno + 2, + _asyncwith.__code__.co_firstlineno + 1, + _asyncwith.__code__.co_firstlineno + 3, + _asyncwith.__code__.co_firstlineno + 1, + _asyncwith.__code__.co_firstlineno + 3, + ) + + def _tryfinally(a, b): try: return a @@ -883,6 +1008,12 @@ def test_disassemble_coroutine(self): def test_disassemble_fstring(self): self.do_disassembly_test(_fstring, dis_fstring) + def test_disassemble_with(self): + self.do_disassembly_test(_with, dis_with) + + def test_disassemble_asyncwith(self): + self.do_disassembly_test(_asyncwith, dis_asyncwith) + def test_disassemble_try_finally(self): self.do_disassembly_test(_tryfinally, dis_tryfinally) self.do_disassembly_test(_tryfinallyconst, dis_tryfinallyconst) @@ -1046,8 +1177,8 @@ def test_show_caches(self): caches = list(self.get_cached_values(quickened, adaptive)) for cache in caches: self.assertRegex(cache, pattern) - self.assertEqual(caches.count(""), 8) - self.assertEqual(len(caches), 22) + self.assertEqual(caches.count(""), 9) + self.assertEqual(len(caches), 23) class DisWithFileTests(DisTests): From 946a191a62e13e17f8ba564c43b2571812687abb Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Thu, 9 Jun 2022 16:58:21 +0100 Subject: [PATCH 4/5] add some checks for the exception table in dis output --- Lib/test/test_dis.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 0dda03d143291e..6c75de21cb8ed8 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -389,6 +389,7 @@ def bug42562(): POP_EXCEPT RERAISE 1 ExceptionTable: +4 rows """ % (TRACEBACK_CODE.co_firstlineno, TRACEBACK_CODE.co_firstlineno + 1, TRACEBACK_CODE.co_firstlineno + 2, @@ -464,6 +465,7 @@ def _with(c): POP_EXCEPT RERAISE 1 ExceptionTable: +2 rows """ % (_with.__code__.co_firstlineno, _with.__code__.co_firstlineno + 1, _with.__code__.co_firstlineno + 2, @@ -536,6 +538,7 @@ async def _asyncwith(c): POP_EXCEPT RERAISE 1 ExceptionTable: +2 rows """ % (_asyncwith.__code__.co_firstlineno, _asyncwith.__code__.co_firstlineno + 1, _asyncwith.__code__.co_firstlineno + 2, @@ -580,6 +583,7 @@ def _tryfinallyconst(b): POP_EXCEPT RERAISE 1 ExceptionTable: +2 rows """ % (_tryfinally.__code__.co_firstlineno, _tryfinally.__code__.co_firstlineno + 1, _tryfinally.__code__.co_firstlineno + 2, @@ -609,6 +613,7 @@ def _tryfinallyconst(b): POP_EXCEPT RERAISE 1 ExceptionTable: +1 row """ % (_tryfinallyconst.__code__.co_firstlineno, _tryfinallyconst.__code__.co_firstlineno + 1, _tryfinallyconst.__code__.co_firstlineno + 2, @@ -803,6 +808,18 @@ def assert_offsets_increasing(self, text, delta): self.assertGreaterEqual(offset, expected_offset, line) expected_offset = offset + delta + def assert_exception_table_increasing(self, lines): + prev_start, prev_end = -1, -1 + count = 0 + for line in lines: + m = re.match(r' (\d+) to (\d+) -> \d+ \[\d+\]', line) + start, end = [int(g) for g in m.groups()] + self.assertGreaterEqual(end, start) + self.assertGreater(start, prev_end) + prev_start, prev_end = start, end + count += 1 + return count + def strip_offsets(self, text): lines = text.splitlines(True) start, end = self.find_offset_column(lines) @@ -816,6 +833,9 @@ def strip_offsets(self, text): res.append(line) else: res.append(line[:start] + line[end:]) + num_rows = self.assert_exception_table_increasing(lines) + if num_rows: + res.append(f"{num_rows} row{'s' if num_rows > 1 else ''}\n") return "".join(res) def do_disassembly_compare(self, got, expected, with_offsets=False): From 38b21f1829c37fd6045ac03f75c942c0f292827f Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Thu, 9 Jun 2022 19:05:35 +0100 Subject: [PATCH 5/5] update tests after merge --- Lib/test/test_dis.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 6c75de21cb8ed8..656d801107f52b 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -1197,8 +1197,8 @@ def test_show_caches(self): caches = list(self.get_cached_values(quickened, adaptive)) for cache in caches: self.assertRegex(cache, pattern) - self.assertEqual(caches.count(""), 9) - self.assertEqual(len(caches), 23) + self.assertEqual(caches.count(""), 8) + self.assertEqual(len(caches), 22) class DisWithFileTests(DisTests):