diff options
Diffstat (limited to 'Python/compile.c')
-rw-r--r-- | Python/compile.c | 482 |
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)) { |