From e5708031b048f5eb03deb4e5d92e396050e79ddd Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 21 Sep 2023 00:08:06 +0100 Subject: [PATCH] gh-109627: duplicated smalll exit blocks need to be assigned jump target labels (#109630) (cherry picked from commit 9ccf0545efd5bc5af5aa51774030c471d49a972b) --- Lib/test/test_compile.py | 18 +++++++++++++++ ...-09-20-23-04-15.gh-issue-109627.xxe7De.rst | 2 ++ Python/flowgraph.c | 22 ++++++++++++++----- 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-09-20-23-04-15.gh-issue-109627.xxe7De.rst diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index fde52d40097f406..ab69e37624fd6a3 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -1216,6 +1216,24 @@ def f(x, y, z): return a self.assertEqual(f("x", "y", "z"), "y") + def test_variable_dependent(self): + # gh-104635: Since the value of b is dependent on the value of a + # the first STORE_FAST for a should not be skipped. (e.g POP_TOP). + # This test case is added to prevent potential regression from aggressive optimization. + def f(): + a = 42; b = a + 54; a = 54 + return a, b + self.assertEqual(f(), (54, 96)) + + def test_duplicated_small_exit_block(self): + # See gh-109627 + def f(): + while element and something: + try: + return something + except: + pass + @requires_debug_ranges() class TestSourcePositions(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-20-23-04-15.gh-issue-109627.xxe7De.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-20-23-04-15.gh-issue-109627.xxe7De.rst new file mode 100644 index 000000000000000..397d76e291419fa --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-09-20-23-04-15.gh-issue-109627.xxe7De.rst @@ -0,0 +1,2 @@ +Fix bug where the compiler does not assign a new jump target label to a +duplicated small exit block. diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 4fe581beb5fc634..0ea32345f9f9be1 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -562,16 +562,23 @@ check_cfg(cfg_builder *g) { return SUCCESS; } -/* Calculate the actual jump target from the target_label */ static int -translate_jump_labels_to_targets(basicblock *entryblock) +get_max_label(basicblock *entryblock) { - int max_label = -1; + int lbl = -1; for (basicblock *b = entryblock; b != NULL; b = b->b_next) { - if (b->b_label.id > max_label) { - max_label = b->b_label.id; + if (b->b_label.id > lbl) { + lbl = b->b_label.id; } } + return lbl; +} + +/* Calculate the actual jump target from the target_label */ +static int +translate_jump_labels_to_targets(basicblock *entryblock) +{ + int max_label = get_max_label(entryblock); size_t mapsize = sizeof(basicblock *) * (max_label + 1); basicblock **label2block = (basicblock **)PyMem_Malloc(mapsize); if (!label2block) { @@ -2035,6 +2042,7 @@ is_exit_without_lineno(basicblock *b) { return true; } + /* PEP 626 mandates that the f_lineno of a frame is correct * after a frame terminates. It would be prohibitively expensive * to continuously update the f_lineno field at runtime, @@ -2048,6 +2056,9 @@ static int duplicate_exits_without_lineno(cfg_builder *g) { assert(no_empty_basic_blocks(g)); + + int next_lbl = get_max_label(g->g_entryblock) + 1; + /* Copy all exit blocks without line number that are targets of a jump. */ basicblock *entryblock = g->g_entryblock; @@ -2066,6 +2077,7 @@ duplicate_exits_without_lineno(cfg_builder *g) target->b_predecessors--; new_target->b_predecessors = 1; new_target->b_next = target->b_next; + new_target->b_label.id = next_lbl++; target->b_next = new_target; } }