summaryrefslogtreecommitdiff
path: root/Python/compile.c
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2020-12-27 12:46:59 -0500
committerJason R. Coombs <jaraco@jaraco.com>2020-12-27 12:46:59 -0500
commita78f0158a28734f965218b834ea8c0b166b7353f (patch)
treedca70268e2a41d49658e7eed783c6fc243d119cd /Python/compile.c
parentec8e6895a3ce9cd69b6ceb75a15fcc74d4a522dc (diff)
parentbf64d9064ab641b1ef9a0c4bda097ebf1204faf4 (diff)
downloadcpython-git-revert-23107-revert-13893-fix-issue-37193.tar.gz
Merge branch 'master' into revert-23107-revert-13893-fix-issue-37193revert-23107-revert-13893-fix-issue-37193
Diffstat (limited to 'Python/compile.c')
-rw-r--r--Python/compile.c947
1 files changed, 584 insertions, 363 deletions
diff --git a/Python/compile.c b/Python/compile.c
index 15a9046065..4ba9140000 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -96,6 +96,10 @@ typedef struct basicblock_ {
/* b_return is true if a RETURN_VALUE opcode is inserted. */
unsigned b_return : 1;
unsigned b_reachable : 1;
+ /* 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) */
+ unsigned b_exit : 1;
/* depth of stack upon entry of block, computed by stackdepth() */
int b_startdepth;
/* instruction offset for block, computed by assemble_jump_offsets() */
@@ -224,7 +228,7 @@ static int compiler_slice(struct compiler *, expr_ty);
static int inplace_binop(operator_ty);
static int are_all_items_const(asdl_expr_seq *, Py_ssize_t, Py_ssize_t);
-static int expr_constant(expr_ty);
+
static int compiler_with(struct compiler *, stmt_ty, int);
static int compiler_async_with(struct compiler *, stmt_ty, int);
@@ -808,6 +812,28 @@ compiler_use_next_block(struct compiler *c, basicblock *block)
return block;
}
+static basicblock *
+compiler_copy_block(struct compiler *c, basicblock *block)
+{
+ /* Cannot copy a block if it has a fallthrough, since
+ * a block can only have one fallthrough predecessor.
+ */
+ assert(block->b_nofallthrough);
+ basicblock *result = compiler_next_block(c);
+ if (result == NULL) {
+ return NULL;
+ }
+ for (int i = 0; i < block->b_iused; i++) {
+ int n = compiler_next_instr(result);
+ if (n < 0) {
+ return NULL;
+ }
+ result->b_instr[n] = block->b_instr[i];
+ }
+ result->b_exit = block->b_exit;
+ return result;
+}
+
/* Returns the offset of the next instruction in the current block's
b_instr array. Resizes the b_instr as necessary.
Returns -1 on failure.
@@ -1406,28 +1432,32 @@ compiler_addop_i(struct compiler *c, int opcode, Py_ssize_t oparg)
return 1;
}
-static int
-compiler_addop_j(struct compiler *c, int opcode, basicblock *b)
+static int add_jump_to_block(basicblock *b, int opcode, int lineno, basicblock *target)
{
- struct instr *i;
- int off;
-
- if (c->c_do_not_emit_bytecode) {
- return 1;
- }
-
assert(HAS_ARG(opcode));
assert(b != NULL);
- off = compiler_next_instr(c->u->u_curblock);
- if (off < 0)
+ assert(target != NULL);
+
+ int off = compiler_next_instr(b);
+ struct instr *i = &b->b_instr[off];
+ if (off < 0) {
return 0;
- i = &c->u->u_curblock->b_instr[off];
+ }
i->i_opcode = opcode;
- i->i_target = b;
- i->i_lineno = c->u->u_lineno;
+ i->i_target = target;
+ i->i_lineno = lineno;
return 1;
}
+static int
+compiler_addop_j(struct compiler *c, int opcode, basicblock *b)
+{
+ if (c->c_do_not_emit_bytecode) {
+ return 1;
+ }
+ return add_jump_to_block(c->u->u_curblock, opcode, c->u->u_lineno, b);
+}
+
/* NEXT_BLOCK() creates an implicit jump from the current block
to the new block.
@@ -1546,17 +1576,6 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b)
} \
}
-/* These macros allows to check only for errors and not emmit bytecode
- * while visiting nodes.
-*/
-
-#define BEGIN_DO_NOT_EMIT_BYTECODE { \
- c->c_do_not_emit_bytecode++;
-
-#define END_DO_NOT_EMIT_BYTECODE \
- c->c_do_not_emit_bytecode--; \
-}
-
/* Search if variable annotations are present statically in a block. */
static int
@@ -1680,19 +1699,22 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
return 1;
case FINALLY_TRY:
+ /* This POP_BLOCK gets the line number of the unwinding statement */
ADDOP(c, POP_BLOCK);
if (preserve_tos) {
if (!compiler_push_fblock(c, POP_VALUE, NULL, NULL, NULL)) {
return 0;
}
}
- /* Emit the finally block, restoring the line number when done */
- int saved_lineno = c->u->u_lineno;
+ /* Emit the finally block */
VISIT_SEQ(c, stmt, info->fb_datum);
- c->u->u_lineno = saved_lineno;
if (preserve_tos) {
compiler_pop_fblock(c, POP_VALUE, NULL);
}
+ /* The finally block should appear to execute after the
+ * statement causing the unwinding, so make the unwinding
+ * instruction artificial */
+ c->u->u_lineno = -1;
return 1;
case FINALLY_END:
@@ -1827,7 +1849,7 @@ compiler_mod(struct compiler *c, mod_ty mod)
return NULL;
}
/* Use 0 for firstlineno initially, will fixup in assemble(). */
- if (!compiler_enter_scope(c, module, COMPILER_SCOPE_MODULE, mod, 0))
+ if (!compiler_enter_scope(c, module, COMPILER_SCOPE_MODULE, mod, 1))
return NULL;
switch (mod->kind) {
case Module_kind:
@@ -2023,26 +2045,24 @@ compiler_visit_annexpr(struct compiler *c, expr_ty annotation)
static int
compiler_visit_argannotation(struct compiler *c, identifier id,
- expr_ty annotation, PyObject *names)
+ expr_ty annotation, Py_ssize_t *annotations_len)
{
if (annotation) {
- PyObject *mangled;
- VISIT(c, annexpr, annotation);
- mangled = _Py_Mangle(c->u->u_private, id);
+ PyObject *mangled = _Py_Mangle(c->u->u_private, id);
if (!mangled)
return 0;
- if (PyList_Append(names, mangled) < 0) {
- Py_DECREF(mangled);
- return 0;
- }
+
+ ADDOP_LOAD_CONST(c, mangled);
Py_DECREF(mangled);
+ VISIT(c, annexpr, annotation);
+ *annotations_len += 2;
}
return 1;
}
static int
compiler_visit_argannotations(struct compiler *c, asdl_arg_seq* args,
- PyObject *names)
+ Py_ssize_t *annotations_len)
{
int i;
for (i = 0; i < asdl_seq_LEN(args); i++) {
@@ -2051,7 +2071,7 @@ compiler_visit_argannotations(struct compiler *c, asdl_arg_seq* args,
c,
arg->arg,
arg->annotation,
- names))
+ annotations_len))
return 0;
}
return 1;
@@ -2061,58 +2081,44 @@ static int
compiler_visit_annotations(struct compiler *c, arguments_ty args,
expr_ty returns)
{
- /* Push arg annotation dict.
+ /* Push arg annotation names and values.
The expressions are evaluated out-of-order wrt the source code.
- Return 0 on error, -1 if no dict pushed, 1 if a dict is pushed.
+ Return 0 on error, -1 if no annotations pushed, 1 if a annotations is pushed.
*/
static identifier return_str;
- PyObject *names;
- Py_ssize_t len;
- names = PyList_New(0);
- if (!names)
- return 0;
+ Py_ssize_t annotations_len = 0;
- if (!compiler_visit_argannotations(c, args->args, names))
- goto error;
- if (!compiler_visit_argannotations(c, args->posonlyargs, names))
- goto error;
+ if (!compiler_visit_argannotations(c, args->args, &annotations_len))
+ return 0;
+ if (!compiler_visit_argannotations(c, args->posonlyargs, &annotations_len))
+ return 0;
if (args->vararg && args->vararg->annotation &&
!compiler_visit_argannotation(c, args->vararg->arg,
- args->vararg->annotation, names))
- goto error;
- if (!compiler_visit_argannotations(c, args->kwonlyargs, names))
- goto error;
+ args->vararg->annotation, &annotations_len))
+ return 0;
+ if (!compiler_visit_argannotations(c, args->kwonlyargs, &annotations_len))
+ return 0;
if (args->kwarg && args->kwarg->annotation &&
!compiler_visit_argannotation(c, args->kwarg->arg,
- args->kwarg->annotation, names))
- goto error;
+ args->kwarg->annotation, &annotations_len))
+ return 0;
if (!return_str) {
return_str = PyUnicode_InternFromString("return");
if (!return_str)
- goto error;
+ return 0;
}
- if (!compiler_visit_argannotation(c, return_str, returns, names)) {
- goto error;
+ if (!compiler_visit_argannotation(c, return_str, returns, &annotations_len)) {
+ return 0;
}
- len = PyList_GET_SIZE(names);
- if (len) {
- PyObject *keytuple = PyList_AsTuple(names);
- Py_DECREF(names);
- ADDOP_LOAD_CONST_NEW(c, keytuple);
- ADDOP_I(c, BUILD_CONST_KEY_MAP, len);
+ if (annotations_len) {
+ ADDOP_I(c, BUILD_TUPLE, annotations_len);
return 1;
}
- else {
- Py_DECREF(names);
- return -1;
- }
-error:
- Py_DECREF(names);
- return 0;
+ return -1;
}
static int
@@ -2271,7 +2277,9 @@ compiler_function(struct compiler *c, stmt_ty s, int is_async)
c->u->u_argcount = asdl_seq_LEN(args->args);
c->u->u_posonlyargcount = asdl_seq_LEN(args->posonlyargs);
c->u->u_kwonlyargcount = asdl_seq_LEN(args->kwonlyargs);
- VISIT_SEQ_IN_SCOPE(c, stmt, body);
+ for (i = docstring ? 1 : 0; i < asdl_seq_LEN(body); i++) {
+ VISIT_IN_SCOPE(c, stmt, (stmt_ty)asdl_seq_GET(body, i));
+ }
co = assemble(c, 1);
qualname = c->u->u_qualname;
Py_INCREF(qualname);
@@ -2581,6 +2589,7 @@ compiler_jump_if(struct compiler *c, expr_ty e, basicblock *next, int cond)
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n));
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, n));
ADDOP_JUMP(c, cond ? POP_JUMP_IF_TRUE : POP_JUMP_IF_FALSE, next);
+ NEXT_BLOCK(c);
basicblock *end = compiler_new_block(c);
if (end == NULL)
return 0;
@@ -2604,6 +2613,7 @@ compiler_jump_if(struct compiler *c, expr_ty e, basicblock *next, int cond)
/* general implementation */
VISIT(c, expr, e);
ADDOP_JUMP(c, cond ? POP_JUMP_IF_TRUE : POP_JUMP_IF_FALSE, next);
+ NEXT_BLOCK(c);
return 1;
}
@@ -2690,48 +2700,28 @@ static int
compiler_if(struct compiler *c, stmt_ty s)
{
basicblock *end, *next;
- int constant;
assert(s->kind == If_kind);
end = compiler_new_block(c);
- if (end == NULL)
+ if (end == NULL) {
return 0;
-
- constant = expr_constant(s->v.If.test);
- /* constant = 0: "if 0"
- * constant = 1: "if 1", "if 2", ...
- * constant = -1: rest */
- if (constant == 0) {
- BEGIN_DO_NOT_EMIT_BYTECODE
- VISIT_SEQ(c, stmt, s->v.If.body);
- END_DO_NOT_EMIT_BYTECODE
- if (s->v.If.orelse) {
- VISIT_SEQ(c, stmt, s->v.If.orelse);
- }
- } else if (constant == 1) {
- VISIT_SEQ(c, stmt, s->v.If.body);
- if (s->v.If.orelse) {
- BEGIN_DO_NOT_EMIT_BYTECODE
- VISIT_SEQ(c, stmt, s->v.If.orelse);
- END_DO_NOT_EMIT_BYTECODE
- }
- } else {
- if (asdl_seq_LEN(s->v.If.orelse)) {
- next = compiler_new_block(c);
- if (next == NULL)
- return 0;
- }
- else {
- next = end;
- }
- if (!compiler_jump_if(c, s->v.If.test, next, 0)) {
+ }
+ if (asdl_seq_LEN(s->v.If.orelse)) {
+ next = compiler_new_block(c);
+ if (next == NULL) {
return 0;
}
- VISIT_SEQ(c, stmt, s->v.If.body);
- if (asdl_seq_LEN(s->v.If.orelse)) {
- ADDOP_JUMP(c, JUMP_FORWARD, end);
- compiler_use_next_block(c, next);
- VISIT_SEQ(c, stmt, s->v.If.orelse);
- }
+ }
+ else {
+ next = end;
+ }
+ if (!compiler_jump_if(c, s->v.If.test, next, 0)) {
+ return 0;
+ }
+ VISIT_SEQ(c, stmt, s->v.If.body);
+ if (asdl_seq_LEN(s->v.If.orelse)) {
+ ADDOP_JUMP(c, JUMP_FORWARD, end);
+ compiler_use_next_block(c, next);
+ VISIT_SEQ(c, stmt, s->v.If.orelse);
}
compiler_use_next_block(c, end);
return 1;
@@ -2740,12 +2730,13 @@ compiler_if(struct compiler *c, stmt_ty s)
static int
compiler_for(struct compiler *c, stmt_ty s)
{
- basicblock *start, *cleanup, *end;
+ basicblock *start, *body, *cleanup, *end;
start = compiler_new_block(c);
+ body = compiler_new_block(c);
cleanup = compiler_new_block(c);
end = compiler_new_block(c);
- if (start == NULL || end == NULL || cleanup == NULL) {
+ if (start == NULL || body == NULL || end == NULL || cleanup == NULL) {
return 0;
}
if (!compiler_push_fblock(c, FOR_LOOP, start, end, NULL)) {
@@ -2755,8 +2746,11 @@ compiler_for(struct compiler *c, stmt_ty s)
ADDOP(c, GET_ITER);
compiler_use_next_block(c, start);
ADDOP_JUMP(c, FOR_ITER, cleanup);
+ compiler_use_next_block(c, body);
VISIT(c, expr, s->v.For.target);
VISIT_SEQ(c, stmt, s->v.For.body);
+ /* Mark jump as artificial */
+ c->u->u_lineno = -1;
ADDOP_JUMP(c, JUMP_ABSOLUTE, start);
compiler_use_next_block(c, cleanup);
@@ -2808,6 +2802,8 @@ compiler_async_for(struct compiler *c, stmt_ty s)
/* Except block for __anext__ */
compiler_use_next_block(c, except);
+
+ c->u->u_lineno = -1;
ADDOP(c, END_ASYNC_FOR);
/* `else` block */
@@ -2821,63 +2817,35 @@ compiler_async_for(struct compiler *c, stmt_ty s)
static int
compiler_while(struct compiler *c, stmt_ty s)
{
- basicblock *loop, *orelse, *end, *anchor = NULL;
- int constant = expr_constant(s->v.While.test);
-
- if (constant == 0) {
- BEGIN_DO_NOT_EMIT_BYTECODE
- // Push a dummy block so the VISIT_SEQ knows that we are
- // inside a while loop so it can correctly evaluate syntax
- // errors.
- if (!compiler_push_fblock(c, WHILE_LOOP, NULL, NULL, NULL)) {
- return 0;
- }
- VISIT_SEQ(c, stmt, s->v.While.body);
- // Remove the dummy block now that is not needed.
- compiler_pop_fblock(c, WHILE_LOOP, NULL);
- END_DO_NOT_EMIT_BYTECODE
- if (s->v.While.orelse) {
- VISIT_SEQ(c, stmt, s->v.While.orelse);
- }
- return 1;
- }
+ basicblock *loop, *body, *end, *anchor = NULL;
loop = compiler_new_block(c);
+ body = compiler_new_block(c);
+ anchor = compiler_new_block(c);
end = compiler_new_block(c);
- if (constant == -1) {
- anchor = compiler_new_block(c);
- if (anchor == NULL)
- return 0;
- }
- if (loop == NULL || end == NULL)
+ if (loop == NULL || body == NULL || anchor == NULL || end == NULL) {
return 0;
- if (s->v.While.orelse) {
- orelse = compiler_new_block(c);
- if (orelse == NULL)
- return 0;
}
- else
- orelse = NULL;
-
compiler_use_next_block(c, loop);
- if (!compiler_push_fblock(c, WHILE_LOOP, loop, end, NULL))
+ if (!compiler_push_fblock(c, WHILE_LOOP, loop, end, NULL)) {
+ return 0;
+ }
+ if (!compiler_jump_if(c, s->v.While.test, anchor, 0)) {
return 0;
- if (constant == -1) {
- if (!compiler_jump_if(c, s->v.While.test, anchor, 0))
- return 0;
}
- VISIT_SEQ(c, stmt, s->v.While.body);
- ADDOP_JUMP(c, JUMP_ABSOLUTE, loop);
- /* XXX should the two POP instructions be in a separate block
- if there is no else clause ?
- */
+ compiler_use_next_block(c, body);
+ VISIT_SEQ(c, stmt, s->v.While.body);
+ SET_LOC(c, s);
+ if (!compiler_jump_if(c, s->v.While.test, body, 1)) {
+ return 0;
+ }
- if (constant == -1)
- compiler_use_next_block(c, anchor);
compiler_pop_fblock(c, WHILE_LOOP, loop);
- if (orelse != NULL) /* what if orelse is just pass? */
+ compiler_use_next_block(c, anchor);
+ if (s->v.While.orelse) {
VISIT_SEQ(c, stmt, s->v.While.orelse);
+ }
compiler_use_next_block(c, end);
return 1;
@@ -2898,6 +2866,12 @@ compiler_return(struct compiler *c, stmt_ty s)
}
if (preserve_tos) {
VISIT(c, expr, s->v.Return.value);
+ } else {
+ /* Emit instruction with line number for expression */
+ if (s->v.Return.value != NULL) {
+ SET_LOC(c, s->v.Return.value);
+ ADDOP(c, NOP);
+ }
}
if (!compiler_unwind_fblock_stack(c, preserve_tos, NULL))
return 0;
@@ -2905,9 +2879,10 @@ compiler_return(struct compiler *c, stmt_ty s)
ADDOP_LOAD_CONST(c, Py_None);
}
else if (!preserve_tos) {
- VISIT(c, expr, s->v.Return.value);
+ ADDOP_LOAD_CONST(c, s->v.Return.value->v.Constant.value);
}
ADDOP(c, RETURN_VALUE);
+ NEXT_BLOCK(c);
return 1;
}
@@ -2926,6 +2901,7 @@ compiler_break(struct compiler *c)
return 0;
}
ADDOP_JUMP(c, JUMP_ABSOLUTE, loop->fb_exit);
+ NEXT_BLOCK(c);
return 1;
}
@@ -2940,6 +2916,7 @@ compiler_continue(struct compiler *c)
return compiler_error(c, "'continue' not properly in loop");
}
ADDOP_JUMP(c, JUMP_ABSOLUTE, loop->fb_block);
+ NEXT_BLOCK(c)
return 1;
}
@@ -2996,6 +2973,8 @@ 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);
compiler_pop_fblock(c, FINALLY_TRY, body);
VISIT_SEQ(c, stmt, s->v.Try.finalbody);
@@ -3006,7 +2985,7 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
return 0;
VISIT_SEQ(c, stmt, s->v.Try.finalbody);
compiler_pop_fblock(c, FINALLY_END, end);
- ADDOP(c, RERAISE);
+ ADDOP_I(c, RERAISE, 0);
compiler_use_next_block(c, exit);
return 1;
}
@@ -3079,6 +3058,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
ADDOP(c, DUP_TOP);
VISIT(c, expr, handler->v.ExceptHandler.type);
ADDOP_JUMP(c, JUMP_IF_NOT_EXC_MATCH, except);
+ NEXT_BLOCK(c);
}
ADDOP(c, POP_TOP);
if (handler->v.ExceptHandler.name) {
@@ -3115,7 +3095,8 @@ compiler_try_except(struct compiler *c, stmt_ty s)
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
ADDOP(c, POP_BLOCK);
ADDOP(c, POP_EXCEPT);
- /* name = None; del name */
+ /* name = None; del name; # Mark as artificial */
+ c->u->u_lineno = -1;
ADDOP_LOAD_CONST(c, Py_None);
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
compiler_nameop(c, handler->v.ExceptHandler.name, Del);
@@ -3124,12 +3105,13 @@ compiler_try_except(struct compiler *c, stmt_ty s)
/* except: */
compiler_use_next_block(c, cleanup_end);
- /* name = None; del name */
+ /* name = None; del name; # Mark as artificial */
+ c->u->u_lineno = -1;
ADDOP_LOAD_CONST(c, Py_None);
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
compiler_nameop(c, handler->v.ExceptHandler.name, Del);
- ADDOP(c, RERAISE);
+ ADDOP_I(c, RERAISE, 1);
}
else {
basicblock *cleanup_body;
@@ -3151,7 +3133,9 @@ compiler_try_except(struct compiler *c, stmt_ty s)
compiler_use_next_block(c, except);
}
compiler_pop_fblock(c, EXCEPTION_HANDLER, NULL);
- ADDOP(c, RERAISE);
+ /* Mark as artificial */
+ c->u->u_lineno = -1;
+ ADDOP_I(c, RERAISE, 0);
compiler_use_next_block(c, orelse);
VISIT_SEQ(c, stmt, s->v.Try.orelse);
compiler_use_next_block(c, end);
@@ -3359,6 +3343,7 @@ compiler_visit_stmt_expr(struct compiler *c, expr_ty value)
if (value->kind == Constant_kind) {
/* ignore constant statement */
+ ADDOP(c, NOP);
return 1;
}
@@ -3416,6 +3401,7 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
}
}
ADDOP_I(c, RAISE_VARARGS, (int)n);
+ NEXT_BLOCK(c);
break;
case Try_kind:
return compiler_try(c, s);
@@ -3431,6 +3417,7 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
case Expr_kind:
return compiler_visit_stmt_expr(c, s->v.Expr.value);
case Pass_kind:
+ ADDOP(c, NOP);
break;
case Break_kind:
return compiler_break(c);
@@ -4771,22 +4758,14 @@ compiler_visit_keyword(struct compiler *c, keyword_ty k)
*/
static int
-expr_constant(expr_ty e)
-{
- if (e->kind == Constant_kind) {
- return PyObject_IsTrue(e->v.Constant.value);
- }
- return -1;
-}
-
-static int
compiler_with_except_finish(struct compiler *c) {
basicblock *exit;
exit = compiler_new_block(c);
if (exit == NULL)
return 0;
ADDOP_JUMP(c, POP_JUMP_IF_TRUE, exit);
- ADDOP(c, RERAISE);
+ NEXT_BLOCK(c);
+ ADDOP_I(c, RERAISE, 1);
compiler_use_next_block(c, exit);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
@@ -5426,27 +5405,14 @@ struct assembler {
PyObject *a_bytecode; /* string containing bytecode */
int a_offset; /* offset into bytecode */
int a_nblocks; /* number of reachable blocks */
- basicblock **a_reverse_postorder; /* list of blocks in dfs postorder */
PyObject *a_lnotab; /* string containing lnotab */
int a_lnotab_off; /* offset into lnotab */
- int a_lineno; /* last lineno of emitted instruction */
- int a_lineno_off; /* bytecode offset of last lineno */
+ int a_prevlineno; /* lineno of last emitted line in line table */
+ int a_lineno; /* lineno of last emitted instruction */
+ int a_lineno_start; /* bytecode start offset of current lineno */
+ basicblock *a_entry;
};
-static void
-dfs(struct compiler *c, basicblock *b, struct assembler *a, int end)
-{
-
- /* There is no real depth-first-search to do here because all the
- * blocks are emitted in topological order already, so we just need to
- * follow the b_next pointers and place them in a->a_reverse_postorder in
- * reverse order and make sure that the first one starts at 0. */
-
- for (a->a_nblocks = 0; b != NULL; b = b->b_next) {
- a->a_reverse_postorder[a->a_nblocks++] = b;
- }
-}
-
Py_LOCAL_INLINE(void)
stackdepth_push(basicblock ***sp, basicblock *b, int depth)
{
@@ -5522,6 +5488,7 @@ stackdepth(struct compiler *c)
}
}
if (next != NULL) {
+ assert(b->b_nofallthrough == 0);
stackdepth_push(&sp, next, depth);
}
}
@@ -5533,24 +5500,25 @@ static int
assemble_init(struct assembler *a, int nblocks, int firstlineno)
{
memset(a, 0, sizeof(struct assembler));
- a->a_lineno = firstlineno;
+ a->a_prevlineno = a->a_lineno = firstlineno;
+ a->a_lnotab = NULL;
a->a_bytecode = PyBytes_FromStringAndSize(NULL, DEFAULT_CODE_SIZE);
- if (!a->a_bytecode)
- return 0;
+ if (a->a_bytecode == NULL) {
+ goto error;
+ }
a->a_lnotab = PyBytes_FromStringAndSize(NULL, DEFAULT_LNOTAB_SIZE);
- if (!a->a_lnotab)
- return 0;
- if ((size_t)nblocks > SIZE_MAX / sizeof(basicblock *)) {
- PyErr_NoMemory();
- return 0;
+ if (a->a_lnotab == NULL) {
+ goto error;
}
- a->a_reverse_postorder = (basicblock **)PyObject_Malloc(
- sizeof(basicblock *) * nblocks);
- if (!a->a_reverse_postorder) {
+ if ((size_t)nblocks > SIZE_MAX / sizeof(basicblock *)) {
PyErr_NoMemory();
- return 0;
+ goto error;
}
return 1;
+error:
+ Py_XDECREF(a->a_bytecode);
+ Py_XDECREF(a->a_lnotab);
+ return 0;
}
static void
@@ -5558,8 +5526,6 @@ assemble_free(struct assembler *a)
{
Py_XDECREF(a->a_bytecode);
Py_XDECREF(a->a_lnotab);
- if (a->a_reverse_postorder)
- PyObject_Free(a->a_reverse_postorder);
}
static int
@@ -5573,114 +5539,82 @@ blocksize(basicblock *b)
return size;
}
-/* Appends a pair to the end of the line number table, a_lnotab, representing
- the instruction's bytecode offset and line number. See
- Objects/lnotab_notes.txt for the description of the line number table. */
-
static int
-assemble_lnotab(struct assembler *a, struct instr *i)
+assemble_emit_linetable_pair(struct assembler *a, int bdelta, int ldelta)
{
- int d_bytecode, d_lineno;
- Py_ssize_t len;
- unsigned char *lnotab;
-
- d_lineno = i->i_lineno - a->a_lineno;
- if (d_lineno == 0) {
- return 1;
+ Py_ssize_t len = PyBytes_GET_SIZE(a->a_lnotab);
+ if (a->a_lnotab_off + 2 >= len) {
+ if (_PyBytes_Resize(&a->a_lnotab, len * 2) < 0)
+ return 0;
}
+ unsigned char *lnotab = (unsigned char *)
+ PyBytes_AS_STRING(a->a_lnotab) + a->a_lnotab_off;
+
+ a->a_lnotab_off += 2;
+ *lnotab++ = bdelta;
+ *lnotab++ = ldelta;
+ return 1;
+}
- d_bytecode = (a->a_offset - a->a_lineno_off) * sizeof(_Py_CODEUNIT);
- assert(d_bytecode >= 0);
+/* Appends a range to the end of the line number table. See
+ * Objects/lnotab_notes.txt for the description of the line number table. */
- if (d_bytecode > 255) {
- int j, nbytes, ncodes = d_bytecode / 255;
- nbytes = a->a_lnotab_off + 2 * ncodes;
- len = PyBytes_GET_SIZE(a->a_lnotab);
- if (nbytes >= len) {
- if ((len <= INT_MAX / 2) && (len * 2 < nbytes))
- len = nbytes;
- else if (len <= INT_MAX / 2)
- len *= 2;
- else {
- PyErr_NoMemory();
+static int
+assemble_line_range(struct assembler *a)
+{
+ int ldelta, bdelta;
+ bdelta = (a->a_offset - a->a_lineno_start) * 2;
+ if (bdelta == 0) {
+ return 1;
+ }
+ if (a->a_lineno < 0) {
+ ldelta = -128;
+ }
+ else {
+ ldelta = a->a_lineno - a->a_prevlineno;
+ a->a_prevlineno = a->a_lineno;
+ while (ldelta > 127) {
+ if (!assemble_emit_linetable_pair(a, 0, 127)) {
return 0;
}
- if (_PyBytes_Resize(&a->a_lnotab, len) < 0)
- return 0;
- }
- lnotab = (unsigned char *)
- PyBytes_AS_STRING(a->a_lnotab) + a->a_lnotab_off;
- for (j = 0; j < ncodes; j++) {
- *lnotab++ = 255;
- *lnotab++ = 0;
- }
- d_bytecode -= ncodes * 255;
- a->a_lnotab_off += ncodes * 2;
- }
- assert(0 <= d_bytecode && d_bytecode <= 255);
-
- if (d_lineno < -128 || 127 < d_lineno) {
- int j, nbytes, ncodes, k;
- if (d_lineno < 0) {
- k = -128;
- /* use division on positive numbers */
- ncodes = (-d_lineno) / 128;
+ ldelta -= 127;
}
- else {
- k = 127;
- ncodes = d_lineno / 127;
- }
- d_lineno -= ncodes * k;
- assert(ncodes >= 1);
- nbytes = a->a_lnotab_off + 2 * ncodes;
- len = PyBytes_GET_SIZE(a->a_lnotab);
- if (nbytes >= len) {
- if ((len <= INT_MAX / 2) && len * 2 < nbytes)
- len = nbytes;
- else if (len <= INT_MAX / 2)
- len *= 2;
- else {
- PyErr_NoMemory();
+ while (ldelta < -127) {
+ if (!assemble_emit_linetable_pair(a, 0, -127)) {
return 0;
}
- if (_PyBytes_Resize(&a->a_lnotab, len) < 0)
- return 0;
+ ldelta += 127;
}
- lnotab = (unsigned char *)
- PyBytes_AS_STRING(a->a_lnotab) + a->a_lnotab_off;
- *lnotab++ = d_bytecode;
- *lnotab++ = k;
- d_bytecode = 0;
- for (j = 1; j < ncodes; j++) {
- *lnotab++ = 0;
- *lnotab++ = k;
- }
- a->a_lnotab_off += ncodes * 2;
}
- assert(-128 <= d_lineno && d_lineno <= 127);
-
- len = PyBytes_GET_SIZE(a->a_lnotab);
- if (a->a_lnotab_off + 2 >= len) {
- if (_PyBytes_Resize(&a->a_lnotab, len * 2) < 0)
+ assert(-128 <= ldelta && ldelta < 128);
+ while (bdelta > 254) {
+ if (!assemble_emit_linetable_pair(a, 254, ldelta)) {
return 0;
+ }
+ ldelta = a->a_lineno < 0 ? -128 : 0;
+ bdelta -= 254;
}
- lnotab = (unsigned char *)
- PyBytes_AS_STRING(a->a_lnotab) + a->a_lnotab_off;
+ if (!assemble_emit_linetable_pair(a, bdelta, ldelta)) {
+ return 0;
+ }
+ a->a_lineno_start = a->a_offset;
+ return 1;
+}
- a->a_lnotab_off += 2;
- if (d_bytecode) {
- *lnotab++ = d_bytecode;
- *lnotab++ = d_lineno;
+static int
+assemble_lnotab(struct assembler *a, struct instr *i)
+{
+ if (i->i_lineno == a->a_lineno) {
+ return 1;
}
- else { /* First line of a block; def stmt, etc. */
- *lnotab++ = 0;
- *lnotab++ = d_lineno;
+ if (!assemble_line_range(a)) {
+ return 0;
}
a->a_lineno = i->i_lineno;
- a->a_lineno_off = a->a_offset;
return 1;
}
+
/* assemble_emit()
Extend the bytecode with a new instruction.
Update lnotab if necessary.
@@ -5720,8 +5654,7 @@ assemble_jump_offsets(struct assembler *a, struct compiler *c)
Replace block pointer with position in bytecode. */
do {
totsize = 0;
- for (i = 0; i < a->a_nblocks; i++) {
- b = a->a_reverse_postorder[i];
+ for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
bsize = blocksize(b);
b->b_offset = totsize;
totsize += bsize;
@@ -5981,15 +5914,22 @@ dump_basicblock(const basicblock *b)
}
#endif
+
+static int
+normalize_basic_block(basicblock *bb);
+
static int
optimize_cfg(struct assembler *a, PyObject *consts);
+static int
+ensure_exits_have_lineno(struct compiler *c);
+
static PyCodeObject *
assemble(struct compiler *c, int addNone)
{
basicblock *b, *entryblock;
struct assembler a;
- int i, j, nblocks;
+ int j, nblocks;
PyCodeObject *co = NULL;
PyObject *consts = NULL;
@@ -5998,12 +5938,22 @@ assemble(struct compiler *c, int addNone)
block ends with a jump or return b_next shouldn't set.
*/
if (!c->u->u_curblock->b_return) {
- NEXT_BLOCK(c);
+ c->u->u_lineno = -1;
if (addNone)
ADDOP_LOAD_CONST(c, Py_None);
ADDOP(c, RETURN_VALUE);
}
+ for (basicblock *b = c->u->u_blocks; b != NULL; b = b->b_list) {
+ if (normalize_basic_block(b)) {
+ goto error;
+ }
+ }
+
+ if (ensure_exits_have_lineno(c)) {
+ goto error;
+ }
+
nblocks = 0;
entryblock = NULL;
for (b = c->u->u_blocks; b != NULL; b = b->b_list) {
@@ -6015,12 +5965,14 @@ assemble(struct compiler *c, int addNone)
if (!c->u->u_firstlineno) {
if (entryblock && entryblock->b_instr && entryblock->b_instr->i_lineno)
c->u->u_firstlineno = entryblock->b_instr->i_lineno;
- else
+ else
c->u->u_firstlineno = 1;
}
+
if (!assemble_init(&a, nblocks, c->u->u_firstlineno))
goto error;
- dfs(c, entryblock, &a, nblocks);
+ a.a_entry = entryblock;
+ a.a_nblocks = nblocks;
consts = consts_dict_keys_inorder(c->u->u_consts);
if (consts == NULL) {
@@ -6033,13 +5985,19 @@ assemble(struct compiler *c, int addNone)
/* Can't modify the bytecode after computing jump offsets. */
assemble_jump_offsets(&a, c);
- /* Emit code in reverse postorder from dfs. */
- for (i = 0; i < a.a_nblocks; i++) {
- b = a.a_reverse_postorder[i];
+ /* Emit code. */
+ for(b = entryblock; b != NULL; b = b->b_next) {
for (j = 0; j < b->b_iused; j++)
if (!assemble_emit(&a, &b->b_instr[j]))
goto error;
}
+ if (!assemble_line_range(&a)) {
+ return 0;
+ }
+ /* Emit sentinel at end of line number table */
+ if (!assemble_emit_linetable_pair(&a, 255, -128)) {
+ goto error;
+ }
if (_PyBytes_Resize(&a.a_lnotab, a.a_lnotab_off) < 0)
goto error;
@@ -6114,6 +6072,29 @@ fold_tuple_on_constants(struct instr *inst,
}
+static int
+eliminate_jump_to_jump(basicblock *bb, int opcode) {
+ assert (bb->b_iused > 0);
+ struct instr *inst = &bb->b_instr[bb->b_iused-1];
+ assert (is_jump(inst));
+ assert (inst->i_target->b_iused > 0);
+ struct instr *target = &inst->i_target->b_instr[0];
+ if (inst->i_target == target->i_target) {
+ /* Nothing to do */
+ return 0;
+ }
+ int lineno = target->i_lineno;
+ if (add_jump_to_block(bb, opcode, lineno, target->i_target) == 0) {
+ return -1;
+ }
+ assert (bb->b_iused >= 2);
+ bb->b_instr[bb->b_iused-2].i_opcode = NOP;
+ return 0;
+}
+
+/* Maximum size of basic block that should be copied in optimizer */
+#define MAX_COPY_SIZE 4
+
/* Optimization */
static int
optimize_basic_block(basicblock *bb, PyObject *consts)
@@ -6122,7 +6103,6 @@ optimize_basic_block(basicblock *bb, PyObject *consts)
struct instr nop;
nop.i_opcode = NOP;
struct instr *target;
- int lineno;
for (int i = 0; i < bb->b_iused; i++) {
struct instr *inst = &bb->b_instr[i];
int oparg = inst->i_oparg;
@@ -6138,23 +6118,50 @@ optimize_basic_block(basicblock *bb, PyObject *consts)
target = &nop;
}
switch (inst->i_opcode) {
- /* Skip over LOAD_CONST trueconst
- POP_JUMP_IF_FALSE xx. This improves
- "while 1" performance. */
+ /* Remove LOAD_CONST const; conditional jump */
case LOAD_CONST:
- if (nextop != POP_JUMP_IF_FALSE) {
- break;
- }
- PyObject* cnt = PyList_GET_ITEM(consts, oparg);
- int is_true = PyObject_IsTrue(cnt);
- if (is_true == -1) {
- goto error;
- }
- if (is_true == 1) {
- inst->i_opcode = NOP;
- bb->b_instr[i+1].i_opcode = NOP;
+ {
+ PyObject* cnt;
+ int is_true;
+ int jump_if_true;
+ switch(nextop) {
+ case POP_JUMP_IF_FALSE:
+ case POP_JUMP_IF_TRUE:
+ cnt = PyList_GET_ITEM(consts, oparg);
+ is_true = PyObject_IsTrue(cnt);
+ if (is_true == -1) {
+ goto error;
+ }
+ inst->i_opcode = NOP;
+ jump_if_true = nextop == POP_JUMP_IF_TRUE;
+ if (is_true == jump_if_true) {
+ bb->b_instr[i+1].i_opcode = JUMP_ABSOLUTE;
+ bb->b_nofallthrough = 1;
+ }
+ else {
+ bb->b_instr[i+1].i_opcode = NOP;
+ }
+ break;
+ case JUMP_IF_FALSE_OR_POP:
+ case JUMP_IF_TRUE_OR_POP:
+ cnt = PyList_GET_ITEM(consts, oparg);
+ is_true = PyObject_IsTrue(cnt);
+ if (is_true == -1) {
+ goto error;
+ }
+ jump_if_true = nextop == JUMP_IF_TRUE_OR_POP;
+ if (is_true == jump_if_true) {
+ bb->b_instr[i+1].i_opcode = JUMP_ABSOLUTE;
+ bb->b_nofallthrough = 1;
+ }
+ else {
+ inst->i_opcode = NOP;
+ bb->b_instr[i+1].i_opcode = NOP;
+ }
+ break;
}
break;
+ }
/* Try to fold tuples of constants.
Skip over BUILD_SEQN 1 UNPACK_SEQN 1.
@@ -6201,17 +6208,27 @@ optimize_basic_block(basicblock *bb, PyObject *consts)
case JUMP_IF_FALSE_OR_POP:
switch(target->i_opcode) {
case POP_JUMP_IF_FALSE:
- *inst = *target;
+ if (inst->i_lineno == target->i_lineno) {
+ *inst = *target;
+ i--;
+ }
break;
case JUMP_ABSOLUTE:
case JUMP_FORWARD:
case JUMP_IF_FALSE_OR_POP:
- inst->i_target = target->i_target;
+ if (inst->i_lineno == target->i_lineno &&
+ inst->i_target != target->i_target) {
+ inst->i_target = target->i_target;
+ i--;
+ }
break;
case JUMP_IF_TRUE_OR_POP:
assert (inst->i_target->b_iused == 1);
- inst->i_opcode = POP_JUMP_IF_FALSE;
- inst->i_target = inst->i_target->b_next;
+ if (inst->i_lineno == target->i_lineno) {
+ inst->i_opcode = POP_JUMP_IF_FALSE;
+ inst->i_target = inst->i_target->b_next;
+ --i;
+ }
break;
}
break;
@@ -6219,17 +6236,27 @@ optimize_basic_block(basicblock *bb, PyObject *consts)
case JUMP_IF_TRUE_OR_POP:
switch(target->i_opcode) {
case POP_JUMP_IF_TRUE:
- *inst = *target;
+ if (inst->i_lineno == target->i_lineno) {
+ *inst = *target;
+ i--;
+ }
break;
case JUMP_ABSOLUTE:
case JUMP_FORWARD:
case JUMP_IF_TRUE_OR_POP:
- inst->i_target = target->i_target;
+ if (inst->i_lineno == target->i_lineno &&
+ inst->i_target != target->i_target) {
+ inst->i_target = target->i_target;
+ i--;
+ }
break;
case JUMP_IF_FALSE_OR_POP:
assert (inst->i_target->b_iused == 1);
- inst->i_opcode = POP_JUMP_IF_TRUE;
- inst->i_target = inst->i_target->b_next;
+ if (inst->i_lineno == target->i_lineno) {
+ inst->i_opcode = POP_JUMP_IF_TRUE;
+ inst->i_target = inst->i_target->b_next;
+ --i;
+ }
break;
}
break;
@@ -6238,7 +6265,10 @@ optimize_basic_block(basicblock *bb, PyObject *consts)
switch(target->i_opcode) {
case JUMP_ABSOLUTE:
case JUMP_FORWARD:
- inst->i_target = target->i_target;
+ if (inst->i_lineno == target->i_lineno) {
+ inst->i_target = target->i_target;
+ i--;
+ }
break;
}
break;
@@ -6247,27 +6277,43 @@ optimize_basic_block(basicblock *bb, PyObject *consts)
switch(target->i_opcode) {
case JUMP_ABSOLUTE:
case JUMP_FORWARD:
- inst->i_target = target->i_target;
+ if (inst->i_lineno == target->i_lineno) {
+ inst->i_target = target->i_target;
+ i--;
+ }
break;
}
break;
case JUMP_ABSOLUTE:
case JUMP_FORWARD:
+ assert (i == bb->b_iused-1);
switch(target->i_opcode) {
case JUMP_FORWARD:
- inst->i_target = target->i_target;
+ if (eliminate_jump_to_jump(bb, inst->i_opcode)) {
+ goto error;
+ }
break;
+
case JUMP_ABSOLUTE:
- case RETURN_VALUE:
- case RERAISE:
- case RAISE_VARARGS:
- lineno = inst->i_lineno;
- *inst = *target;
- inst->i_lineno = lineno;
+ if (eliminate_jump_to_jump(bb, JUMP_ABSOLUTE)) {
+ goto error;
+ }
break;
+ default:
+ if (inst->i_target->b_exit && inst->i_target->b_iused <= MAX_COPY_SIZE) {
+ basicblock *to_copy = inst->i_target;
+ inst->i_opcode = NOP;
+ for (i = 0; i < to_copy->b_iused; i++) {
+ int index = compiler_next_instr(bb);
+ if (index < 0) {
+ return -1;
+ }
+ bb->b_instr[index] = to_copy->b_instr[i];
+ }
+ bb->b_exit = 1;
+ }
}
- break;
}
}
return 0;
@@ -6278,45 +6324,99 @@ error:
static void
clean_basic_block(basicblock *bb) {
- /* Remove NOPs and any code following a return or re-raise. */
+ /* Remove NOPs. */
int dest = 0;
+ int prev_lineno = -1;
for (int src = 0; src < bb->b_iused; src++) {
- switch(bb->b_instr[src].i_opcode) {
- case NOP:
- /* skip */
- break;
- case RETURN_VALUE:
- case RERAISE:
- bb->b_next = NULL;
- bb->b_instr[dest] = bb->b_instr[src];
- dest++;
- goto end;
- default:
- if (dest != src) {
- bb->b_instr[dest] = bb->b_instr[src];
+ int lineno = bb->b_instr[src].i_lineno;
+ if (bb->b_instr[src].i_opcode == NOP) {
+ /* Eliminate no-op if it doesn't have a line number */
+ if (lineno < 0) {
+ continue;
+ }
+ /* or, if the previous instruction had the same line number. */
+ if (prev_lineno == lineno) {
+ continue;
+ }
+ /* or, if the next instruction has same line number or no line number */
+ if (src < bb->b_iused - 1) {
+ int next_lineno = bb->b_instr[src+1].i_lineno;
+ if (next_lineno < 0 || next_lineno == lineno) {
+ bb->b_instr[src+1].i_lineno = lineno;
+ continue;
}
- dest++;
- break;
+ }
+ else {
+ basicblock* next = bb->b_next;
+ while (next && next->b_iused == 0) {
+ next = next->b_next;
+ }
+ /* or if last instruction in BB and next BB has same line number */
+ if (next) {
+ if (lineno == next->b_instr[0].i_lineno) {
+ continue;
+ }
+ }
+ }
+
+ }
+ if (dest != src) {
+ bb->b_instr[dest] = bb->b_instr[src];
}
+ dest++;
+ prev_lineno = lineno;
}
-end:
assert(dest <= bb->b_iused);
bb->b_iused = dest;
}
static int
+normalize_basic_block(basicblock *bb) {
+ /* Mark blocks as exit and/or nofallthrough.
+ Raise SystemError if CFG is malformed. */
+ for (int i = 0; i < bb->b_iused; i++) {
+ switch(bb->b_instr[i].i_opcode) {
+ case RETURN_VALUE:
+ case RAISE_VARARGS:
+ case RERAISE:
+ bb->b_exit = 1;
+ bb->b_nofallthrough = 1;
+ break;
+ case JUMP_ABSOLUTE:
+ case JUMP_FORWARD:
+ bb->b_nofallthrough = 1;
+ /* fall through */
+ case POP_JUMP_IF_FALSE:
+ case POP_JUMP_IF_TRUE:
+ case JUMP_IF_FALSE_OR_POP:
+ case JUMP_IF_TRUE_OR_POP:
+ case FOR_ITER:
+ if (i != bb->b_iused-1) {
+ PyErr_SetString(PyExc_SystemError, "malformed control flow graph.");
+ return -1;
+ }
+ /* Skip over empty basic blocks. */
+ while (bb->b_instr[i].i_target->b_iused == 0) {
+ bb->b_instr[i].i_target = bb->b_instr[i].i_target->b_next;
+ }
+
+ }
+ }
+ return 0;
+}
+
+static int
mark_reachable(struct assembler *a) {
basicblock **stack, **sp;
sp = stack = (basicblock **)PyObject_Malloc(sizeof(basicblock *) * a->a_nblocks);
if (stack == NULL) {
return -1;
}
- basicblock *entry = a->a_reverse_postorder[0];
- entry->b_reachable = 1;
- *sp++ = entry;
+ a->a_entry->b_reachable = 1;
+ *sp++ = a->a_entry;
while (sp > stack) {
basicblock *b = *(--sp);
- if (b->b_next && b->b_next->b_reachable == 0) {
+ if (b->b_next && !b->b_nofallthrough && b->b_next->b_reachable == 0) {
b->b_next->b_reachable = 1;
*sp++ = b->b_next;
}
@@ -6335,8 +6435,27 @@ mark_reachable(struct assembler *a) {
return 0;
}
+/* 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,
+ * but has no impact on the generated line number events.
+ */
+static void
+minimize_lineno_table(struct assembler *a) {
+ for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
+ int prev_lineno = -1;
+ for (int i = 0; i < b->b_iused; i++) {
+ if (b->b_instr[i].i_lineno < 0) {
+ b->b_instr[i].i_lineno = prev_lineno;
+ }
+ else {
+ prev_lineno = b->b_instr[i].i_lineno;
+ }
+ }
+
+ }
+}
-/* Perform basic peephole optimizations on a control flow graph.
+/* Perform optimizations on a control flow graph.
The consts object should still be in list form to allow new constants
to be appended.
@@ -6348,25 +6467,127 @@ mark_reachable(struct assembler *a) {
static int
optimize_cfg(struct assembler *a, PyObject *consts)
{
- for (int i = 0; i < a->a_nblocks; i++) {
- if (optimize_basic_block(a->a_reverse_postorder[i], consts)) {
+ for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
+ if (optimize_basic_block(b, consts)) {
return -1;
}
- clean_basic_block(a->a_reverse_postorder[i]);
- assert(a->a_reverse_postorder[i]->b_reachable == 0);
+ clean_basic_block(b);
+ assert(b->b_reachable == 0);
}
if (mark_reachable(a)) {
return -1;
}
/* Delete unreachable instructions */
- for (int i = 0; i < a->a_nblocks; i++) {
- if (a->a_reverse_postorder[i]->b_reachable == 0) {
- a->a_reverse_postorder[i]->b_iused = 0;
+ for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
+ if (b->b_reachable == 0) {
+ b->b_iused = 0;
+ b->b_nofallthrough = 0;
}
}
+ /* 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.
+ */
+ 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];
+ if (b_last_instr->i_opcode == POP_JUMP_IF_FALSE ||
+ 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) {
+ b->b_nofallthrough = 0;
+ switch(b_last_instr->i_opcode) {
+ case POP_JUMP_IF_FALSE:
+ case POP_JUMP_IF_TRUE:
+ b_last_instr->i_opcode = POP_TOP;
+ b_last_instr->i_target = NULL;
+ b_last_instr->i_oparg = 0;
+ break;
+ case JUMP_ABSOLUTE:
+ case JUMP_FORWARD:
+ b_last_instr->i_opcode = NOP;
+ clean_basic_block(b);
+ 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);
return 0;
}
+static inline int
+is_exit_without_lineno(basicblock *b) {
+ return b->b_exit && b->b_instr[0].i_lineno < 0;
+}
+
+/* PEP 626 mandates that the f_lineno of a frame is correct
+ * after a frame terminates. It would be prohibitively expensive
+ * to continuously update the f_lineno field at runtime,
+ * so we make sure that all exiting instruction (raises and returns)
+ * have a valid line number, allowing us to compute f_lineno lazily.
+ * We can do this by duplicating the exit blocks without line number
+ * so that none have more than one predecessor. We can then safely
+ * copy the line number from the sole predecessor block.
+ */
+static int
+ensure_exits_have_lineno(struct compiler *c)
+{
+ basicblock *entry = NULL;
+ /* Copy all exit blocks without line number that are targets of a jump.
+ */
+ for (basicblock *b = c->u->u_blocks; b != NULL; b = b->b_list) {
+ if (b->b_iused > 0 && 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 (is_exit_without_lineno(target)) {
+ basicblock *new_target = compiler_copy_block(c, target);
+ if (new_target == NULL) {
+ return -1;
+ }
+ new_target->b_instr[0].i_lineno = b->b_instr[b->b_iused-1].i_lineno;
+ b->b_instr[b->b_iused-1].i_target = new_target;
+ }
+ }
+ entry = b;
+ }
+ assert(entry != NULL);
+ if (is_exit_without_lineno(entry)) {
+ entry->b_instr[0].i_lineno = c->u->u_firstlineno;
+ }
+ /* Any remaining reachable exit blocks without line number can only be reached by
+ * fall through, and thus can only have a single predecessor */
+ for (basicblock *b = c->u->u_blocks; b != NULL; b = b->b_list) {
+ if (!b->b_nofallthrough && b->b_next && b->b_iused > 0) {
+ if (is_exit_without_lineno(b->b_next)) {
+ assert(b->b_next->b_iused > 0);
+ b->b_next->b_instr[0].i_lineno = b->b_instr[b->b_iused-1].i_lineno;
+ }
+ }
+ }
+ return 0;
+}
+
+
/* Retained for API compatibility.
* Optimization is now done in optimize_cfg */