aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Python/compile.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/compile.c')
-rw-r--r--Python/compile.c482
1 files changed, 410 insertions, 72 deletions
diff --git a/Python/compile.c b/Python/compile.c
index 4fc8b38d978..b174ef614c8 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -67,6 +67,14 @@
*/
#define MAX_ALLOWED_STACK_USE (STACK_USE_GUIDELINE * 100)
+
+/* Pseudo-instructions used in the compiler,
+ * but turned into NOPs by the assembler. */
+#define SETUP_FINALLY 255
+#define SETUP_CLEANUP 254
+#define SETUP_WITH 253
+#define POP_BLOCK 252
+
#define IS_TOP_LEVEL_AWAIT(c) ( \
(c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT) \
&& (c->u->u_ste->ste_type == ModuleBlock))
@@ -74,10 +82,23 @@
struct instr {
unsigned char i_opcode;
int i_oparg;
- struct basicblock_ *i_target; /* target block (if jump instruction) */
+ /* target block (if jump instruction) */
+ struct basicblock_ *i_target;
+ /* target block when exception is raised, should not be set by front-end. */
+ struct basicblock_ *i_except;
int i_lineno;
};
+typedef struct excepthandler {
+ struct instr *setup;
+ int offset;
+} ExceptHandler;
+
+typedef struct exceptstack {
+ struct basicblock_ *handlers[CO_MAXBLOCKS+1];
+ int depth;
+} ExceptStack;
+
#define LOG_BITS_PER_INT 5
#define MASK_LOW_LOG_BITS 31
@@ -101,7 +122,7 @@ is_relative_jump(struct instr *i)
static inline int
is_jump(struct instr *i)
{
- return is_bit_set_in_table(_PyOpcode_Jump, i->i_opcode);
+ return i->i_opcode >= SETUP_WITH || is_bit_set_in_table(_PyOpcode_Jump, i->i_opcode);
}
typedef struct basicblock_ {
@@ -124,12 +145,18 @@ typedef struct basicblock_ {
int b_predecessors;
/* Basic block has no fall through (it ends with a return, raise or jump) */
unsigned b_nofallthrough : 1;
+ /* Basic block is an exception handler that preserves lasti */
+ unsigned b_preserve_lasti : 1;
+ /* Used by compiler passes to mark whether they have visited a basic block. */
+ unsigned b_visited : 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() */
int b_offset;
+ /* Exception stack at start of block, used by assembler to create the exception handling table */
+ ExceptStack *b_exceptstack;
} basicblock;
/* fblockinfo tracks the current frame block.
@@ -305,6 +332,8 @@ static int compiler_match(struct compiler *, stmt_ty);
static int compiler_pattern_subpattern(struct compiler *, pattern_ty,
pattern_context *);
+static void clean_basic_block(basicblock *bb);
+
static PyCodeObject *assemble(struct compiler *, int addNone);
static PyObject *__doc__, *__annotations__;
@@ -1029,11 +1058,6 @@ stack_effect(int opcode, int oparg, int jump)
case INPLACE_OR:
return -1;
- case SETUP_WITH:
- /* 1 in the normal flow.
- * Restore the stack position and push 6 values before jumping to
- * the handler if an exception be raised. */
- return jump ? 6 : 1;
case RETURN_VALUE:
return -1;
case IMPORT_STAR:
@@ -1048,6 +1072,8 @@ stack_effect(int opcode, int oparg, int jump)
return 0;
case POP_EXCEPT:
return -3;
+ case POP_EXCEPT_AND_RERAISE:
+ return -7;
case STORE_NAME:
return -1;
@@ -1111,14 +1137,26 @@ stack_effect(int opcode, int oparg, int jump)
case LOAD_GLOBAL:
return 1;
- /* Exception handling */
+ /* Exception handling pseudo-instructions */
case SETUP_FINALLY:
/* 0 in the normal flow.
- * Restore the stack position and push 6 values before jumping to
+ * Restore the stack position and push 3 values before jumping to
* the handler if an exception be raised. */
- return jump ? 6 : 0;
+ return jump ? 3 : 0;
+ case SETUP_CLEANUP:
+ /* As SETUP_FINALLY, but pushes lasti as well */
+ return jump ? 4 : 0;
+ case SETUP_WITH:
+ /* 0 in the normal flow.
+ * Restore the stack position to the position before the result
+ * of __(a)enter__ and push 4 values before jumping to the handler
+ * if an exception be raised. */
+ return jump ? -1 + 4 : 0;
+
case RERAISE:
return -3;
+ case PUSH_EXC_INFO:
+ return 3;
case WITH_EXCEPT_START:
return 1;
@@ -1165,13 +1203,9 @@ stack_effect(int opcode, int oparg, int jump)
/* Iterators and generators */
case GET_AWAITABLE:
return 0;
- case SETUP_ASYNC_WITH:
- /* 0 in the normal flow.
- * Restore the stack position to the position before the result
- * of __aenter__ and push 6 values before jumping to the handler
- * if an exception be raised. */
- return jump ? -1 + 6 : 0;
+
case BEFORE_ASYNC_WITH:
+ case BEFORE_WITH:
return 1;
case GET_AITER:
return 0;
@@ -1180,7 +1214,7 @@ stack_effect(int opcode, int oparg, int jump)
case GET_YIELD_FROM_ITER:
return 0;
case END_ASYNC_FOR:
- return -7;
+ return -4;
case FORMAT_VALUE:
/* If there's a fmt_spec on the stack, we go from 2->1,
else 1->1. */
@@ -1238,7 +1272,7 @@ compiler_addop_line(struct compiler *c, int opcode, int line)
basicblock *b;
struct instr *i;
int off;
- assert(!HAS_ARG(opcode));
+ assert(!HAS_ARG(opcode) || IS_ARTIFICIAL(opcode));
off = compiler_next_instr(c->u->u_curblock);
if (off < 0)
return 0;
@@ -1815,6 +1849,7 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
if (preserve_tos) {
ADDOP(c, ROT_FOUR);
}
+ ADDOP(c, POP_BLOCK);
ADDOP(c, POP_EXCEPT);
return 1;
@@ -1845,6 +1880,7 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
if (preserve_tos) {
ADDOP(c, ROT_FOUR);
}
+ ADDOP(c, POP_BLOCK);
ADDOP(c, POP_EXCEPT);
if (info->fb_datum) {
ADDOP_LOAD_CONST(c, Py_None);
@@ -3072,14 +3108,15 @@ compiler_continue(struct compiler *c)
static int
compiler_try_finally(struct compiler *c, stmt_ty s)
{
- basicblock *body, *end, *exit;
+ basicblock *body, *end, *exit, *cleanup;
body = compiler_new_block(c);
end = compiler_new_block(c);
exit = compiler_new_block(c);
- if (body == NULL || end == NULL || exit == NULL)
+ cleanup = compiler_new_block(c);
+ if (body == NULL || end == NULL || exit == NULL || cleanup == NULL) {
return 0;
-
+ }
/* `try` block */
ADDOP_JUMP(c, SETUP_FINALLY, end);
compiler_use_next_block(c, body);
@@ -3098,11 +3135,17 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
ADDOP_JUMP_NOLINE(c, JUMP_FORWARD, exit);
/* `finally` block */
compiler_use_next_block(c, end);
+
+ c->u->u_lineno = -1;
+ ADDOP_JUMP(c, SETUP_CLEANUP, cleanup);
+ ADDOP(c, PUSH_EXC_INFO);
if (!compiler_push_fblock(c, FINALLY_END, end, NULL, NULL))
return 0;
VISIT_SEQ(c, stmt, s->v.Try.finalbody);
compiler_pop_fblock(c, FINALLY_END, end);
ADDOP_I(c, RERAISE, 0);
+ compiler_use_next_block(c, cleanup);
+ ADDOP(c, POP_EXCEPT_AND_RERAISE);
compiler_use_next_block(c, exit);
return 1;
}
@@ -3140,14 +3183,15 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
static int
compiler_try_except(struct compiler *c, stmt_ty s)
{
- basicblock *body, *orelse, *except, *end;
+ basicblock *body, *orelse, *except, *end, *cleanup;
Py_ssize_t i, n;
body = compiler_new_block(c);
except = compiler_new_block(c);
orelse = compiler_new_block(c);
end = compiler_new_block(c);
- if (body == NULL || except == NULL || orelse == NULL || end == NULL)
+ cleanup = compiler_new_block(c);
+ if (body == NULL || except == NULL || orelse == NULL || end == NULL || cleanup == NULL)
return 0;
ADDOP_JUMP(c, SETUP_FINALLY, except);
compiler_use_next_block(c, body);
@@ -3159,15 +3203,20 @@ compiler_try_except(struct compiler *c, stmt_ty s)
ADDOP_JUMP_NOLINE(c, JUMP_FORWARD, orelse);
n = asdl_seq_LEN(s->v.Try.handlers);
compiler_use_next_block(c, except);
+
+ c->u->u_lineno = -1;
+ ADDOP_JUMP(c, SETUP_CLEANUP, cleanup);
+ ADDOP(c, PUSH_EXC_INFO);
/* Runtime will push a block here, so we need to account for that */
if (!compiler_push_fblock(c, EXCEPTION_HANDLER, NULL, NULL, NULL))
return 0;
for (i = 0; i < n; i++) {
excepthandler_ty handler = (excepthandler_ty)asdl_seq_GET(
s->v.Try.handlers, i);
- if (!handler->v.ExceptHandler.type && i < n-1)
- return compiler_error(c, "default 'except:' must be last");
SET_LOC(c, handler);
+ if (!handler->v.ExceptHandler.type && i < n-1) {
+ return compiler_error(c, "default 'except:' must be last");
+ }
except = compiler_new_block(c);
if (except == NULL)
return 0;
@@ -3202,7 +3251,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
*/
/* second try: */
- ADDOP_JUMP(c, SETUP_FINALLY, cleanup_end);
+ ADDOP_JUMP(c, SETUP_CLEANUP, cleanup_end);
compiler_use_next_block(c, cleanup_body);
if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, NULL, handler->v.ExceptHandler.name))
return 0;
@@ -3211,6 +3260,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
ADDOP(c, POP_BLOCK);
+ ADDOP(c, POP_BLOCK);
ADDOP(c, POP_EXCEPT);
/* name = None; del name; # Mark as artificial */
c->u->u_lineno = -1;
@@ -3224,6 +3274,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
/* 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);
@@ -3246,15 +3297,18 @@ compiler_try_except(struct compiler *c, stmt_ty s)
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
/* name = None; del name; # Mark as artificial */
c->u->u_lineno = -1;
+ ADDOP(c, POP_BLOCK);
ADDOP(c, POP_EXCEPT);
ADDOP_JUMP(c, JUMP_FORWARD, end);
}
compiler_use_next_block(c, except);
}
- compiler_pop_fblock(c, EXCEPTION_HANDLER, NULL);
/* Mark as artificial */
c->u->u_lineno = -1;
+ compiler_pop_fblock(c, EXCEPTION_HANDLER, NULL);
ADDOP_I(c, RERAISE, 0);
+ compiler_use_next_block(c, cleanup);
+ ADDOP(c, POP_EXCEPT_AND_RERAISE);
compiler_use_next_block(c, orelse);
VISIT_SEQ(c, stmt, s->v.Try.orelse);
compiler_use_next_block(c, end);
@@ -4764,6 +4818,8 @@ compiler_async_comprehension_generator(struct compiler *c,
compiler_pop_fblock(c, ASYNC_COMPREHENSION_GENERATOR, start);
compiler_use_next_block(c, except);
+ //c->u->u_lineno = -1;
+
ADDOP(c, END_ASYNC_FOR);
return 1;
@@ -4944,20 +5000,24 @@ compiler_visit_keyword(struct compiler *c, keyword_ty k)
*/
static int
-compiler_with_except_finish(struct compiler *c) {
+compiler_with_except_finish(struct compiler *c, basicblock * cleanup) {
basicblock *exit;
exit = compiler_new_block(c);
if (exit == NULL)
return 0;
ADDOP_JUMP(c, POP_JUMP_IF_TRUE, exit);
NEXT_BLOCK(c);
- ADDOP_I(c, RERAISE, 1);
+ ADDOP_I(c, RERAISE, 4);
+ compiler_use_next_block(c, cleanup);
+ ADDOP(c, POP_EXCEPT_AND_RERAISE);
compiler_use_next_block(c, exit);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
+ ADDOP(c, POP_BLOCK);
ADDOP(c, POP_EXCEPT);
ADDOP(c, POP_TOP);
+ ADDOP(c, POP_TOP);
return 1;
}
@@ -4988,7 +5048,7 @@ compiler_with_except_finish(struct compiler *c) {
static int
compiler_async_with(struct compiler *c, stmt_ty s, int pos)
{
- basicblock *block, *final, *exit;
+ basicblock *block, *final, *exit, *cleanup;
withitem_ty item = asdl_seq_GET(s->v.AsyncWith.items, pos);
assert(s->kind == AsyncWith_kind);
@@ -5001,7 +5061,8 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
block = compiler_new_block(c);
final = compiler_new_block(c);
exit = compiler_new_block(c);
- if (!block || !final || !exit)
+ cleanup = compiler_new_block(c);
+ if (!block || !final || !exit || !cleanup)
return 0;
/* Evaluate EXPR */
@@ -5012,9 +5073,9 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
ADDOP_LOAD_CONST(c, Py_None);
ADDOP(c, YIELD_FROM);
- ADDOP_JUMP(c, SETUP_ASYNC_WITH, final);
+ ADDOP_JUMP(c, SETUP_WITH, final);
- /* SETUP_ASYNC_WITH pushes a finally block. */
+ /* SETUP_WITH pushes a finally block. */
compiler_use_next_block(c, block);
if (!compiler_push_fblock(c, ASYNC_WITH, block, final, s)) {
return 0;
@@ -5055,13 +5116,17 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
/* For exceptional outcome: */
compiler_use_next_block(c, final);
+ c->u->u_lineno = -1;
+
+ ADDOP_JUMP(c, SETUP_CLEANUP, cleanup);
+ ADDOP(c, PUSH_EXC_INFO);
ADDOP(c, WITH_EXCEPT_START);
ADDOP(c, GET_AWAITABLE);
ADDOP_LOAD_CONST(c, Py_None);
ADDOP(c, YIELD_FROM);
- compiler_with_except_finish(c);
+ compiler_with_except_finish(c, cleanup);
-compiler_use_next_block(c, exit);
+ compiler_use_next_block(c, exit);
return 1;
}
@@ -5090,7 +5155,7 @@ compiler_use_next_block(c, exit);
static int
compiler_with(struct compiler *c, stmt_ty s, int pos)
{
- basicblock *block, *final, *exit;
+ basicblock *block, *final, *exit, *cleanup;
withitem_ty item = asdl_seq_GET(s->v.With.items, pos);
assert(s->kind == With_kind);
@@ -5098,12 +5163,14 @@ compiler_with(struct compiler *c, stmt_ty s, int pos)
block = compiler_new_block(c);
final = compiler_new_block(c);
exit = compiler_new_block(c);
- if (!block || !final || !exit)
+ cleanup = compiler_new_block(c);
+ if (!block || !final || !exit || !cleanup)
return 0;
/* Evaluate EXPR */
VISIT(c, expr, item->context_expr);
/* Will push bound __exit__ */
+ ADDOP(c, BEFORE_WITH);
ADDOP_JUMP(c, SETUP_WITH, final);
/* SETUP_WITH pushes a finally block. */
@@ -5146,8 +5213,12 @@ compiler_with(struct compiler *c, stmt_ty s, int pos)
/* For exceptional outcome: */
compiler_use_next_block(c, final);
+ c->u->u_lineno = -1;
+
+ ADDOP_JUMP(c, SETUP_CLEANUP, cleanup);
+ ADDOP(c, PUSH_EXC_INFO);
ADDOP(c, WITH_EXCEPT_START);
- compiler_with_except_finish(c);
+ compiler_with_except_finish(c, cleanup);
compiler_use_next_block(c, exit);
return 1;
@@ -6383,11 +6454,13 @@ compiler_match(struct compiler *c, stmt_ty s)
*/
struct assembler {
- PyObject *a_bytecode; /* string containing bytecode */
+ PyObject *a_bytecode; /* bytes containing bytecode */
int a_offset; /* offset into bytecode */
int a_nblocks; /* number of reachable blocks */
- PyObject *a_lnotab; /* string containing lnotab */
+ PyObject *a_lnotab; /* bytes containing lnotab */
int a_lnotab_off; /* offset into lnotab */
+ PyObject *a_except_table; /* bytes containing exception table */
+ int a_except_table_off; /* offset into exception table */
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 */
@@ -6466,7 +6539,8 @@ stackdepth(struct compiler *c)
instr->i_opcode == JUMP_FORWARD ||
instr->i_opcode == RETURN_VALUE ||
instr->i_opcode == RAISE_VARARGS ||
- instr->i_opcode == RERAISE)
+ instr->i_opcode == RERAISE ||
+ instr->i_opcode == POP_EXCEPT_AND_RERAISE)
{
/* remaining code is dead */
next = NULL;
@@ -6488,6 +6562,7 @@ assemble_init(struct assembler *a, int nblocks, int firstlineno)
memset(a, 0, sizeof(struct assembler));
a->a_prevlineno = a->a_lineno = firstlineno;
a->a_lnotab = NULL;
+ a->a_except_table = NULL;
a->a_bytecode = PyBytes_FromStringAndSize(NULL, DEFAULT_CODE_SIZE);
if (a->a_bytecode == NULL) {
goto error;
@@ -6496,6 +6571,10 @@ assemble_init(struct assembler *a, int nblocks, int firstlineno)
if (a->a_lnotab == NULL) {
goto error;
}
+ a->a_except_table = PyBytes_FromStringAndSize(NULL, DEFAULT_LNOTAB_SIZE);
+ if (a->a_except_table == NULL) {
+ goto error;
+ }
if ((size_t)nblocks > SIZE_MAX / sizeof(basicblock *)) {
PyErr_NoMemory();
goto error;
@@ -6504,6 +6583,7 @@ assemble_init(struct assembler *a, int nblocks, int firstlineno)
error:
Py_XDECREF(a->a_bytecode);
Py_XDECREF(a->a_lnotab);
+ Py_XDECREF(a->a_except_table);
return 0;
}
@@ -6512,6 +6592,7 @@ assemble_free(struct assembler *a)
{
Py_XDECREF(a->a_bytecode);
Py_XDECREF(a->a_lnotab);
+ Py_XDECREF(a->a_except_table);
}
static int
@@ -6541,6 +6622,253 @@ assemble_emit_linetable_pair(struct assembler *a, int bdelta, int ldelta)
return 1;
}
+static int
+is_block_push(struct instr *instr)
+{
+ int opcode = instr->i_opcode;
+ return opcode == SETUP_FINALLY || opcode == SETUP_WITH || opcode == SETUP_CLEANUP;
+}
+
+static basicblock *
+push_except_block(ExceptStack *stack, struct instr *setup) {
+ assert(is_block_push(setup));
+ int opcode = setup->i_opcode;
+ basicblock * target = setup->i_target;
+ if (opcode == SETUP_WITH || opcode == SETUP_CLEANUP) {
+ target->b_preserve_lasti = 1;
+ }
+ stack->handlers[++stack->depth] = target;
+ return target;
+}
+
+static basicblock *
+pop_except_block(ExceptStack *stack) {
+ assert(stack->depth > 0);
+ return stack->handlers[--stack->depth];
+}
+
+static basicblock *
+except_stack_top(ExceptStack *stack) {
+ return stack->handlers[stack->depth];
+}
+
+static ExceptStack *
+make_except_stack(void) {
+ ExceptStack *new = PyMem_Malloc(sizeof(ExceptStack));
+ if (new == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ new->depth = 0;
+ new->handlers[0] = NULL;
+ return new;
+}
+
+static ExceptStack *
+copy_except_stack(ExceptStack *stack) {
+ ExceptStack *copy = PyMem_Malloc(sizeof(ExceptStack));
+ if (copy == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ memcpy(copy, stack, sizeof(ExceptStack));
+ return copy;
+}
+
+static int
+label_exception_targets(basicblock *entry) {
+ int nblocks = 0;
+ for (basicblock *b = entry; b != NULL; b = b->b_next) {
+ b->b_visited = 0;
+ nblocks++;
+ }
+ basicblock **todo_stack = PyMem_Malloc(sizeof(basicblock *)*nblocks);
+ if (todo_stack == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ ExceptStack *except_stack = make_except_stack();
+ if (except_stack == NULL) {
+ PyMem_Free(todo_stack);
+ PyErr_NoMemory();
+ return -1;
+ }
+ except_stack->depth = 0;
+ todo_stack[0] = entry;
+ entry->b_visited = 1;
+ entry->b_exceptstack = except_stack;
+ basicblock **todo = &todo_stack[1];
+ basicblock *handler = NULL;
+ while (todo > todo_stack) {
+ todo--;
+ basicblock *b = todo[0];
+ assert(b->b_visited == 1);
+ except_stack = b->b_exceptstack;
+ assert(except_stack != NULL);
+ b->b_exceptstack = NULL;
+ handler = except_stack_top(except_stack);
+ for (int i = 0; i < b->b_iused; i++) {
+ struct instr *instr = &b->b_instr[i];
+ if (is_block_push(instr)) {
+ if (!instr->i_target->b_visited) {
+ ExceptStack *copy = copy_except_stack(except_stack);
+ if (copy == NULL) {
+ goto error;
+ }
+ instr->i_target->b_exceptstack = copy;
+ todo[0] = instr->i_target;
+ instr->i_target->b_visited = 1;
+ todo++;
+ }
+ handler = push_except_block(except_stack, instr);
+ }
+ else if (instr->i_opcode == POP_BLOCK) {
+ handler = pop_except_block(except_stack);
+ }
+ else if (is_jump(instr)) {
+ instr->i_except = handler;
+ assert(i == b->b_iused -1);
+ if (!instr->i_target->b_visited) {
+ if (b->b_nofallthrough == 0) {
+ ExceptStack *copy = copy_except_stack(except_stack);
+ if (copy == NULL) {
+ goto error;
+ }
+ instr->i_target->b_exceptstack = copy;
+ }
+ else {
+ instr->i_target->b_exceptstack = except_stack;
+ except_stack = NULL;
+ }
+ todo[0] = instr->i_target;
+ instr->i_target->b_visited = 1;
+ todo++;
+ }
+ }
+ else {
+ instr->i_except = handler;
+ }
+ }
+ if (b->b_nofallthrough == 0 && !b->b_next->b_visited) {
+ assert(except_stack != NULL);
+ b->b_next->b_exceptstack = except_stack;
+ todo[0] = b->b_next;
+ b->b_next->b_visited = 1;
+ todo++;
+ }
+ else if (except_stack != NULL) {
+ PyMem_Free(except_stack);
+ }
+ }
+#ifdef Py_DEBUG
+ for (basicblock *b = entry; b != NULL; b = b->b_next) {
+ assert(b->b_exceptstack == NULL);
+ }
+#endif
+ PyMem_Free(todo_stack);
+ return 0;
+error:
+ PyMem_Free(todo_stack);
+ PyMem_Free(except_stack);
+ return -1;
+}
+
+
+static void
+convert_exception_handlers_to_nops(basicblock *entry) {
+ for (basicblock *b = entry; b != NULL; b = b->b_next) {
+ for (int i = 0; i < b->b_iused; i++) {
+ struct instr *instr = &b->b_instr[i];
+ if (is_block_push(instr) || instr->i_opcode == POP_BLOCK) {
+ instr->i_opcode = NOP;
+ }
+ }
+ }
+}
+
+static inline void
+write_except_byte(struct assembler *a, int byte) {
+ unsigned char *p = (unsigned char *) PyBytes_AS_STRING(a->a_except_table);
+ p[a->a_except_table_off++] = byte;
+}
+
+#define CONTINUATION_BIT 64
+
+static void
+assemble_emit_exception_table_item(struct assembler *a, int value, int msb)
+{
+ assert ((msb | 128) == 128);
+ assert(value >= 0 && value < (1 << 30));
+ if (value >= 1 << 24) {
+ write_except_byte(a, (value >> 24) | CONTINUATION_BIT | msb);
+ msb = 0;
+ }
+ if (value >= 1 << 18) {
+ write_except_byte(a, ((value >> 18)&0x3f) | CONTINUATION_BIT | msb);
+ msb = 0;
+ }
+ if (value >= 1 << 12) {
+ write_except_byte(a, ((value >> 12)&0x3f) | CONTINUATION_BIT | msb);
+ msb = 0;
+ }
+ if (value >= 1 << 6) {
+ write_except_byte(a, ((value >> 6)&0x3f) | CONTINUATION_BIT | msb);
+ msb = 0;
+ }
+ write_except_byte(a, (value&0x3f) | msb);
+}
+
+/* See Objects/exception_table_notes.txt for details of layout */
+#define MAX_SIZE_OF_ENTRY 20
+
+static int
+assemble_emit_exception_table_entry(struct assembler *a, int start, int end, basicblock *handler)
+{
+ Py_ssize_t len = PyBytes_GET_SIZE(a->a_except_table);
+ if (a->a_except_table_off + MAX_SIZE_OF_ENTRY >= len) {
+ if (_PyBytes_Resize(&a->a_except_table, len * 2) < 0)
+ return 0;
+ }
+ int size = end-start;
+ assert(end > start);
+ int target = handler->b_offset;
+ int depth = handler->b_preserve_lasti ? handler->b_startdepth-4 : handler->b_startdepth-3;
+ assert(depth >= 0);
+ int depth_lasti = (depth<<1) | handler->b_preserve_lasti;
+ assemble_emit_exception_table_item(a, start, (1<<7));
+ assemble_emit_exception_table_item(a, size, 0);
+ assemble_emit_exception_table_item(a, target, 0);
+ assemble_emit_exception_table_item(a, depth_lasti, 0);
+ return 1;
+}
+
+static int
+assemble_exception_table(struct assembler *a)
+{
+ basicblock *b;
+ int ioffset = 0;
+ basicblock *handler = NULL;
+ int start = -1;
+ for (b = a->a_entry; b != NULL; b = b->b_next) {
+ ioffset = b->b_offset;
+ for (int i = 0; i < b->b_iused; i++) {
+ struct instr *instr = &b->b_instr[i];
+ if (instr->i_except != handler) {
+ if (handler != NULL) {
+ RETURN_IF_FALSE(assemble_emit_exception_table_entry(a, start, ioffset, handler));
+ }
+ start = ioffset;
+ handler = instr->i_except;
+ }
+ ioffset += instrsize(instr->i_oparg);
+ }
+ }
+ if (handler != NULL) {
+ RETURN_IF_FALSE(assemble_emit_exception_table_entry(a, start, ioffset, handler));
+ }
+ return 1;
+}
+
/* Appends a range to the end of the line number table. See
* Objects/lnotab_notes.txt for the description of the line number table. */
@@ -6793,7 +7121,7 @@ merge_const_one(struct compiler *c, PyObject **obj)
}
static PyCodeObject *
-makecode(struct compiler *c, struct assembler *a, PyObject *consts)
+makecode(struct compiler *c, struct assembler *a, PyObject *consts, int maxdepth)
{
PyCodeObject *co = NULL;
PyObject *names = NULL;
@@ -6804,7 +7132,7 @@ makecode(struct compiler *c, struct assembler *a, PyObject *consts)
Py_ssize_t nlocals;
int nlocals_int;
int flags;
- int posorkeywordargcount, posonlyargcount, kwonlyargcount, maxdepth;
+ int posorkeywordargcount, posonlyargcount, kwonlyargcount;
names = dict_keys_inorder(c->u->u_names, 0);
varnames = dict_keys_inorder(c->u->u_varnames, 0);
@@ -6846,23 +7174,11 @@ makecode(struct compiler *c, struct assembler *a, PyObject *consts)
posonlyargcount = Py_SAFE_DOWNCAST(c->u->u_posonlyargcount, Py_ssize_t, int);
posorkeywordargcount = Py_SAFE_DOWNCAST(c->u->u_argcount, Py_ssize_t, int);
kwonlyargcount = Py_SAFE_DOWNCAST(c->u->u_kwonlyargcount, Py_ssize_t, int);
- maxdepth = stackdepth(c);
- if (maxdepth < 0) {
- Py_DECREF(consts);
- goto error;
- }
- if (maxdepth > MAX_ALLOWED_STACK_USE) {
- PyErr_Format(PyExc_SystemError,
- "excessive stack use: stack is %d deep",
- maxdepth);
- Py_DECREF(consts);
- goto error;
- }
co = PyCode_NewWithPosOnlyArgs(posonlyargcount+posorkeywordargcount,
posonlyargcount, kwonlyargcount, nlocals_int,
maxdepth, flags, a->a_bytecode, consts, names,
varnames, freevars, cellvars, c->c_filename,
- c->u->u_name, c->u->u_firstlineno, a->a_lnotab);
+ c->u->u_name, c->u->u_firstlineno, a->a_lnotab, a->a_except_table);
Py_DECREF(consts);
error:
Py_XDECREF(names);
@@ -7015,6 +7331,25 @@ assemble(struct compiler *c, int addNone)
goto error;
}
+ int maxdepth = stackdepth(c);
+ if (maxdepth < 0) {
+ goto error;
+ }
+ if (maxdepth > MAX_ALLOWED_STACK_USE) {
+ PyErr_Format(PyExc_SystemError,
+ "excessive stack use: stack is %d deep",
+ maxdepth);
+ goto error;
+ }
+
+ if (label_exception_targets(entryblock)) {
+ goto error;
+ }
+ convert_exception_handlers_to_nops(entryblock);
+ for (basicblock *b = a.a_entry; b != NULL; b = b->b_next) {
+ clean_basic_block(b);
+ }
+
/* Can't modify the bytecode after computing jump offsets. */
assemble_jump_offsets(&a, c);
@@ -7024,6 +7359,10 @@ assemble(struct compiler *c, int addNone)
if (!assemble_emit(&a, &b->b_instr[j]))
goto error;
}
+
+ if (!assemble_exception_table(&a)) {
+ return 0;
+ }
if (!assemble_line_range(&a)) {
return 0;
}
@@ -7031,6 +7370,11 @@ assemble(struct compiler *c, int addNone)
if (_PyBytes_Resize(&a.a_lnotab, a.a_lnotab_off) < 0) {
goto error;
}
+
+ if (_PyBytes_Resize(&a.a_except_table, a.a_except_table_off) < 0) {
+ goto error;
+ }
+
if (!merge_const_one(c, &a.a_lnotab)) {
goto error;
}
@@ -7041,7 +7385,7 @@ assemble(struct compiler *c, int addNone)
goto error;
}
- co = makecode(c, &a, consts);
+ co = makecode(c, &a, consts, maxdepth);
error:
Py_XDECREF(consts);
assemble_free(&a);
@@ -7417,9 +7761,10 @@ error:
static void
-clean_basic_block(basicblock *bb, int prev_lineno) {
+clean_basic_block(basicblock *bb) {
/* Remove NOPs when legal to do so. */
int dest = 0;
+ int prev_lineno = -1;
for (int src = 0; src < bb->b_iused; src++) {
int lineno = bb->b_instr[src].i_lineno;
if (bb->b_instr[src].i_opcode == NOP) {
@@ -7451,7 +7796,6 @@ clean_basic_block(basicblock *bb, int prev_lineno) {
}
}
}
-
}
if (dest != src) {
bb->b_instr[dest] = bb->b_instr[src];
@@ -7472,6 +7816,7 @@ normalize_basic_block(basicblock *bb) {
case RETURN_VALUE:
case RAISE_VARARGS:
case RERAISE:
+ case POP_EXCEPT_AND_RERAISE:
bb->b_exit = 1;
bb->b_nofallthrough = 1;
break;
@@ -7588,9 +7933,9 @@ propogate_line_numbers(struct assembler *a) {
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:
+ case SETUP_CLEANUP:
continue;
}
basicblock *target = b->b_instr[b->b_iused-1].i_target;
@@ -7619,7 +7964,7 @@ optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts)
if (optimize_basic_block(c, b, consts)) {
return -1;
}
- clean_basic_block(b, -1);
+ clean_basic_block(b);
assert(b->b_predecessors == 0);
}
if (mark_reachable(a)) {
@@ -7632,16 +7977,10 @@ optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts)
b->b_nofallthrough = 0;
}
}
- basicblock *pred = NULL;
+ eliminate_empty_basic_blocks(a->a_entry);
for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
- int prev_lineno = -1;
- if (pred && pred->b_iused) {
- prev_lineno = pred->b_instr[pred->b_iused-1].i_lineno;
- }
- clean_basic_block(b, prev_lineno);
- pred = b->b_nofallthrough ? NULL : b;
+ clean_basic_block(b);
}
- 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
@@ -7657,7 +7996,6 @@ optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts)
assert(b->b_next->b_iused);
b->b_nofallthrough = 0;
b_last_instr->i_opcode = NOP;
- clean_basic_block(b, -1);
maybe_empty_blocks = 1;
}
}
@@ -7691,12 +8029,13 @@ ensure_exits_have_lineno(struct compiler *c)
/* 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) {
+ entry = b;
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:
+ case SETUP_CLEANUP:
continue;
}
basicblock *target = b->b_instr[b->b_iused-1].i_target;
@@ -7709,7 +8048,6 @@ ensure_exits_have_lineno(struct compiler *c)
b->b_instr[b->b_iused-1].i_target = new_target;
}
}
- entry = b;
}
assert(entry != NULL);
if (is_exit_without_lineno(entry)) {