Skip to content

Commit 0004ff7

Browse files
iritkatrielaisk
authored andcommitted
pythongh-114265: move line number propagation before cfg optimization, remove guarantee_lineno_for_exits (python#114267)
1 parent b6b5fd9 commit 0004ff7

File tree

4 files changed

+61
-64
lines changed

4 files changed

+61
-64
lines changed

Lib/importlib/_bootstrap_external.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ def _write_atomic(path, data, mode=0o666):
463463
# Python 3.13a1 3564 (Removed oparg from YIELD_VALUE, changed oparg values of RESUME)
464464
# Python 3.13a1 3565 (Oparg of YIELD_VALUE indicates whether it is in a yield-from)
465465
# Python 3.13a1 3566 (Emit JUMP_NO_INTERRUPT instead of JUMP for non-loop no-lineno cases)
466+
# Python 3.13a1 3567 (Reimplement line number propagation by the compiler)
466467

467468
# Python 3.14 will start with 3600
468469

@@ -479,7 +480,7 @@ def _write_atomic(path, data, mode=0o666):
479480
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
480481
# in PC/launcher.c must also be updated.
481482

482-
MAGIC_NUMBER = (3566).to_bytes(2, 'little') + b'\r\n'
483+
MAGIC_NUMBER = (3567).to_bytes(2, 'little') + b'\r\n'
483484

484485
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
485486

Lib/test/test_dis.py

+4-10
Original file line numberDiff line numberDiff line change
@@ -577,14 +577,10 @@ async def _asyncwith(c):
577577
RETURN_CONST 0 (None)
578578
579579
%4d L12: CLEANUP_THROW
580-
581-
-- L13: JUMP_BACKWARD_NO_INTERRUPT 25 (to L5)
582-
583-
%4d L14: CLEANUP_THROW
584-
585-
-- L15: JUMP_BACKWARD_NO_INTERRUPT 9 (to L11)
586-
587-
%4d L16: PUSH_EXC_INFO
580+
L13: JUMP_BACKWARD_NO_INTERRUPT 25 (to L5)
581+
L14: CLEANUP_THROW
582+
L15: JUMP_BACKWARD_NO_INTERRUPT 9 (to L11)
583+
L16: PUSH_EXC_INFO
588584
WITH_EXCEPT_START
589585
GET_AWAITABLE 2
590586
LOAD_CONST 0 (None)
@@ -630,8 +626,6 @@ async def _asyncwith(c):
630626
_asyncwith.__code__.co_firstlineno + 1,
631627
_asyncwith.__code__.co_firstlineno + 3,
632628
_asyncwith.__code__.co_firstlineno + 1,
633-
_asyncwith.__code__.co_firstlineno + 1,
634-
_asyncwith.__code__.co_firstlineno + 1,
635629
_asyncwith.__code__.co_firstlineno + 3,
636630
)
637631

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Compiler propagates line numbers before optimization, leading to more optimization opportunities and removing the need for the ``guarantee_lineno_for_exits`` hack.

Python/flowgraph.c

+54-53
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ typedef struct _PyCfgInstruction {
2929
int i_opcode;
3030
int i_oparg;
3131
_PyCompilerSrcLocation i_loc;
32+
unsigned i_loc_propagated : 1; /* location was set by propagate_line_numbers */
3233
struct _PyCfgBasicblock *i_target; /* target block (if jump instruction) */
3334
struct _PyCfgBasicblock *i_except; /* target block when exception is raised */
3435
} cfg_instr;
@@ -504,6 +505,21 @@ no_redundant_jumps(cfg_builder *g) {
504505
return true;
505506
}
506507

508+
static bool
509+
all_exits_have_lineno(basicblock *entryblock) {
510+
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
511+
for (int i = 0; i < b->b_iused; i++) {
512+
cfg_instr *instr = &b->b_instr[i];
513+
if (instr->i_opcode == RETURN_VALUE) {
514+
if (instr->i_loc.lineno < 0) {
515+
assert(0);
516+
return false;
517+
}
518+
}
519+
}
520+
}
521+
return true;
522+
}
507523
#endif
508524

