diff options
author | Mark Shannon <mark@hotpy.org> | 2021-01-13 12:05:43 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-13 12:05:43 (GMT) |
commit | 3bd6035b6baf1a7d51b7cc2c6bb2c81886236b67 (patch) | |
tree | 22746e3a5738e389725765ade3167911eba51b9a /Python/compile.c | |
parent | 2396614b8958ad202378fd71a598eb4106ac5896 (diff) | |
download | cpython-3bd6035b6baf1a7d51b7cc2c6bb2c81886236b67.zip cpython-3bd6035b6baf1a7d51b7cc2c6bb2c81886236b67.tar.gz cpython-3bd6035b6baf1a7d51b7cc2c6bb2c81886236b67.tar.bz2 |
bpo-42908: Mark cleanup code at end of try-except and with artificial (#24202)
* Mark bytecodes at end of try-except as artificial.
* Make sure that the CFG is consistent throughout optimiization.
* Extend line-number propagation logic so that implicit returns after 'try-except' or 'with' have the correct line numbers.
* Update importlib
Diffstat (limited to 'Python/compile.c')
-rw-r--r-- | Python/compile.c | 135 |
1 files changed, 104 insertions, 31 deletions
diff --git a/Python/compile.c b/Python/compile.c index cf5c639..268c5aa 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -95,7 +95,8 @@ typedef struct basicblock_ { struct basicblock_ *b_next; /* b_return is true if a RETURN_VALUE opcode is inserted. */ unsigned b_return : 1; - unsigned b_reachable : 1; + /* Number of predecssors that a block has. */ + int b_predecessors; /* Basic block has no fall through (it ends with a return, raise or jump) */ unsigned b_nofallthrough : 1; /* Basic block exits scope (it ends with a return or raise) */ @@ -825,6 +826,7 @@ compiler_copy_block(struct compiler *c, basicblock *block) result->b_instr[n] = block->b_instr[i]; } result->b_exit = block->b_exit; + result->b_nofallthrough = 1; return result; } @@ -1169,7 +1171,7 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg) */ static int -compiler_addop(struct compiler *c, int opcode) +compiler_addop_line(struct compiler *c, int opcode, int line) { basicblock *b; struct instr *i; @@ -1184,10 +1186,23 @@ compiler_addop(struct compiler *c, int opcode) i->i_oparg = 0; if (opcode == RETURN_VALUE) b->b_return = 1; - i->i_lineno = c->u->u_lineno; + i->i_lineno = line; return 1; } +static int +compiler_addop(struct compiler *c, int opcode) +{ + return compiler_addop_line(c, opcode, c->u->u_lineno); +} + +static int +compiler_addop_noline(struct compiler *c, int opcode) +{ + return compiler_addop_line(c, opcode, -1); +} + + static Py_ssize_t compiler_add_o(PyObject *dict, PyObject *o) { @@ -1448,6 +1463,11 @@ compiler_addop_j_noline(struct compiler *c, int opcode, basicblock *b) return 0; \ } +#define ADDOP_NOLINE(C, OP) { \ + if (!compiler_addop_noline((C), (OP))) \ + return 0; \ +} + #define ADDOP_IN_SCOPE(C, OP) { \ if (!compiler_addop((C), (OP))) { \ compiler_exit_scope(c); \ @@ -2955,9 +2975,7 @@ compiler_try_finally(struct compiler *c, stmt_ty s) else { VISIT_SEQ(c, stmt, s->v.Try.body); } - /* Mark code as artificial */ - c->u->u_lineno = -1; - ADDOP(c, POP_BLOCK); + ADDOP_NOLINE(c, POP_BLOCK); compiler_pop_fblock(c, FINALLY_TRY, body); VISIT_SEQ(c, stmt, s->v.Try.finalbody); ADDOP_JUMP_NOLINE(c, JUMP_FORWARD, exit); @@ -3019,9 +3037,9 @@ compiler_try_except(struct compiler *c, stmt_ty s) if (!compiler_push_fblock(c, TRY_EXCEPT, body, NULL, NULL)) return 0; VISIT_SEQ(c, stmt, s->v.Try.body); - ADDOP(c, POP_BLOCK); compiler_pop_fblock(c, TRY_EXCEPT, body); - ADDOP_JUMP(c, JUMP_FORWARD, orelse); + ADDOP_NOLINE(c, POP_BLOCK); + ADDOP_JUMP_NOLINE(c, JUMP_FORWARD, orelse); n = asdl_seq_LEN(s->v.Try.handlers); compiler_use_next_block(c, except); /* Runtime will push a block here, so we need to account for that */ @@ -4925,6 +4943,9 @@ compiler_with(struct compiler *c, stmt_ty s, int pos) else if (!compiler_with(c, s, pos)) return 0; + + /* Mark all following code as artificial */ + c->u->u_lineno = -1; ADDOP(c, POP_BLOCK); compiler_pop_fblock(c, WITH, block); @@ -6396,22 +6417,24 @@ mark_reachable(struct assembler *a) { if (stack == NULL) { return -1; } - a->a_entry->b_reachable = 1; + a->a_entry->b_predecessors = 1; *sp++ = a->a_entry; while (sp > stack) { basicblock *b = *(--sp); - if (b->b_next && !b->b_nofallthrough && b->b_next->b_reachable == 0) { - b->b_next->b_reachable = 1; - *sp++ = b->b_next; + if (b->b_next && !b->b_nofallthrough) { + if (b->b_next->b_predecessors == 0) { + *sp++ = b->b_next; + } + b->b_next->b_predecessors++; } for (int i = 0; i < b->b_iused; i++) { basicblock *target; if (is_jump(&b->b_instr[i])) { target = b->b_instr[i].i_target; - if (target->b_reachable == 0) { - target->b_reachable = 1; + if (target->b_predecessors == 0) { *sp++ = target; } + target->b_predecessors++; } } } @@ -6419,13 +6442,46 @@ mark_reachable(struct assembler *a) { return 0; } +static void +eliminate_empty_basic_blocks(basicblock *entry) { + /* Eliminate empty blocks */ + for (basicblock *b = entry; b != NULL; b = b->b_next) { + basicblock *next = b->b_next; + if (next) { + while (next->b_iused == 0 && next->b_next) { + next = next->b_next; + } + b->b_next = next; + } + } + for (basicblock *b = entry; b != NULL; b = b->b_next) { + if (b->b_iused == 0) { + continue; + } + if (is_jump(&b->b_instr[b->b_iused-1])) { + basicblock *target = b->b_instr[b->b_iused-1].i_target; + while (target->b_iused == 0) { + target = target->b_next; + } + b->b_instr[b->b_iused-1].i_target = target; + } + } +} + + /* If an instruction has no line number, but it's predecessor in the BB does, - * then copy the line number. This reduces the size of the line number table, + * then copy the line number. If a successor block has no line number, and only + * one predecessor, then inherit the line number. + * This ensures that all exit blocks (with one predecessor) receive a line number. + * Also reduces the size of the line number table, * but has no impact on the generated line number events. */ static void -minimize_lineno_table(struct assembler *a) { +propogate_line_numbers(struct assembler *a) { for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) { + if (b->b_iused == 0) { + continue; + } int prev_lineno = -1; for (int i = 0; i < b->b_iused; i++) { if (b->b_instr[i].i_lineno < 0) { @@ -6435,7 +6491,27 @@ minimize_lineno_table(struct assembler *a) { prev_lineno = b->b_instr[i].i_lineno; } } - + if (!b->b_nofallthrough && b->b_next->b_predecessors == 1) { + assert(b->b_next->b_iused); + if (b->b_next->b_instr[0].i_lineno < 0) { + b->b_next->b_instr[0].i_lineno = prev_lineno; + } + } + if (is_jump(&b->b_instr[b->b_iused-1])) { + switch (b->b_instr[b->b_iused-1].i_opcode) { + /* Note: Only actual jumps, not exception handlers */ + case SETUP_ASYNC_WITH: + case SETUP_WITH: + case SETUP_FINALLY: + continue; + } + basicblock *target = b->b_instr[b->b_iused-1].i_target; + if (target->b_predecessors == 1) { + if (target->b_instr[0].i_lineno < 0) { + target->b_instr[0].i_lineno = prev_lineno; + } + } + } } } @@ -6456,23 +6532,25 @@ optimize_cfg(struct assembler *a, PyObject *consts) return -1; } clean_basic_block(b); - assert(b->b_reachable == 0); + assert(b->b_predecessors == 0); } if (mark_reachable(a)) { return -1; } /* Delete unreachable instructions */ for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) { - if (b->b_reachable == 0) { + if (b->b_predecessors == 0) { b->b_iused = 0; b->b_nofallthrough = 0; } } + eliminate_empty_basic_blocks(a->a_entry); /* Delete jump instructions made redundant by previous step. If a non-empty block ends with a jump instruction, check if the next non-empty block reached through normal flow control is the target of that jump. If it is, then the jump instruction is redundant and can be deleted. */ + int maybe_empty_blocks = 0; for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) { if (b->b_iused > 0) { struct instr *b_last_instr = &b->b_instr[b->b_iused - 1]; @@ -6480,11 +6558,8 @@ optimize_cfg(struct assembler *a, PyObject *consts) b_last_instr->i_opcode == POP_JUMP_IF_TRUE || b_last_instr->i_opcode == JUMP_ABSOLUTE || b_last_instr->i_opcode == JUMP_FORWARD) { - basicblock *b_next_act = b->b_next; - while (b_next_act != NULL && b_next_act->b_iused == 0) { - b_next_act = b_next_act->b_next; - } - if (b_last_instr->i_target == b_next_act) { + if (b_last_instr->i_target == b->b_next) { + assert(b->b_next->b_iused); b->b_nofallthrough = 0; switch(b_last_instr->i_opcode) { case POP_JUMP_IF_FALSE: @@ -6497,19 +6572,17 @@ optimize_cfg(struct assembler *a, PyObject *consts) case JUMP_FORWARD: b_last_instr->i_opcode = NOP; clean_basic_block(b); + maybe_empty_blocks = 1; break; } - /* The blocks after this one are now reachable through it */ - b_next_act = b->b_next; - while (b_next_act != NULL && b_next_act->b_iused == 0) { - b_next_act->b_reachable = 1; - b_next_act = b_next_act->b_next; - } } } } } - minimize_lineno_table(a); + if (maybe_empty_blocks) { + eliminate_empty_basic_blocks(a->a_entry); + } + propogate_line_numbers(a); return 0; } |