bpo-17611. Move unwinding of stack for "pseudo exceptions" from interpreter to compiler. (GH-5006)

Co-authored-by: Mark Shannon <mark@hotpy.org>
Co-authored-by: Antoine Pitrou <antoine@python.org>
This commit is contained in:
Serhiy Storchaka 2018-02-22 23:33:30 +02:00 committed by GitHub
parent 4af8fd5614
commit 520b7ae27e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 4506 additions and 4392 deletions

View file

@ -500,17 +500,6 @@ _Py_CheckRecursiveCall(const char *where)
return 0;
}
/* Status code for main loop (reason for stack unwind) */
enum why_code {
WHY_NOT = 0x0001, /* No error */
WHY_EXCEPTION = 0x0002, /* Exception occurred */
WHY_RETURN = 0x0008, /* 'return' statement */
WHY_BREAK = 0x0010, /* 'break' statement */
WHY_CONTINUE = 0x0020, /* 'continue' statement */
WHY_YIELD = 0x0040, /* 'yield' operator */
WHY_SILENCED = 0x0080 /* Exception silenced by 'with' */
};
static int do_raise(PyObject *, PyObject *);
static int unpack_iterable(PyObject *, int, int, PyObject **);
@ -556,7 +545,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
const _Py_CODEUNIT *next_instr;
int opcode; /* Current opcode */
int oparg; /* Current opcode argument, if any */
enum why_code why; /* Reason for block stack unwind */
PyObject **fastlocals, **freevars;
PyObject *retval = NULL; /* Return value */
PyThreadState *tstate = PyThreadState_GET();
@ -914,8 +902,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
lltrace = _PyDict_GetItemId(f->f_globals, &PyId___ltrace__) != NULL;
#endif
why = WHY_NOT;
if (throwflag) /* support for generator.throw() */
goto error;
@ -926,6 +912,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
assert(!PyErr_Occurred());
#endif
main_loop:
for (;;) {
assert(stack_pointer >= f->f_valuestack); /* else underflow */
assert(STACK_LEVEL() <= co->co_stacksize); /* else overflow */
@ -1056,9 +1043,8 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
switch (opcode) {
/* BEWARE!
It is essential that any operation that fails sets either
x to NULL, err to nonzero, or why to anything but WHY_NOT,
and that no operation that succeeds does this! */
It is essential that any operation that fails must goto error
and that all operation that succeed call [FAST_]DISPATCH() ! */
TARGET(NOP)
FAST_DISPATCH();
@ -1115,6 +1101,18 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
FAST_DISPATCH();
}
TARGET(ROT_FOUR) {
PyObject *top = TOP();
PyObject *second = SECOND();
PyObject *third = THIRD();
PyObject *fourth = FOURTH();
SET_TOP(second);
SET_SECOND(third);
SET_THIRD(fourth);
SET_FOURTH(top);
FAST_DISPATCH();
}
TARGET(DUP_TOP) {
PyObject *top = TOP();
Py_INCREF(top);
@ -1618,8 +1616,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
/* fall through */
case 0:
if (do_raise(exc, cause)) {
why = WHY_EXCEPTION;
goto fast_block_end;
goto exception_unwind;
}
break;
default:
@ -1632,8 +1629,8 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
TARGET(RETURN_VALUE) {
retval = POP();
why = WHY_RETURN;
goto fast_block_end;
assert(f->f_iblock == 0);
goto return_or_yield;
}
TARGET(GET_AITER) {
@ -1794,11 +1791,10 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
}
/* receiver remains on stack, retval is value to be yielded */
f->f_stacktop = stack_pointer;
why = WHY_YIELD;
/* and repeat... */
assert(f->f_lasti >= (int)sizeof(_Py_CODEUNIT));
f->f_lasti -= sizeof(_Py_CODEUNIT);
goto fast_yield;
goto return_or_yield;
}
TARGET(YIELD_VALUE) {
@ -1815,67 +1811,137 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
}
f->f_stacktop = stack_pointer;
why = WHY_YIELD;
goto fast_yield;
goto return_or_yield;
}
TARGET(POP_EXCEPT) {
PyObject *type, *value, *traceback;
_PyErr_StackItem *exc_info;
PyTryBlock *b = PyFrame_BlockPop(f);
if (b->b_type != EXCEPT_HANDLER) {
PyErr_SetString(PyExc_SystemError,
"popped block is not an except handler");
goto error;
}
UNWIND_EXCEPT_HANDLER(b);
assert(STACK_LEVEL() >= (b)->b_level + 3 &&
STACK_LEVEL() <= (b)->b_level + 4);
exc_info = tstate->exc_info;
type = exc_info->exc_type;
value = exc_info->exc_value;
traceback = exc_info->exc_traceback;
exc_info->exc_type = POP();
exc_info->exc_value = POP();
exc_info->exc_traceback = POP();
Py_XDECREF(type);
Py_XDECREF(value);
Py_XDECREF(traceback);
DISPATCH();
}
PREDICTED(POP_BLOCK);
TARGET(POP_BLOCK) {
PyTryBlock *b = PyFrame_BlockPop(f);
UNWIND_BLOCK(b);
PyFrame_BlockPop(f);
DISPATCH();
}
TARGET(POP_FINALLY) {
/* If oparg is 0 at the top of the stack are 1 or 6 values:
Either:
- TOP = NULL or an integer
or:
- (TOP, SECOND, THIRD) = exc_info()
- (FOURTH, FITH, SIXTH) = previous exception for EXCEPT_HANDLER
If oparg is 1 the value for 'return' was additionally pushed
at the top of the stack.
*/
PyObject *res = NULL;
if (oparg) {
res = POP();
}
PyObject *exc = POP();
if (exc == NULL || PyLong_CheckExact(exc)) {
Py_XDECREF(exc);
}
else {
Py_DECREF(exc);
Py_DECREF(POP());
Py_DECREF(POP());
PyObject *type, *value, *traceback;
_PyErr_StackItem *exc_info;
PyTryBlock *b = PyFrame_BlockPop(f);
if (b->b_type != EXCEPT_HANDLER) {
PyErr_SetString(PyExc_SystemError,
"popped block is not an except handler");
Py_XDECREF(res);
goto error;
}
assert(STACK_LEVEL() == (b)->b_level + 3);
exc_info = tstate->exc_info;
type = exc_info->exc_type;
value = exc_info->exc_value;
traceback = exc_info->exc_traceback;
exc_info->exc_type = POP();
exc_info->exc_value = POP();
exc_info->exc_traceback = POP();
Py_XDECREF(type);
Py_XDECREF(value);
Py_XDECREF(traceback);
}
if (oparg) {
PUSH(res);
}
DISPATCH();
}
TARGET(CALL_FINALLY) {
PyObject *ret = PyLong_FromLong(INSTR_OFFSET());
if (ret == NULL) {
goto error;
}
PUSH(ret);
JUMPBY(oparg);
FAST_DISPATCH();
}
TARGET(BEGIN_FINALLY) {
/* Push NULL onto the stack for using it in END_FINALLY,
POP_FINALLY, WITH_CLEANUP_START and WITH_CLEANUP_FINISH.
*/
PUSH(NULL);
FAST_DISPATCH();
}
PREDICTED(END_FINALLY);
TARGET(END_FINALLY) {
PyObject *status = POP();
if (PyLong_Check(status)) {
why = (enum why_code) PyLong_AS_LONG(status);
assert(why != WHY_YIELD && why != WHY_EXCEPTION);
if (why == WHY_RETURN ||
why == WHY_CONTINUE)
retval = POP();
if (why == WHY_SILENCED) {
/* An exception was silenced by 'with', we must
manually unwind the EXCEPT_HANDLER block which was
created when the exception was caught, otherwise
the stack will be in an inconsistent state. */
PyTryBlock *b = PyFrame_BlockPop(f);
assert(b->b_type == EXCEPT_HANDLER);
UNWIND_EXCEPT_HANDLER(b);
why = WHY_NOT;
Py_DECREF(status);
DISPATCH();
/* At the top of the stack are 1 or 6 values:
Either:
- TOP = NULL or an integer
or:
- (TOP, SECOND, THIRD) = exc_info()
- (FOURTH, FITH, SIXTH) = previous exception for EXCEPT_HANDLER
*/
PyObject *exc = POP();
if (exc == NULL) {
FAST_DISPATCH();
}
else if (PyLong_CheckExact(exc)) {
int ret = _PyLong_AsInt(exc);
Py_DECREF(exc);
if (ret == -1 && PyErr_Occurred()) {
goto error;
}
Py_DECREF(status);
goto fast_block_end;
JUMPTO(ret);
FAST_DISPATCH();
}
else if (PyExceptionClass_Check(status)) {
PyObject *exc = POP();
else {
assert(PyExceptionClass_Check(exc));
PyObject *val = POP();
PyObject *tb = POP();
PyErr_Restore(status, exc, tb);
why = WHY_EXCEPTION;
goto fast_block_end;
PyErr_Restore(exc, val, tb);
goto exception_unwind;
}
else if (status != Py_None) {
PyErr_SetString(PyExc_SystemError,
"'finally' pops bad exception");
Py_DECREF(status);
goto error;
}
Py_DECREF(status);
DISPATCH();
}
TARGET(LOAD_BUILD_CLASS) {
@ -2815,28 +2881,13 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
DISPATCH();
}
TARGET(BREAK_LOOP) {
why = WHY_BREAK;
goto fast_block_end;
}
TARGET(CONTINUE_LOOP) {
retval = PyLong_FromLong(oparg);
if (retval == NULL)
goto error;
why = WHY_CONTINUE;
goto fast_block_end;
}
TARGET(SETUP_LOOP)
TARGET(SETUP_EXCEPT)
TARGET(SETUP_FINALLY) {
/* NOTE: If you add any new block-setup opcodes that
are not try/except/finally handlers, you may need
to update the PyGen_NeedsFinalizing() function.
*/
PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg,
PyFrame_BlockSetup(f, SETUP_FINALLY, INSTR_OFFSET() + oparg,
STACK_LEVEL());
DISPATCH();
}
@ -2904,60 +2955,40 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
}
TARGET(WITH_CLEANUP_START) {
/* At the top of the stack are 1-6 values indicating
/* At the top of the stack are 1 or 6 values indicating
how/why we entered the finally clause:
- TOP = None
- (TOP, SECOND) = (WHY_{RETURN,CONTINUE}), retval
- TOP = WHY_*; no retval below it
- TOP = NULL
- (TOP, SECOND, THIRD) = exc_info()
(FOURTH, FITH, SIXTH) = previous exception for EXCEPT_HANDLER
Below them is EXIT, the context.__exit__ bound method.
In the last case, we must call
EXIT(TOP, SECOND, THIRD)
otherwise we must call
Below them is EXIT, the context.__exit__ or context.__aexit__
bound method.
In the first case, we must call
EXIT(None, None, None)
otherwise we must call
EXIT(TOP, SECOND, THIRD)
In the first three cases, we remove EXIT from the
stack, leaving the rest in the same order. In the
fourth case, we shift the bottom 3 values of the
stack down, and replace the empty spot with NULL.
In the first case, we remove EXIT from the
stack, leaving TOP, and push TOP on the stack.
Otherwise we shift the bottom 3 values of the
stack down, replace the empty spot with NULL, and push
None on the stack.
In addition, if the stack represents an exception,
*and* the function call returns a 'true' value, we
push WHY_SILENCED onto the stack. END_FINALLY will
then not re-raise the exception. (But non-local
gotos should still be resumed.)
Finally we push the result of the call.
*/
PyObject* stack[3];
PyObject *stack[3];
PyObject *exit_func;
PyObject *exc, *val, *tb, *res;
val = tb = Py_None;
exc = TOP();
if (exc == Py_None) {
(void)POP();
if (exc == NULL) {
STACKADJ(-1);
exit_func = TOP();
SET_TOP(exc);
}
else if (PyLong_Check(exc)) {
STACKADJ(-1);
switch (PyLong_AsLong(exc)) {
case WHY_RETURN:
case WHY_CONTINUE:
/* Retval in TOP. */
exit_func = SECOND();
SET_SECOND(TOP());
SET_TOP(exc);
break;
default:
exit_func = TOP();
SET_TOP(exc);
break;
}
exc = Py_None;
}
else {
assert(PyExceptionClass_Check(exc));
PyObject *tp2, *exc2, *tb2;
PyTryBlock *block;
val = SECOND();
@ -2974,8 +3005,10 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
/* We just shifted the stack down, so we have
to tell the except handler block that the
values are lower than it expects. */
assert(f->f_iblock > 0);
block = &f->f_blockstack[f->f_iblock - 1];
assert(block->b_type == EXCEPT_HANDLER);
assert(block->b_level > 0);
block->b_level--;
}
@ -2996,6 +3029,12 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
PREDICTED(WITH_CLEANUP_FINISH);
TARGET(WITH_CLEANUP_FINISH) {
/* TOP = the result of calling the context.__exit__ bound method
SECOND = either None or exception type
If SECOND is None below is NULL or the return address,
otherwise below are 7 values representing an exception.
*/
PyObject *res = POP();
PyObject *exc = POP();
int err;
@ -3011,8 +3050,15 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
if (err < 0)
goto error;
else if (err > 0) {
/* There was an exception and a True return */
PUSH(PyLong_FromLong((long) WHY_SILENCED));
/* There was an exception and a True return.
* We must manually unwind the EXCEPT_HANDLER block
* which was created when the exception was caught,
* otherwise the stack will be in an inconsisten state.
*/
PyTryBlock *b = PyFrame_BlockPop(f);
assert(b->b_type == EXCEPT_HANDLER);
UNWIND_EXCEPT_HANDLER(b);
PUSH(NULL);
}
PREDICT(END_FINALLY);
DISPATCH();
@ -3322,10 +3368,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
Py_UNREACHABLE();
error:
assert(why == WHY_NOT);
why = WHY_EXCEPTION;
/* Double-check exception status. */
#ifdef NDEBUG
if (!PyErr_Occurred())
@ -3342,36 +3384,18 @@ error:
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj,
tstate, f);
fast_block_end:
assert(why != WHY_NOT);
/* Unwind stacks if a (pseudo) exception occurred */
while (why != WHY_NOT && f->f_iblock > 0) {
/* Peek at the current block. */
PyTryBlock *b = &f->f_blockstack[f->f_iblock - 1];
assert(why != WHY_YIELD);
if (b->b_type == SETUP_LOOP && why == WHY_CONTINUE) {
why = WHY_NOT;
JUMPTO(PyLong_AS_LONG(retval));
Py_DECREF(retval);
break;
}
/* Now we have to pop the block. */
f->f_iblock--;
exception_unwind:
/* Unwind stacks if an exception occurred */
while (f->f_iblock > 0) {
/* Pop the current block. */
PyTryBlock *b = &f->f_blockstack[--f->f_iblock];
if (b->b_type == EXCEPT_HANDLER) {
UNWIND_EXCEPT_HANDLER(b);
continue;
}
UNWIND_BLOCK(b);
if (b->b_type == SETUP_LOOP && why == WHY_BREAK) {
why = WHY_NOT;
JUMPTO(b->b_handler);
break;
}
if (why == WHY_EXCEPTION && (b->b_type == SETUP_EXCEPT
|| b->b_type == SETUP_FINALLY)) {
if (b->b_type == SETUP_FINALLY) {
PyObject *exc, *val, *tb;
int handler = b->b_handler;
_PyErr_StackItem *exc_info = tstate->exc_info;
@ -3408,70 +3432,37 @@ fast_block_end:
PUSH(tb);
PUSH(val);
PUSH(exc);
why = WHY_NOT;
JUMPTO(handler);
break;
}
if (b->b_type == SETUP_FINALLY) {
if (why & (WHY_RETURN | WHY_CONTINUE))
PUSH(retval);
PUSH(PyLong_FromLong((long)why));
why = WHY_NOT;
JUMPTO(b->b_handler);
break;
/* Resume normal execution */
goto main_loop;
}
} /* unwind stack */
/* End the loop if we still have an error (or return) */
if (why != WHY_NOT)
break;
assert(!PyErr_Occurred());
/* End the loop as we still have an error */
break;
} /* main loop */
assert(why != WHY_YIELD);
/* Pop remaining stack entries. */
while (!EMPTY()) {
PyObject *o = POP();
Py_XDECREF(o);
}
if (why != WHY_RETURN)
retval = NULL;
assert((retval != NULL) ^ (PyErr_Occurred() != NULL));
fast_yield:
assert(retval == NULL);
assert(PyErr_Occurred());
return_or_yield:
if (tstate->use_tracing) {
if (tstate->c_tracefunc) {
if (why == WHY_RETURN || why == WHY_YIELD) {
if (call_trace(tstate->c_tracefunc, tstate->c_traceobj,
tstate, f,
PyTrace_RETURN, retval)) {
Py_CLEAR(retval);
why = WHY_EXCEPTION;
}
}
else if (why == WHY_EXCEPTION) {
call_trace_protected(tstate->c_tracefunc, tstate->c_traceobj,
tstate, f,
PyTrace_RETURN, NULL);
if (call_trace_protected(tstate->c_tracefunc, tstate->c_traceobj,
tstate, f, PyTrace_RETURN, retval)) {
Py_CLEAR(retval);
}
}
if (tstate->c_profilefunc) {
if (why == WHY_EXCEPTION)
call_trace_protected(tstate->c_profilefunc,
tstate->c_profileobj,
tstate, f,
PyTrace_RETURN, NULL);
else if (call_trace(tstate->c_profilefunc, tstate->c_profileobj,
tstate, f,
PyTrace_RETURN, retval)) {
if (call_trace_protected(tstate->c_profilefunc, tstate->c_profileobj,
tstate, f, PyTrace_RETURN, retval)) {
Py_CLEAR(retval);
/* why = WHY_EXCEPTION; useless yet but cause compiler warnings */
}
}
}

View file

@ -81,11 +81,14 @@ It's called a frame block to distinguish it from a basic block in the
compiler IR.
*/
enum fblocktype { LOOP, EXCEPT, FINALLY_TRY, FINALLY_END };
enum fblocktype { WHILE_LOOP, FOR_LOOP, EXCEPT, FINALLY_TRY, FINALLY_END,
WITH, ASYNC_WITH, HANDLER_CLEANUP };
struct fblockinfo {
enum fblocktype fb_type;
basicblock *fb_block;
/* (optional) type-specific exit or cleanup block */
basicblock *fb_exit;
};
enum {
@ -183,13 +186,6 @@ static int compiler_annassign(struct compiler *, stmt_ty);
static int compiler_visit_slice(struct compiler *, slice_ty,
expr_context_ty);
static int compiler_push_fblock(struct compiler *, enum fblocktype,
basicblock *);
static void compiler_pop_fblock(struct compiler *, enum fblocktype,
basicblock *);
/* Returns true if there is a loop on the fblock stack. */
static int compiler_in_loop(struct compiler *);
static int inplace_binop(struct compiler *, operator_ty);
static int expr_constant(expr_ty);
@ -846,7 +842,7 @@ compiler_next_instr(struct compiler *c, basicblock *b)
- when entering a new scope
- on each statement
- on each expression that start a new line
- before the "except" clause
- before the "except" and "finally" clauses
- before the "for" and "while" expressions
*/
@ -881,6 +877,7 @@ stack_effect(int opcode, int oparg, int jump)
return -1;
case ROT_TWO:
case ROT_THREE:
case ROT_FOUR:
return 0;
case DUP_TOP:
return 1;
@ -947,8 +944,7 @@ stack_effect(int opcode, int oparg, int jump)
case INPLACE_XOR:
case INPLACE_OR:
return -1;
case BREAK_LOOP:
return 0;
case SETUP_WITH:
/* 1 in the normal flow.
* Restore the stack position and push 6 values before jumping to
@ -975,6 +971,7 @@ stack_effect(int opcode, int oparg, int jump)
case POP_EXCEPT:
return -3;
case END_FINALLY:
case POP_FINALLY:
/* Pop 6 values when an exception was raised. */
return -6;
@ -1043,16 +1040,20 @@ stack_effect(int opcode, int oparg, int jump)
case LOAD_GLOBAL:
return 1;
case CONTINUE_LOOP:
return 0;
case SETUP_LOOP:
return 0;
case SETUP_EXCEPT:
/* Exception handling */
case SETUP_FINALLY:
/* 0 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 : 0;
case BEGIN_FINALLY:
/* Actually pushes 1 value, but count 6 for balancing with
* END_FINALLY and POP_FINALLY.
* This is the main reason of using this opcode instead of
* "LOAD_CONST None". */
return 6;
case CALL_FINALLY:
return jump ? 1 : 0;
case LOAD_FAST:
return 1;
@ -1458,6 +1459,103 @@ find_ann(asdl_seq *stmts)
return res;
}
/*
* Frame block handling functions
*/
static int
compiler_push_fblock(struct compiler *c, enum fblocktype t, basicblock *b,
basicblock *exit)
{
struct fblockinfo *f;
if (c->u->u_nfblocks >= CO_MAXBLOCKS) {
PyErr_SetString(PyExc_SyntaxError,
"too many statically nested blocks");
return 0;
}
f = &c->u->u_fblock[c->u->u_nfblocks++];
f->fb_type = t;
f->fb_block = b;
f->fb_exit = exit;
return 1;
}
static void
compiler_pop_fblock(struct compiler *c, enum fblocktype t, basicblock *b)
{
struct compiler_unit *u = c->u;
assert(u->u_nfblocks > 0);
u->u_nfblocks--;
assert(u->u_fblock[u->u_nfblocks].fb_type == t);
assert(u->u_fblock[u->u_nfblocks].fb_block == b);
}
/* Unwind a frame block. If preserve_tos is true, the TOS before
* popping the blocks will be restored afterwards.
*/
static int
compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
int preserve_tos)
{
switch (info->fb_type) {
case WHILE_LOOP:
return 1;
case FINALLY_END:
ADDOP_I(c, POP_FINALLY, preserve_tos);
return 1;
case FOR_LOOP:
/* Pop the iterator */
if (preserve_tos) {
ADDOP(c, ROT_TWO);
}
ADDOP(c, POP_TOP);
return 1;
case EXCEPT:
ADDOP(c, POP_BLOCK);
return 1;
case FINALLY_TRY:
ADDOP(c, POP_BLOCK);
ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
return 1;
case WITH:
case ASYNC_WITH:
ADDOP(c, POP_BLOCK);
if (preserve_tos) {
ADDOP(c, ROT_TWO);
}
ADDOP(c, BEGIN_FINALLY);
ADDOP(c, WITH_CLEANUP_START);
if (info->fb_type == ASYNC_WITH) {
ADDOP(c, GET_AWAITABLE);
ADDOP_O(c, LOAD_CONST, Py_None, consts);
ADDOP(c, YIELD_FROM);
}
ADDOP(c, WITH_CLEANUP_FINISH);
ADDOP_I(c, POP_FINALLY, 0);
return 1;
case HANDLER_CLEANUP:
if (preserve_tos) {
ADDOP(c, ROT_FOUR);
}
if (info->fb_exit) {
ADDOP(c, POP_BLOCK);
ADDOP(c, POP_EXCEPT);
ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
}
else {
ADDOP(c, POP_EXCEPT);
}
return 1;
}
Py_UNREACHABLE();
}
/* Compile a sequence of statements, checking for a docstring
and for annotations. */
@ -2312,9 +2410,10 @@ compiler_for(struct compiler *c, stmt_ty s)
end = compiler_new_block(c);
if (start == NULL || end == NULL || cleanup == NULL)
return 0;
ADDOP_JREL(c, SETUP_LOOP, end);
if (!compiler_push_fblock(c, LOOP, start))
if (!compiler_push_fblock(c, FOR_LOOP, start, end))
return 0;
VISIT(c, expr, s->v.For.iter);
ADDOP(c, GET_ITER);
compiler_use_next_block(c, start);
@ -2323,8 +2422,9 @@ compiler_for(struct compiler *c, stmt_ty s)
VISIT_SEQ(c, stmt, s->v.For.body);
ADDOP_JABS(c, JUMP_ABSOLUTE, start);
compiler_use_next_block(c, cleanup);
ADDOP(c, POP_BLOCK);
compiler_pop_fblock(c, LOOP, start);
compiler_pop_fblock(c, FOR_LOOP, start);
VISIT_SEQ(c, stmt, s->v.For.orelse);
compiler_use_next_block(c, end);
return 1;
@ -2356,8 +2456,7 @@ compiler_async_for(struct compiler *c, stmt_ty s)
|| after_try == NULL || try_cleanup == NULL)
return 0;
ADDOP_JREL(c, SETUP_LOOP, after_loop);
if (!compiler_push_fblock(c, LOOP, try))
if (!compiler_push_fblock(c, FOR_LOOP, try, after_loop))
return 0;
VISIT(c, expr, s->v.AsyncFor.iter);
@ -2366,19 +2465,21 @@ compiler_async_for(struct compiler *c, stmt_ty s)
compiler_use_next_block(c, try);
ADDOP_JREL(c, SETUP_EXCEPT, except);
if (!compiler_push_fblock(c, EXCEPT, try))
/* SETUP_FINALLY to guard the __anext__ call */
ADDOP_JREL(c, SETUP_FINALLY, except);
if (!compiler_push_fblock(c, EXCEPT, try, NULL))
return 0;
ADDOP(c, GET_ANEXT);
ADDOP_O(c, LOAD_CONST, Py_None, consts);
ADDOP(c, YIELD_FROM);
VISIT(c, expr, s->v.AsyncFor.target);
ADDOP(c, POP_BLOCK);
ADDOP(c, POP_BLOCK); /* for SETUP_FINALLY */
compiler_pop_fblock(c, EXCEPT, try);
ADDOP_JREL(c, JUMP_FORWARD, after_try);
/* Except block for __anext__ */
compiler_use_next_block(c, except);
ADDOP(c, DUP_TOP);
ADDOP_O(c, LOAD_GLOBAL, stop_aiter_error, names);
@ -2388,25 +2489,26 @@ compiler_async_for(struct compiler *c, stmt_ty s)
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_EXCEPT); /* for SETUP_EXCEPT */
ADDOP(c, POP_TOP); /* for correct calculation of stack effect */
ADDOP(c, POP_BLOCK); /* for SETUP_LOOP */
ADDOP(c, POP_EXCEPT); /* for SETUP_FINALLY */
ADDOP(c, POP_TOP); /* pop iterator from stack */
ADDOP_JABS(c, JUMP_ABSOLUTE, after_loop_else);
compiler_use_next_block(c, try_cleanup);
ADDOP(c, END_FINALLY);
/* Success block for __anext__ */
compiler_use_next_block(c, after_try);
VISIT_SEQ(c, stmt, s->v.AsyncFor.body);
ADDOP_JABS(c, JUMP_ABSOLUTE, try);
ADDOP(c, POP_BLOCK); /* for SETUP_LOOP */
compiler_pop_fblock(c, LOOP, try);
compiler_pop_fblock(c, FOR_LOOP, try);
/* Block reached after `break`ing from loop */
compiler_use_next_block(c, after_loop);
ADDOP_JABS(c, JUMP_ABSOLUTE, end);
/* `else` block */
compiler_use_next_block(c, after_loop_else);
VISIT_SEQ(c, stmt, s->v.For.orelse);
@ -2443,9 +2545,8 @@ compiler_while(struct compiler *c, stmt_ty s)
else
orelse = NULL;
ADDOP_JREL(c, SETUP_LOOP, end);
compiler_use_next_block(c, loop);
if (!compiler_push_fblock(c, LOOP, loop))
if (!compiler_push_fblock(c, WHILE_LOOP, loop, end))
return 0;
if (constant == -1) {
if (!compiler_jump_if(c, s->v.While.test, anchor, 0))
@ -2460,8 +2561,8 @@ compiler_while(struct compiler *c, stmt_ty s)
if (constant == -1)
compiler_use_next_block(c, anchor);
ADDOP(c, POP_BLOCK);
compiler_pop_fblock(c, LOOP, loop);
compiler_pop_fblock(c, WHILE_LOOP, loop);
if (orelse != NULL) /* what if orelse is just pass? */
VISIT_SEQ(c, stmt, s->v.While.orelse);
compiler_use_next_block(c, end);
@ -2470,46 +2571,83 @@ compiler_while(struct compiler *c, stmt_ty s)
}
static int
compiler_continue(struct compiler *c)
compiler_return(struct compiler *c, stmt_ty s)
{
static const char LOOP_ERROR_MSG[] = "'continue' not properly in loop";
static const char IN_FINALLY_ERROR_MSG[] =
"'continue' not supported inside 'finally' clause";
int i;
if (!c->u->u_nfblocks)
return compiler_error(c, LOOP_ERROR_MSG);
i = c->u->u_nfblocks - 1;
switch (c->u->u_fblock[i].fb_type) {
case LOOP:
ADDOP_JABS(c, JUMP_ABSOLUTE, c->u->u_fblock[i].fb_block);
break;
case EXCEPT:
case FINALLY_TRY:
while (--i >= 0 && c->u->u_fblock[i].fb_type != LOOP) {
/* Prevent continue anywhere under a finally
even if hidden in a sub-try or except. */
if (c->u->u_fblock[i].fb_type == FINALLY_END)
return compiler_error(c, IN_FINALLY_ERROR_MSG);
}
if (i == -1)
return compiler_error(c, LOOP_ERROR_MSG);
ADDOP_JABS(c, CONTINUE_LOOP, c->u->u_fblock[i].fb_block);
break;
case FINALLY_END:
return compiler_error(c, IN_FINALLY_ERROR_MSG);
int preserve_tos = ((s->v.Return.value != NULL) &&
!is_const(s->v.Return.value));
if (c->u->u_ste->ste_type != FunctionBlock)
return compiler_error(c, "'return' outside function");
if (s->v.Return.value != NULL &&
c->u->u_ste->ste_coroutine && c->u->u_ste->ste_generator)
{
return compiler_error(
c, "'return' with value in async generator");
}
if (preserve_tos) {
VISIT(c, expr, s->v.Return.value);
}
for (int depth = c->u->u_nfblocks; depth--;) {
struct fblockinfo *info = &c->u->u_fblock[depth];
if (!compiler_unwind_fblock(c, info, preserve_tos))
return 0;
}
if (s->v.Return.value == NULL) {
ADDOP_O(c, LOAD_CONST, Py_None, consts);
}
else if (!preserve_tos) {
VISIT(c, expr, s->v.Return.value);
}
ADDOP(c, RETURN_VALUE);
return 1;
}
static int
compiler_break(struct compiler *c)
{
for (int depth = c->u->u_nfblocks; depth--;) {
struct fblockinfo *info = &c->u->u_fblock[depth];
if (!compiler_unwind_fblock(c, info, 0))
return 0;
if (info->fb_type == WHILE_LOOP || info->fb_type == FOR_LOOP) {
ADDOP_JABS(c, JUMP_ABSOLUTE, info->fb_exit);
return 1;
}
}
return compiler_error(c, "'break' outside loop");
}
static int
compiler_continue(struct compiler *c)
{
for (int depth = c->u->u_nfblocks; depth--;) {
struct fblockinfo *info = &c->u->u_fblock[depth];
if (info->fb_type == WHILE_LOOP || info->fb_type == FOR_LOOP) {
ADDOP_JABS(c, JUMP_ABSOLUTE, info->fb_block);
return 1;
}
if (info->fb_type == FINALLY_END) {
return compiler_error(c,
"'continue' not supported inside 'finally' clause");
}
if (!compiler_unwind_fblock(c, info, 0))
return 0;
}
return compiler_error(c, "'continue' not properly in loop");
}
/* Code generated for "try: <body> finally: <finalbody>" is as follows:
SETUP_FINALLY L
<code for body>
POP_BLOCK
LOAD_CONST <None>
L: <code for finalbody>
BEGIN_FINALLY
L:
<code for finalbody>
END_FINALLY
The special instructions use the block stack. Each block
@ -2521,33 +2659,34 @@ compiler_continue(struct compiler *c)
Pushes the current value stack level and the label
onto the block stack.
POP_BLOCK:
Pops en entry from the block stack, and pops the value
stack until its level is the same as indicated on the
block stack. (The label is ignored.)
Pops en entry from the block stack.
BEGIN_FINALLY
Pushes NULL onto the value stack.
END_FINALLY:
Pops a variable number of entries from the *value* stack
and re-raises the exception they specify. The number of
entries popped depends on the (pseudo) exception type.
Pops 1 (NULL or int) or 6 entries from the *value* stack and restore
the raised and the caught exceptions they specify.
The block stack is unwound when an exception is raised:
when a SETUP_FINALLY entry is found, the exception is pushed
onto the value stack (and the exception condition is cleared),
and the interpreter jumps to the label gotten from the block
stack.
when a SETUP_FINALLY entry is found, the raised and the caught
exceptions are pushed onto the value stack (and the exception
condition is cleared), and the interpreter jumps to the label
gotten from the block stack.
*/
static int
compiler_try_finally(struct compiler *c, stmt_ty s)
{
basicblock *body, *end;
body = compiler_new_block(c);
end = compiler_new_block(c);
if (body == NULL || end == NULL)
return 0;
/* `try` block */
ADDOP_JREL(c, SETUP_FINALLY, end);
compiler_use_next_block(c, body);
if (!compiler_push_fblock(c, FINALLY_TRY, body))
if (!compiler_push_fblock(c, FINALLY_TRY, body, end))
return 0;
if (s->v.Try.handlers && asdl_seq_LEN(s->v.Try.handlers)) {
if (!compiler_try_except(c, s))
@ -2557,16 +2696,16 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
VISIT_SEQ(c, stmt, s->v.Try.body);
}
ADDOP(c, POP_BLOCK);
ADDOP(c, BEGIN_FINALLY);
compiler_pop_fblock(c, FINALLY_TRY, body);
ADDOP_O(c, LOAD_CONST, Py_None, consts);
/* `finally` block */
compiler_use_next_block(c, end);
if (!compiler_push_fblock(c, FINALLY_END, end))
if (!compiler_push_fblock(c, FINALLY_END, end, NULL))
return 0;
VISIT_SEQ(c, stmt, s->v.Try.finalbody);
ADDOP(c, END_FINALLY);
compiler_pop_fblock(c, FINALLY_END, end);
return 1;
}
@ -2577,7 +2716,7 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
associated value, and 'exc' the exception.)
Value stack Label Instruction Argument
[] SETUP_EXCEPT L1
[] SETUP_FINALLY L1
[] <code for S>
[] POP_BLOCK
[] JUMP_FORWARD L0
@ -2613,9 +2752,9 @@ compiler_try_except(struct compiler *c, stmt_ty s)
end = compiler_new_block(c);
if (body == NULL || except == NULL || orelse == NULL || end == NULL)
return 0;
ADDOP_JREL(c, SETUP_EXCEPT, except);
ADDOP_JREL(c, SETUP_FINALLY, except);
compiler_use_next_block(c, body);
if (!compiler_push_fblock(c, EXCEPT, body))
if (!compiler_push_fblock(c, EXCEPT, body, NULL))
return 0;
VISIT_SEQ(c, stmt, s->v.Try.body);
ADDOP(c, POP_BLOCK);
@ -2666,25 +2805,23 @@ compiler_try_except(struct compiler *c, stmt_ty s)
/* second try: */
ADDOP_JREL(c, SETUP_FINALLY, cleanup_end);
compiler_use_next_block(c, cleanup_body);
if (!compiler_push_fblock(c, FINALLY_TRY, cleanup_body))
if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, cleanup_end))
return 0;
/* second # body */
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
ADDOP(c, POP_BLOCK);
compiler_pop_fblock(c, FINALLY_TRY, cleanup_body);
ADDOP(c, BEGIN_FINALLY);
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
/* finally: */
ADDOP_O(c, LOAD_CONST, Py_None, consts);
compiler_use_next_block(c, cleanup_end);
if (!compiler_push_fblock(c, FINALLY_END, cleanup_end))
if (!compiler_push_fblock(c, FINALLY_END, cleanup_end, NULL))
return 0;
/* name = None */
/* name = None; del name */
ADDOP_O(c, LOAD_CONST, Py_None, consts);
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
/* del name */
compiler_nameop(c, handler->v.ExceptHandler.name, Del);
ADDOP(c, END_FINALLY);
@ -2701,11 +2838,11 @@ compiler_try_except(struct compiler *c, stmt_ty s)
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
compiler_use_next_block(c, cleanup_body);
if (!compiler_push_fblock(c, FINALLY_TRY, cleanup_body))
if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, NULL))
return 0;
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
ADDOP(c, POP_EXCEPT);
compiler_pop_fblock(c, FINALLY_TRY, cleanup_body);
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
}
ADDOP_JREL(c, JUMP_FORWARD, end);
compiler_use_next_block(c, except);
@ -2964,18 +3101,7 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
case ClassDef_kind:
return compiler_class(c, s);
case Return_kind:
if (c->u->u_ste->ste_type != FunctionBlock)
return compiler_error(c, "'return' outside function");
if (s->v.Return.value) {
if (c->u->u_ste->ste_coroutine && c->u->u_ste->ste_generator)
return compiler_error(
c, "'return' with value in async generator");
VISIT(c, expr, s->v.Return.value);
}
else
ADDOP_O(c, LOAD_CONST, Py_None, consts);
ADDOP(c, RETURN_VALUE);
break;
return compiler_return(c, s);
case Delete_kind:
VISIT_SEQ(c, expr, s->v.Delete.targets)
break;
@ -3027,10 +3153,7 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
case Pass_kind:
break;
case Break_kind:
if (!compiler_in_loop(c))
return compiler_error(c, "'break' outside loop");
ADDOP(c, BREAK_LOOP);
break;
return compiler_break(c);
case Continue_kind:
return compiler_continue(c);
case With_kind:
@ -3771,8 +3894,6 @@ compiler_call_helper(struct compiler *c,
The LC/SC version returns the populated container, while the GE version is
flagged in symtable.c as a generator, so it returns the generator object
when the function is called.
This code *knows* that the loop cannot contain break, continue, or return,
so it cheats and skips the SETUP_LOOP/POP_BLOCK steps used in normal loops.
Possible cleanups:
- iterate over the generator sequence instead of using recursion
@ -3932,8 +4053,8 @@ compiler_async_comprehension_generator(struct compiler *c,
compiler_use_next_block(c, try);
ADDOP_JREL(c, SETUP_EXCEPT, except);
if (!compiler_push_fblock(c, EXCEPT, try))
ADDOP_JREL(c, SETUP_FINALLY, except);
if (!compiler_push_fblock(c, EXCEPT, try, NULL))
return 0;
ADDOP(c, GET_ANEXT);
@ -3954,7 +4075,7 @@ compiler_async_comprehension_generator(struct compiler *c,
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_EXCEPT); /* for SETUP_EXCEPT */
ADDOP(c, POP_EXCEPT); /* for SETUP_FINALLY */
ADDOP_JABS(c, JUMP_ABSOLUTE, anchor);
@ -4246,7 +4367,7 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
/* SETUP_ASYNC_WITH pushes a finally block. */
compiler_use_next_block(c, block);
if (!compiler_push_fblock(c, FINALLY_TRY, block)) {
if (!compiler_push_fblock(c, ASYNC_WITH, block, finally)) {
return 0;
}
@ -4267,11 +4388,11 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
/* End of try block; start the finally block */
ADDOP(c, POP_BLOCK);
compiler_pop_fblock(c, FINALLY_TRY, block);
ADDOP(c, BEGIN_FINALLY);
compiler_pop_fblock(c, ASYNC_WITH, block);
ADDOP_O(c, LOAD_CONST, Py_None, consts);
compiler_use_next_block(c, finally);
if (!compiler_push_fblock(c, FINALLY_END, finally))
if (!compiler_push_fblock(c, FINALLY_END, finally, NULL))
return 0;
/* Finally block starts; context.__exit__ is on the stack under
@ -4334,7 +4455,7 @@ compiler_with(struct compiler *c, stmt_ty s, int pos)
/* SETUP_WITH pushes a finally block. */
compiler_use_next_block(c, block);
if (!compiler_push_fblock(c, FINALLY_TRY, block)) {
if (!compiler_push_fblock(c, WITH, block, finally)) {
return 0;
}
@ -4355,11 +4476,11 @@ compiler_with(struct compiler *c, stmt_ty s, int pos)
/* End of try block; start the finally block */
ADDOP(c, POP_BLOCK);
compiler_pop_fblock(c, FINALLY_TRY, block);
ADDOP(c, BEGIN_FINALLY);
compiler_pop_fblock(c, WITH, block);
ADDOP_O(c, LOAD_CONST, Py_None, consts);
compiler_use_next_block(c, finally);
if (!compiler_push_fblock(c, FINALLY_END, finally))
if (!compiler_push_fblock(c, FINALLY_END, finally, NULL))
return 0;
/* Finally block starts; context.__exit__ is on the stack under
@ -4745,41 +4866,6 @@ compiler_annassign(struct compiler *c, stmt_ty s)
return 1;
}
static int
compiler_push_fblock(struct compiler *c, enum fblocktype t, basicblock *b)
{
struct fblockinfo *f;
if (c->u->u_nfblocks >= CO_MAXBLOCKS) {
PyErr_SetString(PyExc_SyntaxError,
"too many statically nested blocks");
return 0;
}
f = &c->u->u_fblock[c->u->u_nfblocks++];
f->fb_type = t;
f->fb_block = b;
return 1;
}
static void
compiler_pop_fblock(struct compiler *c, enum fblocktype t, basicblock *b)
{
struct compiler_unit *u = c->u;
assert(u->u_nfblocks > 0);
u->u_nfblocks--;
assert(u->u_fblock[u->u_nfblocks].fb_type == t);
assert(u->u_fblock[u->u_nfblocks].fb_block == b);
}
static int
compiler_in_loop(struct compiler *c) {
int i;
struct compiler_unit *u = c->u;
for (i = 0; i < u->u_nfblocks; ++i) {
if (u->u_fblock[i].fb_type == LOOP)
return 1;
}
return 0;
}
/* Raises a SyntaxError and returns 0.
If something goes wrong, a different exception may be raised.
*/
@ -4974,9 +5060,7 @@ dfs(struct compiler *c, basicblock *b, struct assembler *a, int end)
Py_LOCAL_INLINE(void)
stackdepth_push(basicblock ***sp, basicblock *b, int depth)
{
/* XXX b->b_startdepth > depth only for the target of SETUP_FINALLY,
* SETUP_WITH and SETUP_ASYNC_WITH. */
assert(b->b_startdepth < 0 || b->b_startdepth >= depth);
assert(b->b_startdepth < 0 || b->b_startdepth == depth);
if (b->b_startdepth < depth) {
assert(b->b_startdepth < 0);
b->b_startdepth = depth;
@ -5033,15 +5117,11 @@ stackdepth(struct compiler *c)
maxdepth = target_depth;
}
assert(target_depth >= 0); /* invalid code or bug in stackdepth() */
if (instr->i_opcode == CONTINUE_LOOP) {
/* Pops a variable number of values from the stack,
* but the target should be already proceeding.
*/
if (instr->i_opcode == CALL_FINALLY) {
assert(instr->i_target->b_startdepth >= 0);
assert(instr->i_target->b_startdepth <= depth);
/* remaining code is dead */
next = NULL;
break;
assert(instr->i_target->b_startdepth >= target_depth);
depth = new_depth;
continue;
}
stackdepth_push(&sp, instr->i_target, target_depth);
}
@ -5049,8 +5129,7 @@ stackdepth(struct compiler *c)
if (instr->i_opcode == JUMP_ABSOLUTE ||
instr->i_opcode == JUMP_FORWARD ||
instr->i_opcode == RETURN_VALUE ||
instr->i_opcode == RAISE_VARARGS ||
instr->i_opcode == BREAK_LOOP)
instr->i_opcode == RAISE_VARARGS)
{
/* remaining code is dead */
next = NULL;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@ static void *opcode_targets[256] = {
&&TARGET_ROT_THREE,
&&TARGET_DUP_TOP,
&&TARGET_DUP_TOP_TWO,
&&_unknown_opcode,
&&TARGET_ROT_FOUR,
&&_unknown_opcode,
&&_unknown_opcode,
&&TARGET_NOP,
@ -52,7 +52,7 @@ static void *opcode_targets[256] = {
&&TARGET_GET_AITER,
&&TARGET_GET_ANEXT,
&&TARGET_BEFORE_ASYNC_WITH,
&&_unknown_opcode,
&&TARGET_BEGIN_FINALLY,
&&_unknown_opcode,
&&TARGET_INPLACE_ADD,
&&TARGET_INPLACE_SUBTRACT,
@ -79,7 +79,7 @@ static void *opcode_targets[256] = {
&&TARGET_INPLACE_AND,
&&TARGET_INPLACE_XOR,
&&TARGET_INPLACE_OR,
&&TARGET_BREAK_LOOP,
&&_unknown_opcode,
&&TARGET_WITH_CLEANUP_START,
&&TARGET_WITH_CLEANUP_FINISH,
&&TARGET_RETURN_VALUE,
@ -118,9 +118,9 @@ static void *opcode_targets[256] = {
&&TARGET_LOAD_GLOBAL,
&&_unknown_opcode,
&&_unknown_opcode,
&&TARGET_CONTINUE_LOOP,
&&TARGET_SETUP_LOOP,
&&TARGET_SETUP_EXCEPT,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&TARGET_SETUP_FINALLY,
&&_unknown_opcode,
&&TARGET_LOAD_FAST,
@ -161,8 +161,8 @@ static void *opcode_targets[256] = {
&&_unknown_opcode,
&&TARGET_LOAD_METHOD,
&&TARGET_CALL_METHOD,
&&_unknown_opcode,
&&_unknown_opcode,
&&TARGET_CALL_FINALLY,
&&TARGET_POP_FINALLY,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,

View file

@ -13,7 +13,7 @@
#define UNCONDITIONAL_JUMP(op) (op==JUMP_ABSOLUTE || op==JUMP_FORWARD)
#define CONDITIONAL_JUMP(op) (op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP \
#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE \
|| op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
#define JUMPS_ON_TRUE(op) (op==POP_JUMP_IF_TRUE || op==JUMP_IF_TRUE_OR_POP)
@ -185,12 +185,10 @@ markblocks(_Py_CODEUNIT *code, Py_ssize_t len)
case POP_JUMP_IF_FALSE:
case POP_JUMP_IF_TRUE:
case JUMP_ABSOLUTE:
case CONTINUE_LOOP:
case SETUP_LOOP:
case SETUP_EXCEPT:
case SETUP_FINALLY:
case SETUP_WITH:
case SETUP_ASYNC_WITH:
case CALL_FINALLY:
j = GETJUMPTGT(code, i);
assert(j < len);
blocks[j] = 1;
@ -373,15 +371,8 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
/* Replace jumps to unconditional jumps */
case POP_JUMP_IF_FALSE:
case POP_JUMP_IF_TRUE:
case FOR_ITER:
case JUMP_FORWARD:
case JUMP_ABSOLUTE:
case CONTINUE_LOOP:
case SETUP_LOOP:
case SETUP_EXCEPT:
case SETUP_FINALLY:
case SETUP_WITH:
case SETUP_ASYNC_WITH:
h = GETJUMPTGT(codestr, i);
tgt = find_op(codestr, h);
/* Replace JUMP_* to a RETURN into just a RETURN */
@ -407,7 +398,21 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
/* Remove unreachable ops after RETURN */
case RETURN_VALUE:
h = i + 1;
while (h < codelen && ISBASICBLOCK(blocks, i, h)) {
/* END_FINALLY should be kept since it denotes the end of
the 'finally' block in frame_setlineno() in frameobject.c.
SETUP_FINALLY should be kept for balancing.
*/
while (h < codelen && ISBASICBLOCK(blocks, i, h) &&
_Py_OPCODE(codestr[h]) != END_FINALLY)
{
if (_Py_OPCODE(codestr[h]) == SETUP_FINALLY) {
while (h > i + 1 &&
_Py_OPCODE(codestr[h - 1]) == EXTENDED_ARG)
{
h--;
}
break;
}
h++;
}
if (h > i + 1) {
@ -452,7 +457,6 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
case NOP:continue;
case JUMP_ABSOLUTE:
case CONTINUE_LOOP:
case POP_JUMP_IF_FALSE:
case POP_JUMP_IF_TRUE:
case JUMP_IF_FALSE_OR_POP:
@ -462,11 +466,10 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
case FOR_ITER:
case JUMP_FORWARD:
case SETUP_LOOP:
case SETUP_EXCEPT:
case SETUP_FINALLY:
case SETUP_WITH:
case SETUP_ASYNC_WITH:
case CALL_FINALLY:
j = blocks[j / sizeof(_Py_CODEUNIT) + i + 1] - blocks[i] - 1;
j *= sizeof(_Py_CODEUNIT);
break;