summaryrefslogtreecommitdiffstats
path: root/Python/compile.c
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2021-01-13 12:05:43 (GMT)
committerGitHub <noreply@github.com>2021-01-13 12:05:43 (GMT)
commit3bd6035b6baf1a7d51b7cc2c6bb2c81886236b67 (patch)
tree22746e3a5738e389725765ade3167911eba51b9a /Python/compile.c
parent2396614b8958ad202378fd71a598eb4106ac5896 (diff)
downloadcpython-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.c135
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;
}