509525
/***** CFG preprocessing (jump targets and exceptions) *****/
@@ -940,7 +956,10 @@ label_exception_targets(basicblock *entryblock) {
940956
/***** CFG optimizations *****/
941957

942958
static int
943-
mark_reachable(basicblock *entryblock) {
959+
remove_unreachable(basicblock *entryblock) {
960+
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
961+
b->b_predecessors = 0;
962+
}
944963
basicblock **stack = make_cfg_traversal_stack(entryblock);
945964
if (stack == NULL) {
946965
return ERROR;
@@ -972,6 +991,14 @@ mark_reachable(basicblock *entryblock) {
972991
}
973992
}
974993
PyMem_Free(stack);
994+
995+
/* Delete unreachable instructions */
996+
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
997+
if (b->b_predecessors == 0) {
998+
b->b_iused = 0;
999+
b->b_except_handler = 0;
1000+
}
1001+
}
9751002
return SUCCESS;
9761003
}
9771004

@@ -1149,13 +1176,15 @@ jump_thread(cfg_instr *inst, cfg_instr *target, int opcode)
11491176
assert(is_jump(target));
11501177
// bpo-45773: If inst->i_target == target->i_target, then nothing actually
11511178
// changes (and we fall into an infinite loop):
1179+
if (inst->i_loc.lineno == -1) assert(inst->i_loc_propagated);
1180+
if (target->i_loc.lineno == -1) assert(target->i_loc_propagated);
11521181
if ((inst->i_loc.lineno == target->i_loc.lineno ||
1153-
inst->i_loc.lineno == -1 || target->i_loc.lineno == -1) &&
1182+
inst->i_loc_propagated || target->i_loc_propagated) &&
11541183
inst->i_target != target->i_target)
11551184
{
11561185
inst->i_target = target->i_target;
11571186
inst->i_opcode = opcode;
1158-
if (inst->i_loc.lineno == -1) {
1187+
if (inst->i_loc_propagated && !target->i_loc_propagated) {
11591188
inst->i_loc = target->i_loc;
11601189
}
11611190
return true;
@@ -1714,6 +1743,7 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts)
17141743
return ERROR;
17151744
}
17161745

1746+
static int resolve_line_numbers(cfg_builder *g, int firstlineno);
17171747

17181748
/* Perform optimizations on a control flow graph.
17191749
The consts object should still be in list form to allow new constants
@@ -1723,41 +1753,31 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts)
17231753
NOPs. Later those NOPs are removed.
17241754
*/
17251755
static int
1726-
optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache)
1756+
optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache, int firstlineno)
17271757
{
17281758
assert(PyDict_CheckExact(const_cache));
17291759
RETURN_IF_ERROR(check_cfg(g));
17301760
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
17311761
RETURN_IF_ERROR(inline_small_exit_blocks(b));
17321762
}
1763+
RETURN_IF_ERROR(remove_unreachable(g->g_entryblock));
1764+
RETURN_IF_ERROR(resolve_line_numbers(g, firstlineno));
17331765
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
17341766
RETURN_IF_ERROR(optimize_basic_block(const_cache, b, consts));
1735-
assert(b->b_predecessors == 0);
17361767
}
17371768
RETURN_IF_ERROR(remove_redundant_nops_and_pairs(g->g_entryblock));
17381769
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
17391770
RETURN_IF_ERROR(inline_small_exit_blocks(b));
17401771
}
1741-
RETURN_IF_ERROR(mark_reachable(g->g_entryblock));
1742-
1743-
/* Delete unreachable instructions */
1744-
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
1745-
if (b->b_predecessors == 0) {
1746-
b->b_iused = 0;
1747-
b->b_except_handler = 0;
1748-
}
1749-
}
1750-
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
1751-
remove_redundant_nops(b);
1752-
}
1753-
RETURN_IF_ERROR(remove_redundant_jumps(g));
1772+
RETURN_IF_ERROR(remove_unreachable(g->g_entryblock));
17541773

1755-
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
1756-
remove_redundant_nops(b);
1774+
for (int n = 0; n < 2; n++) {
1775+
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
1776+
remove_redundant_nops(b);
1777+
}
1778+
RETURN_IF_ERROR(remove_redundant_jumps(g));
17571779
}
17581780

1759-
RETURN_IF_ERROR(remove_redundant_jumps(g));
1760-
17611781
assert(no_redundant_jumps(g));
17621782
return SUCCESS;
17631783
}
@@ -2174,7 +2194,13 @@ push_cold_blocks_to_end(cfg_builder *g) {
21742194
if (!IS_LABEL(b->b_next->b_label)) {
21752195
b->b_next->b_label.id = next_lbl++;
21762196
}
2177-
basicblock_addop(explicit_jump, JUMP_NO_INTERRUPT, b->b_next->b_label.id, NO_LOCATION);
2197+
cfg_instr *prev_instr = basicblock_last_instr(b);
2198+
// b cannot be empty because at the end of an exception handler
2199+
// there is always a POP_EXCEPT + RERAISE/RETURN
2200+
assert(prev_instr);
2201+
2202+
basicblock_addop(explicit_jump, JUMP_NO_INTERRUPT, b->b_next->b_label.id,
2203+
prev_instr->i_loc);
21782204
explicit_jump->b_cold = 1;
21792205
explicit_jump->b_next = b->b_next;
21802206
b->b_next = explicit_jump;
@@ -2345,6 +2371,7 @@ propagate_line_numbers(basicblock *entryblock) {
23452371
for (int i = 0; i < b->b_iused; i++) {
23462372
if (b->b_instr[i].i_loc.lineno < 0) {
23472373
b->b_instr[i].i_loc = prev_location;
2374+
b->b_instr[i].i_loc_propagated = 1;
23482375
}
23492376
else {
23502377
prev_location = b->b_instr[i].i_loc;
@@ -2354,6 +2381,7 @@ propagate_line_numbers(basicblock *entryblock) {
23542381
if (b->b_next->b_iused > 0) {
23552382
if (b->b_next->b_instr[0].i_loc.lineno < 0) {
23562383
b->b_next->b_instr[0].i_loc = prev_location;
2384+
b->b_next->b_instr[0].i_loc_propagated = 1;
23572385
}
23582386
}
23592387
}
@@ -2362,46 +2390,18 @@ propagate_line_numbers(basicblock *entryblock) {
23622390
if (target->b_predecessors == 1) {
23632391
if (target->b_instr[0].i_loc.lineno < 0) {
23642392
target->b_instr[0].i_loc = prev_location;
2393+
target->b_instr[0].i_loc_propagated = 1;
23652394
}
23662395
}
23672396
}
23682397
}
23692398
}
23702399

2371-
/* Make sure that all returns have a line number, even if early passes
2372-
* have failed to propagate a correct line number.
2373-
* The resulting line number may not be correct according to PEP 626,
2374-
* but should be "good enough", and no worse than in older versions. */
2375-
static void
2376-
guarantee_lineno_for_exits(basicblock *entryblock, int firstlineno) {
2377-
int lineno = firstlineno;
2378-
assert(lineno > 0);
2379-
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
2380-
cfg_instr *last = basicblock_last_instr(b);
2381-
if (last == NULL) {
2382-
continue;
2383-
}
2384-
if (last->i_loc.lineno < 0) {
2385-
if (last->i_opcode == RETURN_VALUE) {
2386-
for (int i = 0; i < b->b_iused; i++) {
2387-
assert(b->b_instr[i].i_loc.lineno < 0);
2388-
2389-
b->b_instr[i].i_loc.lineno = lineno;
2390-
}
2391-
}
2392-
}
2393-
else {
2394-
lineno = last->i_loc.lineno;
2395-
}
2396-
}
2397-
}
2398-
23992400
static int
24002401
resolve_line_numbers(cfg_builder *g, int firstlineno)
24012402
{
24022403
RETURN_IF_ERROR(duplicate_exits_without_lineno(g));
24032404
propagate_line_numbers(g->g_entryblock);
2404-
guarantee_lineno_for_exits(g->g_entryblock, firstlineno);
24052405
return SUCCESS;
24062406
}
24072407

@@ -2417,14 +2417,15 @@ _PyCfg_OptimizeCodeUnit(cfg_builder *g, PyObject *consts, PyObject *const_cache,
24172417
RETURN_IF_ERROR(label_exception_targets(g->g_entryblock));
24182418

24192419
/** Optimization **/
2420-
RETURN_IF_ERROR(optimize_cfg(g, consts, const_cache));
2420+
RETURN_IF_ERROR(optimize_cfg(g, consts, const_cache, firstlineno));
24212421
RETURN_IF_ERROR(remove_unused_consts(g->g_entryblock, consts));
24222422
RETURN_IF_ERROR(
24232423
add_checks_for_loads_of_uninitialized_variables(
24242424
g->g_entryblock, nlocals, nparams));
24252425
insert_superinstructions(g);
24262426

24272427
RETURN_IF_ERROR(push_cold_blocks_to_end(g));
2428+
assert(all_exits_have_lineno(g->g_entryblock));
24282429
RETURN_IF_ERROR(resolve_line_numbers(g, firstlineno));
24292430
return SUCCESS;
24302431
}

0 commit comments

Comments
 (0)