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;  }  | 
