mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
Issue #26647: Python interpreter now uses 16-bit wordcode instead of bytecode.
Patch by Demur Rumed.
This commit is contained in:
parent
c35f491a06
commit
b0f80b0312
18 changed files with 4747 additions and 5022 deletions
129
Python/ceval.c
129
Python/ceval.c
|
@ -886,24 +886,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
/* Import the static jump table */
|
||||
#include "opcode_targets.h"
|
||||
|
||||
/* This macro is used when several opcodes defer to the same implementation
|
||||
(e.g. SETUP_LOOP, SETUP_FINALLY) */
|
||||
#define TARGET_WITH_IMPL(op, impl) \
|
||||
TARGET_##op: \
|
||||
opcode = op; \
|
||||
if (HAS_ARG(op)) \
|
||||
oparg = NEXTARG(); \
|
||||
case op: \
|
||||
goto impl; \
|
||||
|
||||
#define TARGET(op) \
|
||||
TARGET_##op: \
|
||||
opcode = op; \
|
||||
if (HAS_ARG(op)) \
|
||||
oparg = NEXTARG(); \
|
||||
case op:
|
||||
|
||||
|
||||
#define DISPATCH() \
|
||||
{ \
|
||||
if (!_Py_atomic_load_relaxed(&eval_breaker)) { \
|
||||
|
@ -917,7 +903,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
{ \
|
||||
if (!lltrace && !_Py_TracingPossible) { \
|
||||
f->f_lasti = INSTR_OFFSET(); \
|
||||
goto *opcode_targets[*next_instr++]; \
|
||||
opcode = NEXTOP(); \
|
||||
oparg = NEXTARG(); \
|
||||
goto *opcode_targets[opcode]; \
|
||||
} \
|
||||
goto fast_next_opcode; \
|
||||
}
|
||||
|
@ -926,7 +914,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
{ \
|
||||
if (!_Py_TracingPossible) { \
|
||||
f->f_lasti = INSTR_OFFSET(); \
|
||||
goto *opcode_targets[*next_instr++]; \
|
||||
opcode = NEXTOP(); \
|
||||
oparg = NEXTARG(); \
|
||||
goto *opcode_targets[opcode]; \
|
||||
} \
|
||||
goto fast_next_opcode; \
|
||||
}
|
||||
|
@ -935,10 +925,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
#else
|
||||
#define TARGET(op) \
|
||||
case op:
|
||||
#define TARGET_WITH_IMPL(op, impl) \
|
||||
/* silence compiler warnings about `impl` unused */ \
|
||||
if (0) goto impl; \
|
||||
case op:
|
||||
|
||||
#define DISPATCH() continue
|
||||
#define FAST_DISPATCH() goto fast_next_opcode
|
||||
#endif
|
||||
|
@ -995,9 +982,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
/* Code access macros */
|
||||
|
||||
#define INSTR_OFFSET() ((int)(next_instr - first_instr))
|
||||
#define NEXTOP() (*next_instr++)
|
||||
#define NEXTARG() (next_instr += 2, (next_instr[-1]<<8) + next_instr[-2])
|
||||
#define PEEKARG() ((next_instr[2]<<8) + next_instr[1])
|
||||
#define NEXTOP() (next_instr+=2, next_instr[-2])
|
||||
#define NEXTARG() (next_instr[-1])
|
||||
#define PEEKARG() (next_instr[1])
|
||||
#define JUMPTO(x) (next_instr = first_instr + (x))
|
||||
#define JUMPBY(x) (next_instr += (x))
|
||||
|
||||
|
@ -1012,10 +999,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
processor's own internal branch predication has a high likelihood of
|
||||
success, resulting in a nearly zero-overhead transition to the
|
||||
next opcode. A successful prediction saves a trip through the eval-loop
|
||||
including its two unpredictable branches, the HAS_ARG test and the
|
||||
switch-case. Combined with the processor's internal branch prediction,
|
||||
a successful PREDICT has the effect of making the two opcodes run as if
|
||||
they were a single new opcode with the bodies combined.
|
||||
including its unpredictable switch-case branch. Combined with the
|
||||
processor's internal branch prediction, a successful PREDICT has the
|
||||
effect of making the two opcodes run as if they were a single new opcode
|
||||
with the bodies combined.
|
||||
|
||||
If collecting opcode statistics, your choices are to either keep the
|
||||
predictions turned-on and interpret the results as if some opcodes
|
||||
|
@ -1030,13 +1017,18 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
|
||||
#if defined(DYNAMIC_EXECUTION_PROFILE) || USE_COMPUTED_GOTOS
|
||||
#define PREDICT(op) if (0) goto PRED_##op
|
||||
#define PREDICTED(op) PRED_##op:
|
||||
#define PREDICTED_WITH_ARG(op) PRED_##op:
|
||||
#else
|
||||
#define PREDICT(op) if (*next_instr == op) goto PRED_##op
|
||||
#define PREDICTED(op) PRED_##op: next_instr++
|
||||
#define PREDICTED_WITH_ARG(op) PRED_##op: oparg = PEEKARG(); next_instr += 3
|
||||
#define PREDICT(op) \
|
||||
do{ \
|
||||
if (*next_instr == op){ \
|
||||
opcode = op; \
|
||||
oparg = PEEKARG(); \
|
||||
next_instr += 2; \
|
||||
goto PRED_##op; \
|
||||
} \
|
||||
} while(0)
|
||||
#endif
|
||||
#define PREDICTED(op) PRED_##op:
|
||||
|
||||
|
||||
/* Stack manipulation macros */
|
||||
|
@ -1100,7 +1092,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
}
|
||||
|
||||
#define UNWIND_EXCEPT_HANDLER(b) \
|
||||
{ \
|
||||
do { \
|
||||
PyObject *type, *value, *traceback; \
|
||||
assert(STACK_LEVEL() >= (b)->b_level + 3); \
|
||||
while (STACK_LEVEL() > (b)->b_level + 3) { \
|
||||
|
@ -1116,7 +1108,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
Py_XDECREF(type); \
|
||||
Py_XDECREF(value); \
|
||||
Py_XDECREF(traceback); \
|
||||
}
|
||||
} while(0)
|
||||
|
||||
/* Start of code */
|
||||
|
||||
|
@ -1166,15 +1158,11 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
fastlocals = f->f_localsplus;
|
||||
freevars = f->f_localsplus + co->co_nlocals;
|
||||
first_instr = (unsigned char*) PyBytes_AS_STRING(co->co_code);
|
||||
/* An explanation is in order for the next line.
|
||||
/*
|
||||
f->f_lasti refers to the index of the last instruction,
|
||||
unless it's -1 in which case next_instr should be first_instr.
|
||||
|
||||
f->f_lasti now refers to the index of the last instruction
|
||||
executed. You might think this was obvious from the name, but
|
||||
this wasn't always true before 2.3! PyFrame_New now sets
|
||||
f->f_lasti to -1 (i.e. the index *before* the first instruction)
|
||||
and YIELD_VALUE doesn't fiddle with f_lasti any more. So this
|
||||
does work. Promise.
|
||||
YIELD_FROM sets f_lasti to itself, in order to repeated yield
|
||||
YIELD_FROM sets f_lasti to itself, in order to repeatedly yield
|
||||
multiple values.
|
||||
|
||||
When the PREDICT() macros are enabled, some opcode pairs follow in
|
||||
|
@ -1183,9 +1171,12 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
were a single new opcode; accordingly,f->f_lasti will point to
|
||||
the first code in the pair (for instance, GET_ITER followed by
|
||||
FOR_ITER is effectively a single opcode and f->f_lasti will point
|
||||
at to the beginning of the combined pair.)
|
||||
to the beginning of the combined pair.)
|
||||
*/
|
||||
next_instr = first_instr + f->f_lasti + 1;
|
||||
next_instr = first_instr;
|
||||
if (f->f_lasti >= 0) {
|
||||
next_instr += f->f_lasti + 2;
|
||||
}
|
||||
stack_pointer = f->f_stacktop;
|
||||
assert(stack_pointer != NULL);
|
||||
f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */
|
||||
|
@ -1323,10 +1314,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
/* Extract opcode and argument */
|
||||
|
||||
opcode = NEXTOP();
|
||||
oparg = 0; /* allows oparg to be stored in a register because
|
||||
it doesn't have to be remembered across a full loop */
|
||||
if (HAS_ARG(opcode))
|
||||
oparg = NEXTARG();
|
||||
oparg = NEXTARG();
|
||||
dispatch_opcode:
|
||||
#ifdef DYNAMIC_EXECUTION_PROFILE
|
||||
#ifdef DXPAIRS
|
||||
|
@ -1384,7 +1372,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
FAST_DISPATCH();
|
||||
}
|
||||
|
||||
PREDICTED_WITH_ARG(STORE_FAST);
|
||||
PREDICTED(STORE_FAST);
|
||||
TARGET(STORE_FAST) {
|
||||
PyObject *value = POP();
|
||||
SETLOCAL(oparg, value);
|
||||
|
@ -2075,7 +2063,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
f->f_stacktop = stack_pointer;
|
||||
why = WHY_YIELD;
|
||||
/* and repeat... */
|
||||
f->f_lasti--;
|
||||
f->f_lasti -= 2;
|
||||
goto fast_yield;
|
||||
}
|
||||
|
||||
|
@ -2213,7 +2201,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
PREDICTED_WITH_ARG(UNPACK_SEQUENCE);
|
||||
PREDICTED(UNPACK_SEQUENCE);
|
||||
TARGET(UNPACK_SEQUENCE) {
|
||||
PyObject *seq = POP(), *item, **items;
|
||||
if (PyTuple_CheckExact(seq) &&
|
||||
|
@ -2511,9 +2499,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET_WITH_IMPL(BUILD_TUPLE_UNPACK, _build_list_unpack)
|
||||
TARGET(BUILD_LIST_UNPACK)
|
||||
_build_list_unpack: {
|
||||
TARGET(BUILD_TUPLE_UNPACK)
|
||||
TARGET(BUILD_LIST_UNPACK) {
|
||||
int convert_to_tuple = opcode == BUILD_TUPLE_UNPACK;
|
||||
int i;
|
||||
PyObject *sum = PyList_New(0);
|
||||
|
@ -2610,9 +2597,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET_WITH_IMPL(BUILD_MAP_UNPACK_WITH_CALL, _build_map_unpack)
|
||||
TARGET(BUILD_MAP_UNPACK)
|
||||
_build_map_unpack: {
|
||||
TARGET(BUILD_MAP_UNPACK_WITH_CALL)
|
||||
TARGET(BUILD_MAP_UNPACK) {
|
||||
int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL;
|
||||
int num_maps;
|
||||
int function_location;
|
||||
|
@ -2819,7 +2805,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
FAST_DISPATCH();
|
||||
}
|
||||
|
||||
PREDICTED_WITH_ARG(POP_JUMP_IF_FALSE);
|
||||
PREDICTED(POP_JUMP_IF_FALSE);
|
||||
TARGET(POP_JUMP_IF_FALSE) {
|
||||
PyObject *cond = POP();
|
||||
int err;
|
||||
|
@ -2843,7 +2829,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
PREDICTED_WITH_ARG(POP_JUMP_IF_TRUE);
|
||||
PREDICTED(POP_JUMP_IF_TRUE);
|
||||
TARGET(POP_JUMP_IF_TRUE) {
|
||||
PyObject *cond = POP();
|
||||
int err;
|
||||
|
@ -2920,7 +2906,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
PREDICTED_WITH_ARG(JUMP_ABSOLUTE);
|
||||
PREDICTED(JUMP_ABSOLUTE);
|
||||
TARGET(JUMP_ABSOLUTE) {
|
||||
JUMPTO(oparg);
|
||||
#if FAST_LOOPS
|
||||
|
@ -2977,7 +2963,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
PREDICTED_WITH_ARG(FOR_ITER);
|
||||
PREDICTED(FOR_ITER);
|
||||
TARGET(FOR_ITER) {
|
||||
/* before: [iter]; after: [iter, iter()] *or* [] */
|
||||
PyObject *iter = TOP();
|
||||
|
@ -3015,10 +3001,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
goto fast_block_end;
|
||||
}
|
||||
|
||||
TARGET_WITH_IMPL(SETUP_LOOP, _setup_finally)
|
||||
TARGET_WITH_IMPL(SETUP_EXCEPT, _setup_finally)
|
||||
TARGET(SETUP_FINALLY)
|
||||
_setup_finally: {
|
||||
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.
|
||||
|
@ -3213,10 +3198,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET_WITH_IMPL(CALL_FUNCTION_VAR, _call_function_var_kw)
|
||||
TARGET_WITH_IMPL(CALL_FUNCTION_KW, _call_function_var_kw)
|
||||
TARGET(CALL_FUNCTION_VAR_KW)
|
||||
_call_function_var_kw: {
|
||||
TARGET(CALL_FUNCTION_VAR)
|
||||
TARGET(CALL_FUNCTION_KW)
|
||||
TARGET(CALL_FUNCTION_VAR_KW) {
|
||||
int na = oparg & 0xff;
|
||||
int nk = (oparg>>8) & 0xff;
|
||||
int flags = (opcode - CALL_FUNCTION) & 3;
|
||||
|
@ -3258,9 +3242,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET_WITH_IMPL(MAKE_CLOSURE, _make_function)
|
||||
TARGET(MAKE_FUNCTION)
|
||||
_make_function: {
|
||||
TARGET(MAKE_CLOSURE)
|
||||
TARGET(MAKE_FUNCTION) {
|
||||
int posdefaults = oparg & 0xff;
|
||||
int kwdefaults = (oparg>>8) & 0xff;
|
||||
int num_annotations = (oparg >> 16) & 0x7fff;
|
||||
|
@ -3450,7 +3433,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
|
||||
TARGET(EXTENDED_ARG) {
|
||||
opcode = NEXTOP();
|
||||
oparg = oparg<<16 | NEXTARG();
|
||||
oparg = oparg<<8 | NEXTARG();
|
||||
goto dispatch_opcode;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "code.h"
|
||||
#include "symtable.h"
|
||||
#include "opcode.h"
|
||||
#include "wordcode_helpers.h"
|
||||
|
||||
#define DEFAULT_BLOCK_SIZE 16
|
||||
#define DEFAULT_BLOCKS 8
|
||||
|
@ -43,7 +44,6 @@
|
|||
struct instr {
|
||||
unsigned i_jabs : 1;
|
||||
unsigned i_jrel : 1;
|
||||
unsigned i_hasarg : 1;
|
||||
unsigned char i_opcode;
|
||||
int i_oparg;
|
||||
struct basicblock_ *i_target; /* target block (if jump instruction) */
|
||||
|
@ -1080,13 +1080,14 @@ compiler_addop(struct compiler *c, int opcode)
|
|||
basicblock *b;
|
||||
struct instr *i;
|
||||
int off;
|
||||
assert(!HAS_ARG(opcode));
|
||||
off = compiler_next_instr(c, c->u->u_curblock);
|
||||
if (off < 0)
|
||||
return 0;
|
||||
b = c->u->u_curblock;
|
||||
i = &b->b_instr[off];
|
||||
i->i_opcode = opcode;
|
||||
i->i_hasarg = 0;
|
||||
i->i_oparg = 0;
|
||||
if (opcode == RETURN_VALUE)
|
||||
b->b_return = 1;
|
||||
compiler_set_lineno(c, off);
|
||||
|
@ -1168,8 +1169,9 @@ compiler_addop_i(struct compiler *c, int opcode, Py_ssize_t oparg)
|
|||
|
||||
Limit to 32-bit signed C int (rather than INT_MAX) for portability.
|
||||
|
||||
The argument of a concrete bytecode instruction is limited to 16-bit.
|
||||
EXTENDED_ARG is used for 32-bit arguments. */
|
||||
The argument of a concrete bytecode instruction is limited to 8-bit.
|
||||
EXTENDED_ARG is used for 16, 24, and 32-bit arguments. */
|
||||
assert(HAS_ARG(opcode));
|
||||
assert(0 <= oparg && oparg <= 2147483647);
|
||||
|
||||
off = compiler_next_instr(c, c->u->u_curblock);
|
||||
|
@ -1178,7 +1180,6 @@ compiler_addop_i(struct compiler *c, int opcode, Py_ssize_t oparg)
|
|||
i = &c->u->u_curblock->b_instr[off];
|
||||
i->i_opcode = opcode;
|
||||
i->i_oparg = Py_SAFE_DOWNCAST(oparg, Py_ssize_t, int);
|
||||
i->i_hasarg = 1;
|
||||
compiler_set_lineno(c, off);
|
||||
return 1;
|
||||
}
|
||||
|
@ -1189,6 +1190,7 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute)
|
|||
struct instr *i;
|
||||
int off;
|
||||
|
||||
assert(HAS_ARG(opcode));
|
||||
assert(b != NULL);
|
||||
off = compiler_next_instr(c, c->u->u_curblock);
|
||||
if (off < 0)
|
||||
|
@ -1196,7 +1198,6 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute)
|
|||
i = &c->u->u_curblock->b_instr[off];
|
||||
i->i_opcode = opcode;
|
||||
i->i_target = b;
|
||||
i->i_hasarg = 1;
|
||||
if (absolute)
|
||||
i->i_jabs = 1;
|
||||
else
|
||||
|
@ -4397,18 +4398,6 @@ assemble_free(struct assembler *a)
|
|||
PyObject_Free(a->a_postorder);
|
||||
}
|
||||
|
||||
/* Return the size of a basic block in bytes. */
|
||||
|
||||
static int
|
||||
instrsize(struct instr *instr)
|
||||
{
|
||||
if (!instr->i_hasarg)
|
||||
return 1; /* 1 byte for the opcode*/
|
||||
if (instr->i_oparg > 0xffff)
|
||||
return 6; /* 1 (opcode) + 1 (EXTENDED_ARG opcode) + 2 (oparg) + 2(oparg extended) */
|
||||
return 3; /* 1 (opcode) + 2 (oparg) */
|
||||
}
|
||||
|
||||
static int
|
||||
blocksize(basicblock *b)
|
||||
{
|
||||
|
@ -4416,7 +4405,7 @@ blocksize(basicblock *b)
|
|||
int size = 0;
|
||||
|
||||
for (i = 0; i < b->b_iused; i++)
|
||||
size += instrsize(&b->b_instr[i]);
|
||||
size += instrsize(b->b_instr[i].i_oparg);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
@ -4536,15 +4525,12 @@ assemble_lnotab(struct assembler *a, struct instr *i)
|
|||
static int
|
||||
assemble_emit(struct assembler *a, struct instr *i)
|
||||
{
|
||||
int size, arg = 0, ext = 0;
|
||||
int size, arg = 0;
|
||||
Py_ssize_t len = PyBytes_GET_SIZE(a->a_bytecode);
|
||||
char *code;
|
||||
|
||||
size = instrsize(i);
|
||||
if (i->i_hasarg) {
|
||||
arg = i->i_oparg;
|
||||
ext = arg >> 16;
|
||||
}
|
||||
arg = i->i_oparg;
|
||||
size = instrsize(arg);
|
||||
if (i->i_lineno && !assemble_lnotab(a, i))
|
||||
return 0;
|
||||
if (a->a_offset + size >= len) {
|
||||
|
@ -4555,19 +4541,7 @@ assemble_emit(struct assembler *a, struct instr *i)
|
|||
}
|
||||
code = PyBytes_AS_STRING(a->a_bytecode) + a->a_offset;
|
||||
a->a_offset += size;
|
||||
if (size == 6) {
|
||||
assert(i->i_hasarg);
|
||||
*code++ = (char)EXTENDED_ARG;
|
||||
*code++ = ext & 0xff;
|
||||
*code++ = ext >> 8;
|
||||
arg &= 0xffff;
|
||||
}
|
||||
*code++ = i->i_opcode;
|
||||
if (i->i_hasarg) {
|
||||
assert(size == 3 || size == 6);
|
||||
*code++ = arg & 0xff;
|
||||
*code++ = arg >> 8;
|
||||
}
|
||||
write_op_arg((unsigned char*)code, i->i_opcode, arg, size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -4575,7 +4549,7 @@ static void
|
|||
assemble_jump_offsets(struct assembler *a, struct compiler *c)
|
||||
{
|
||||
basicblock *b;
|
||||
int bsize, totsize, extended_arg_count = 0, last_extended_arg_count;
|
||||
int bsize, totsize, extended_arg_recompile;
|
||||
int i;
|
||||
|
||||
/* Compute the size of each block and fixup jump args.
|
||||
|
@ -4588,27 +4562,26 @@ assemble_jump_offsets(struct assembler *a, struct compiler *c)
|
|||
b->b_offset = totsize;
|
||||
totsize += bsize;
|
||||
}
|
||||
last_extended_arg_count = extended_arg_count;
|
||||
extended_arg_count = 0;
|
||||
extended_arg_recompile = 0;
|
||||
for (b = c->u->u_blocks; b != NULL; b = b->b_list) {
|
||||
bsize = b->b_offset;
|
||||
for (i = 0; i < b->b_iused; i++) {
|
||||
struct instr *instr = &b->b_instr[i];
|
||||
int isize = instrsize(instr->i_oparg);
|
||||
/* Relative jumps are computed relative to
|
||||
the instruction pointer after fetching
|
||||
the jump instruction.
|
||||
*/
|
||||
bsize += instrsize(instr);
|
||||
if (instr->i_jabs)
|
||||
bsize += isize;
|
||||
if (instr->i_jabs || instr->i_jrel) {
|
||||
instr->i_oparg = instr->i_target->b_offset;
|
||||
else if (instr->i_jrel) {
|
||||
int delta = instr->i_target->b_offset - bsize;
|
||||
instr->i_oparg = delta;
|
||||
if (instr->i_jrel) {
|
||||
instr->i_oparg -= bsize;
|
||||
}
|
||||
if (instrsize(instr->i_oparg) != isize) {
|
||||
extended_arg_recompile = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
continue;
|
||||
if (instr->i_oparg > 0xffff)
|
||||
extended_arg_count++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4618,7 +4591,7 @@ assemble_jump_offsets(struct assembler *a, struct compiler *c)
|
|||
|
||||
The issue is that in the first loop blocksize() is called
|
||||
which calls instrsize() which requires i_oparg be set
|
||||
appropriately. There is a bootstrap problem because
|
||||
appropriately. There is a bootstrap problem because
|
||||
i_oparg is calculated in the second loop above.
|
||||
|
||||
So we loop until we stop seeing new EXTENDED_ARGs.
|
||||
|
@ -4626,7 +4599,7 @@ assemble_jump_offsets(struct assembler *a, struct compiler *c)
|
|||
ones in jump instructions. So this should converge
|
||||
fairly quickly.
|
||||
*/
|
||||
} while (last_extended_arg_count != extended_arg_count);
|
||||
} while (extended_arg_recompile);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
|
@ -4772,9 +4745,9 @@ dump_instr(const struct instr *i)
|
|||
char arg[128];
|
||||
|
||||
*arg = '\0';
|
||||
if (i->i_hasarg)
|
||||
if (HAS_ARG(i->i_opcode)) {
|
||||
sprintf(arg, "arg: %d ", i->i_oparg);
|
||||
|
||||
}
|
||||
fprintf(stderr, "line: %d, opcode: %d %s%s%s\n",
|
||||
i->i_lineno, i->i_opcode, arg, jabs, jrel);
|
||||
}
|
||||
|
|
|
@ -14,17 +14,15 @@
|
|||
the appropriate bytes from M___main__.c. */
|
||||
|
||||
static unsigned char M___hello__[] = {
|
||||
99,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,
|
||||
0,64,0,0,0,115,20,0,0,0,100,2,0,90,1,0,
|
||||
101,2,0,100,0,0,131,1,0,1,100,1,0,83,40,3,
|
||||
0,0,0,117,12,0,0,0,72,101,108,108,111,32,119,111,
|
||||
114,108,100,33,78,84,40,3,0,0,0,117,4,0,0,0,
|
||||
84,114,117,101,117,11,0,0,0,105,110,105,116,105,97,108,
|
||||
105,122,101,100,117,5,0,0,0,112,114,105,110,116,40,0,
|
||||
0,0,0,40,0,0,0,0,40,0,0,0,0,117,7,0,
|
||||
0,0,102,108,97,103,46,112,121,117,8,0,0,0,60,109,
|
||||
111,100,117,108,101,62,1,0,0,0,115,2,0,0,0,6,
|
||||
1,
|
||||
227,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,
|
||||
0,64,0,0,0,115,16,0,0,0,100,0,90,0,101,1,
|
||||
100,1,131,1,1,0,100,2,83,0,41,3,84,122,12,72,
|
||||
101,108,108,111,32,119,111,114,108,100,33,78,41,2,218,11,
|
||||
105,110,105,116,105,97,108,105,122,101,100,218,5,112,114,105,
|
||||
110,116,169,0,114,3,0,0,0,114,3,0,0,0,250,22,
|
||||
46,47,84,111,111,108,115,47,102,114,101,101,122,101,47,102,
|
||||
108,97,103,46,112,121,218,8,60,109,111,100,117,108,101,62,
|
||||
1,0,0,0,115,2,0,0,0,4,1,
|
||||
};
|
||||
|
||||
#define SIZE (int)sizeof(M___hello__)
|
||||
|
|
3723
Python/importlib.h
3723
Python/importlib.h
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -8,8 +8,8 @@
|
|||
#include "code.h"
|
||||
#include "symtable.h"
|
||||
#include "opcode.h"
|
||||
#include "wordcode_helpers.h"
|
||||
|
||||
#define GETARG(arr, i) ((int)((arr[i+2]<<8) + arr[i+1]))
|
||||
#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)
|
||||
|
@ -17,22 +17,15 @@
|
|||
|| 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)
|
||||
#define GETJUMPTGT(arr, i) (GETARG(arr,i) + (ABSOLUTE_JUMP(arr[i]) ? 0 : i+3))
|
||||
#define SETARG(arr, i, val) do { \
|
||||
assert(0 <= val && val <= 0xffff); \
|
||||
arr[i+2] = (unsigned char)(((unsigned int)val)>>8); \
|
||||
arr[i+1] = (unsigned char)(((unsigned int)val) & 255); \
|
||||
} while(0)
|
||||
#define CODESIZE(op) (HAS_ARG(op) ? 3 : 1)
|
||||
#define ISBASICBLOCK(blocks, start, bytes) \
|
||||
(blocks[start]==blocks[start+bytes-1])
|
||||
#define GETJUMPTGT(arr, i) (get_arg(arr, i) + (ABSOLUTE_JUMP(arr[i]) ? 0 : i+2))
|
||||
#define ISBASICBLOCK(blocks, start, end) \
|
||||
(blocks[start]==blocks[end])
|
||||
|
||||
|
||||
#define CONST_STACK_CREATE() { \
|
||||
const_stack_size = 256; \
|
||||
const_stack = PyMem_New(PyObject *, const_stack_size); \
|
||||
load_const_stack = PyMem_New(Py_ssize_t, const_stack_size); \
|
||||
if (!const_stack || !load_const_stack) { \
|
||||
if (!const_stack) { \
|
||||
PyErr_NoMemory(); \
|
||||
goto exitError; \
|
||||
} \
|
||||
|
@ -41,27 +34,23 @@
|
|||
#define CONST_STACK_DELETE() do { \
|
||||
if (const_stack) \
|
||||
PyMem_Free(const_stack); \
|
||||
if (load_const_stack) \
|
||||
PyMem_Free(load_const_stack); \
|
||||
} while(0)
|
||||
|
||||
#define CONST_STACK_LEN() (const_stack_top + 1)
|
||||
#define CONST_STACK_LEN() ((unsigned)(const_stack_top + 1))
|
||||
|
||||
#define CONST_STACK_PUSH_OP(i) do { \
|
||||
PyObject *_x; \
|
||||
assert(codestr[i] == LOAD_CONST); \
|
||||
assert(PyList_GET_SIZE(consts) > GETARG(codestr, i)); \
|
||||
_x = PyList_GET_ITEM(consts, GETARG(codestr, i)); \
|
||||
assert(PyList_GET_SIZE(consts) > (Py_ssize_t)get_arg(codestr, i)); \
|
||||
_x = PyList_GET_ITEM(consts, get_arg(codestr, i)); \
|
||||
if (++const_stack_top >= const_stack_size) { \
|
||||
const_stack_size *= 2; \
|
||||
PyMem_Resize(const_stack, PyObject *, const_stack_size); \
|
||||
PyMem_Resize(load_const_stack, Py_ssize_t, const_stack_size); \
|
||||
if (!const_stack || !load_const_stack) { \
|
||||
if (!const_stack) { \
|
||||
PyErr_NoMemory(); \
|
||||
goto exitError; \
|
||||
} \
|
||||
} \
|
||||
load_const_stack[const_stack_top] = i; \
|
||||
const_stack[const_stack_top] = _x; \
|
||||
in_consts = 1; \
|
||||
} while(0)
|
||||
|
@ -70,22 +59,108 @@
|
|||
const_stack_top = -1; \
|
||||
} while(0)
|
||||
|
||||
#define CONST_STACK_TOP() \
|
||||
const_stack[const_stack_top]
|
||||
|
||||
#define CONST_STACK_LASTN(i) \
|
||||
&const_stack[const_stack_top - i + 1]
|
||||
&const_stack[CONST_STACK_LEN() - i]
|
||||
|
||||
#define CONST_STACK_POP(i) do { \
|
||||
assert(const_stack_top + 1 >= i); \
|
||||
assert(CONST_STACK_LEN() >= i); \
|
||||
const_stack_top -= i; \
|
||||
} while(0)
|
||||
|
||||
#define CONST_STACK_OP_LASTN(i) \
|
||||
((const_stack_top >= i - 1) ? load_const_stack[const_stack_top - i + 1] : -1)
|
||||
/* Scans back N consecutive LOAD_CONST instructions, skipping NOPs,
|
||||
returns index of the Nth last's LOAD_CONST's EXTENDED_ARG prefix.
|
||||
Callers are responsible to check CONST_STACK_LEN beforehand.
|
||||
*/
|
||||
static Py_ssize_t
|
||||
lastn_const_start(unsigned char *codestr, Py_ssize_t i, Py_ssize_t n)
|
||||
{
|
||||
assert(n > 0 && (i&1) == 0);
|
||||
for (;;) {
|
||||
i -= 2;
|
||||
assert(i >= 0);
|
||||
if (codestr[i] == LOAD_CONST) {
|
||||
if (!--n) {
|
||||
while (i > 0 && codestr[i-2] == EXTENDED_ARG) {
|
||||
i -= 2;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(codestr[i] == NOP || codestr[i] == EXTENDED_ARG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Scans through EXTENDED ARGs, seeking the index of the effective opcode */
|
||||
static Py_ssize_t
|
||||
find_op(unsigned char *codestr, Py_ssize_t i)
|
||||
{
|
||||
assert((i&1) == 0);
|
||||
while (codestr[i] == EXTENDED_ARG) {
|
||||
i += 2;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Replace LOAD_CONST c1. LOAD_CONST c2 ... LOAD_CONST cn BUILD_TUPLE n
|
||||
/* Given the index of the effective opcode,
|
||||
scan back to construct the oparg with EXTENDED_ARG */
|
||||
static unsigned int
|
||||
get_arg(unsigned char *codestr, Py_ssize_t i)
|
||||
{
|
||||
unsigned int oparg = codestr[i+1];
|
||||
assert((i&1) == 0);
|
||||
if (i >= 2 && codestr[i-2] == EXTENDED_ARG) {
|
||||
oparg |= codestr[i-1] << 8;
|
||||
if (i >= 4 && codestr[i-4] == EXTENDED_ARG) {
|
||||
oparg |= codestr[i-3] << 16;
|
||||
if (i >= 6 && codestr[i-6] == EXTENDED_ARG) {
|
||||
oparg |= codestr[i-5] << 24;
|
||||
}
|
||||
}
|
||||
}
|
||||
return oparg;
|
||||
}
|
||||
|
||||
/* Given the index of the effective opcode,
|
||||
attempt to replace the argument, taking into account EXTENDED_ARG.
|
||||
Returns -1 on failure, or the new op index on success */
|
||||
static Py_ssize_t
|
||||
set_arg(unsigned char *codestr, Py_ssize_t i, unsigned int oparg)
|
||||
{
|
||||
unsigned int curarg = get_arg(codestr, i);
|
||||
int curilen, newilen;
|
||||
if (curarg == oparg)
|
||||
return i;
|
||||
curilen = instrsize(curarg);
|
||||
newilen = instrsize(oparg);
|
||||
if (curilen < newilen) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_op_arg(codestr + i + 2 - curilen, codestr[i], oparg, newilen);
|
||||
memset(codestr + i + 2 - curilen + newilen, NOP, curilen - newilen);
|
||||
return i-curilen+newilen;
|
||||
}
|
||||
|
||||
/* Attempt to write op/arg at end of specified region of memory.
|
||||
Preceding memory in the region is overwritten with NOPs.
|
||||
Returns -1 on failure, op index on success */
|
||||
static Py_ssize_t
|
||||
copy_op_arg(unsigned char *codestr, Py_ssize_t i, unsigned char op,
|
||||
unsigned int oparg, Py_ssize_t maxi)
|
||||
{
|
||||
int ilen = instrsize(oparg);
|
||||
assert((i&1) == 0);
|
||||
if (i + ilen > maxi) {
|
||||
return -1;
|
||||
}
|
||||
write_op_arg(codestr + maxi - ilen, op, oparg, ilen);
|
||||
memset(codestr + i, NOP, maxi - i - ilen);
|
||||
return maxi - 2;
|
||||
}
|
||||
|
||||
/* Replace LOAD_CONST c1, LOAD_CONST c2 ... LOAD_CONST cn, BUILD_TUPLE n
|
||||
with LOAD_CONST (c1, c2, ... cn).
|
||||
The consts table must still be in list form so that the
|
||||
new constant (c1, c2, ... cn) can be appended.
|
||||
|
@ -94,9 +169,10 @@
|
|||
Also works for BUILD_LIST and BUILT_SET when followed by an "in" or "not in"
|
||||
test; for BUILD_SET it assembles a frozenset rather than a tuple.
|
||||
*/
|
||||
static int
|
||||
tuple_of_constants(unsigned char *codestr, Py_ssize_t n,
|
||||
PyObject *consts, PyObject **objs)
|
||||
static Py_ssize_t
|
||||
fold_tuple_on_constants(unsigned char *codestr, Py_ssize_t c_start,
|
||||
Py_ssize_t opcode_end, unsigned char opcode,
|
||||
PyObject *consts, PyObject **objs, int n)
|
||||
{
|
||||
PyObject *newconst, *constant;
|
||||
Py_ssize_t i, len_consts;
|
||||
|
@ -106,9 +182,9 @@ tuple_of_constants(unsigned char *codestr, Py_ssize_t n,
|
|||
|
||||
/* Buildup new tuple of constants */
|
||||
newconst = PyTuple_New(n);
|
||||
if (newconst == NULL)
|
||||
return 0;
|
||||
len_consts = PyList_GET_SIZE(consts);
|
||||
if (newconst == NULL) {
|
||||
return -1;
|
||||
}
|
||||
for (i=0 ; i<n ; i++) {
|
||||
constant = objs[i];
|
||||
Py_INCREF(constant);
|
||||
|
@ -116,28 +192,26 @@ tuple_of_constants(unsigned char *codestr, Py_ssize_t n,
|
|||
}
|
||||
|
||||
/* If it's a BUILD_SET, use the PyTuple we just built to create a
|
||||
PyFrozenSet, and use that as the constant instead: */
|
||||
if (codestr[0] == BUILD_SET) {
|
||||
Py_XSETREF(newconst, PyFrozenSet_New(newconst));
|
||||
if (newconst == NULL)
|
||||
return 0;
|
||||
PyFrozenSet, and use that as the constant instead: */
|
||||
if (opcode == BUILD_SET) {
|
||||
Py_SETREF(newconst, PyFrozenSet_New(newconst));
|
||||
if (newconst == NULL) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Append folded constant onto consts */
|
||||
len_consts = PyList_GET_SIZE(consts);
|
||||
if (PyList_Append(consts, newconst)) {
|
||||
Py_DECREF(newconst);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
Py_DECREF(newconst);
|
||||
|
||||
/* Write NOPs over old LOAD_CONSTS and
|
||||
add a new LOAD_CONST newconst on top of the BUILD_TUPLE n */
|
||||
codestr[0] = LOAD_CONST;
|
||||
SETARG(codestr, 0, len_consts);
|
||||
return 1;
|
||||
return copy_op_arg(codestr, c_start, LOAD_CONST, len_consts, opcode_end);
|
||||
}
|
||||
|
||||
/* Replace LOAD_CONST c1. LOAD_CONST c2 BINOP
|
||||
/* Replace LOAD_CONST c1, LOAD_CONST c2, BINOP
|
||||
with LOAD_CONST binop(c1,c2)
|
||||
The consts table must still be in list form so that the
|
||||
new constant can be appended.
|
||||
|
@ -147,20 +221,21 @@ tuple_of_constants(unsigned char *codestr, Py_ssize_t n,
|
|||
is below a threshold value. That keeps pyc files from
|
||||
becoming large in the presence of code like: (None,)*1000.
|
||||
*/
|
||||
static int
|
||||
fold_binops_on_constants(unsigned char *codestr, PyObject *consts, PyObject **objs)
|
||||
static Py_ssize_t
|
||||
fold_binops_on_constants(unsigned char *codestr, Py_ssize_t c_start,
|
||||
Py_ssize_t opcode_end, unsigned char opcode,
|
||||
PyObject *consts, PyObject **objs)
|
||||
{
|
||||
PyObject *newconst, *v, *w;
|
||||
Py_ssize_t len_consts, size;
|
||||
int opcode;
|
||||
|
||||
/* Pre-conditions */
|
||||
assert(PyList_CheckExact(consts));
|
||||
len_consts = PyList_GET_SIZE(consts);
|
||||
|
||||
/* Create new constant */
|
||||
v = objs[0];
|
||||
w = objs[1];
|
||||
opcode = codestr[0];
|
||||
switch (opcode) {
|
||||
case BINARY_POWER:
|
||||
newconst = PyNumber_Power(v, w, Py_None);
|
||||
|
@ -206,50 +281,48 @@ fold_binops_on_constants(unsigned char *codestr, PyObject *consts, PyObject **ob
|
|||
PyErr_Format(PyExc_SystemError,
|
||||
"unexpected binary operation %d on a constant",
|
||||
opcode);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
if (newconst == NULL) {
|
||||
if(!PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
|
||||
if(!PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) {
|
||||
PyErr_Clear();
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
size = PyObject_Size(newconst);
|
||||
if (size == -1) {
|
||||
if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
|
||||
return 0;
|
||||
if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) {
|
||||
return -1;
|
||||
}
|
||||
PyErr_Clear();
|
||||
} else if (size > 20) {
|
||||
Py_DECREF(newconst);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Append folded constant into consts table */
|
||||
len_consts = PyList_GET_SIZE(consts);
|
||||
if (PyList_Append(consts, newconst)) {
|
||||
Py_DECREF(newconst);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
Py_DECREF(newconst);
|
||||
|
||||
/* Write NOP NOP NOP NOP LOAD_CONST newconst */
|
||||
codestr[-2] = LOAD_CONST;
|
||||
SETARG(codestr, -2, len_consts);
|
||||
return 1;
|
||||
return copy_op_arg(codestr, c_start, LOAD_CONST, len_consts, opcode_end);
|
||||
}
|
||||
|
||||
static int
|
||||
fold_unaryops_on_constants(unsigned char *codestr, PyObject *consts, PyObject *v)
|
||||
static Py_ssize_t
|
||||
fold_unaryops_on_constants(unsigned char *codestr, Py_ssize_t c_start,
|
||||
Py_ssize_t opcode_end, unsigned char opcode,
|
||||
PyObject *consts, PyObject *v)
|
||||
{
|
||||
PyObject *newconst;
|
||||
Py_ssize_t len_consts;
|
||||
int opcode;
|
||||
|
||||
/* Pre-conditions */
|
||||
assert(PyList_CheckExact(consts));
|
||||
assert(codestr[0] == LOAD_CONST);
|
||||
len_consts = PyList_GET_SIZE(consts);
|
||||
|
||||
/* Create new constant */
|
||||
opcode = codestr[3];
|
||||
switch (opcode) {
|
||||
case UNARY_NEGATIVE:
|
||||
newconst = PyNumber_Negative(v);
|
||||
|
@ -265,35 +338,31 @@ fold_unaryops_on_constants(unsigned char *codestr, PyObject *consts, PyObject *v
|
|||
PyErr_Format(PyExc_SystemError,
|
||||
"unexpected unary operation %d on a constant",
|
||||
opcode);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
if (newconst == NULL) {
|
||||
if(!PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
|
||||
if(!PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) {
|
||||
PyErr_Clear();
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Append folded constant into consts table */
|
||||
len_consts = PyList_GET_SIZE(consts);
|
||||
if (PyList_Append(consts, newconst)) {
|
||||
Py_DECREF(newconst);
|
||||
PyErr_Clear();
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
Py_DECREF(newconst);
|
||||
|
||||
/* Write NOP LOAD_CONST newconst */
|
||||
codestr[0] = NOP;
|
||||
codestr[1] = LOAD_CONST;
|
||||
SETARG(codestr, 1, len_consts);
|
||||
return 1;
|
||||
return copy_op_arg(codestr, c_start, LOAD_CONST, len_consts, opcode_end);
|
||||
}
|
||||
|
||||
static unsigned int *
|
||||
markblocks(unsigned char *code, Py_ssize_t len)
|
||||
{
|
||||
unsigned int *blocks = PyMem_New(unsigned int, len);
|
||||
int i,j, opcode, blockcnt = 0;
|
||||
int i, j, opcode, blockcnt = 0;
|
||||
|
||||
if (blocks == NULL) {
|
||||
PyErr_NoMemory();
|
||||
|
@ -302,7 +371,7 @@ markblocks(unsigned char *code, Py_ssize_t len)
|
|||
memset(blocks, 0, len*sizeof(int));
|
||||
|
||||
/* Mark labels in the first pass */
|
||||
for (i=0 ; i<len ; i+=CODESIZE(opcode)) {
|
||||
for (i=0 ; i<len ; i+=2) {
|
||||
opcode = code[i];
|
||||
switch (opcode) {
|
||||
case FOR_ITER:
|
||||
|
@ -324,7 +393,7 @@ markblocks(unsigned char *code, Py_ssize_t len)
|
|||
}
|
||||
}
|
||||
/* Build block numbers in the second pass */
|
||||
for (i=0 ; i<len ; i++) {
|
||||
for (i=0 ; i<len ; i+=2) {
|
||||
blockcnt += blocks[i]; /* increment blockcnt over labels */
|
||||
blocks[i] = blockcnt;
|
||||
}
|
||||
|
@ -335,33 +404,27 @@ markblocks(unsigned char *code, Py_ssize_t len)
|
|||
The consts object should still be in list form to allow new constants
|
||||
to be appended.
|
||||
|
||||
To keep the optimizer simple, it bails out (does nothing) for code that
|
||||
has a length over 32,700, and does not calculate extended arguments.
|
||||
That allows us to avoid overflow and sign issues. Likewise, it bails when
|
||||
the lineno table has complex encoding for gaps >= 255. EXTENDED_ARG can
|
||||
appear before MAKE_FUNCTION; in this case both opcodes are skipped.
|
||||
EXTENDED_ARG preceding any other opcode causes the optimizer to bail.
|
||||
To keep the optimizer simple, it bails when the lineno table has complex
|
||||
encoding for gaps >= 255.
|
||||
|
||||
Optimizations are restricted to simple transformations occuring within a
|
||||
single basic block. All transformations keep the code size the same or
|
||||
smaller. For those that reduce size, the gaps are initially filled with
|
||||
NOPs. Later those NOPs are removed and the jump addresses retargeted in
|
||||
a single pass. Code offset is adjusted accordingly. */
|
||||
a single pass. */
|
||||
|
||||
PyObject *
|
||||
PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
||||
PyObject *lnotab_obj)
|
||||
{
|
||||
Py_ssize_t i, j, codelen;
|
||||
int nops, h, adj;
|
||||
int tgt, tgttgt, opcode;
|
||||
Py_ssize_t h, i, nexti, op_start, codelen, tgt;
|
||||
unsigned int j, nops;
|
||||
unsigned char opcode, nextop;
|
||||
unsigned char *codestr = NULL;
|
||||
unsigned char *lnotab;
|
||||
int *addrmap = NULL;
|
||||
int cum_orig_offset, last_offset;
|
||||
unsigned int cum_orig_offset, last_offset;
|
||||
Py_ssize_t tabsiz;
|
||||
PyObject **const_stack = NULL;
|
||||
Py_ssize_t *load_const_stack = NULL;
|
||||
Py_ssize_t const_stack_top = -1;
|
||||
Py_ssize_t const_stack_size = 0;
|
||||
int in_consts = 0; /* whether we are in a LOAD_CONST sequence */
|
||||
|
@ -383,11 +446,9 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
/* Note: -128 and 127 special values for line number delta are ok,
|
||||
the peephole optimizer doesn't modify line numbers. */
|
||||
|
||||
/* Avoid situations where jump retargeting could overflow */
|
||||
assert(PyBytes_Check(code));
|
||||
codelen = PyBytes_GET_SIZE(code);
|
||||
if (codelen > 32700)
|
||||
goto exitUnchanged;
|
||||
assert(codelen % 2 == 0);
|
||||
|
||||
/* Make a modifiable copy of the code string */
|
||||
codestr = (unsigned char *)PyMem_Malloc(codelen);
|
||||
|
@ -398,21 +459,6 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
codestr = (unsigned char *)memcpy(codestr,
|
||||
PyBytes_AS_STRING(code), codelen);
|
||||
|
||||
/* Verify that RETURN_VALUE terminates the codestring. This allows
|
||||
the various transformation patterns to look ahead several
|
||||
instructions without additional checks to make sure they are not
|
||||
looking beyond the end of the code string.
|
||||
*/
|
||||
if (codestr[codelen-1] != RETURN_VALUE)
|
||||
goto exitUnchanged;
|
||||
|
||||
/* Mapping to new jump targets after NOPs are removed */
|
||||
addrmap = PyMem_New(int, codelen);
|
||||
if (addrmap == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto exitError;
|
||||
}
|
||||
|
||||
blocks = markblocks(codestr, codelen);
|
||||
if (blocks == NULL)
|
||||
goto exitError;
|
||||
|
@ -420,9 +466,17 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
|
||||
CONST_STACK_CREATE();
|
||||
|
||||
for (i=0 ; i<codelen ; i += CODESIZE(codestr[i])) {
|
||||
reoptimize_current:
|
||||
for (i=find_op(codestr, 0) ; i<codelen ; i=nexti) {
|
||||
opcode = codestr[i];
|
||||
op_start = i;
|
||||
while (op_start >= 2 && codestr[op_start-2] == EXTENDED_ARG) {
|
||||
op_start -= 2;
|
||||
}
|
||||
|
||||
nexti = i + 2;
|
||||
while (nexti < codelen && codestr[nexti] == EXTENDED_ARG)
|
||||
nexti += 2;
|
||||
nextop = nexti < codelen ? codestr[nexti] : 0;
|
||||
|
||||
if (!in_consts) {
|
||||
CONST_STACK_RESET();
|
||||
|
@ -433,14 +487,12 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
/* Replace UNARY_NOT POP_JUMP_IF_FALSE
|
||||
with POP_JUMP_IF_TRUE */
|
||||
case UNARY_NOT:
|
||||
if (codestr[i+1] != POP_JUMP_IF_FALSE
|
||||
|| !ISBASICBLOCK(blocks,i,4))
|
||||
continue;
|
||||
j = GETARG(codestr, i+1);
|
||||
codestr[i] = POP_JUMP_IF_TRUE;
|
||||
SETARG(codestr, i, j);
|
||||
codestr[i+3] = NOP;
|
||||
goto reoptimize_current;
|
||||
if (nextop != POP_JUMP_IF_FALSE
|
||||
|| !ISBASICBLOCK(blocks, op_start, i+2))
|
||||
break;
|
||||
memset(codestr + op_start, NOP, i - op_start + 2);
|
||||
codestr[nexti] = POP_JUMP_IF_TRUE;
|
||||
break;
|
||||
|
||||
/* not a is b --> a is not b
|
||||
not a in b --> a not in b
|
||||
|
@ -448,78 +500,79 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
not a not in b --> a in b
|
||||
*/
|
||||
case COMPARE_OP:
|
||||
j = GETARG(codestr, i);
|
||||
if (j < 6 || j > 9 ||
|
||||
codestr[i+3] != UNARY_NOT ||
|
||||
!ISBASICBLOCK(blocks,i,4))
|
||||
continue;
|
||||
SETARG(codestr, i, (j^1));
|
||||
codestr[i+3] = NOP;
|
||||
j = get_arg(codestr, i);
|
||||
if (j < 6 || j > 9 ||
|
||||
nextop != UNARY_NOT ||
|
||||
!ISBASICBLOCK(blocks, op_start, i + 2))
|
||||
break;
|
||||
codestr[i+1] = (j^1);
|
||||
memset(codestr + i + 2, NOP, nexti - i);
|
||||
break;
|
||||
|
||||
/* Skip over LOAD_CONST trueconst
|
||||
POP_JUMP_IF_FALSE xx. This improves
|
||||
"while 1" performance. */
|
||||
POP_JUMP_IF_FALSE xx. This improves
|
||||
"while 1" performance. */
|
||||
case LOAD_CONST:
|
||||
CONST_STACK_PUSH_OP(i);
|
||||
j = GETARG(codestr, i);
|
||||
if (codestr[i+3] != POP_JUMP_IF_FALSE ||
|
||||
!ISBASICBLOCK(blocks,i,6) ||
|
||||
!PyObject_IsTrue(PyList_GET_ITEM(consts, j)))
|
||||
continue;
|
||||
memset(codestr+i, NOP, 6);
|
||||
CONST_STACK_RESET();
|
||||
if (nextop != POP_JUMP_IF_FALSE ||
|
||||
!ISBASICBLOCK(blocks, op_start, i + 2) ||
|
||||
!PyObject_IsTrue(PyList_GET_ITEM(consts, get_arg(codestr, i))))
|
||||
break;
|
||||
memset(codestr + op_start, NOP, nexti - op_start + 2);
|
||||
CONST_STACK_POP(1);
|
||||
break;
|
||||
|
||||
/* Try to fold tuples of constants (includes a case for lists and sets
|
||||
which are only used for "in" and "not in" tests).
|
||||
/* Try to fold tuples of constants (includes a case for lists
|
||||
and sets which are only used for "in" and "not in" tests).
|
||||
Skip over BUILD_SEQN 1 UNPACK_SEQN 1.
|
||||
Replace BUILD_SEQN 2 UNPACK_SEQN 2 with ROT2.
|
||||
Replace BUILD_SEQN 3 UNPACK_SEQN 3 with ROT3 ROT2. */
|
||||
case BUILD_TUPLE:
|
||||
case BUILD_LIST:
|
||||
case BUILD_SET:
|
||||
j = GETARG(codestr, i);
|
||||
if (j == 0)
|
||||
break;
|
||||
h = CONST_STACK_OP_LASTN(j);
|
||||
assert((h >= 0 || CONST_STACK_LEN() < j));
|
||||
if (h >= 0 && j > 0 && j <= CONST_STACK_LEN() &&
|
||||
((opcode == BUILD_TUPLE &&
|
||||
ISBASICBLOCK(blocks, h, i-h+3)) ||
|
||||
((opcode == BUILD_LIST || opcode == BUILD_SET) &&
|
||||
codestr[i+3]==COMPARE_OP &&
|
||||
ISBASICBLOCK(blocks, h, i-h+6) &&
|
||||
(GETARG(codestr,i+3)==6 ||
|
||||
GETARG(codestr,i+3)==7))) &&
|
||||
tuple_of_constants(&codestr[i], j, consts, CONST_STACK_LASTN(j))) {
|
||||
assert(codestr[i] == LOAD_CONST);
|
||||
memset(&codestr[h], NOP, i - h);
|
||||
CONST_STACK_POP(j);
|
||||
CONST_STACK_PUSH_OP(i);
|
||||
break;
|
||||
j = get_arg(codestr, i);
|
||||
if (j > 0 && CONST_STACK_LEN() >= j) {
|
||||
h = lastn_const_start(codestr, op_start, j);
|
||||
if ((opcode == BUILD_TUPLE &&
|
||||
ISBASICBLOCK(blocks, h, op_start)) ||
|
||||
((opcode == BUILD_LIST || opcode == BUILD_SET) &&
|
||||
((nextop==COMPARE_OP &&
|
||||
(codestr[nexti+1]==6 ||
|
||||
codestr[nexti+1]==7)) ||
|
||||
nextop == GET_ITER) && ISBASICBLOCK(blocks, h, i + 2))) {
|
||||
h = fold_tuple_on_constants(codestr, h, i+2, opcode,
|
||||
consts, CONST_STACK_LASTN(j), j);
|
||||
if (h >= 0) {
|
||||
CONST_STACK_POP(j);
|
||||
CONST_STACK_PUSH_OP(h);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (codestr[i+3] != UNPACK_SEQUENCE ||
|
||||
!ISBASICBLOCK(blocks,i,6) ||
|
||||
j != GETARG(codestr, i+3) ||
|
||||
if (nextop != UNPACK_SEQUENCE ||
|
||||
!ISBASICBLOCK(blocks, op_start, i + 2) ||
|
||||
j != get_arg(codestr, nexti) ||
|
||||
opcode == BUILD_SET)
|
||||
continue;
|
||||
if (j == 1) {
|
||||
memset(codestr+i, NOP, 6);
|
||||
break;
|
||||
if (j < 2) {
|
||||
memset(codestr+op_start, NOP, nexti - op_start + 2);
|
||||
} else if (j == 2) {
|
||||
codestr[i] = ROT_TWO;
|
||||
memset(codestr+i+1, NOP, 5);
|
||||
codestr[op_start] = ROT_TWO;
|
||||
codestr[op_start + 1] = 0;
|
||||
memset(codestr + op_start + 2, NOP, nexti - op_start);
|
||||
CONST_STACK_RESET();
|
||||
} else if (j == 3) {
|
||||
codestr[i] = ROT_THREE;
|
||||
codestr[i+1] = ROT_TWO;
|
||||
memset(codestr+i+2, NOP, 4);
|
||||
codestr[op_start] = ROT_THREE;
|
||||
codestr[op_start + 1] = 0;
|
||||
codestr[op_start + 2] = ROT_TWO;
|
||||
codestr[op_start + 3] = 0;
|
||||
memset(codestr + op_start + 4, NOP, nexti - op_start - 2);
|
||||
CONST_STACK_RESET();
|
||||
}
|
||||
break;
|
||||
|
||||
/* Fold binary ops on constants.
|
||||
LOAD_CONST c1 LOAD_CONST c2 BINOP --> LOAD_CONST binop(c1,c2) */
|
||||
LOAD_CONST c1 LOAD_CONST c2 BINOP --> LOAD_CONST binop(c1,c2) */
|
||||
case BINARY_POWER:
|
||||
case BINARY_MULTIPLY:
|
||||
case BINARY_TRUE_DIVIDE:
|
||||
|
@ -533,35 +586,34 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
case BINARY_AND:
|
||||
case BINARY_XOR:
|
||||
case BINARY_OR:
|
||||
/* NOTE: LOAD_CONST is saved at `i-2` since it has an arg
|
||||
while BINOP hasn't */
|
||||
h = CONST_STACK_OP_LASTN(2);
|
||||
assert((h >= 0 || CONST_STACK_LEN() < 2));
|
||||
if (h >= 0 &&
|
||||
ISBASICBLOCK(blocks, h, i-h+1) &&
|
||||
fold_binops_on_constants(&codestr[i], consts, CONST_STACK_LASTN(2))) {
|
||||
i -= 2;
|
||||
memset(&codestr[h], NOP, i - h);
|
||||
assert(codestr[i] == LOAD_CONST);
|
||||
CONST_STACK_POP(2);
|
||||
CONST_STACK_PUSH_OP(i);
|
||||
if (CONST_STACK_LEN() < 2)
|
||||
break;
|
||||
h = lastn_const_start(codestr, op_start, 2);
|
||||
if (ISBASICBLOCK(blocks, h, op_start)) {
|
||||
h = fold_binops_on_constants(codestr, h, i+2, opcode,
|
||||
consts, CONST_STACK_LASTN(2));
|
||||
if (h >= 0) {
|
||||
CONST_STACK_POP(2);
|
||||
CONST_STACK_PUSH_OP(h);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* Fold unary ops on constants.
|
||||
LOAD_CONST c1 UNARY_OP --> LOAD_CONST unary_op(c) */
|
||||
LOAD_CONST c1 UNARY_OP --> LOAD_CONST unary_op(c) */
|
||||
case UNARY_NEGATIVE:
|
||||
case UNARY_INVERT:
|
||||
case UNARY_POSITIVE:
|
||||
h = CONST_STACK_OP_LASTN(1);
|
||||
assert((h >= 0 || CONST_STACK_LEN() < 1));
|
||||
if (h >= 0 &&
|
||||
ISBASICBLOCK(blocks, h, i-h+1) &&
|
||||
fold_unaryops_on_constants(&codestr[i-3], consts, CONST_STACK_TOP())) {
|
||||
i -= 2;
|
||||
assert(codestr[i] == LOAD_CONST);
|
||||
CONST_STACK_POP(1);
|
||||
CONST_STACK_PUSH_OP(i);
|
||||
if (CONST_STACK_LEN() < 1)
|
||||
break;
|
||||
h = lastn_const_start(codestr, op_start, 1);
|
||||
if (ISBASICBLOCK(blocks, h, op_start)) {
|
||||
h = fold_unaryops_on_constants(codestr, h, i+2, opcode,
|
||||
consts, *CONST_STACK_LASTN(1));
|
||||
if (h >= 0) {
|
||||
CONST_STACK_POP(1);
|
||||
CONST_STACK_PUSH_OP(h);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -576,25 +628,24 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
x:JUMP_IF_FALSE_OR_POP y y:JUMP_IF_FALSE_OR_POP z
|
||||
--> x:JUMP_IF_FALSE_OR_POP z
|
||||
x:JUMP_IF_FALSE_OR_POP y y:JUMP_IF_TRUE_OR_POP z
|
||||
--> x:POP_JUMP_IF_FALSE y+3
|
||||
where y+3 is the instruction following the second test.
|
||||
--> x:POP_JUMP_IF_FALSE y+2
|
||||
where y+2 is the instruction following the second test.
|
||||
*/
|
||||
case JUMP_IF_FALSE_OR_POP:
|
||||
case JUMP_IF_TRUE_OR_POP:
|
||||
tgt = GETJUMPTGT(codestr, i);
|
||||
h = get_arg(codestr, i);
|
||||
tgt = find_op(codestr, h);
|
||||
|
||||
j = codestr[tgt];
|
||||
if (CONDITIONAL_JUMP(j)) {
|
||||
/* NOTE: all possible jumps here are
|
||||
absolute! */
|
||||
if (JUMPS_ON_TRUE(j) == JUMPS_ON_TRUE(opcode)) {
|
||||
/* The second jump will be
|
||||
taken iff the first is. */
|
||||
tgttgt = GETJUMPTGT(codestr, tgt);
|
||||
/* The current opcode inherits
|
||||
its target's stack behaviour */
|
||||
codestr[i] = j;
|
||||
SETARG(codestr, i, tgttgt);
|
||||
goto reoptimize_current;
|
||||
taken iff the first is.
|
||||
The current opcode inherits
|
||||
its target's stack effect */
|
||||
h = set_arg(codestr, i, get_arg(codestr, tgt));
|
||||
} else {
|
||||
/* The second jump is not taken
|
||||
if the first is (so jump past
|
||||
|
@ -603,12 +654,15 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
they're not taken (so change
|
||||
the first jump to pop its
|
||||
argument when it's taken). */
|
||||
if (JUMPS_ON_TRUE(opcode))
|
||||
codestr[i] = POP_JUMP_IF_TRUE;
|
||||
else
|
||||
codestr[i] = POP_JUMP_IF_FALSE;
|
||||
SETARG(codestr, i, (tgt + 3));
|
||||
goto reoptimize_current;
|
||||
h = set_arg(codestr, i, tgt + 2);
|
||||
j = opcode == JUMP_IF_TRUE_OR_POP ?
|
||||
POP_JUMP_IF_TRUE : POP_JUMP_IF_FALSE;
|
||||
}
|
||||
|
||||
if (h >= 0) {
|
||||
nexti = h;
|
||||
codestr[nexti] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Intentional fallthrough */
|
||||
|
@ -625,76 +679,73 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
case SETUP_FINALLY:
|
||||
case SETUP_WITH:
|
||||
case SETUP_ASYNC_WITH:
|
||||
tgt = GETJUMPTGT(codestr, i);
|
||||
h = GETJUMPTGT(codestr, i);
|
||||
tgt = find_op(codestr, h);
|
||||
/* Replace JUMP_* to a RETURN into just a RETURN */
|
||||
if (UNCONDITIONAL_JUMP(opcode) &&
|
||||
codestr[tgt] == RETURN_VALUE) {
|
||||
codestr[i] = RETURN_VALUE;
|
||||
memset(codestr+i+1, NOP, 2);
|
||||
continue;
|
||||
codestr[op_start] = RETURN_VALUE;
|
||||
codestr[op_start + 1] = 0;
|
||||
memset(codestr + op_start + 2, NOP, i - op_start);
|
||||
} else if (UNCONDITIONAL_JUMP(codestr[tgt])) {
|
||||
j = GETJUMPTGT(codestr, tgt);
|
||||
if (opcode == JUMP_FORWARD) { /* JMP_ABS can go backwards */
|
||||
opcode = JUMP_ABSOLUTE;
|
||||
} else if (!ABSOLUTE_JUMP(opcode)) {
|
||||
if ((Py_ssize_t)j < i + 2) {
|
||||
break; /* No backward relative jumps */
|
||||
}
|
||||
j -= i + 2; /* Calc relative jump addr */
|
||||
}
|
||||
copy_op_arg(codestr, op_start, opcode, j, i+2);
|
||||
}
|
||||
if (!UNCONDITIONAL_JUMP(codestr[tgt]))
|
||||
continue;
|
||||
tgttgt = GETJUMPTGT(codestr, tgt);
|
||||
if (opcode == JUMP_FORWARD) /* JMP_ABS can go backwards */
|
||||
opcode = JUMP_ABSOLUTE;
|
||||
if (!ABSOLUTE_JUMP(opcode))
|
||||
tgttgt -= i + 3; /* Calc relative jump addr */
|
||||
if (tgttgt < 0) /* No backward relative jumps */
|
||||
continue;
|
||||
codestr[i] = opcode;
|
||||
SETARG(codestr, i, tgttgt);
|
||||
break;
|
||||
|
||||
case EXTENDED_ARG:
|
||||
if (codestr[i+3] != MAKE_FUNCTION)
|
||||
goto exitUnchanged;
|
||||
/* don't visit MAKE_FUNCTION as GETARG will be wrong */
|
||||
i += 3;
|
||||
break;
|
||||
|
||||
/* Replace RETURN LOAD_CONST None RETURN with just RETURN */
|
||||
/* Remove unreachable JUMPs after RETURN */
|
||||
/* Remove unreachable ops after RETURN */
|
||||
case RETURN_VALUE:
|
||||
if (i+4 >= codelen)
|
||||
continue;
|
||||
if (codestr[i+4] == RETURN_VALUE &&
|
||||
ISBASICBLOCK(blocks,i,5))
|
||||
memset(codestr+i+1, NOP, 4);
|
||||
else if (UNCONDITIONAL_JUMP(codestr[i+1]) &&
|
||||
ISBASICBLOCK(blocks,i,4))
|
||||
memset(codestr+i+1, NOP, 3);
|
||||
h = i + 2;
|
||||
while (h + 2 < codelen && ISBASICBLOCK(blocks, i, h + 2)) {
|
||||
h += 2;
|
||||
}
|
||||
if (h > i + 2) {
|
||||
memset(codestr + i + 2, NOP, h - i);
|
||||
nexti = find_op(codestr, h);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fixup lnotab */
|
||||
for (i=0, nops=0 ; i<codelen ; i += CODESIZE(codestr[i])) {
|
||||
for (i=0, nops=0 ; i<codelen ; i += 2) {
|
||||
assert(i - nops <= INT_MAX);
|
||||
/* original code offset => new code offset */
|
||||
addrmap[i] = (int)(i - nops);
|
||||
blocks[i] = i - nops;
|
||||
if (codestr[i] == NOP)
|
||||
nops++;
|
||||
nops += 2;
|
||||
}
|
||||
cum_orig_offset = 0;
|
||||
last_offset = 0;
|
||||
for (i=0 ; i < tabsiz ; i+=2) {
|
||||
int offset_delta, new_offset;
|
||||
unsigned int offset_delta, new_offset;
|
||||
cum_orig_offset += lnotab[i];
|
||||
new_offset = addrmap[cum_orig_offset];
|
||||
assert((cum_orig_offset & 1) == 0);
|
||||
new_offset = blocks[cum_orig_offset];
|
||||
offset_delta = new_offset - last_offset;
|
||||
assert(0 <= offset_delta && offset_delta <= 255);
|
||||
assert(offset_delta <= 255);
|
||||
lnotab[i] = (unsigned char)offset_delta;
|
||||
last_offset = new_offset;
|
||||
}
|
||||
|
||||
/* Remove NOPs and fixup jump targets */
|
||||
for (i=0, h=0 ; i<codelen ; ) {
|
||||
for (op_start=0, i=0, h=0 ; i<codelen ; i+=2, op_start=i) {
|
||||
j = codestr[i+1];
|
||||
while (codestr[i] == EXTENDED_ARG) {
|
||||
i += 2;
|
||||
j = j<<8 | codestr[i+1];
|
||||
}
|
||||
opcode = codestr[i];
|
||||
switch (opcode) {
|
||||
case NOP:
|
||||
i++;
|
||||
continue;
|
||||
case NOP:continue;
|
||||
|
||||
case JUMP_ABSOLUTE:
|
||||
case CONTINUE_LOOP:
|
||||
|
@ -702,8 +753,7 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
case POP_JUMP_IF_TRUE:
|
||||
case JUMP_IF_FALSE_OR_POP:
|
||||
case JUMP_IF_TRUE_OR_POP:
|
||||
j = addrmap[GETARG(codestr, i)];
|
||||
SETARG(codestr, i, j);
|
||||
j = blocks[j];
|
||||
break;
|
||||
|
||||
case FOR_ITER:
|
||||
|
@ -713,31 +763,31 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
case SETUP_FINALLY:
|
||||
case SETUP_WITH:
|
||||
case SETUP_ASYNC_WITH:
|
||||
j = addrmap[GETARG(codestr, i) + i + 3] - addrmap[i] - 3;
|
||||
SETARG(codestr, i, j);
|
||||
j = blocks[j + i + 2] - blocks[i] - 2;
|
||||
break;
|
||||
}
|
||||
adj = CODESIZE(opcode);
|
||||
while (adj--)
|
||||
codestr[h++] = codestr[i++];
|
||||
nexti = i - op_start + 2;
|
||||
if (instrsize(j) > nexti)
|
||||
goto exitUnchanged;
|
||||
/* If instrsize(j) < nexti, we'll emit EXTENDED_ARG 0 */
|
||||
write_op_arg(codestr + h, opcode, j, nexti);
|
||||
h += nexti;
|
||||
}
|
||||
assert(h + nops == codelen);
|
||||
assert(h + (Py_ssize_t)nops == codelen);
|
||||
|
||||
code = PyBytes_FromStringAndSize((char *)codestr, h);
|
||||
CONST_STACK_DELETE();
|
||||
PyMem_Free(addrmap);
|
||||
PyMem_Free(codestr);
|
||||
PyMem_Free(blocks);
|
||||
code = PyBytes_FromStringAndSize((char *)codestr, h);
|
||||
PyMem_Free(codestr);
|
||||
return code;
|
||||
|
||||
exitError:
|
||||
code = NULL;
|
||||
|
||||
exitUnchanged:
|
||||
Py_XINCREF(code);
|
||||
CONST_STACK_DELETE();
|
||||
PyMem_Free(blocks);
|
||||
PyMem_Free(addrmap);
|
||||
PyMem_Free(codestr);
|
||||
Py_XINCREF(code);
|
||||
return code;
|
||||
}
|
||||
|
|
38
Python/wordcode_helpers.h
Normal file
38
Python/wordcode_helpers.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* This file contains code shared by the compiler and the peephole
|
||||
optimizer.
|
||||
*/
|
||||
|
||||
/* Minimum number of bytes necessary to encode instruction with EXTENDED_ARGs */
|
||||
static int
|
||||
instrsize(unsigned int oparg)
|
||||
{
|
||||
return oparg <= 0xff ? 2 :
|
||||
oparg <= 0xffff ? 4 :
|
||||
oparg <= 0xffffff ? 6 :
|
||||
8;
|
||||
}
|
||||
|
||||
/* Spits out op/oparg pair using ilen bytes. codestr should be pointed at the
|
||||
desired location of the first EXTENDED_ARG */
|
||||
static void
|
||||
write_op_arg(unsigned char *codestr, unsigned char opcode,
|
||||
unsigned int oparg, int ilen)
|
||||
{
|
||||
switch (ilen) {
|
||||
case 8:
|
||||
*codestr++ = EXTENDED_ARG;
|
||||
*codestr++ = (oparg >> 24) & 0xff;
|
||||
case 6:
|
||||
*codestr++ = EXTENDED_ARG;
|
||||
*codestr++ = (oparg >> 16) & 0xff;
|
||||
case 4:
|
||||
*codestr++ = EXTENDED_ARG;
|
||||
*codestr++ = (oparg >> 8) & 0xff;
|
||||
case 2:
|
||||
*codestr++ = opcode;
|
||||
*codestr++ = oparg & 0xff;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue