mirror of
https://github.com/python/cpython.git
synced 2025-10-10 00:43:41 +00:00
bpo-40222: "Zero cost" exception handling (GH-25729)
"Zero cost" exception handling. * Uses a lookup table to determine how to handle exceptions. * Removes SETUP_FINALLY and POP_TOP block instructions, eliminating (most of) the runtime overhead of try statements. * Reduces the size of the frame object by about 60%.
This commit is contained in:
parent
b32c8e9795
commit
adcd220556
30 changed files with 6614 additions and 5687 deletions
371
Python/ceval.c
371
Python/ceval.c
|
@ -95,6 +95,7 @@ static PyObject * special_lookup(PyThreadState *, PyObject *, _Py_Identifier *);
|
|||
static int check_args_iterable(PyThreadState *, PyObject *func, PyObject *vararg);
|
||||
static void format_kwargs_error(PyThreadState *, PyObject *func, PyObject *kwargs);
|
||||
static void format_awaitable_error(PyThreadState *, PyTypeObject *, int, int);
|
||||
static PyTryBlock get_exception_handler(PyCodeObject *, int);
|
||||
|
||||
#define NAME_ERROR_MSG \
|
||||
"name '%.200s' is not defined"
|
||||
|
@ -1448,34 +1449,6 @@ eval_frame_handle_pending(PyThreadState *tstate)
|
|||
GETLOCAL(i) = value; \
|
||||
Py_XDECREF(tmp); } while (0)
|
||||
|
||||
|
||||
#define UNWIND_BLOCK(b) \
|
||||
while (STACK_LEVEL() > (b)->b_level) { \
|
||||
PyObject *v = POP(); \
|
||||
Py_XDECREF(v); \
|
||||
}
|
||||
|
||||
#define UNWIND_EXCEPT_HANDLER(b) \
|
||||
do { \
|
||||
PyObject *type, *value, *traceback; \
|
||||
_PyErr_StackItem *exc_info; \
|
||||
assert(STACK_LEVEL() >= (b)->b_level + 3); \
|
||||
while (STACK_LEVEL() > (b)->b_level + 3) { \
|
||||
value = POP(); \
|
||||
Py_XDECREF(value); \
|
||||
} \
|
||||
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); \
|
||||
} while(0)
|
||||
|
||||
/* macros for opcode cache */
|
||||
#define OPCACHE_CHECK() \
|
||||
do { \
|
||||
|
@ -1738,7 +1711,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
|
|||
assert(!_PyErr_Occurred(tstate));
|
||||
#endif
|
||||
|
||||
main_loop:
|
||||
for (;;) {
|
||||
assert(stack_pointer >= f->f_valuestack); /* else underflow */
|
||||
assert(STACK_LEVEL() <= co->co_stacksize); /* else overflow */
|
||||
|
@ -1754,9 +1726,7 @@ main_loop:
|
|||
|
||||
if (_Py_atomic_load_relaxed(eval_breaker)) {
|
||||
opcode = _Py_OPCODE(*next_instr);
|
||||
if (opcode != SETUP_FINALLY &&
|
||||
opcode != SETUP_WITH &&
|
||||
opcode != BEFORE_ASYNC_WITH &&
|
||||
if (opcode != BEFORE_ASYNC_WITH &&
|
||||
opcode != YIELD_FROM) {
|
||||
/* Few cases where we skip running signal handlers and other
|
||||
pending calls:
|
||||
|
@ -1800,14 +1770,14 @@ main_loop:
|
|||
tstate->c_traceobj,
|
||||
tstate, f,
|
||||
&trace_info);
|
||||
/* Reload possibly changed frame fields */
|
||||
JUMPTO(f->f_lasti);
|
||||
stack_pointer = f->f_valuestack+f->f_stackdepth;
|
||||
f->f_stackdepth = -1;
|
||||
if (err) {
|
||||
/* trace function raised an exception */
|
||||
goto error;
|
||||
}
|
||||
/* Reload possibly changed frame fields */
|
||||
JUMPTO(f->f_lasti);
|
||||
stack_pointer = f->f_valuestack+f->f_stackdepth;
|
||||
f->f_stackdepth = -1;
|
||||
NEXTOPARG();
|
||||
}
|
||||
|
||||
|
@ -2425,7 +2395,6 @@ main_loop:
|
|||
|
||||
case TARGET(RETURN_VALUE): {
|
||||
retval = POP();
|
||||
assert(f->f_iblock == 0);
|
||||
assert(EMPTY());
|
||||
f->f_state = FRAME_RETURNED;
|
||||
f->f_stackdepth = 0;
|
||||
|
@ -2664,14 +2633,6 @@ main_loop:
|
|||
case TARGET(POP_EXCEPT): {
|
||||
PyObject *type, *value, *traceback;
|
||||
_PyErr_StackItem *exc_info;
|
||||
PyTryBlock *b = PyFrame_BlockPop(f);
|
||||
if (b->b_type != EXCEPT_HANDLER) {
|
||||
_PyErr_SetString(tstate, PyExc_SystemError,
|
||||
"popped block is not an except handler");
|
||||
goto error;
|
||||
}
|
||||
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;
|
||||
|
@ -2685,15 +2646,48 @@ main_loop:
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(POP_BLOCK): {
|
||||
PyFrame_BlockPop(f);
|
||||
DISPATCH();
|
||||
case TARGET(POP_EXCEPT_AND_RERAISE): {
|
||||
PyObject *lasti = PEEK(4);
|
||||
if (PyLong_Check(lasti)) {
|
||||
f->f_lasti = PyLong_AsLong(lasti);
|
||||
assert(!_PyErr_Occurred(tstate));
|
||||
}
|
||||
else {
|
||||
_PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int");
|
||||
goto error;
|
||||
}
|
||||
PyObject *type, *value, *traceback;
|
||||
_PyErr_StackItem *exc_info;
|
||||
type = POP();
|
||||
value = POP();
|
||||
traceback = POP();
|
||||
Py_DECREF(POP()); /* lasti */
|
||||
_PyErr_Restore(tstate, type, value, traceback);
|
||||
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);
|
||||
goto exception_unwind;
|
||||
}
|
||||
|
||||
case TARGET(RERAISE): {
|
||||
assert(f->f_iblock > 0);
|
||||
if (oparg) {
|
||||
f->f_lasti = f->f_blockstack[f->f_iblock-1].b_handler;
|
||||
PyObject *lasti = PEEK(oparg+3);
|
||||
if (PyLong_Check(lasti)) {
|
||||
f->f_lasti = PyLong_AsLong(lasti);
|
||||
assert(!_PyErr_Occurred(tstate));
|
||||
}
|
||||
else {
|
||||
assert(PyLong_Check(lasti));
|
||||
_PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
PyObject *exc = POP();
|
||||
PyObject *val = POP();
|
||||
|
@ -2705,19 +2699,17 @@ main_loop:
|
|||
|
||||
case TARGET(END_ASYNC_FOR): {
|
||||
PyObject *exc = POP();
|
||||
PyObject *val = POP();
|
||||
PyObject *tb = POP();
|
||||
assert(PyExceptionClass_Check(exc));
|
||||
if (PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) {
|
||||
PyTryBlock *b = PyFrame_BlockPop(f);
|
||||
assert(b->b_type == EXCEPT_HANDLER);
|
||||
Py_DECREF(exc);
|
||||
UNWIND_EXCEPT_HANDLER(b);
|
||||
Py_DECREF(val);
|
||||
Py_DECREF(tb);
|
||||
Py_DECREF(POP());
|
||||
JUMPBY(oparg);
|
||||
DISPATCH();
|
||||
}
|
||||
else {
|
||||
PyObject *val = POP();
|
||||
PyObject *tb = POP();
|
||||
_PyErr_Restore(tstate, exc, val, tb);
|
||||
goto exception_unwind;
|
||||
}
|
||||
|
@ -4022,12 +4014,6 @@ main_loop:
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(SETUP_FINALLY): {
|
||||
PyFrame_BlockSetup(f, SETUP_FINALLY, INSTR_OFFSET() + oparg,
|
||||
STACK_LEVEL());
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(BEFORE_ASYNC_WITH): {
|
||||
_Py_IDENTIFIER(__aenter__);
|
||||
_Py_IDENTIFIER(__aexit__);
|
||||
|
@ -4053,17 +4039,7 @@ main_loop:
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(SETUP_ASYNC_WITH): {
|
||||
PyObject *res = POP();
|
||||
/* Setup the finally block before pushing the result
|
||||
of __aenter__ on the stack. */
|
||||
PyFrame_BlockSetup(f, SETUP_FINALLY, INSTR_OFFSET() + oparg,
|
||||
STACK_LEVEL());
|
||||
PUSH(res);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(SETUP_WITH): {
|
||||
case TARGET(BEFORE_WITH): {
|
||||
_Py_IDENTIFIER(__enter__);
|
||||
_Py_IDENTIFIER(__exit__);
|
||||
PyObject *mgr = TOP();
|
||||
|
@ -4081,23 +4057,20 @@ main_loop:
|
|||
Py_DECREF(mgr);
|
||||
res = _PyObject_CallNoArg(enter);
|
||||
Py_DECREF(enter);
|
||||
if (res == NULL)
|
||||
if (res == NULL) {
|
||||
goto error;
|
||||
/* Setup the finally block before pushing the result
|
||||
of __enter__ on the stack. */
|
||||
PyFrame_BlockSetup(f, SETUP_FINALLY, INSTR_OFFSET() + oparg,
|
||||
STACK_LEVEL());
|
||||
|
||||
}
|
||||
PUSH(res);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(WITH_EXCEPT_START): {
|
||||
/* At the top of the stack are 7 values:
|
||||
/* At the top of the stack are 8 values:
|
||||
- (TOP, SECOND, THIRD) = exc_info()
|
||||
- (FOURTH, FIFTH, SIXTH) = previous exception for EXCEPT_HANDLER
|
||||
- SEVENTH: the context.__exit__ bound method
|
||||
We call SEVENTH(TOP, SECOND, THIRD).
|
||||
- (FOURTH, FIFTH, SIXTH) = previous exception
|
||||
- SEVENTH: lasti of exception in exc_info()
|
||||
- EIGHTH: the context.__exit__ bound method
|
||||
We call EIGHTH(TOP, SECOND, THIRD).
|
||||
Then we push again the TOP exception and the __exit__
|
||||
return value.
|
||||
*/
|
||||
|
@ -4109,7 +4082,8 @@ main_loop:
|
|||
tb = THIRD();
|
||||
assert(!Py_IsNone(exc));
|
||||
assert(!PyLong_Check(exc));
|
||||
exit_func = PEEK(7);
|
||||
assert(PyLong_Check(PEEK(7)));
|
||||
exit_func = PEEK(8);
|
||||
PyObject *stack[4] = {NULL, exc, val, tb};
|
||||
res = PyObject_Vectorcall(exit_func, stack + 1,
|
||||
3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
|
||||
|
@ -4120,6 +4094,37 @@ main_loop:
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(PUSH_EXC_INFO): {
|
||||
PyObject *type = TOP();
|
||||
PyObject *value = SECOND();
|
||||
PyObject *tb = THIRD();
|
||||
_PyErr_StackItem *exc_info = tstate->exc_info;
|
||||
SET_THIRD(exc_info->exc_traceback);
|
||||
SET_SECOND(exc_info->exc_value);
|
||||
if (exc_info->exc_type != NULL) {
|
||||
SET_TOP(exc_info->exc_type);
|
||||
}
|
||||
else {
|
||||
Py_INCREF(Py_None);
|
||||
SET_TOP(Py_None);
|
||||
}
|
||||
Py_INCREF(tb);
|
||||
PUSH(tb);
|
||||
exc_info->exc_traceback = tb;
|
||||
|
||||
Py_INCREF(value);
|
||||
PUSH(value);
|
||||
assert(PyExceptionInstance_Check(value));
|
||||
exc_info->exc_value = value;
|
||||
|
||||
Py_INCREF(type);
|
||||
PUSH(type);
|
||||
assert(PyExceptionClass_Check(type));
|
||||
exc_info->exc_type = type;
|
||||
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(LOAD_METHOD): {
|
||||
/* Designed to work in tandem with CALL_METHOD. */
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
|
@ -4455,64 +4460,54 @@ error:
|
|||
}
|
||||
exception_unwind:
|
||||
f->f_state = FRAME_UNWINDING;
|
||||
/* Unwind stacks if an exception occurred */
|
||||
while (f->f_iblock > 0) {
|
||||
/* Pop the current block. */
|
||||
PyTryBlock *b = &f->f_blockstack[--f->f_iblock];
|
||||
/* We can't use f->f_lasti here, as RERAISE may have set it */
|
||||
int lasti = INSTR_OFFSET()-1;
|
||||
PyTryBlock from_table = get_exception_handler(co, lasti);
|
||||
if (from_table.b_handler < 0) {
|
||||
// No handlers, so exit.
|
||||
break;
|
||||
}
|
||||
|
||||
if (b->b_type == EXCEPT_HANDLER) {
|
||||
UNWIND_EXCEPT_HANDLER(b);
|
||||
continue;
|
||||
assert(STACK_LEVEL() >= from_table.b_level);
|
||||
while (STACK_LEVEL() > from_table.b_level) {
|
||||
PyObject *v = POP();
|
||||
Py_XDECREF(v);
|
||||
}
|
||||
PyObject *exc, *val, *tb;
|
||||
int handler = from_table.b_handler;
|
||||
if (from_table.b_type) {
|
||||
PyObject *lasti = PyLong_FromLong(f->f_lasti);
|
||||
if (lasti == NULL) {
|
||||
goto exception_unwind;
|
||||
}
|
||||
UNWIND_BLOCK(b);
|
||||
if (b->b_type == SETUP_FINALLY) {
|
||||
PyObject *exc, *val, *tb;
|
||||
int handler = b->b_handler;
|
||||
_PyErr_StackItem *exc_info = tstate->exc_info;
|
||||
/* Beware, this invalidates all b->b_* fields */
|
||||
PyFrame_BlockSetup(f, EXCEPT_HANDLER, f->f_lasti, STACK_LEVEL());
|
||||
PUSH(exc_info->exc_traceback);
|
||||
PUSH(exc_info->exc_value);
|
||||
if (exc_info->exc_type != NULL) {
|
||||
PUSH(exc_info->exc_type);
|
||||
}
|
||||
else {
|
||||
Py_INCREF(Py_None);
|
||||
PUSH(Py_None);
|
||||
}
|
||||
_PyErr_Fetch(tstate, &exc, &val, &tb);
|
||||
/* Make the raw exception data
|
||||
available to the handler,
|
||||
so a program can emulate the
|
||||
Python main loop. */
|
||||
_PyErr_NormalizeException(tstate, &exc, &val, &tb);
|
||||
if (tb != NULL)
|
||||
PyException_SetTraceback(val, tb);
|
||||
else
|
||||
PyException_SetTraceback(val, Py_None);
|
||||
Py_INCREF(exc);
|
||||
exc_info->exc_type = exc;
|
||||
Py_INCREF(val);
|
||||
exc_info->exc_value = val;
|
||||
exc_info->exc_traceback = tb;
|
||||
if (tb == NULL)
|
||||
tb = Py_None;
|
||||
Py_INCREF(tb);
|
||||
PUSH(tb);
|
||||
PUSH(val);
|
||||
PUSH(exc);
|
||||
JUMPTO(handler);
|
||||
if (trace_info.cframe.use_tracing) {
|
||||
trace_info.instr_prev = INT_MAX;
|
||||
}
|
||||
/* Resume normal execution */
|
||||
f->f_state = FRAME_EXECUTING;
|
||||
goto main_loop;
|
||||
}
|
||||
} /* unwind stack */
|
||||
|
||||
/* End the loop as we still have an error */
|
||||
break;
|
||||
PUSH(lasti);
|
||||
}
|
||||
_PyErr_Fetch(tstate, &exc, &val, &tb);
|
||||
/* Make the raw exception data
|
||||
available to the handler,
|
||||
so a program can emulate the
|
||||
Python main loop. */
|
||||
_PyErr_NormalizeException(tstate, &exc, &val, &tb);
|
||||
if (tb != NULL)
|
||||
PyException_SetTraceback(val, tb);
|
||||
else
|
||||
PyException_SetTraceback(val, Py_None);
|
||||
if (tb == NULL) {
|
||||
tb = Py_None;
|
||||
Py_INCREF(Py_None);
|
||||
}
|
||||
PUSH(tb);
|
||||
PUSH(val);
|
||||
PUSH(exc);
|
||||
JUMPTO(handler);
|
||||
if (trace_info.cframe.use_tracing) {
|
||||
trace_info.instr_prev = INT_MAX;
|
||||
}
|
||||
/* Resume normal execution */
|
||||
f->f_state = FRAME_EXECUTING;
|
||||
f->f_lasti = handler;
|
||||
NEXTOPARG();
|
||||
goto dispatch_opcode;
|
||||
} /* main loop */
|
||||
|
||||
assert(retval == NULL);
|
||||
|
@ -4777,6 +4772,102 @@ fail:
|
|||
|
||||
}
|
||||
|
||||
/* Exception table parsing code.
|
||||
* See Objects/exception_table_notes.txt for details.
|
||||
*/
|
||||
|
||||
static inline unsigned char *
|
||||
parse_varint(unsigned char *p, int *result) {
|
||||
int val = p[0] & 63;
|
||||
while (p[0] & 64) {
|
||||
p++;
|
||||
val = (val << 6) | (p[0] & 63);
|
||||
}
|
||||
*result = val;
|
||||
return p+1;
|
||||
}
|
||||
|
||||
static inline unsigned char *
|
||||
scan_back_to_entry_start(unsigned char *p) {
|
||||
for (; (p[0]&128) == 0; p--);
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline unsigned char *
|
||||
skip_to_next_entry(unsigned char *p) {
|
||||
for (; (p[0]&128) == 0; p++);
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline unsigned char *
|
||||
parse_range(unsigned char *p, int *start, int*end)
|
||||
{
|
||||
p = parse_varint(p, start);
|
||||
int size;
|
||||
p = parse_varint(p, &size);
|
||||
*end = *start + size;
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline void
|
||||
parse_block(unsigned char *p, PyTryBlock *block) {
|
||||
int depth_and_lasti;
|
||||
p = parse_varint(p, &block->b_handler);
|
||||
p = parse_varint(p, &depth_and_lasti);
|
||||
block->b_level = depth_and_lasti >> 1;
|
||||
block->b_type = depth_and_lasti & 1;
|
||||
}
|
||||
|
||||
#define MAX_LINEAR_SEARCH 40
|
||||
|
||||
static PyTryBlock
|
||||
get_exception_handler(PyCodeObject *code, int index)
|
||||
{
|
||||
PyTryBlock res;
|
||||
unsigned char *start = (unsigned char *)PyBytes_AS_STRING(code->co_exceptiontable);
|
||||
unsigned char *end = start + PyBytes_GET_SIZE(code->co_exceptiontable);
|
||||
/* Invariants:
|
||||
* start_table == end_table OR
|
||||
* start_table points to a legal entry and end_table points
|
||||
* beyond the table or to a legal entry that is after index.
|
||||
*/
|
||||
if (end - start > MAX_LINEAR_SEARCH) {
|
||||
int offset;
|
||||
parse_varint(start, &offset);
|
||||
if (offset > index) {
|
||||
res.b_handler = -1;
|
||||
return res;
|
||||
}
|
||||
do {
|
||||
unsigned char * mid = start + ((end-start)>>1);
|
||||
mid = scan_back_to_entry_start(mid);
|
||||
parse_varint(mid, &offset);
|
||||
if (offset > index) {
|
||||
end = mid;
|
||||
}
|
||||
else {
|
||||
start = mid;
|
||||
}
|
||||
|
||||
} while (end - start > MAX_LINEAR_SEARCH);
|
||||
}
|
||||
unsigned char *scan = start;
|
||||
while (scan < end) {
|
||||
int start_offset, size;
|
||||
scan = parse_varint(scan, &start_offset);
|
||||
if (start_offset > index) {
|
||||
break;
|
||||
}
|
||||
scan = parse_varint(scan, &size);
|
||||
if (start_offset + size > index) {
|
||||
parse_block(scan, &res);
|
||||
return res;
|
||||
}
|
||||
scan = skip_to_next_entry(scan);
|
||||
}
|
||||
res.b_handler = -1;
|
||||
return res;
|
||||
}
|
||||
|
||||
PyFrameObject *
|
||||
_PyEval_MakeFrameVector(PyThreadState *tstate,
|
||||
|
|
484
Python/compile.c
484
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;
|
||||
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;
|
||||
}
|
||||
eliminate_empty_basic_blocks(a->a_entry);
|
||||
for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
|
||||
clean_basic_block(b);
|
||||
}
|
||||
/* 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)) {
|
||||
|
|
|
@ -8,5 +8,5 @@ const unsigned char _Py_M__hello[] = {
|
|||
5,112,114,105,110,116,169,0,114,1,0,0,0,114,1,0,
|
||||
0,0,122,14,60,102,114,111,122,101,110,32,104,101,108,108,
|
||||
111,62,218,8,60,109,111,100,117,108,101,62,1,0,0,0,
|
||||
115,4,0,0,0,4,0,12,1,
|
||||
115,4,0,0,0,4,0,12,1,243,0,0,0,0,
|
||||
};
|
||||
|
|
3507
Python/importlib.h
generated
3507
Python/importlib.h
generated
File diff suppressed because it is too large
Load diff
4981
Python/importlib_external.h
generated
4981
Python/importlib_external.h
generated
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -525,6 +525,7 @@ w_complex_object(PyObject *v, char flag, WFILE *p)
|
|||
w_object(co->co_name, p);
|
||||
w_long(co->co_firstlineno, p);
|
||||
w_object(co->co_linetable, p);
|
||||
w_object(co->co_exceptiontable, p);
|
||||
}
|
||||
else if (PyObject_CheckBuffer(v)) {
|
||||
/* Write unknown bytes-like objects as a bytes object */
|
||||
|
@ -1313,6 +1314,7 @@ r_object(RFILE *p)
|
|||
PyObject *name = NULL;
|
||||
int firstlineno;
|
||||
PyObject *linetable = NULL;
|
||||
PyObject *exceptiontable = NULL;
|
||||
|
||||
idx = r_ref_reserve(flag, p);
|
||||
if (idx < 0)
|
||||
|
@ -1370,6 +1372,10 @@ r_object(RFILE *p)
|
|||
linetable = r_object(p);
|
||||
if (linetable == NULL)
|
||||
goto code_error;
|
||||
exceptiontable = r_object(p);
|
||||
if (exceptiontable == NULL)
|
||||
goto code_error;
|
||||
|
||||
|
||||
if (PySys_Audit("code.__new__", "OOOiiiiii",
|
||||
code, filename, name, argcount, posonlyargcount,
|
||||
|
@ -1382,7 +1388,7 @@ r_object(RFILE *p)
|
|||
nlocals, stacksize, flags,
|
||||
code, consts, names, varnames,
|
||||
freevars, cellvars, filename, name,
|
||||
firstlineno, linetable);
|
||||
firstlineno, linetable, exceptiontable);
|
||||
v = r_ref_insert(v, idx, flag, p);
|
||||
|
||||
code_error:
|
||||
|
@ -1395,6 +1401,7 @@ r_object(RFILE *p)
|
|||
Py_XDECREF(filename);
|
||||
Py_XDECREF(name);
|
||||
Py_XDECREF(linetable);
|
||||
Py_XDECREF(exceptiontable);
|
||||
}
|
||||
retval = v;
|
||||
break;
|
||||
|
|
14
Python/opcode_targets.h
generated
14
Python/opcode_targets.h
generated
|
@ -34,9 +34,9 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_MATCH_SEQUENCE,
|
||||
&&TARGET_MATCH_KEYS,
|
||||
&&TARGET_COPY_DICT_WITHOUT_KEYS,
|
||||
&&TARGET_PUSH_EXC_INFO,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_POP_EXCEPT_AND_RERAISE,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
|
@ -52,7 +52,7 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_GET_AITER,
|
||||
&&TARGET_GET_ANEXT,
|
||||
&&TARGET_BEFORE_ASYNC_WITH,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_BEFORE_WITH,
|
||||
&&TARGET_END_ASYNC_FOR,
|
||||
&&TARGET_INPLACE_ADD,
|
||||
&&TARGET_INPLACE_SUBTRACT,
|
||||
|
@ -86,7 +86,7 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_IMPORT_STAR,
|
||||
&&TARGET_SETUP_ANNOTATIONS,
|
||||
&&TARGET_YIELD_VALUE,
|
||||
&&TARGET_POP_BLOCK,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_POP_EXCEPT,
|
||||
&&TARGET_STORE_NAME,
|
||||
|
@ -121,7 +121,7 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_RERAISE,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_JUMP_IF_NOT_EXC_MATCH,
|
||||
&&TARGET_SETUP_FINALLY,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_LOAD_FAST,
|
||||
&&TARGET_STORE_FAST,
|
||||
|
@ -142,7 +142,7 @@ static void *opcode_targets[256] = {
|
|||
&&_unknown_opcode,
|
||||
&&TARGET_CALL_FUNCTION_KW,
|
||||
&&TARGET_CALL_FUNCTION_EX,
|
||||
&&TARGET_SETUP_WITH,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_EXTENDED_ARG,
|
||||
&&TARGET_LIST_APPEND,
|
||||
&&TARGET_SET_ADD,
|
||||
|
@ -153,7 +153,7 @@ static void *opcode_targets[256] = {
|
|||
&&_unknown_opcode,
|
||||
&&TARGET_MATCH_CLASS,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_SETUP_ASYNC_WITH,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_FORMAT_VALUE,
|
||||
&&TARGET_BUILD_CONST_KEY_MAP,
|
||||
&&TARGET_BUILD_STRING,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue