Skip to content

Commit 74c953d

Browse files
tiraniritkatriel
andauthored
[3.11] gh-92228: disable the compiler's 'small exit block inlining' optimization for blocks that have a line number (GH-94592) (GH-94643)
Inlining of code that corresponds to source code lines, can make it hard to distinguish later between code which is only reachable from except handlers, and that which is reachable in normal control flow. This caused problems with the debugger's jump feature. This PR turns off the inlining optimisation for code which has line numbers. We still inline things like the implicit "return None".. (cherry picked from commit bde06e1) Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
1 parent 3517c13 commit 74c953d

File tree

5 files changed

+34
-10
lines changed

5 files changed

+34
-10
lines changed

Lib/test/test_dis.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -366,14 +366,12 @@ def bug42562():
366366
LOAD_CONST 2 (0)
367367
--> BINARY_OP 11 (/)
368368
POP_TOP
369-
370-
%3d LOAD_FAST 1 (tb)
371-
RETURN_VALUE
369+
JUMP_FORWARD 30 (to 76)
372370
>> PUSH_EXC_INFO
373371
374372
%3d LOAD_GLOBAL 0 (Exception)
375373
CHECK_EXC_MATCH
376-
POP_JUMP_FORWARD_IF_FALSE 18 (to 72)
374+
POP_JUMP_FORWARD_IF_FALSE 17 (to 68)
377375
STORE_FAST 0 (e)
378376
379377
%3d LOAD_FAST 0 (e)
@@ -383,9 +381,7 @@ def bug42562():
383381
LOAD_CONST 0 (None)
384382
STORE_FAST 0 (e)
385383
DELETE_FAST 0 (e)
386-
387-
%3d LOAD_FAST 1 (tb)
388-
RETURN_VALUE
384+
JUMP_FORWARD 8 (to 76)
389385
>> LOAD_CONST 0 (None)
390386
STORE_FAST 0 (e)
391387
DELETE_FAST 0 (e)
@@ -395,15 +391,17 @@ def bug42562():
395391
>> COPY 3
396392
POP_EXCEPT
397393
RERAISE 1
394+
395+
%3d >> LOAD_FAST 1 (tb)
396+
RETURN_VALUE
398397
ExceptionTable:
399398
""" % (TRACEBACK_CODE.co_firstlineno,
400399
TRACEBACK_CODE.co_firstlineno + 1,
401400
TRACEBACK_CODE.co_firstlineno + 2,
402-
TRACEBACK_CODE.co_firstlineno + 5,
403401
TRACEBACK_CODE.co_firstlineno + 3,
404402
TRACEBACK_CODE.co_firstlineno + 4,
405-
TRACEBACK_CODE.co_firstlineno + 5,
406-
TRACEBACK_CODE.co_firstlineno + 3)
403+
TRACEBACK_CODE.co_firstlineno + 3,
404+
TRACEBACK_CODE.co_firstlineno + 5)
407405

408406
def _fstring(a, b, c, d):
409407
return f'{a} {b:4} {c!r} {d!r:4}'

Lib/test/test_peepholer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,8 @@ def f(x):
344344
self.assertEqual(len(returns), 1)
345345
self.check_lnotab(f)
346346

347+
@unittest.skip("Following gh-92228 the return has two predecessors "
348+
"and that prevents jump elimination.")
347349
def test_elim_jump_to_return(self):
348350
# JUMP_FORWARD to RETURN --> RETURN
349351
def f(cond, true_value, false_value):

Lib/test/test_sys_settrace.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2042,6 +2042,15 @@ def test_no_jump_within_except_block(output):
20422042
output.append(6)
20432043
output.append(7)
20442044

2045+
@jump_test(6, 1, [1, 5, 1, 5])
2046+
def test_jump_over_try_except(output):
2047+
output.append(1)
2048+
try:
2049+
1 / 0
2050+
except ZeroDivisionError as e:
2051+
output.append(5)
2052+
x = 42 # has to be a two-instruction block
2053+
20452054
@jump_test(2, 4, [1, 4, 5, -4])
20462055
def test_jump_across_with(output):
20472056
output.append(1)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Disable the compiler's inline-small-exit-blocks optimization for exit blocks that are associated with source code lines. This fixes a bug where the debugger cannot tell where an exception handler ends and the following code block begins.

Python/compile.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8976,6 +8976,16 @@ optimize_basic_block(struct compiler *c, basicblock *bb, PyObject *consts)
89768976
return -1;
89778977
}
89788978

8979+
static bool
8980+
basicblock_has_lineno(const basicblock *bb) {
8981+
for (int i = 0; i < bb->b_iused; i++) {
8982+
if (bb->b_instr[i].i_lineno > 0) {
8983+
return true;
8984+
}
8985+
}
8986+
return false;
8987+
}
8988+
89798989
/* If this block ends with an unconditional jump to an exit block,
89808990
* then remove the jump and extend this block with the target.
89818991
*/
@@ -8992,6 +9002,10 @@ extend_block(basicblock *bb) {
89929002
}
89939003
if (last->i_target->b_exit && last->i_target->b_iused <= MAX_COPY_SIZE) {
89949004
basicblock *to_copy = last->i_target;
9005+
if (basicblock_has_lineno(to_copy)) {
9006+
/* copy only blocks without line number (like implicit 'return None's) */
9007+
return 0;
9008+
}
89959009
last->i_opcode = NOP;
89969010
for (int i = 0; i < to_copy->b_iused; i++) {
89979011
int index = compiler_next_instr(bb);

0 commit comments

Comments
 (0)