Index: Python/ceval.c =================================================================== --- Python/ceval.c (revision 68532) +++ Python/ceval.c (working copy) @@ -12,6 +12,7 @@ #include "Python.h" #include "code.h" +#include "instructionsobject.h" #include "frameobject.h" #include "eval.h" #include "opcode.h" @@ -111,7 +112,7 @@ PyFrameObject *, int, PyObject *); static void call_exc_trace(Py_tracefunc, PyObject *, PyFrameObject *); static int maybe_call_line_trace(Py_tracefunc, PyObject *, - PyFrameObject *, int *, int *, int *); + PyFrameObject *, int *, int *, int *); static PyObject * apply_slice(PyObject *, PyObject *, PyObject *); static int assign_slice(PyObject *, PyObject *, @@ -126,7 +127,7 @@ static void reset_exc_info(PyThreadState *); static void format_exc_check_arg(PyObject *, char *, PyObject *); static PyObject * string_concatenate(PyObject *, PyObject *, - PyFrameObject *, unsigned char *); + PyFrameObject *, int, int ); #define NAME_ERROR_MSG \ "name '%.200s' is not defined" @@ -568,7 +569,7 @@ /* The interpreter's recursion limit */ #ifndef Py_DEFAULT_RECURSION_LIMIT -#define Py_DEFAULT_RECURSION_LIMIT 1000 +#define Py_DEFAULT_RECURSION_LIMIT 500 #endif static int recursion_limit = Py_DEFAULT_RECURSION_LIMIT; int _Py_CheckRecursionLimit = Py_DEFAULT_RECURSION_LIMIT; @@ -640,9 +641,127 @@ int _Py_CheckInterval = 100; volatile int _Py_Ticker = 0; /* so that we hit a "tick" first thing */ +/* Interface to Vmgen. + + The generated code looks roughly as follows: + + LABEL(instruction) + NAME("instruction") + { + // local variable declarations: + foo bar; + NEXT_P0; + + // code for transferring stack content and immediate arguments to locals: + IF_stackTOS(); + vm_foo2bar(); + + // code for adjusting stack pointers + stack_pointer += ; + + { + // instruction code... + } + + // instruction tail (transfer locals to stack): + NEXT_P1; + vm_bar2foo(); + IF_stackTOS(); + NEXT_P2; + } + +*/ + +#define Cell vmgen_Cell + +#define LABEL(inst) I_##inst: +#define INST_ADDR(inst) ((Opcode) &&I_##inst) + +/* XXX patched pythonrun.c */ +#define MAYBE(stmt) /* if (Py_IsInitialized() == 42) { (stmt); } */ +#define NAME(inst) MAYBE(printf("%s: %s: %d: %s [%d] {%p, %p} NOS==%p\n", \ + PyString_AS_STRING(co->co_filename), \ + PyString_AS_STRING(co->co_name), \ + INSTR_OFFSET(), inst, STACK_LEVEL(), \ + TOP(), stack_pointerTOS, SECOND())); + +/* Vmgen's tracing stuff */ +#undef VM_DEBUG +#define vm_out stdout +int vm_debug = 1; +static void printarg_Cell(Cell cell) { printf("%d", cell.oparg); } +static void printarg_i(int i) { printf("%d", i); } +static void printarg_a(PyObject *a) { PyObject_Print(a, stdout, 0); } + +/* Instruction stream & value stack types; see also code.h */ +typedef PyObject *Obj; +#define vm_Cell2i(inst,arg) ({(arg) = (inst).oparg;}) +#define vm_Cell2Cell(c1,c2) ({(c2) = (c1);}) +#define vm_Obj2a(obj,a) ({(a) = (obj);}) +#define vm_a2Obj(a,obj) ({(obj) = (a);}) + +/* This is the first code executed in any instruction. */ +#define NEXT_P0 do { \ + f->f_lasti = INSTR_OFFSET(); \ + next_instr++; \ + } while (0) + +/* Reference counting annotations */ +#define vm_a2decref(a,_) Py_DECREF(a) +#define vm_a2incref(a,_) Py_INCREF(a) + +/* There are three ``kinds'' of instructions as far as dispatch is concerned. + Firstly, we want to use certain instructions as superinstruction-prefixes, + and these should dispatch via NEXT_P2. */ +#define NEXT_P1 /* Nothing */ +#define NEXT_P2 do { \ + if (_Py_TracingPossible) goto fast_next_opcode; \ + goto *(next_instr->opcode); \ + } while(0) + +/* Most instructions dispatch as indicated by their next: annotation. + Vmgen will insert a vm_foo2next() macro just before NEXT_P2. */ +#define vm_next_opcode2next(_x,_y) NEXT() +#define vm_on_error2next(_x,_y) ERROR() +#define vm_fast_block_end2next(_x,_y) ({goto fast_block_end;}) +#define vm_fast_yield2next(_x,_y) ({goto fast_yield;}) +#define vm_a2next(a,_) \ + ({if ((a) != NULL) { \ + x = Py_None; \ + NEXT(); \ + } else { \ + x = NULL; \ + ERROR(); \ + }}) +#define vm_error2next(_x,_y) \ + ({if (err == 0) { \ + NEXT(); \ + } else { \ + ERROR(); \ + }}) + +/* Finally, a few instructions are just too hairy to be conveniently described + by Vmgen's stack effect language. + For these, we do stack manipulation manually and use the following macros + directly to dispatch the next instruction. */ +#define NEXT() ({if (--_Py_Ticker < 0) goto next_opcode; else NEXT_P2;}) +#define ERROR() ({goto on_error;}) + +/* Misc */ +#define DEF_CA /* Nothing */ +#define LABEL2(inst_name) /* Nothing */ +#define MAYBE_UNUSED __attribute__((unused)) +#define IMM_ARG(access,value) (access) + +/* Communicate instruction addresses to translator */ +Opcode *vm_prim; + +/* end of Vmgen stuff */ + PyObject * PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals) { + /* XXX raise SystemError if globals is NULL */ return PyEval_EvalCodeEx(co, globals, locals, (PyObject **)NULL, 0, @@ -665,21 +784,15 @@ PyObject * PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) { -#ifdef DXPAIRS - int lastopcode = 0; -#endif - register PyObject **stack_pointer; /* Next free slot in value stack */ - register unsigned char *next_instr; - register int opcode; /* Current opcode */ - register int oparg; /* Current opcode argument, if any */ + PyObject **stack_pointer; /* Next free slot in value stack */ + Inst *next_instr; register enum why_code why; /* Reason for block stack unwind */ register int err; /* Error status -- nonzero if error */ register PyObject *x; /* Result object -- NULL if error */ - register PyObject *v; /* Temporary objects popped off stack */ - register PyObject *w; - register PyObject *u; - register PyObject *t; - register PyObject *stream = NULL; /* for PRINT opcodes */ + PyObject *a1; /* Temporary objects popped off stack */ + PyObject *a2; + PyObject *a3; + PyObject *stream = NULL; /* for PRINT opcodes */ register PyObject **fastlocals, **freevars; PyObject *retval = NULL; /* Return value */ PyThreadState *tstate = PyThreadState_GET(); @@ -694,10 +807,10 @@ time it is tested. */ int instr_ub = -1, instr_lb = 0, instr_prev = -1; - unsigned char *first_instr; + Inst *first_instr; PyObject *names; PyObject *consts; -#if defined(Py_DEBUG) || defined(LLTRACE) +#if defined(Py_DEBUG) /* Make it easier to find out where we are with a debugger */ char *filename; #endif @@ -710,87 +823,19 @@ #define GETITEM(v, i) PyTuple_GetItem((v), (i)) #endif -#ifdef WITH_TSC -/* Use Pentium timestamp counter to mark certain events: - inst0 -- beginning of switch statement for opcode dispatch - inst1 -- end of switch statement (may be skipped) - loop0 -- the top of the mainloop - loop1 -- place where control returns again to top of mainloop - (may be skipped) - intr1 -- beginning of long interruption - intr2 -- end of long interruption - - Many opcodes call out to helper C functions. In some cases, the - time in those functions should be counted towards the time for the - opcode, but not in all cases. For example, a CALL_FUNCTION opcode - calls another Python function; there's no point in charge all the - bytecode executed by the called function to the caller. - - It's hard to make a useful judgement statically. In the presence - of operator overloading, it's impossible to tell if a call will - execute new Python code or not. - - It's a case-by-case judgement. I'll use intr1 for the following - cases: - - EXEC_STMT - IMPORT_STAR - IMPORT_FROM - CALL_FUNCTION (and friends) - - */ - uint64 inst0, inst1, loop0, loop1, intr0 = 0, intr1 = 0; - int ticked = 0; - - READ_TIMESTAMP(inst0); - READ_TIMESTAMP(inst1); - READ_TIMESTAMP(loop0); - READ_TIMESTAMP(loop1); - - /* shut up the compiler */ - opcode = 0; -#endif - /* 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 JUMPTO(x) (next_instr = first_instr + (x)) -#define JUMPBY(x) (next_instr += (x)) +#define JUMPTO(x) SET_IP(first_instr + (x)) +#define JUMPBY(x) INC_IP(x) +#define IP (next_instr) +#define IPTOS (*next_instr) +#define INC_IP(n) ({next_instr += (n);}) +#define SET_IP(target) ({next_instr = (target);}) -/* OpCode prediction macros - Some opcodes tend to come in pairs thus making it possible to - predict the second code when the first is run. For example, - COMPARE_OP is often followed by JUMP_IF_FALSE or JUMP_IF_TRUE. And, - those opcodes are often followed by a POP_TOP. - - Verifying the prediction costs a single high-speed test of a register - variable against a constant. If the pairing was good, then the - 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. - - If collecting opcode statistics, your choices are to either keep the - predictions turned-on and interpret the results as if some opcodes - had been combined or turn-off predictions so that the opcode frequency - counter updates for both opcodes. -*/ - -#ifdef DYNAMIC_EXECUTION_PROFILE -#define PREDICT(op) if (0) goto PRED_##op -#else -#define PREDICT(op) if (*next_instr == op) goto PRED_##op -#endif - -#define PREDICTED(op) PRED_##op: next_instr++ -#define PREDICTED_WITH_ARG(op) PRED_##op: oparg = PEEKARG(); next_instr += 3 - /* Stack manipulation macros */ /* The stack can grow at most MAXINT deep, as co_nlocals and @@ -809,25 +854,14 @@ #define BASIC_PUSH(v) (*stack_pointer++ = (v)) #define BASIC_POP() (*--stack_pointer) -#ifdef LLTRACE -#define PUSH(v) { (void)(BASIC_PUSH(v), \ - lltrace && prtrace(TOP(), "push")); \ - assert(STACK_LEVEL() <= co->co_stacksize); } -#define POP() ((void)(lltrace && prtrace(TOP(), "pop")), \ - BASIC_POP()) -#define STACKADJ(n) { (void)(BASIC_STACKADJ(n), \ - lltrace && prtrace(TOP(), "stackadj")); \ - assert(STACK_LEVEL() <= co->co_stacksize); } -#define EXT_POP(STACK_POINTER) ((void)(lltrace && \ - prtrace((STACK_POINTER)[-1], "ext_pop")), \ - *--(STACK_POINTER)) -#else #define PUSH(v) BASIC_PUSH(v) #define POP() BASIC_POP() #define STACKADJ(n) BASIC_STACKADJ(n) #define EXT_POP(STACK_POINTER) (*--(STACK_POINTER)) -#endif +#define stack_pointerTOS TOP() +#define IF_stack_pointerTOS(x) /* Nothing */ + /* Local variable macros */ #define GETLOCAL(i) (fastlocals[i]) @@ -892,7 +926,31 @@ consts = co->co_consts; fastlocals = f->f_localsplus; freevars = f->f_localsplus + co->co_nlocals; - first_instr = (unsigned char*) PyString_AS_STRING(co->co_code); + first_instr = co->co_tcode; + /* Export instruction addresses, then translate CPython bytecode + to DTC. Store result in the code object. */ + static Opcode labels[] = { +#include "ceval-labels.i" + }; + + if (first_instr == NULL) { + Py_ssize_t len, i; + PyPInst *pinsts = ((PyInstructionsObject *)co->co_code)->inst; + len = Py_SIZE(co->co_code); + first_instr = (Inst *) calloc(len, sizeof(Inst)); + for (i = 0; i < len; ++i) { + if (pinsts[i].is_arg) { + first_instr[i].oparg = + PyPInst_GET_ARG(pinsts + i); + } + else { + first_instr[i].opcode = + labels[PyPInst_GET_OPCODE(pinsts + i)]; + } + } + co->co_tcode = first_instr; + } + /* An explanation is in order for the next line. f->f_lasti now refers to the index of the last instruction @@ -900,1905 +958,235 @@ 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. - - When the PREDICT() macros are enabled, some opcode pairs follow in - direct succession without updating f->f_lasti. A successful - prediction effectively links the two codes together as if they - 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.) - */ + does work. Promise. */ next_instr = first_instr + f->f_lasti + 1; stack_pointer = f->f_stacktop; assert(stack_pointer != NULL); f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ -#ifdef LLTRACE - lltrace = PyDict_GetItemString(f->f_globals, "__lltrace__") != NULL; -#endif -#if defined(Py_DEBUG) || defined(LLTRACE) +#if defined(Py_DEBUG) filename = PyString_AsString(co->co_filename); #endif why = WHY_NOT; err = 0; x = Py_None; /* Not a reference, just anything non-NULL */ - w = NULL; + a1 = a2 = NULL; if (throwflag) { /* support for generator.throw() */ why = WHY_EXCEPTION; goto on_error; } - for (;;) { -#ifdef WITH_TSC - if (inst1 == 0) { - /* Almost surely, the opcode executed a break - or a continue, preventing inst1 from being set - on the way out of the loop. - */ - READ_TIMESTAMP(inst1); - loop1 = inst1; - } - dump_tsc(opcode, ticked, inst0, inst1, loop0, loop1, - intr0, intr1); - ticked = 0; - inst1 = 0; - intr0 = 0; - intr1 = 0; - READ_TIMESTAMP(loop0); -#endif - assert(stack_pointer >= f->f_valuestack); /* else underflow */ - assert(STACK_LEVEL() <= co->co_stacksize); /* else overflow */ +next_opcode: + assert(stack_pointer >= f->f_valuestack); /* else underflow */ + assert(STACK_LEVEL() <= co->co_stacksize); /* else overflow */ - /* Do periodic things. Doing this every time through - the loop would add too much overhead, so we do it - only every Nth instruction. We also do it if - ``pendingcalls_to_do'' is set, i.e. when an asynchronous - event needs attention (e.g. a signal handler or - async I/O handler); see Py_AddPendingCall() and - Py_MakePendingCalls() above. */ + /* Do periodic things. Doing this every time through + the loop would add too much overhead, so we do it + only every Nth instruction. We also do it if + ``pendingcalls_to_do'' is set, i.e. when an asynchronous + event needs attention (e.g. a signal handler or + async I/O handler); see Py_AddPendingCall() and + Py_MakePendingCalls() above. */ - if (--_Py_Ticker < 0) { - if (*next_instr == SETUP_FINALLY) { - /* Make the last opcode before - a try: finally: block uninterruptable. */ - goto fast_next_opcode; - } - _Py_Ticker = _Py_CheckInterval; - tstate->tick_counter++; -#ifdef WITH_TSC - ticked = 1; -#endif - if (pendingcalls_to_do) { - if (Py_MakePendingCalls() < 0) { - why = WHY_EXCEPTION; - goto on_error; - } - if (pendingcalls_to_do) - /* MakePendingCalls() didn't succeed. - Force early re-execution of this - "periodic" code, possibly after - a thread switch */ - _Py_Ticker = 0; - } + if (next_instr->opcode == INST_ADDR(SETUP_FINALLY)) { + /* Make the last opcode before + a try: finally: block uninterruptable. */ + goto fast_next_opcode; + } + _Py_Ticker = _Py_CheckInterval; + tstate->tick_counter++; + if (pendingcalls_to_do) { + if (Py_MakePendingCalls() < 0) { + why = WHY_EXCEPTION; + goto on_error; + } + if (pendingcalls_to_do) + /* MakePendingCalls() didn't succeed. + Force early re-execution of this + "periodic" code, possibly after + a thread switch */ + _Py_Ticker = 0; + } #ifdef WITH_THREAD - if (interpreter_lock) { - /* Give another thread a chance */ + if (interpreter_lock) { + /* Give another thread a chance */ - if (PyThreadState_Swap(NULL) != tstate) - Py_FatalError("ceval: tstate mix-up"); - PyThread_release_lock(interpreter_lock); + if (PyThreadState_Swap(NULL) != tstate) + Py_FatalError("ceval: tstate mix-up"); + PyThread_release_lock(interpreter_lock); - /* Other threads may run now */ + /* Other threads may run now */ - PyThread_acquire_lock(interpreter_lock, 1); - if (PyThreadState_Swap(tstate) != NULL) - Py_FatalError("ceval: orphan tstate"); + PyThread_acquire_lock(interpreter_lock, 1); + if (PyThreadState_Swap(tstate) != NULL) + Py_FatalError("ceval: orphan tstate"); - /* Check for thread interrupts */ + /* Check for thread interrupts */ - if (tstate->async_exc != NULL) { - x = tstate->async_exc; - tstate->async_exc = NULL; - PyErr_SetNone(x); - Py_DECREF(x); - why = WHY_EXCEPTION; - goto on_error; - } - } + if (tstate->async_exc != NULL) { + x = tstate->async_exc; + tstate->async_exc = NULL; + PyErr_SetNone(x); + Py_DECREF(x); + why = WHY_EXCEPTION; + goto on_error; + } + } #endif - } - fast_next_opcode: - f->f_lasti = INSTR_OFFSET(); +fast_next_opcode: + f->f_lasti = INSTR_OFFSET(); - /* line-by-line tracing support */ + /* line-by-line tracing support */ - if (_Py_TracingPossible && - tstate->c_tracefunc != NULL && !tstate->tracing) { - /* see maybe_call_line_trace - for expository comments */ - f->f_stacktop = stack_pointer; + if (_Py_TracingPossible && + tstate->c_tracefunc != NULL && !tstate->tracing) { + /* see maybe_call_line_trace + for expository comments */ + f->f_stacktop = stack_pointer; - err = maybe_call_line_trace(tstate->c_tracefunc, - tstate->c_traceobj, - f, &instr_lb, &instr_ub, - &instr_prev); - /* Reload possibly changed frame fields */ - JUMPTO(f->f_lasti); - if (f->f_stacktop != NULL) { - stack_pointer = f->f_stacktop; - f->f_stacktop = NULL; - } - if (err) { - /* trace function raised an exception */ - goto on_error; - } + err = maybe_call_line_trace(tstate->c_tracefunc, + tstate->c_traceobj, + f, &instr_lb, &instr_ub, + &instr_prev); + /* Reload possibly changed frame fields */ + JUMPTO(f->f_lasti); + if (f->f_stacktop != NULL) { + stack_pointer = f->f_stacktop; + f->f_stacktop = NULL; } - - /* 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(); - dispatch_opcode: -#ifdef DYNAMIC_EXECUTION_PROFILE -#ifdef DXPAIRS - dxpairs[lastopcode][opcode]++; - lastopcode = opcode; -#endif - dxp[opcode]++; -#endif - -#ifdef LLTRACE - /* Instruction tracing */ - - if (lltrace) { - if (HAS_ARG(opcode)) { - printf("%d: %d, %d\n", - f->f_lasti, opcode, oparg); - } - else { - printf("%d: %d\n", - f->f_lasti, opcode); - } + if (err) { + /* trace function raised an exception */ + goto on_error; } -#endif + } - /* Main switch on opcode */ - READ_TIMESTAMP(inst0); + /* Dispatch */ + goto *(next_instr->opcode); - switch (opcode) { +#include "ceval-vm.i" - /* BEWARE! - It is essential that any operation that fails sets either - x to NULL, err to nonzero, or why to anything but WHY_NOT, - and that no operation that succeeds does this! */ +on_error: - /* case STOP_CODE: this is an error! */ + /* Quickly continue if no error occurred */ - case NOP: - goto fast_next_opcode; + if (why == WHY_NOT) { + if (err == 0 && x != NULL) { + NEXT(); /* Normal, fast path */ + } + why = WHY_EXCEPTION; + x = Py_None; + err = 0; + } - case LOAD_FAST: - x = GETLOCAL(oparg); - if (x != NULL) { - Py_INCREF(x); - PUSH(x); - goto fast_next_opcode; - } - format_exc_check_arg(PyExc_UnboundLocalError, - UNBOUNDLOCAL_ERROR_MSG, - PyTuple_GetItem(co->co_varnames, oparg)); - break; + /* Double-check exception status */ - case LOAD_CONST: - x = GETITEM(consts, oparg); - Py_INCREF(x); - PUSH(x); - goto fast_next_opcode; + if (why == WHY_EXCEPTION || why == WHY_RERAISE) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_SystemError, + "error return without exception set"); + why = WHY_EXCEPTION; + } + } - PREDICTED_WITH_ARG(STORE_FAST); - case STORE_FAST: - v = POP(); - SETLOCAL(oparg, v); - goto fast_next_opcode; + /* Log traceback info if this is a real exception */ - PREDICTED(POP_TOP); - case POP_TOP: - v = POP(); - Py_DECREF(v); - goto fast_next_opcode; + if (why == WHY_EXCEPTION) { + PyTraceBack_Here(f); - case ROT_TWO: - v = TOP(); - w = SECOND(); - SET_TOP(w); - SET_SECOND(v); - goto fast_next_opcode; + if (tstate->c_tracefunc != NULL) + call_exc_trace(tstate->c_tracefunc, + tstate->c_traceobj, f); + } - case ROT_THREE: - v = TOP(); - w = SECOND(); - x = THIRD(); - SET_TOP(w); - SET_SECOND(x); - SET_THIRD(v); - goto fast_next_opcode; + /* For the rest, treat WHY_RERAISE as WHY_EXCEPTION */ - case ROT_FOUR: - u = TOP(); - v = SECOND(); - w = THIRD(); - x = FOURTH(); - SET_TOP(v); - SET_SECOND(w); - SET_THIRD(x); - SET_FOURTH(u); - goto fast_next_opcode; + if (why == WHY_RERAISE) + why = WHY_EXCEPTION; - case DUP_TOP: - v = TOP(); - Py_INCREF(v); - PUSH(v); - goto fast_next_opcode; + /* Unwind stacks if a (pseudo) exception occurred */ - case DUP_TOPX: - if (oparg == 2) { - x = TOP(); - Py_INCREF(x); - w = SECOND(); - Py_INCREF(w); - STACKADJ(2); - SET_TOP(x); - SET_SECOND(w); - goto fast_next_opcode; - } else if (oparg == 3) { - x = TOP(); - Py_INCREF(x); - w = SECOND(); - Py_INCREF(w); - v = THIRD(); - Py_INCREF(v); - STACKADJ(3); - SET_TOP(x); - SET_SECOND(w); - SET_THIRD(v); - goto fast_next_opcode; - } - Py_FatalError("invalid argument to DUP_TOPX" - " (bytecode corruption?)"); - /* Never returns, so don't bother to set why. */ - break; - - case UNARY_POSITIVE: - v = TOP(); - x = PyNumber_Positive(v); - Py_DECREF(v); - SET_TOP(x); - if (x != NULL) continue; - break; - - case UNARY_NEGATIVE: - v = TOP(); - x = PyNumber_Negative(v); - Py_DECREF(v); - SET_TOP(x); - if (x != NULL) continue; - break; - - case UNARY_NOT: - v = TOP(); - err = PyObject_IsTrue(v); - Py_DECREF(v); - if (err == 0) { - Py_INCREF(Py_True); - SET_TOP(Py_True); - continue; - } - else if (err > 0) { - Py_INCREF(Py_False); - SET_TOP(Py_False); - err = 0; - continue; - } - STACKADJ(-1); - break; - - case UNARY_CONVERT: - v = TOP(); - x = PyObject_Repr(v); - Py_DECREF(v); - SET_TOP(x); - if (x != NULL) continue; - break; - - case UNARY_INVERT: - v = TOP(); - x = PyNumber_Invert(v); - Py_DECREF(v); - SET_TOP(x); - if (x != NULL) continue; - break; - - case BINARY_POWER: - w = POP(); - v = TOP(); - x = PyNumber_Power(v, w, Py_None); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case BINARY_MULTIPLY: - w = POP(); - v = TOP(); - x = PyNumber_Multiply(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case BINARY_DIVIDE: - if (!_Py_QnewFlag) { - w = POP(); - v = TOP(); - x = PyNumber_Divide(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - } - /* -Qnew is in effect: fall through to - BINARY_TRUE_DIVIDE */ - case BINARY_TRUE_DIVIDE: - w = POP(); - v = TOP(); - x = PyNumber_TrueDivide(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case BINARY_FLOOR_DIVIDE: - w = POP(); - v = TOP(); - x = PyNumber_FloorDivide(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case BINARY_MODULO: - w = POP(); - v = TOP(); - x = PyNumber_Remainder(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case BINARY_ADD: - w = POP(); - v = TOP(); - if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) { - /* INLINE: int + int */ - register long a, b, i; - a = PyInt_AS_LONG(v); - b = PyInt_AS_LONG(w); - i = a + b; - if ((i^a) < 0 && (i^b) < 0) - goto slow_add; - x = PyInt_FromLong(i); - } - else if (PyString_CheckExact(v) && - PyString_CheckExact(w)) { - x = string_concatenate(v, w, f, next_instr); - /* string_concatenate consumed the ref to v */ - goto skip_decref_vx; - } - else { - slow_add: - x = PyNumber_Add(v, w); - } - Py_DECREF(v); - skip_decref_vx: - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case BINARY_SUBTRACT: - w = POP(); - v = TOP(); - if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) { - /* INLINE: int - int */ - register long a, b, i; - a = PyInt_AS_LONG(v); - b = PyInt_AS_LONG(w); - i = a - b; - if ((i^a) < 0 && (i^~b) < 0) - goto slow_sub; - x = PyInt_FromLong(i); - } - else { - slow_sub: - x = PyNumber_Subtract(v, w); - } - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case BINARY_SUBSCR: - w = POP(); - v = TOP(); - if (PyList_CheckExact(v) && PyInt_CheckExact(w)) { - /* INLINE: list[int] */ - Py_ssize_t i = PyInt_AsSsize_t(w); - if (i < 0) - i += PyList_GET_SIZE(v); - if (i >= 0 && i < PyList_GET_SIZE(v)) { - x = PyList_GET_ITEM(v, i); - Py_INCREF(x); - } - else - goto slow_get; - } - else - slow_get: - x = PyObject_GetItem(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case BINARY_LSHIFT: - w = POP(); - v = TOP(); - x = PyNumber_Lshift(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case BINARY_RSHIFT: - w = POP(); - v = TOP(); - x = PyNumber_Rshift(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case BINARY_AND: - w = POP(); - v = TOP(); - x = PyNumber_And(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case BINARY_XOR: - w = POP(); - v = TOP(); - x = PyNumber_Xor(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case BINARY_OR: - w = POP(); - v = TOP(); - x = PyNumber_Or(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case LIST_APPEND: - w = POP(); - v = stack_pointer[-oparg]; - err = PyList_Append(v, w); - Py_DECREF(w); - if (err == 0) { - PREDICT(JUMP_ABSOLUTE); - continue; - } - break; - - case INPLACE_POWER: - w = POP(); - v = TOP(); - x = PyNumber_InPlacePower(v, w, Py_None); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case INPLACE_MULTIPLY: - w = POP(); - v = TOP(); - x = PyNumber_InPlaceMultiply(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case INPLACE_DIVIDE: - if (!_Py_QnewFlag) { - w = POP(); - v = TOP(); - x = PyNumber_InPlaceDivide(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - } - /* -Qnew is in effect: fall through to - INPLACE_TRUE_DIVIDE */ - case INPLACE_TRUE_DIVIDE: - w = POP(); - v = TOP(); - x = PyNumber_InPlaceTrueDivide(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case INPLACE_FLOOR_DIVIDE: - w = POP(); - v = TOP(); - x = PyNumber_InPlaceFloorDivide(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case INPLACE_MODULO: - w = POP(); - v = TOP(); - x = PyNumber_InPlaceRemainder(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case INPLACE_ADD: - w = POP(); - v = TOP(); - if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) { - /* INLINE: int + int */ - register long a, b, i; - a = PyInt_AS_LONG(v); - b = PyInt_AS_LONG(w); - i = a + b; - if ((i^a) < 0 && (i^b) < 0) - goto slow_iadd; - x = PyInt_FromLong(i); - } - else if (PyString_CheckExact(v) && - PyString_CheckExact(w)) { - x = string_concatenate(v, w, f, next_instr); - /* string_concatenate consumed the ref to v */ - goto skip_decref_v; - } - else { - slow_iadd: - x = PyNumber_InPlaceAdd(v, w); - } - Py_DECREF(v); - skip_decref_v: - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case INPLACE_SUBTRACT: - w = POP(); - v = TOP(); - if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) { - /* INLINE: int - int */ - register long a, b, i; - a = PyInt_AS_LONG(v); - b = PyInt_AS_LONG(w); - i = a - b; - if ((i^a) < 0 && (i^~b) < 0) - goto slow_isub; - x = PyInt_FromLong(i); - } - else { - slow_isub: - x = PyNumber_InPlaceSubtract(v, w); - } - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case INPLACE_LSHIFT: - w = POP(); - v = TOP(); - x = PyNumber_InPlaceLshift(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case INPLACE_RSHIFT: - w = POP(); - v = TOP(); - x = PyNumber_InPlaceRshift(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case INPLACE_AND: - w = POP(); - v = TOP(); - x = PyNumber_InPlaceAnd(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case INPLACE_XOR: - w = POP(); - v = TOP(); - x = PyNumber_InPlaceXor(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case INPLACE_OR: - w = POP(); - v = TOP(); - x = PyNumber_InPlaceOr(v, w); - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case SLICE+0: - case SLICE+1: - case SLICE+2: - case SLICE+3: - if ((opcode-SLICE) & 2) - w = POP(); - else - w = NULL; - if ((opcode-SLICE) & 1) - v = POP(); - else - v = NULL; - u = TOP(); - x = apply_slice(u, v, w); - Py_DECREF(u); - Py_XDECREF(v); - Py_XDECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case STORE_SLICE+0: - case STORE_SLICE+1: - case STORE_SLICE+2: - case STORE_SLICE+3: - if ((opcode-STORE_SLICE) & 2) - w = POP(); - else - w = NULL; - if ((opcode-STORE_SLICE) & 1) - v = POP(); - else - v = NULL; - u = POP(); - t = POP(); - err = assign_slice(u, v, w, t); /* u[v:w] = t */ - Py_DECREF(t); - Py_DECREF(u); - Py_XDECREF(v); - Py_XDECREF(w); - if (err == 0) continue; - break; - - case DELETE_SLICE+0: - case DELETE_SLICE+1: - case DELETE_SLICE+2: - case DELETE_SLICE+3: - if ((opcode-DELETE_SLICE) & 2) - w = POP(); - else - w = NULL; - if ((opcode-DELETE_SLICE) & 1) - v = POP(); - else - v = NULL; - u = POP(); - err = assign_slice(u, v, w, (PyObject *)NULL); - /* del u[v:w] */ - Py_DECREF(u); - Py_XDECREF(v); - Py_XDECREF(w); - if (err == 0) continue; - break; - - case STORE_SUBSCR: - w = TOP(); - v = SECOND(); - u = THIRD(); - STACKADJ(-3); - /* v[w] = u */ - err = PyObject_SetItem(v, w, u); - Py_DECREF(u); - Py_DECREF(v); - Py_DECREF(w); - if (err == 0) continue; - break; - - case DELETE_SUBSCR: - w = TOP(); - v = SECOND(); - STACKADJ(-2); - /* del v[w] */ - err = PyObject_DelItem(v, w); - Py_DECREF(v); - Py_DECREF(w); - if (err == 0) continue; - break; - - case PRINT_EXPR: - v = POP(); - w = PySys_GetObject("displayhook"); - if (w == NULL) { - PyErr_SetString(PyExc_RuntimeError, - "lost sys.displayhook"); - err = -1; - x = NULL; - } - if (err == 0) { - x = PyTuple_Pack(1, v); - if (x == NULL) - err = -1; - } - if (err == 0) { - w = PyEval_CallObject(w, x); - Py_XDECREF(w); - if (w == NULL) - err = -1; - } - Py_DECREF(v); - Py_XDECREF(x); - break; - - case PRINT_ITEM_TO: - w = stream = POP(); - /* fall through to PRINT_ITEM */ - - case PRINT_ITEM: - v = POP(); - if (stream == NULL || stream == Py_None) { - w = PySys_GetObject("stdout"); - if (w == NULL) { - PyErr_SetString(PyExc_RuntimeError, - "lost sys.stdout"); - err = -1; - } - } - /* PyFile_SoftSpace() can exececute arbitrary code - if sys.stdout is an instance with a __getattr__. - If __getattr__ raises an exception, w will - be freed, so we need to prevent that temporarily. */ - Py_XINCREF(w); - if (w != NULL && PyFile_SoftSpace(w, 0)) - err = PyFile_WriteString(" ", w); - if (err == 0) - err = PyFile_WriteObject(v, w, Py_PRINT_RAW); - if (err == 0) { - /* XXX move into writeobject() ? */ - if (PyString_Check(v)) { - char *s = PyString_AS_STRING(v); - Py_ssize_t len = PyString_GET_SIZE(v); - if (len == 0 || - !isspace(Py_CHARMASK(s[len-1])) || - s[len-1] == ' ') - PyFile_SoftSpace(w, 1); - } -#ifdef Py_USING_UNICODE - else if (PyUnicode_Check(v)) { - Py_UNICODE *s = PyUnicode_AS_UNICODE(v); - Py_ssize_t len = PyUnicode_GET_SIZE(v); - if (len == 0 || - !Py_UNICODE_ISSPACE(s[len-1]) || - s[len-1] == ' ') - PyFile_SoftSpace(w, 1); - } -#endif - else - PyFile_SoftSpace(w, 1); - } - Py_XDECREF(w); - Py_DECREF(v); - Py_XDECREF(stream); - stream = NULL; - if (err == 0) - continue; - break; - - case PRINT_NEWLINE_TO: - w = stream = POP(); - /* fall through to PRINT_NEWLINE */ - - case PRINT_NEWLINE: - if (stream == NULL || stream == Py_None) { - w = PySys_GetObject("stdout"); - if (w == NULL) { - PyErr_SetString(PyExc_RuntimeError, - "lost sys.stdout"); - why = WHY_EXCEPTION; - } - } - if (w != NULL) { - /* w.write() may replace sys.stdout, so we - * have to keep our reference to it */ - Py_INCREF(w); - err = PyFile_WriteString("\n", w); - if (err == 0) - PyFile_SoftSpace(w, 0); - Py_DECREF(w); - } - Py_XDECREF(stream); - stream = NULL; - break; - - -#ifdef CASE_TOO_BIG - default: switch (opcode) { -#endif - case RAISE_VARARGS: - u = v = w = NULL; - switch (oparg) { - case 3: - u = POP(); /* traceback */ - /* Fallthrough */ - case 2: - v = POP(); /* value */ - /* Fallthrough */ - case 1: - w = POP(); /* exc */ - case 0: /* Fallthrough */ - why = do_raise(w, v, u); - break; - default: - PyErr_SetString(PyExc_SystemError, - "bad RAISE_VARARGS oparg"); - why = WHY_EXCEPTION; - break; - } - break; - - case LOAD_LOCALS: - if ((x = f->f_locals) != NULL) { - Py_INCREF(x); - PUSH(x); - continue; - } - PyErr_SetString(PyExc_SystemError, "no locals"); - break; - - case RETURN_VALUE: - retval = POP(); - why = WHY_RETURN; - goto fast_block_end; - - case YIELD_VALUE: - retval = POP(); - f->f_stacktop = stack_pointer; - why = WHY_YIELD; - goto fast_yield; - - case EXEC_STMT: - w = TOP(); - v = SECOND(); - u = THIRD(); - STACKADJ(-3); - READ_TIMESTAMP(intr0); - err = exec_statement(f, u, v, w); - READ_TIMESTAMP(intr1); - Py_DECREF(u); - Py_DECREF(v); - Py_DECREF(w); - break; - - case POP_BLOCK: - { - PyTryBlock *b = PyFrame_BlockPop(f); - while (STACK_LEVEL() > b->b_level) { - v = POP(); - Py_DECREF(v); - } - } - continue; - - PREDICTED(END_FINALLY); - case END_FINALLY: - v = POP(); - if (PyInt_Check(v)) { - why = (enum why_code) PyInt_AS_LONG(v); - assert(why != WHY_YIELD); - if (why == WHY_RETURN || - why == WHY_CONTINUE) - retval = POP(); - } - else if (PyExceptionClass_Check(v) || - PyString_Check(v)) { - w = POP(); - u = POP(); - PyErr_Restore(v, w, u); - why = WHY_RERAISE; - break; - } - else if (v != Py_None) { - PyErr_SetString(PyExc_SystemError, - "'finally' pops bad exception"); - why = WHY_EXCEPTION; - } - Py_DECREF(v); - break; - - case BUILD_CLASS: - u = TOP(); - v = SECOND(); - w = THIRD(); - STACKADJ(-2); - x = build_class(u, v, w); - SET_TOP(x); - Py_DECREF(u); - Py_DECREF(v); - Py_DECREF(w); - break; - - case STORE_NAME: - w = GETITEM(names, oparg); - v = POP(); - if ((x = f->f_locals) != NULL) { - if (PyDict_CheckExact(x)) - err = PyDict_SetItem(x, w, v); - else - err = PyObject_SetItem(x, w, v); - Py_DECREF(v); - if (err == 0) continue; - break; - } - PyErr_Format(PyExc_SystemError, - "no locals found when storing %s", - PyObject_REPR(w)); - break; - - case DELETE_NAME: - w = GETITEM(names, oparg); - if ((x = f->f_locals) != NULL) { - if ((err = PyObject_DelItem(x, w)) != 0) - format_exc_check_arg(PyExc_NameError, - NAME_ERROR_MSG, - w); - break; - } - PyErr_Format(PyExc_SystemError, - "no locals when deleting %s", - PyObject_REPR(w)); - break; - - PREDICTED_WITH_ARG(UNPACK_SEQUENCE); - case UNPACK_SEQUENCE: - v = POP(); - if (PyTuple_CheckExact(v) && - PyTuple_GET_SIZE(v) == oparg) { - PyObject **items = \ - ((PyTupleObject *)v)->ob_item; - while (oparg--) { - w = items[oparg]; - Py_INCREF(w); - PUSH(w); - } - Py_DECREF(v); - continue; - } else if (PyList_CheckExact(v) && - PyList_GET_SIZE(v) == oparg) { - PyObject **items = \ - ((PyListObject *)v)->ob_item; - while (oparg--) { - w = items[oparg]; - Py_INCREF(w); - PUSH(w); - } - } else if (unpack_iterable(v, oparg, - stack_pointer + oparg)) { - stack_pointer += oparg; - } else { - /* unpack_iterable() raised an exception */ - why = WHY_EXCEPTION; - } - Py_DECREF(v); - break; - - case STORE_ATTR: - w = GETITEM(names, oparg); - v = TOP(); - u = SECOND(); - STACKADJ(-2); - err = PyObject_SetAttr(v, w, u); /* v.w = u */ - Py_DECREF(v); - Py_DECREF(u); - if (err == 0) continue; - break; - - case DELETE_ATTR: - w = GETITEM(names, oparg); - v = POP(); - err = PyObject_SetAttr(v, w, (PyObject *)NULL); - /* del v.w */ - Py_DECREF(v); - break; - - case STORE_GLOBAL: - w = GETITEM(names, oparg); - v = POP(); - err = PyDict_SetItem(f->f_globals, w, v); - Py_DECREF(v); - if (err == 0) continue; - break; - - case DELETE_GLOBAL: - w = GETITEM(names, oparg); - if ((err = PyDict_DelItem(f->f_globals, w)) != 0) - format_exc_check_arg( - PyExc_NameError, GLOBAL_NAME_ERROR_MSG, w); - break; - - case LOAD_NAME: - w = GETITEM(names, oparg); - if ((v = f->f_locals) == NULL) { - PyErr_Format(PyExc_SystemError, - "no locals when loading %s", - PyObject_REPR(w)); - why = WHY_EXCEPTION; - break; - } - if (PyDict_CheckExact(v)) { - x = PyDict_GetItem(v, w); - Py_XINCREF(x); - } - else { - x = PyObject_GetItem(v, w); - if (x == NULL && PyErr_Occurred()) { - if (!PyErr_ExceptionMatches( - PyExc_KeyError)) - break; - PyErr_Clear(); - } - } - if (x == NULL) { - x = PyDict_GetItem(f->f_globals, w); - if (x == NULL) { - x = PyDict_GetItem(f->f_builtins, w); - if (x == NULL) { - format_exc_check_arg( - PyExc_NameError, - NAME_ERROR_MSG, w); - break; - } - } - Py_INCREF(x); - } - PUSH(x); - continue; - - case LOAD_GLOBAL: - w = GETITEM(names, oparg); - if (PyString_CheckExact(w)) { - /* Inline the PyDict_GetItem() calls. - WARNING: this is an extreme speed hack. - Do not try this at home. */ - long hash = ((PyStringObject *)w)->ob_shash; - if (hash != -1) { - PyDictObject *d; - PyDictEntry *e; - d = (PyDictObject *)(f->f_globals); - e = d->ma_lookup(d, w, hash); - if (e == NULL) { - x = NULL; - break; - } - x = e->me_value; - if (x != NULL) { - Py_INCREF(x); - PUSH(x); - continue; - } - d = (PyDictObject *)(f->f_builtins); - e = d->ma_lookup(d, w, hash); - if (e == NULL) { - x = NULL; - break; - } - x = e->me_value; - if (x != NULL) { - Py_INCREF(x); - PUSH(x); - continue; - } - goto load_global_error; - } - } - /* This is the un-inlined version of the code above */ - x = PyDict_GetItem(f->f_globals, w); - if (x == NULL) { - x = PyDict_GetItem(f->f_builtins, w); - if (x == NULL) { - load_global_error: - format_exc_check_arg( - PyExc_NameError, - GLOBAL_NAME_ERROR_MSG, w); - break; - } - } - Py_INCREF(x); - PUSH(x); - continue; - - case DELETE_FAST: - x = GETLOCAL(oparg); - if (x != NULL) { - SETLOCAL(oparg, NULL); - continue; - } - format_exc_check_arg( - PyExc_UnboundLocalError, - UNBOUNDLOCAL_ERROR_MSG, - PyTuple_GetItem(co->co_varnames, oparg) - ); - break; - - case LOAD_CLOSURE: - x = freevars[oparg]; - Py_INCREF(x); - PUSH(x); - if (x != NULL) continue; - break; - - case LOAD_DEREF: - x = freevars[oparg]; - w = PyCell_Get(x); - if (w != NULL) { - PUSH(w); - continue; - } - err = -1; - /* Don't stomp existing exception */ - if (PyErr_Occurred()) - break; - if (oparg < PyTuple_GET_SIZE(co->co_cellvars)) { - v = PyTuple_GET_ITEM(co->co_cellvars, - oparg); - format_exc_check_arg( - PyExc_UnboundLocalError, - UNBOUNDLOCAL_ERROR_MSG, - v); - } else { - v = PyTuple_GET_ITEM(co->co_freevars, oparg - - PyTuple_GET_SIZE(co->co_cellvars)); - format_exc_check_arg(PyExc_NameError, - UNBOUNDFREE_ERROR_MSG, v); - } - break; - - case STORE_DEREF: - w = POP(); - x = freevars[oparg]; - PyCell_Set(x, w); - Py_DECREF(w); - continue; - - case BUILD_TUPLE: - x = PyTuple_New(oparg); - if (x != NULL) { - for (; --oparg >= 0;) { - w = POP(); - PyTuple_SET_ITEM(x, oparg, w); - } - PUSH(x); - continue; - } - break; - - case BUILD_LIST: - x = PyList_New(oparg); - if (x != NULL) { - for (; --oparg >= 0;) { - w = POP(); - PyList_SET_ITEM(x, oparg, w); - } - PUSH(x); - continue; - } - break; - - case BUILD_MAP: - x = _PyDict_NewPresized((Py_ssize_t)oparg); - PUSH(x); - if (x != NULL) continue; - break; - - case STORE_MAP: - w = TOP(); /* key */ - u = SECOND(); /* value */ - v = THIRD(); /* dict */ - STACKADJ(-2); - assert (PyDict_CheckExact(v)); - err = PyDict_SetItem(v, w, u); /* v[w] = u */ - Py_DECREF(u); - Py_DECREF(w); - if (err == 0) continue; - break; - - case LOAD_ATTR: - w = GETITEM(names, oparg); - v = TOP(); - x = PyObject_GetAttr(v, w); - Py_DECREF(v); - SET_TOP(x); - if (x != NULL) continue; - break; - - case COMPARE_OP: - w = POP(); - v = TOP(); - if (PyInt_CheckExact(w) && PyInt_CheckExact(v)) { - /* INLINE: cmp(int, int) */ - register long a, b; - register int res; - a = PyInt_AS_LONG(v); - b = PyInt_AS_LONG(w); - switch (oparg) { - case PyCmp_LT: res = a < b; break; - case PyCmp_LE: res = a <= b; break; - case PyCmp_EQ: res = a == b; break; - case PyCmp_NE: res = a != b; break; - case PyCmp_GT: res = a > b; break; - case PyCmp_GE: res = a >= b; break; - case PyCmp_IS: res = v == w; break; - case PyCmp_IS_NOT: res = v != w; break; - default: goto slow_compare; - } - x = res ? Py_True : Py_False; - Py_INCREF(x); - } - else { - slow_compare: - x = cmp_outcome(oparg, v, w); - } - Py_DECREF(v); - Py_DECREF(w); - SET_TOP(x); - if (x == NULL) break; - PREDICT(JUMP_IF_FALSE); - PREDICT(JUMP_IF_TRUE); - continue; - - case IMPORT_NAME: - w = GETITEM(names, oparg); - x = PyDict_GetItemString(f->f_builtins, "__import__"); - if (x == NULL) { - PyErr_SetString(PyExc_ImportError, - "__import__ not found"); - break; - } - Py_INCREF(x); - v = POP(); - u = TOP(); - if (PyInt_AsLong(u) != -1 || PyErr_Occurred()) - w = PyTuple_Pack(5, - w, - f->f_globals, - f->f_locals == NULL ? - Py_None : f->f_locals, - v, - u); - else - w = PyTuple_Pack(4, - w, - f->f_globals, - f->f_locals == NULL ? - Py_None : f->f_locals, - v); - Py_DECREF(v); - Py_DECREF(u); - if (w == NULL) { - u = POP(); - Py_DECREF(x); - x = NULL; - break; - } - READ_TIMESTAMP(intr0); - v = x; - x = PyEval_CallObject(v, w); - Py_DECREF(v); - READ_TIMESTAMP(intr1); - Py_DECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case IMPORT_STAR: - v = POP(); - PyFrame_FastToLocals(f); - if ((x = f->f_locals) == NULL) { - PyErr_SetString(PyExc_SystemError, - "no locals found during 'import *'"); - break; - } - READ_TIMESTAMP(intr0); - err = import_all_from(x, v); - READ_TIMESTAMP(intr1); - PyFrame_LocalsToFast(f, 0); - Py_DECREF(v); - if (err == 0) continue; - break; - - case IMPORT_FROM: - w = GETITEM(names, oparg); - v = TOP(); - READ_TIMESTAMP(intr0); - x = import_from(v, w); - READ_TIMESTAMP(intr1); - PUSH(x); - if (x != NULL) continue; - break; - - case JUMP_FORWARD: - JUMPBY(oparg); - goto fast_next_opcode; - - PREDICTED_WITH_ARG(JUMP_IF_FALSE); - case JUMP_IF_FALSE: - w = TOP(); - if (w == Py_True) { - PREDICT(POP_TOP); - goto fast_next_opcode; - } - if (w == Py_False) { - JUMPBY(oparg); - goto fast_next_opcode; - } - err = PyObject_IsTrue(w); - if (err > 0) - err = 0; - else if (err == 0) - JUMPBY(oparg); - else - break; - continue; - - PREDICTED_WITH_ARG(JUMP_IF_TRUE); - case JUMP_IF_TRUE: - w = TOP(); - if (w == Py_False) { - PREDICT(POP_TOP); - goto fast_next_opcode; - } - if (w == Py_True) { - JUMPBY(oparg); - goto fast_next_opcode; - } - err = PyObject_IsTrue(w); - if (err > 0) { - err = 0; - JUMPBY(oparg); - } - else if (err == 0) - ; - else - break; - continue; - - PREDICTED_WITH_ARG(JUMP_ABSOLUTE); - case JUMP_ABSOLUTE: - JUMPTO(oparg); -#if FAST_LOOPS - /* Enabling this path speeds-up all while and for-loops by bypassing - the per-loop checks for signals. By default, this should be turned-off - because it prevents detection of a control-break in tight loops like - "while 1: pass". Compile with this option turned-on when you need - the speed-up and do not need break checking inside tight loops (ones - that contain only instructions ending with goto fast_next_opcode). - */ - goto fast_next_opcode; -#else - continue; -#endif - - case GET_ITER: - /* before: [obj]; after [getiter(obj)] */ - v = TOP(); - x = PyObject_GetIter(v); - Py_DECREF(v); - if (x != NULL) { - SET_TOP(x); - PREDICT(FOR_ITER); - continue; - } - STACKADJ(-1); - break; - - PREDICTED_WITH_ARG(FOR_ITER); - case FOR_ITER: - /* before: [iter]; after: [iter, iter()] *or* [] */ - v = TOP(); - x = (*v->ob_type->tp_iternext)(v); - if (x != NULL) { - PUSH(x); - PREDICT(STORE_FAST); - PREDICT(UNPACK_SEQUENCE); - continue; - } - if (PyErr_Occurred()) { - if (!PyErr_ExceptionMatches( - PyExc_StopIteration)) - break; - PyErr_Clear(); - } - /* iterator ended normally */ - x = v = POP(); - Py_DECREF(v); - JUMPBY(oparg); - continue; - - case BREAK_LOOP: - why = WHY_BREAK; - goto fast_block_end; - - case CONTINUE_LOOP: - retval = PyInt_FromLong(oparg); - if (!retval) { - x = NULL; - break; - } - why = WHY_CONTINUE; - goto fast_block_end; - - case SETUP_LOOP: - case SETUP_EXCEPT: - case SETUP_FINALLY: - /* NOTE: If you add any new block-setup opcodes that - are not try/except/finally handlers, you may need - to update the PyGen_NeedsFinalizing() function. - */ - - PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg, - STACK_LEVEL()); - continue; - - case WITH_CLEANUP: - { - /* At the top of the stack are 1-3 values indicating - how/why we entered the finally clause: - - TOP = None - - (TOP, SECOND) = (WHY_{RETURN,CONTINUE}), retval - - TOP = WHY_*; no retval below it - - (TOP, SECOND, THIRD) = exc_info() - Below them is EXIT, the context.__exit__ bound method. - In the last case, we must call - EXIT(TOP, SECOND, THIRD) - otherwise we must call - EXIT(None, None, None) - - In all cases, we remove EXIT from the stack, leaving - the rest in the same order. - - In addition, if the stack represents an exception, - *and* the function call returns a 'true' value, we - "zap" this information, to prevent END_FINALLY from - re-raising the exception. (But non-local gotos - should still be resumed.) - */ - - PyObject *exit_func; - - u = POP(); - if (u == Py_None) { - exit_func = TOP(); - SET_TOP(u); - v = w = Py_None; - } - else if (PyInt_Check(u)) { - switch(PyInt_AS_LONG(u)) { - case WHY_RETURN: - case WHY_CONTINUE: - /* Retval in TOP. */ - exit_func = SECOND(); - SET_SECOND(TOP()); - SET_TOP(u); - break; - default: - exit_func = TOP(); - SET_TOP(u); - break; - } - u = v = w = Py_None; - } - else { - v = TOP(); - w = SECOND(); - exit_func = THIRD(); - SET_TOP(u); - SET_SECOND(v); - SET_THIRD(w); - } - /* XXX Not the fastest way to call it... */ - x = PyObject_CallFunctionObjArgs(exit_func, u, v, w, - NULL); - Py_DECREF(exit_func); - if (x == NULL) - break; /* Go to error exit */ - - if (u != Py_None) - err = PyObject_IsTrue(x); - else - err = 0; - Py_DECREF(x); - - if (err < 0) - break; /* Go to error exit */ - else if (err > 0) { - err = 0; - /* There was an exception and a true return */ - STACKADJ(-2); - Py_INCREF(Py_None); - SET_TOP(Py_None); - Py_DECREF(u); - Py_DECREF(v); - Py_DECREF(w); - } else { - /* The stack was rearranged to remove EXIT - above. Let END_FINALLY do its thing */ - } - PREDICT(END_FINALLY); - break; - } - - case CALL_FUNCTION: - { - PyObject **sp; - PCALL(PCALL_ALL); - sp = stack_pointer; -#ifdef WITH_TSC - x = call_function(&sp, oparg, &intr0, &intr1); -#else - x = call_function(&sp, oparg); -#endif - stack_pointer = sp; - PUSH(x); - if (x != NULL) - continue; - break; - } - - case CALL_FUNCTION_VAR: - case CALL_FUNCTION_KW: - case CALL_FUNCTION_VAR_KW: - { - int na = oparg & 0xff; - int nk = (oparg>>8) & 0xff; - int flags = (opcode - CALL_FUNCTION) & 3; - int n = na + 2 * nk; - PyObject **pfunc, *func, **sp; - PCALL(PCALL_ALL); - if (flags & CALL_FLAG_VAR) - n++; - if (flags & CALL_FLAG_KW) - n++; - pfunc = stack_pointer - n - 1; - func = *pfunc; - - if (PyMethod_Check(func) - && PyMethod_GET_SELF(func) != NULL) { - PyObject *self = PyMethod_GET_SELF(func); - Py_INCREF(self); - func = PyMethod_GET_FUNCTION(func); - Py_INCREF(func); - Py_DECREF(*pfunc); - *pfunc = self; - na++; - n++; - } else - Py_INCREF(func); - sp = stack_pointer; - READ_TIMESTAMP(intr0); - x = ext_do_call(func, &sp, flags, na, nk); - READ_TIMESTAMP(intr1); - stack_pointer = sp; - Py_DECREF(func); - - while (stack_pointer > pfunc) { - w = POP(); - Py_DECREF(w); - } - PUSH(x); - if (x != NULL) - continue; - break; - } - - case MAKE_FUNCTION: - v = POP(); /* code object */ - x = PyFunction_New(v, f->f_globals); - Py_DECREF(v); - /* XXX Maybe this should be a separate opcode? */ - if (x != NULL && oparg > 0) { - v = PyTuple_New(oparg); - if (v == NULL) { - Py_DECREF(x); - x = NULL; - break; - } - while (--oparg >= 0) { - w = POP(); - PyTuple_SET_ITEM(v, oparg, w); - } - err = PyFunction_SetDefaults(x, v); - Py_DECREF(v); - } - PUSH(x); - break; - - case MAKE_CLOSURE: - { - v = POP(); /* code object */ - x = PyFunction_New(v, f->f_globals); - Py_DECREF(v); - if (x != NULL) { - v = POP(); - if (PyFunction_SetClosure(x, v) != 0) { - /* Can't happen unless bytecode is corrupt. */ - why = WHY_EXCEPTION; - } - Py_DECREF(v); - } - if (x != NULL && oparg > 0) { - v = PyTuple_New(oparg); - if (v == NULL) { - Py_DECREF(x); - x = NULL; - break; - } - while (--oparg >= 0) { - w = POP(); - PyTuple_SET_ITEM(v, oparg, w); - } - if (PyFunction_SetDefaults(x, v) != 0) { - /* Can't happen unless - PyFunction_SetDefaults changes. */ - why = WHY_EXCEPTION; - } - Py_DECREF(v); - } - PUSH(x); - break; - } - - case BUILD_SLICE: - if (oparg == 3) - w = POP(); - else - w = NULL; - v = POP(); - u = TOP(); - x = PySlice_New(u, v, w); - Py_DECREF(u); - Py_DECREF(v); - Py_XDECREF(w); - SET_TOP(x); - if (x != NULL) continue; - break; - - case EXTENDED_ARG: - opcode = NEXTOP(); - oparg = oparg<<16 | NEXTARG(); - goto dispatch_opcode; - - default: - fprintf(stderr, - "XXX lineno: %d, opcode: %d\n", - PyCode_Addr2Line(f->f_code, f->f_lasti), - opcode); - PyErr_SetString(PyExc_SystemError, "unknown opcode"); - why = WHY_EXCEPTION; - break; - -#ifdef CASE_TOO_BIG - } -#endif - - } /* switch */ - - on_error: - - READ_TIMESTAMP(inst1); - - /* Quickly continue if no error occurred */ - - if (why == WHY_NOT) { - if (err == 0 && x != NULL) { -#ifdef CHECKEXC - /* This check is expensive! */ - if (PyErr_Occurred()) - fprintf(stderr, - "XXX undetected error\n"); - else { -#endif - READ_TIMESTAMP(loop1); - continue; /* Normal, fast path */ -#ifdef CHECKEXC - } -#endif - } - why = WHY_EXCEPTION; - x = Py_None; - err = 0; - } - - /* Double-check exception status */ - - if (why == WHY_EXCEPTION || why == WHY_RERAISE) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_SystemError, - "error return without exception set"); - why = WHY_EXCEPTION; - } - } -#ifdef CHECKEXC - else { - /* This check is expensive! */ - if (PyErr_Occurred()) { - char buf[128]; - sprintf(buf, "Stack unwind with exception " - "set and why=%d", why); - Py_FatalError(buf); - } - } -#endif - - /* Log traceback info if this is a real exception */ - - if (why == WHY_EXCEPTION) { - PyTraceBack_Here(f); - - if (tstate->c_tracefunc != NULL) - call_exc_trace(tstate->c_tracefunc, - tstate->c_traceobj, f); - } - - /* For the rest, treat WHY_RERAISE as WHY_EXCEPTION */ - - if (why == WHY_RERAISE) - why = WHY_EXCEPTION; - - /* Unwind stacks if a (pseudo) exception occurred */ - fast_block_end: - while (why != WHY_NOT && f->f_iblock > 0) { - PyTryBlock *b = PyFrame_BlockPop(f); + while (why != WHY_NOT && f->f_iblock > 0) { + PyTryBlock *b = PyFrame_BlockPop(f); - assert(why != WHY_YIELD); - if (b->b_type == SETUP_LOOP && why == WHY_CONTINUE) { - /* For a continue inside a try block, - don't pop the block for the loop. */ - PyFrame_BlockSetup(f, b->b_type, b->b_handler, - b->b_level); - why = WHY_NOT; - JUMPTO(PyInt_AS_LONG(retval)); - Py_DECREF(retval); - break; - } + assert(why != WHY_YIELD); + if (b->b_type == SETUP_LOOP && why == WHY_CONTINUE) { + /* For a continue inside a try block, + don't pop the block for the loop. */ + PyFrame_BlockSetup(f, b->b_type, b->b_handler, + b->b_level); + why = WHY_NOT; + JUMPTO(PyInt_AS_LONG(retval)); + Py_DECREF(retval); + break; + } - while (STACK_LEVEL() > b->b_level) { - v = POP(); - Py_XDECREF(v); - } - if (b->b_type == SETUP_LOOP && why == WHY_BREAK) { - why = WHY_NOT; - JUMPTO(b->b_handler); - break; - } - if (b->b_type == SETUP_FINALLY || - (b->b_type == SETUP_EXCEPT && - why == WHY_EXCEPTION)) { - if (why == WHY_EXCEPTION) { - PyObject *exc, *val, *tb; - PyErr_Fetch(&exc, &val, &tb); - if (val == NULL) { - val = Py_None; - Py_INCREF(val); - } - /* Make the raw exception data - available to the handler, - so a program can emulate the - Python main loop. Don't do - this for 'finally'. */ - if (b->b_type == SETUP_EXCEPT) { - PyErr_NormalizeException( - &exc, &val, &tb); - set_exc_info(tstate, - exc, val, tb); - } - if (tb == NULL) { - Py_INCREF(Py_None); - PUSH(Py_None); - } else - PUSH(tb); - PUSH(val); - PUSH(exc); - } - else { - if (why & (WHY_RETURN | WHY_CONTINUE)) - PUSH(retval); - v = PyInt_FromLong((long)why); - PUSH(v); - } - why = WHY_NOT; - JUMPTO(b->b_handler); - break; - } - } /* unwind stack */ + while (STACK_LEVEL() > b->b_level) { + a1 = POP(); + Py_XDECREF(a1); + } + if (b->b_type == SETUP_LOOP && why == WHY_BREAK) { + why = WHY_NOT; + JUMPTO(b->b_handler); + break; + } + if (b->b_type == SETUP_FINALLY || + (b->b_type == SETUP_EXCEPT && + why == WHY_EXCEPTION)) { + if (why == WHY_EXCEPTION) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + if (val == NULL) { + val = Py_None; + Py_INCREF(val); + } + /* Make the raw exception data + available to the handler, + so a program can emulate the + Python main loop. Don't do + this for 'finally'. */ + if (b->b_type == SETUP_EXCEPT) { + PyErr_NormalizeException( + &exc, &val, &tb); + set_exc_info(tstate, + exc, val, tb); + } + if (tb == NULL) { + Py_INCREF(Py_None); + PUSH(Py_None); + } else + PUSH(tb); + PUSH(val); + PUSH(exc); + } + else { + if (why & (WHY_RETURN | WHY_CONTINUE)) + PUSH(retval); + a1 = PyInt_FromLong((long)why); + PUSH(a1); + } + why = WHY_NOT; + JUMPTO(b->b_handler); + break; + } + } /* unwind stack */ - /* End the loop if we still have an error (or return) */ + /* End the loop if we still have an error (or return) */ - if (why != WHY_NOT) - break; - READ_TIMESTAMP(loop1); + if (why != WHY_NOT) + goto block_end; - } /* main loop */ + NEXT(); /* main loop */ +block_end: assert(why != WHY_YIELD); /* Pop remaining stack entries. */ while (!EMPTY()) { - v = POP(); - Py_XDECREF(v); + a1 = POP(); + Py_XDECREF(a1); } if (why != WHY_RETURN) @@ -3508,6 +1896,7 @@ return result; } +/* Returns nonzero on exception. */ static int maybe_call_line_trace(Py_tracefunc func, PyObject *obj, PyFrameObject *frame, int *instr_lb, int *instr_ub, @@ -4571,7 +2960,7 @@ static PyObject * string_concatenate(PyObject *v, PyObject *w, - PyFrameObject *f, unsigned char *next_instr) + PyFrameObject *f, int opcode, int oparg) { /* This function implements 'variable += expr' when both arguments are strings. */ @@ -4591,10 +2980,9 @@ * 'variable'. We try to delete the variable now to reduce * the refcnt to 1. */ - switch (*next_instr) { + switch (opcode) { case STORE_FAST: { - int oparg = PEEKARG(); PyObject **fastlocals = f->f_localsplus; if (GETLOCAL(oparg) == v) SETLOCAL(oparg, NULL); @@ -4602,9 +2990,8 @@ } case STORE_DEREF: { - PyObject **freevars = (f->f_localsplus + - f->f_code->co_nlocals); - PyObject *c = freevars[PEEKARG()]; + PyObject **freevars = f->f_localsplus + f->f_code->co_nlocals; + PyObject *c = freevars[oparg]; if (PyCell_GET(c) == v) PyCell_Set(c, NULL); break; @@ -4612,7 +2999,7 @@ case STORE_NAME: { PyObject *names = f->f_code->co_names; - PyObject *name = GETITEM(names, PEEKARG()); + PyObject *name = GETITEM(names, oparg); PyObject *locals = f->f_locals; if (PyDict_CheckExact(locals) && PyDict_GetItem(locals, name) == v) { Index: Python/peephole.c =================================================================== --- Python/peephole.c (revision 68532) +++ Python/peephole.c (working copy) @@ -8,18 +8,32 @@ #include "ast.h" #include "code.h" #include "compile.h" +#include "instructionsobject.h" #include "symtable.h" #include "opcode.h" -#define GETARG(arr, i) ((int)((arr[i+2]<<8) + arr[i+1])) +static void prepare_peeptable(void); + +#define GETOP(inst) PyPInst_GET_OPCODE(&(inst)) +#define OP_EQ(inst, EXPECTED) (!(inst).is_arg && GETOP(inst) == EXPECTED) +#define GETARG(arr, i) PyPInst_GET_ARG(&(arr)[(i)+1]) #define UNCONDITIONAL_JUMP(op) (op==JUMP_ABSOLUTE || op==JUMP_FORWARD) #define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP) -#define GETJUMPTGT(arr, i) (GETARG(arr,i) + (ABSOLUTE_JUMP(arr[i]) ? 0 : i+3)) -#define SETARG(arr, i, val) arr[i+2] = val>>8; arr[i+1] = val & 255 -#define CODESIZE(op) (HAS_ARG(op) ? 3 : 1) +#define GETJUMPTGT(arr, i) (GETARG(arr,i) + \ + (ABSOLUTE_JUMP(GETOP((arr)[(i)])) ? 0 : i+2)) +#define SETOP(inst, val) PyPInst_SET_OPCODE(&(inst), (val)); +#define SETARG(arr, i, val) PyPInst_SET_ARG(&(arr)[(i)+1], (val)) #define ISBASICBLOCK(blocks, start, bytes) \ (blocks[start]==blocks[start+bytes-1]) +static void +set_nops(PyPInst *inststr, Py_ssize_t num_nops) { + PyPInst *last_inst = inststr + num_nops; + for (; inststr != last_inst; ++inststr) { + SETOP(*inststr, NOP); + } +} + /* 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 @@ -29,17 +43,18 @@ Also works for BUILD_LIST when followed by an "in" or "not in" test. */ static int -tuple_of_constants(unsigned char *codestr, Py_ssize_t n, PyObject *consts) +tuple_of_constants(PyPInst *inststr, Py_ssize_t n, PyObject *consts) { PyObject *newconst, *constant; Py_ssize_t i, arg, len_consts; /* Pre-conditions */ assert(PyList_CheckExact(consts)); - assert(codestr[n*3] == BUILD_TUPLE || codestr[n*3] == BUILD_LIST); - assert(GETARG(codestr, (n*3)) == n); + assert(GETOP(inststr[n*2]) == BUILD_TUPLE || + GETOP(inststr[n*2]) == BUILD_LIST); + assert(GETARG(inststr, (n*2)) == n); for (i=0 ; i> 5)) & (HASH_SIZE-1)) + +typedef struct idx_combination { + int prefix; /* instruction or superinstruction prefix index */ + int lastprim; /* most recently added instruction index */ + int combination; /* resulting superinstruction index */ + struct idx_combination *next; +} IdxCombination; + +static IdxCombination peephole_table[] = { +#include "ceval-peephole.i" +}; + +static IdxCombination *peeptable[HASH_SIZE]; + +static void +prepare_peeptable() +{ + long i; + + for (i = 0; i < sizeof(peephole_table)/sizeof(peephole_table[0]); i++) { + IdxCombination *c = &(peephole_table[i]); + IdxCombination *p = (IdxCombination*) malloc(sizeof(*p)); + + p->prefix = c->prefix; + p->lastprim = c->lastprim; + p->combination = c->combination; + + long h = HASH(p->prefix, p->lastprim); + p->next = peeptable[h]; + peeptable[h] = p; + } +} + +static int +combine_two(int op1, int op2) +{ + IdxCombination *p; + + for (p = peeptable[HASH(op1, op2)]; p != NULL; p = p->next) + if (op1 == p->prefix && op2 == p->lastprim) + return p->combination; + + return -1; +} + +/* Combines basic instructions into superinstructions */ +static void +combine_to_superinstructions(PyPInst *inststr, Py_ssize_t codelen, + unsigned int *blocks) +{ + /* First element of inststr is always an opcode. */ + int working_on = 0; + int next_arg = 1; + int i; + + for (i=1; i < codelen; i++) { + if (inststr[i].is_arg) { + inststr[next_arg] = inststr[i]; + next_arg++; + } + else { + int super_op = -1; + /* See ISBASICBLOCK() */ + if (blocks[working_on] == blocks[i]) { + super_op = combine_two(GETOP(inststr[working_on]), + GETOP(inststr[i])); + } + if (super_op == -1) { + /* Start new superinstruction */ + set_nops(inststr + next_arg, i - next_arg); + working_on = i; + next_arg = i + 1; + } + else { + /* Continue an existing superinstruction */ + SETOP(inststr[working_on], super_op); + } + } + } +} + /* Perform basic peephole optimizations to components of a code object. 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 - containing extended arguments or that has a length over 32,700. That - allows us to avoid overflow and sign issues. Likewise, it bails when - the lineno table has complex encoding for gaps >= 255. + To keep the optimizer simple, it bails out (does nothing) for code that + has a length over 32,700. That allows us to avoid overflow and sign + issues. Likewise, 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 @@ -285,16 +398,23 @@ PyObject *lineno_obj) { Py_ssize_t i, j, codelen; - int nops, h, adj; + int nops, h; int tgt, tgttgt, opcode; - unsigned char *codestr = NULL; + PyInstructionsObject *modcode = NULL; + PyPInst *inststr; unsigned char *lineno; int *addrmap = NULL; - int new_line, cum_orig_line, last_line, tabsiz; + int new_code, cum_orig_code_offset, last_code, tabsiz; int cumlc=0, lastlc=0; /* Count runs of consecutive LOAD_CONSTs */ unsigned int *blocks = NULL; char *name; + static int init = 1; + if (init) { + prepare_peeptable(); + init = 0; + } + /* Bail out if an exception is set */ if (PyErr_Occurred()) goto exitUnchanged; @@ -307,24 +427,24 @@ goto exitUnchanged; /* Avoid situations where jump retargeting could overflow */ - assert(PyString_Check(code)); - codelen = PyString_GET_SIZE(code); + codelen = Py_SIZE(code); if (codelen > 32700) goto exitUnchanged; /* Make a modifiable copy of the code string */ - codestr = (unsigned char *)PyMem_Malloc(codelen); - if (codestr == NULL) + modcode = _PyInstructions_New(codelen); + if (modcode == NULL) goto exitUnchanged; - codestr = (unsigned char *)memcpy(codestr, - PyString_AS_STRING(code), codelen); + inststr = modcode->inst; + memcpy(inststr, ((PyInstructionsObject *)code)->inst, + codelen * sizeof(modcode->inst[0])); /* 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) + if (!OP_EQ(inststr[codelen-1], RETURN_VALUE)) goto exitUnchanged; /* Mapping to new jump targets after NOPs are removed */ @@ -332,13 +452,15 @@ if (addrmap == NULL) goto exitUnchanged; - blocks = markblocks(codestr, codelen); + blocks = markblocks(inststr, Py_SIZE(modcode)); if (blocks == NULL) goto exitUnchanged; assert(PyList_Check(consts)); - for (i=0 ; i a is not b @@ -368,20 +490,20 @@ not a not in b --> a in b */ case COMPARE_OP: - j = GETARG(codestr, i); + j = GETARG(inststr, i); if (j < 6 || j > 9 || - codestr[i+3] != UNARY_NOT || - !ISBASICBLOCK(blocks,i,4)) + GETOP(inststr[i+2]) != UNARY_NOT || + !ISBASICBLOCK(blocks,i,3)) continue; - SETARG(codestr, i, (j^1)); - codestr[i+3] = NOP; + SETARG(inststr, i, (j^1)); + SETOP(inststr[i+2], NOP); break; /* Replace LOAD_GLOBAL/LOAD_NAME None with LOAD_CONST None */ case LOAD_NAME: case LOAD_GLOBAL: - j = GETARG(codestr, i); + j = GETARG(inststr, i); name = PyString_AsString(PyTuple_GET_ITEM(names, j)); if (name == NULL || strcmp(name, "None") != 0) continue; @@ -394,8 +516,8 @@ goto exitUnchanged; } assert(PyList_GET_ITEM(consts, j) == Py_None); - codestr[i] = LOAD_CONST; - SETARG(codestr, i, j); + SETOP(inststr[i], LOAD_CONST); + SETARG(inststr, i, j); cumlc = lastlc + 1; break; @@ -403,13 +525,13 @@ JUMP_IF_FALSE xx POP_TOP */ case LOAD_CONST: cumlc = lastlc + 1; - j = GETARG(codestr, i); - if (codestr[i+3] != JUMP_IF_FALSE || - codestr[i+6] != POP_TOP || - !ISBASICBLOCK(blocks,i,7) || + j = GETARG(inststr, i); + if (GETOP(inststr[i+2]) != JUMP_IF_FALSE || + GETOP(inststr[i+4]) != POP_TOP || + !ISBASICBLOCK(blocks,i,5) || !PyObject_IsTrue(PyList_GET_ITEM(consts, j))) continue; - memset(codestr+i, NOP, 7); + set_nops(inststr+i, 5); cumlc = 0; break; @@ -420,35 +542,35 @@ Replace BUILD_SEQN 3 UNPACK_SEQN 3 with ROT3 ROT2. */ case BUILD_TUPLE: case BUILD_LIST: - j = GETARG(codestr, i); - h = i - 3 * j; + j = GETARG(inststr, i); + h = i - 2 * j; if (h >= 0 && j <= lastlc && ((opcode == BUILD_TUPLE && - ISBASICBLOCK(blocks, h, 3*(j+1))) || + ISBASICBLOCK(blocks, h, 2*(j+1))) || (opcode == BUILD_LIST && - codestr[i+3]==COMPARE_OP && - ISBASICBLOCK(blocks, h, 3*(j+2)) && - (GETARG(codestr,i+3)==6 || - GETARG(codestr,i+3)==7))) && - tuple_of_constants(&codestr[h], j, consts)) { - assert(codestr[i] == LOAD_CONST); + GETOP(inststr[i+2])==COMPARE_OP && + ISBASICBLOCK(blocks, h, 2*(j+2)) && + (GETARG(inststr,i+2)==6 || + GETARG(inststr,i+2)==7))) && + tuple_of_constants(&inststr[h], j, consts)) { + assert(GETOP(inststr[i]) == LOAD_CONST); cumlc = 1; break; } - if (codestr[i+3] != UNPACK_SEQUENCE || - !ISBASICBLOCK(blocks,i,6) || - j != GETARG(codestr, i+3)) + if (GETOP(inststr[i+2]) != UNPACK_SEQUENCE || + !ISBASICBLOCK(blocks,i,4) || + j != GETARG(inststr, i+2)) continue; if (j == 1) { - memset(codestr+i, NOP, 6); + set_nops(inststr+i, 4); } else if (j == 2) { - codestr[i] = ROT_TWO; - memset(codestr+i+1, NOP, 5); + SETOP(inststr[i], ROT_TWO); + set_nops(inststr+i+1, 3); } else if (j == 3) { - codestr[i] = ROT_THREE; - codestr[i+1] = ROT_TWO; - memset(codestr+i+2, NOP, 4); + SETOP(inststr[i], ROT_THREE); + SETOP(inststr[i+1], ROT_TWO); + set_nops(inststr+i+2, 2); } break; @@ -468,10 +590,10 @@ case BINARY_XOR: case BINARY_OR: if (lastlc >= 2 && - ISBASICBLOCK(blocks, i-6, 7) && - fold_binops_on_constants(&codestr[i-6], consts)) { - i -= 2; - assert(codestr[i] == LOAD_CONST); + ISBASICBLOCK(blocks, i-4, 5) && + fold_binops_on_constants(&inststr[i-4], consts)) { + i -= 1; + assert(GETOP(inststr[i]) == LOAD_CONST); cumlc = 1; } break; @@ -482,10 +604,10 @@ case UNARY_CONVERT: case UNARY_INVERT: if (lastlc >= 1 && - ISBASICBLOCK(blocks, i-3, 4) && - fold_unaryops_on_constants(&codestr[i-3], consts)) { - i -= 2; - assert(codestr[i] == LOAD_CONST); + ISBASICBLOCK(blocks, i-2, 3) && + fold_unaryops_on_constants(&inststr[i-2], consts)) { + i -= 1; + assert(GETOP(inststr[i]) == LOAD_CONST); cumlc = 1; } break; @@ -499,20 +621,20 @@ "a and b or c" "(a and b) and c" x:JUMP_IF_FALSE y y:JUMP_IF_FALSE z --> x:JUMP_IF_FALSE z - x:JUMP_IF_FALSE y y:JUMP_IF_TRUE z --> x:JUMP_IF_FALSE y+3 - where y+3 is the instruction following the second test. + x:JUMP_IF_FALSE y y:JUMP_IF_TRUE z --> x:JUMP_IF_FALSE y+2 + where y+2 is the instruction following the second test. */ case JUMP_IF_FALSE: case JUMP_IF_TRUE: - tgt = GETJUMPTGT(codestr, i); - j = codestr[tgt]; + tgt = GETJUMPTGT(inststr, i); + j = GETOP(inststr[tgt]); if (j == JUMP_IF_FALSE || j == JUMP_IF_TRUE) { if (j == opcode) { - tgttgt = GETJUMPTGT(codestr, tgt) - i - 3; - SETARG(codestr, i, tgttgt); + tgttgt = GETJUMPTGT(inststr, tgt) - i - 2; + SETARG(inststr, i, tgttgt); } else { tgt -= i; - SETARG(codestr, i, tgt); + SETARG(inststr, i, tgt); } break; } @@ -526,64 +648,75 @@ case SETUP_LOOP: case SETUP_EXCEPT: case SETUP_FINALLY: - tgt = GETJUMPTGT(codestr, i); + tgt = GETJUMPTGT(inststr, i); /* 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); + GETOP(inststr[tgt]) == RETURN_VALUE) { + SETOP(inststr[i], RETURN_VALUE); + memset(inststr+i+1, NOP, 1); continue; } - if (!UNCONDITIONAL_JUMP(codestr[tgt])) + if (!UNCONDITIONAL_JUMP(GETOP(inststr[tgt]))) continue; - tgttgt = GETJUMPTGT(codestr, tgt); + tgttgt = GETJUMPTGT(inststr, tgt); if (opcode == JUMP_FORWARD) /* JMP_ABS can go backwards */ opcode = JUMP_ABSOLUTE; if (!ABSOLUTE_JUMP(opcode)) - tgttgt -= i + 3; /* Calc relative jump addr */ + tgttgt -= i + 2; /* Calc relative jump addr */ if (tgttgt < 0) /* No backward relative jumps */ continue; - codestr[i] = opcode; - SETARG(codestr, i, tgttgt); + SETOP(inststr[i], opcode); + SETARG(inststr, i, tgttgt); break; - case EXTENDED_ARG: - goto exitUnchanged; - /* Replace RETURN LOAD_CONST None RETURN with just RETURN */ /* Remove unreachable JUMPs after RETURN */ case RETURN_VALUE: - if (i+4 >= codelen) + if (i+3 >= 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); + if (OP_EQ(inststr[i+3], RETURN_VALUE) && + ISBASICBLOCK(blocks,i,4)) + set_nops(inststr+i+1, 3); + else if (UNCONDITIONAL_JUMP(GETOP(inststr[i+1])) && + ISBASICBLOCK(blocks,i,3)) + set_nops(inststr+i+1, 2); break; } } + combine_to_superinstructions(inststr, codelen, blocks); + + last_code = 0; /* Fixup linenotab */ - for (i=0, nops=0 ; iwordcode for vmgen) . */ -#define MAGIC (62171 | ((long)'\r'<<16) | ((long)'\n'<<24)) +#define MAGIC (62181 | ((long)'\r'<<16) | ((long)'\n'<<24)) /* Magic word as global; note that _PyImport_Init() can change the value of this global to accommodate for alterations of how the Index: Python/mangler.awk =================================================================== --- Python/mangler.awk (revision 0) +++ Python/mangler.awk (revision 0) @@ -0,0 +1,21 @@ +#!/usr/bin/awk -f +# Mangle VMGEN output, evilly. + +/\/\* \*\// { next } +/^#line/ { next } + +/^IF___none__TOS/ { next } +/^__none__/ { next } +/^incref/ { next } +/^decref/ { next } +/^next/ { next } + +/[ \t]+$/ { sub(/[ \t]+$/, "") } + +BEGIN { s = 0 } +/^#ifdef VM_DEBUG$/ { s = 1 } +s == 0 { print } +s == 1 { } +/^#endif$/ { s = 0 } + +# eof Index: Python/marshal.c =================================================================== --- Python/marshal.c (revision 68532) +++ Python/marshal.c (working copy) @@ -9,6 +9,7 @@ #include "Python.h" #include "longintrepr.h" #include "code.h" +#include "instructionsobject.h" #include "marshal.h" /* High water mark to determine when the marshalled object is dangerously deep @@ -387,6 +388,11 @@ w_long((long)n, p); w_string(s, (int)n, p); } + else if (PyInstructions_Check(v)) { + PyObject *temp_list = PySequence_List(v); + w_object(temp_list, p); + Py_DECREF(temp_list); + } else { w_byte(TYPE_UNKNOWN, p); p->error = 1; @@ -902,6 +908,7 @@ int nlocals; int stacksize; int flags; + PyObject *code_list = NULL; PyObject *code = NULL; PyObject *consts = NULL; PyObject *names = NULL; @@ -920,7 +927,10 @@ nlocals = (int)r_long(p); stacksize = (int)r_long(p); flags = (int)r_long(p); - code = r_object(p); + code_list = r_object(p); + if (code_list == NULL) + goto code_error; + code = PyInstructions_FromSequence(code_list); if (code == NULL) goto code_error; consts = r_object(p); @@ -956,6 +966,7 @@ firstlineno, lnotab); code_error: + Py_XDECREF(code_list); Py_XDECREF(code); Py_XDECREF(consts); Py_XDECREF(names); Index: Python/compile.c =================================================================== --- Python/compile.c (revision 68532) +++ Python/compile.c (working copy) @@ -29,6 +29,7 @@ #include "ast.h" #include "code.h" #include "compile.h" +#include "instructionsobject.h" #include "symtable.h" #include "opcode.h" @@ -709,31 +710,31 @@ case INPLACE_TRUE_DIVIDE: return -1; - case SLICE+0: + case SLICE_NONE: return 1; - case SLICE+1: + case SLICE_LEFT: return 0; - case SLICE+2: + case SLICE_RIGHT: return 0; - case SLICE+3: + case SLICE_BOTH: return -1; - case STORE_SLICE+0: + case STORE_SLICE_NONE: return -2; - case STORE_SLICE+1: + case STORE_SLICE_LEFT: return -3; - case STORE_SLICE+2: + case STORE_SLICE_RIGHT: return -3; - case STORE_SLICE+3: + case STORE_SLICE_BOTH: return -4; - case DELETE_SLICE+0: + case DELETE_SLICE_NONE: return -1; - case DELETE_SLICE+1: + case DELETE_SLICE_LEFT: return -2; - case DELETE_SLICE+2: + case DELETE_SLICE_RIGHT: return -2; - case DELETE_SLICE+3: + case DELETE_SLICE_BOTH: return -3; case INPLACE_ADD: @@ -815,8 +816,10 @@ return -1; case DELETE_GLOBAL: return 0; - case DUP_TOPX: - return oparg; + case DUP_TOP_TWO: + return 2; + case DUP_TOP_THREE: + return 3; case LOAD_CONST: return 1; case LOAD_NAME: @@ -859,25 +862,26 @@ case DELETE_FAST: return 0; - case RAISE_VARARGS: - return -oparg; + case RAISE_VARARGS_ZERO: + return 0; + case RAISE_VARARGS_ONE: + return -1; + case RAISE_VARARGS_TWO: + return -2; + case RAISE_VARARGS_THREE: + return -3; #define NARGS(o) (((o) % 256) + 2*((o) / 256)) case CALL_FUNCTION: return -NARGS(oparg); - case CALL_FUNCTION_VAR: - case CALL_FUNCTION_KW: - return -NARGS(oparg)-1; case CALL_FUNCTION_VAR_KW: - return -NARGS(oparg)-2; + return -NARGS(oparg>>16) - ((oparg&1) + ((oparg>>1)&1)); #undef NARGS case MAKE_FUNCTION: return -oparg; - case BUILD_SLICE: - if (oparg == 3) - return -2; - else - return -1; - + case BUILD_SLICE_TWO: + return -1; + case BUILD_SLICE_THREE: + return -2; case MAKE_CLOSURE: return -oparg; case LOAD_CLOSURE: @@ -2089,10 +2093,10 @@ ADDOP_O(c, LOAD_GLOBAL, assertion_error, names); if (s->v.Assert.msg) { VISIT(c, expr, s->v.Assert.msg); - ADDOP_I(c, RAISE_VARARGS, 2); + ADDOP(c, RAISE_VARARGS_TWO); } else { - ADDOP_I(c, RAISE_VARARGS, 1); + ADDOP(c, RAISE_VARARGS_ONE); } compiler_use_next_block(c, end); ADDOP(c, POP_TOP); @@ -2103,6 +2107,8 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s) { int i, n; + const int raise_varargs[] = { RAISE_VARARGS_ZERO, RAISE_VARARGS_ONE, + RAISE_VARARGS_TWO, RAISE_VARARGS_THREE }; /* Always assign a lineno to the next instruction for a stmt. */ c->u->u_lineno = s->lineno; @@ -2160,7 +2166,7 @@ } } } - ADDOP_I(c, RAISE_VARARGS, n); + ADDOP(c, raise_varargs[n]); break; case TryExcept_kind: return compiler_try_except(c, s); @@ -2591,13 +2597,11 @@ ADDOP_I(c, CALL_FUNCTION, n); break; case 1: - ADDOP_I(c, CALL_FUNCTION_VAR, n); - break; case 2: - ADDOP_I(c, CALL_FUNCTION_KW, n); - break; case 3: - ADDOP_I(c, CALL_FUNCTION_VAR_KW, n); + /* XXX this doesn't work with >2^8 positional or >2^7 + keyword arguments */ + ADDOP_I(c, CALL_FUNCTION_VAR_KW, (n << 16) | code); break; } return 1; @@ -3211,7 +3215,7 @@ return 0; } if (ctx == AugLoad) { - ADDOP_I(c, DUP_TOPX, 2); + ADDOP(c, DUP_TOP_TWO); } else if (ctx == AugStore) { ADDOP(c, ROT_THREE); @@ -3223,6 +3227,7 @@ static int compiler_slice(struct compiler *c, slice_ty s, expr_context_ty ctx) { + const int build_slice[] = { -1, -1, BUILD_SLICE_TWO, BUILD_SLICE_THREE }; int n = 2; assert(s->kind == Slice_kind); @@ -3245,14 +3250,20 @@ n++; VISIT(c, expr, s->v.Slice.step); } - ADDOP_I(c, BUILD_SLICE, n); + ADDOP(c, build_slice[n]); return 1; } static int compiler_simple_slice(struct compiler *c, slice_ty s, expr_context_ty ctx) { - int op = 0, slice_offset = 0, stack_count = 0; + const int slice[] = { SLICE_NONE, SLICE_LEFT, SLICE_RIGHT, SLICE_BOTH }; + const int store_slice[] = { STORE_SLICE_NONE, STORE_SLICE_LEFT, + STORE_SLICE_RIGHT, STORE_SLICE_BOTH }; + const int delete_slice[] = { DELETE_SLICE_NONE, DELETE_SLICE_LEFT, + DELETE_SLICE_RIGHT, DELETE_SLICE_BOTH }; + const int *op = NULL; + int slice_offset = 0, stack_count = 0; assert(s->v.Slice.step == NULL); if (s->v.Slice.lower) { @@ -3271,8 +3282,8 @@ if (ctx == AugLoad) { switch (stack_count) { case 0: ADDOP(c, DUP_TOP); break; - case 1: ADDOP_I(c, DUP_TOPX, 2); break; - case 2: ADDOP_I(c, DUP_TOPX, 3); break; + case 1: ADDOP(c, DUP_TOP_TWO); break; + case 2: ADDOP(c, DUP_TOP_THREE); break; } } else if (ctx == AugStore) { @@ -3285,10 +3296,10 @@ switch (ctx) { case AugLoad: /* fall through to Load */ - case Load: op = SLICE; break; + case Load: op = slice; break; case AugStore:/* fall through to Store */ - case Store: op = STORE_SLICE; break; - case Del: op = DELETE_SLICE; break; + case Store: op = store_slice; break; + case Del: op = delete_slice; break; case Param: default: PyErr_SetString(PyExc_SystemError, @@ -3296,7 +3307,7 @@ return 0; } - ADDOP(c, op + slice_offset); + ADDOP(c, op[slice_offset]); return 1; } @@ -3379,7 +3390,7 @@ */ struct assembler { - PyObject *a_bytecode; /* string containing bytecode */ + PyInstructionsObject *a_code; /* contains opcode.h, not vmgen codes */ int a_offset; /* offset into bytecode */ int a_nblocks; /* number of reachable blocks */ basicblock **a_postorder; /* list of blocks in dfs postorder */ @@ -3462,8 +3473,8 @@ { memset(a, 0, sizeof(struct assembler)); a->a_lineno = firstlineno; - a->a_bytecode = PyString_FromStringAndSize(NULL, DEFAULT_CODE_SIZE); - if (!a->a_bytecode) + a->a_code = _PyInstructions_New(DEFAULT_CODE_SIZE); + if (a->a_code == NULL) return 0; a->a_lnotab = PyString_FromStringAndSize(NULL, DEFAULT_LNOTAB_SIZE); if (!a->a_lnotab) @@ -3484,22 +3495,20 @@ static void assemble_free(struct assembler *a) { - Py_XDECREF(a->a_bytecode); + Py_XDECREF(a->a_code); Py_XDECREF(a->a_lnotab); if (a->a_postorder) PyObject_Free(a->a_postorder); } -/* Return the size of a basic block in bytes. */ +/* Return the size of a basic block in PyPInst (usually 4-byte) units. */ 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) */ + return 2; /* 1 (opcode) + 1 (arg) */ } static int @@ -3661,8 +3670,8 @@ assemble_emit(struct assembler *a, struct instr *i) { int size, arg = 0, ext = 0; - Py_ssize_t len = PyString_GET_SIZE(a->a_bytecode); - char *code; + Py_ssize_t len = Py_SIZE(a->a_code); + PyPInst *code; size = instrsize(i); if (i->i_hasarg) { @@ -3674,23 +3683,15 @@ if (a->a_offset + size >= len) { if (len > PY_SSIZE_T_MAX / 2) return 0; - if (_PyString_Resize(&a->a_bytecode, len * 2) < 0) + if (_PyInstructions_Resize(&a->a_code, len * 2) < 0) return 0; } - code = PyString_AS_STRING(a->a_bytecode) + a->a_offset; + code = a->a_code->inst + 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; + PyPInst_SET_OPCODE(code++, i->i_opcode); if (i->i_hasarg) { - assert(size == 3 || size == 6); - *code++ = arg & 0xff; - *code++ = arg >> 8; + assert(size == 2); + PyPInst_SET_ARG(code++, arg); } return 1; } @@ -3823,6 +3824,7 @@ { PyObject *tmp; PyCodeObject *co = NULL; + PyObject *code = NULL; PyObject *consts = NULL; PyObject *names = NULL; PyObject *varnames = NULL; @@ -3830,7 +3832,6 @@ PyObject *name = NULL; PyObject *freevars = NULL; PyObject *cellvars = NULL; - PyObject *bytecode = NULL; int nlocals, flags; tmp = dict_keys_inorder(c->u->u_consts, 0); @@ -3859,8 +3860,8 @@ if (flags < 0) goto error; - bytecode = PyCode_Optimize(a->a_bytecode, consts, names, a->a_lnotab); - if (!bytecode) + code = PyCode_Optimize((PyObject *)a->a_code, consts, names, a->a_lnotab); + if (!code) goto error; tmp = PyList_AsTuple(consts); /* PyCode_New requires a tuple */ @@ -3870,7 +3871,7 @@ consts = tmp; co = PyCode_New(c->u->u_argcount, nlocals, stackdepth(c), flags, - bytecode, consts, names, varnames, + code, consts, names, varnames, freevars, cellvars, filename, c->u->u_name, c->u->u_firstlineno, @@ -3883,7 +3884,7 @@ Py_XDECREF(name); Py_XDECREF(freevars); Py_XDECREF(cellvars); - Py_XDECREF(bytecode); + Py_XDECREF(code); return co; } @@ -3972,7 +3973,7 @@ if (_PyString_Resize(&a.a_lnotab, a.a_lnotab_off) < 0) goto error; - if (_PyString_Resize(&a.a_bytecode, a.a_offset) < 0) + if (_PyInstructions_Resize(&a.a_code, a.a_offset) < 0) goto error; co = makecode(c, &a); Index: Python/ceval.vmg =================================================================== --- Python/ceval.vmg (revision 0) +++ Python/ceval.vmg (revision 0) @@ -0,0 +1,1166 @@ + +\ VM instruction implementations + +\ To compile this file with vmgen, find prims2x.fs in the gforth distribution +\ and change "4 constant max-stacks" to "9 constant max-stacks". + +\ _. Stack definitions &c. + +\ stack inst-stream next_instr Cell +\E s" Oparg" single inst-stream type-prefix i +\E inst-stream stack-prefix # + +\E stack data-stack stack_pointer Obj +\E : sp-access-transform ( itemnum -- index ) negate 1- ; +\E ' sp-access-transform ' data-stack >body stack-access-transform ! +\E s" Obj" single data-stack type-prefix a + + +\E stack dummy-stack0 __none__ incref +\E dummy-stack0 stack-prefix inc: + +\E stack dummy-stack1 __none__ decref +\E dummy-stack1 stack-prefix dec: + +\E stack dummy-stack2 __none__ next +\E dummy-stack2 stack-prefix next: +\E s" __none__" single dummy-stack2 type-prefix next_opcode +\E s" __none__" single dummy-stack2 type-prefix on_error +\E s" __none__" single dummy-stack2 type-prefix fast_block_end +\E s" __none__" single dummy-stack2 type-prefix fast_yield +\E s" __none__" single dummy-stack2 type-prefix error + + +\E store-optimization on + +\ _. Instructions + +\ _ , Superinstruction Prefix Approved Opcodes (TM) +NOP ( -- ) + +LOAD_FAST ( #i -- a inc:a ) +x = a = GETLOCAL(i); +if (a == NULL) { + format_exc_check_arg( + PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + PyTuple_GetItem(co->co_varnames, i)); + /* On exception, make sure the stack is valid. */ + STACKADJ(-1); + ERROR(); +} + +LOAD_CONST ( #i -- a inc:a ) +x = a = GETITEM(consts, i); + +STORE_FAST ( #i a -- ) +SETLOCAL(i, a); + +POP_TOP ( a -- dec:a ) + +ROT_TWO ( a1 a2 -- a2 a1 ) + +ROT_THREE ( a1 a2 a3 -- a3 a1 a2 ) + +ROT_FOUR ( a1 a2 a3 a4 -- a4 a1 a2 a3 ) + +DUP_TOP ( a -- a a inc:a ) + +DUP_TOP_TWO ( a1 a2 -- a1 a2 a1 a2 inc:a1 inc:a2 ) + +DUP_TOP_THREE ( a1 a2 a3 -- a1 a2 a3 a1 a2 a3 inc:a1 inc:a2 inc:a3 ) + +\ _ , UNARY +UNARY_POSITIVE ( a1 -- a2 dec:a1 next:a2 ) +a2 = PyNumber_Positive(a1); + +UNARY_NEGATIVE ( a1 -- a2 dec:a1 next:a2 ) +a2 = PyNumber_Negative(a1); + +UNARY_NOT ( -- ) +a1 = TOP(); +err = PyObject_IsTrue(a1); +Py_DECREF(a1); +if (err == 0) { + Py_INCREF(Py_True); + SET_TOP(Py_True); + NEXT(); +} else if (err > 0) { + Py_INCREF(Py_False); + SET_TOP(Py_False); + err = 0; + NEXT(); +} +STACKADJ(-1); +ERROR(); + +UNARY_CONVERT ( a1 -- a2 dec:a1 next:a2 ) +a2 = PyObject_Repr(a1); + +UNARY_INVERT ( a1 -- a2 dec:a1 next:a2 ) +a2 = PyNumber_Invert(a1); + +\ _ , BINARY +BINARY_POWER ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_Power(a1, a2, Py_None); + +BINARY_MULTIPLY ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_Multiply(a1, a2); + +BINARY_DIVIDE ( a1 a2 -- a dec:a1 dec:a2 next:a ) +if (!_Py_QnewFlag) + a = PyNumber_Divide(a1, a2); +else + a = PyNumber_TrueDivide(a1, a2); + +BINARY_TRUE_DIVIDE ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_TrueDivide(a1, a2); + +BINARY_FLOOR_DIVIDE ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_FloorDivide(a1, a2); + +BINARY_MODULO ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_Remainder(a1, a2); + +BINARY_ADD ( a1 a2 -- a next:a ) +if (PyInt_CheckExact(a1) && PyInt_CheckExact(a2)) { + /* INLINE: int + int */ + register long u, v, i; + u = PyInt_AS_LONG(a1); + v = PyInt_AS_LONG(a2); + i = u + v; + if ((i^u) < 0 && (i^v) < 0) + a = PyNumber_Add(a1, a2); + else + a = PyInt_FromLong(i); + Py_DECREF(a1); +} else if (PyString_CheckExact(a1) && PyString_CheckExact(a2)) { + /* Look in the parallel PyInstructions object to find the + symbolic opcode. */ + int opcode = PyPInst_GET_OPCODE( + &((PyInstructionsObject *)co->co_code)->inst[INSTR_OFFSET()]); + a = string_concatenate(a1, a2, f, opcode, (next_instr+1)->oparg); + /* string_concatenate consumed the ref to v */ +} else { + a = PyNumber_Add(a1, a2); + Py_DECREF(a1); +} +Py_DECREF(a2); + +BINARY_SUBTRACT ( a1 a2 -- a dec:a1 dec:a2 next:a ) +if (PyInt_CheckExact(a1) && PyInt_CheckExact(a2)) { + /* INLINE: int - int */ + register long u, v, i; + u = PyInt_AS_LONG(a1); + v = PyInt_AS_LONG(a2); + i = u - v; + if ((i^u) < 0 && (i^~v) < 0) + a = PyNumber_Subtract(a1, a2); + else + a = PyInt_FromLong(i); +} else + a = PyNumber_Subtract(a1, a2); + +BINARY_SUBSCR ( a1 a2 -- a dec:a1 dec:a2 next:a ) +if (PyList_CheckExact(a1) && PyInt_CheckExact(a2)) { + /* INLINE: list[int] */ + Py_ssize_t i = PyInt_AsSsize_t(a2); + if (i < 0) + i += PyList_GET_SIZE(a1); + if (i >= 0 && i < PyList_GET_SIZE(a1)) { + a = PyList_GET_ITEM(a1, i); + Py_INCREF(a); + } else + a = PyObject_GetItem(a1, a2); +} else + a = PyObject_GetItem(a1, a2); + +BINARY_LSHIFT ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_Lshift(a1, a2); + +BINARY_RSHIFT ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_Rshift(a1, a2); + +BINARY_AND ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_And(a1, a2); + +BINARY_XOR ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_Xor(a1, a2); + +BINARY_OR ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_Or(a1, a2); + +\ _ , LIST_APPEND +LIST_APPEND ( #i a2 -- dec:a2 next:error ) +a1 = stack_pointer[-i]; +err = PyList_Append(a1, a2); + +\ _ , INPLACE +INPLACE_POWER ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_InPlacePower(a1, a2, Py_None); + +INPLACE_MULTIPLY ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_InPlaceMultiply(a1, a2); + +INPLACE_DIVIDE ( a1 a2 -- a dec:a1 dec:a2 next:a ) +if (!_Py_QnewFlag) + a = PyNumber_InPlaceDivide(a1, a2); +else + a = PyNumber_InPlaceTrueDivide(a1, a2); + +INPLACE_TRUE_DIVIDE ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_InPlaceTrueDivide(a1, a2); + +INPLACE_FLOOR_DIVIDE ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_InPlaceFloorDivide(a1, a2); + +INPLACE_MODULO ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_InPlaceRemainder(a1, a2); + +INPLACE_ADD ( a1 a2 -- a next:a ) +if (PyInt_CheckExact(a1) && PyInt_CheckExact(a2)) { + /* INLINE: int + int */ + register long u, v, i; + u = PyInt_AS_LONG(a1); + v = PyInt_AS_LONG(a2); + i = u + v; + if ((i^u) < 0 && (i^v) < 0) + a = PyNumber_InPlaceAdd(a1, a2); + else + a = PyInt_FromLong(i); + Py_DECREF(a1); +} else if (PyString_CheckExact(a1) && PyString_CheckExact(a2)) { + /* Look in the parallel PyInstructions object to find the + symbolic opcode. */ + int opcode = PyPInst_GET_OPCODE( + &((PyInstructionsObject *)co->co_code)->inst[INSTR_OFFSET()]); + a = string_concatenate(a1, a2, f, opcode, (next_instr+1)->oparg); + /* string_concatenate consumed the ref to v */ +} else { + a = PyNumber_InPlaceAdd(a1, a2); + Py_DECREF(a1); +} +Py_DECREF(a2); + +INPLACE_SUBTRACT ( a1 a2 -- a dec:a1 dec:a2 next:a ) +if (PyInt_CheckExact(a1) && PyInt_CheckExact(a2)) { + /* INLINE: int - int */ + register long u, v, i; + u = PyInt_AS_LONG(a1); + v = PyInt_AS_LONG(a2); + i = u - v; + if ((i^u) < 0 && (i^~v) < 0) + a = PyNumber_InPlaceSubtract(a1, a2); + else + a = PyInt_FromLong(i); +} else + a = PyNumber_InPlaceSubtract(a1, a2); + +INPLACE_LSHIFT ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_InPlaceLshift(a1, a2); + +INPLACE_RSHIFT ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_InPlaceRshift(a1, a2); + +INPLACE_AND ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_InPlaceAnd(a1, a2); + +INPLACE_XOR ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_InPlaceXor(a1, a2); + +INPLACE_OR ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PyNumber_InPlaceOr(a1, a2); + +\ _ , SLICE +SLICE_NONE ( a1 -- a2 dec:a1 next:a2 ) +a2 = apply_slice(a1, NULL, NULL); + +SLICE_LEFT ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = apply_slice(a1, a2, NULL); + +SLICE_RIGHT ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = apply_slice(a1, NULL, a2); + +SLICE_BOTH ( a1 a2 a3 -- a dec:a1 dec:a2 dec:a3 next:a ) +a = apply_slice(a1, a2, a3); + +STORE_SLICE_NONE ( a1 a2 -- dec:a1 dec:a2 next:error ) +err = assign_slice(a2, NULL, NULL, a1); + +STORE_SLICE_LEFT ( a1 a2 a3 -- dec:a1 dec:a2 dec:a3 next:error ) +err = assign_slice(a2, a3, NULL, a1); + +STORE_SLICE_RIGHT ( a1 a2 a3 -- dec:a1 dec:a2 dec:a3 next:error ) +err = assign_slice(a2, NULL, a3, a1); + +STORE_SLICE_BOTH ( a1 a2 a3 a4 -- dec:a1 dec:a2 dec:a3 dec:a4 next:error ) +err = assign_slice(a2, a3, a4, a1); /* a2[a3:a4] = a1 */ + +DELETE_SLICE_NONE ( a1 -- dec:a1 next:error ) +err = assign_slice(a1, NULL, NULL, (PyObject *) NULL); + +DELETE_SLICE_LEFT ( a1 a2 -- dec:a1 dec:a2 next:error ) +err = assign_slice(a1, a2, NULL, (PyObject *) NULL); + +DELETE_SLICE_RIGHT ( a1 a2 -- dec:a1 dec:a2 next:error ) +err = assign_slice(a1, NULL, a2, (PyObject *) NULL); + +DELETE_SLICE_BOTH ( a1 a2 a3 -- dec:a1 dec:a2 dec:a3 next:error ) +err = assign_slice(a1, a2, a3, (PyObject *) NULL); /* del a1[a2:a3] */ + +\ _ , SUBSCR +STORE_SUBSCR ( a1 a2 a3 -- dec:a1 dec:a2 dec:a3 next:error ) +err = PyObject_SetItem(a2, a3, a1); + +DELETE_SUBSCR ( a1 a2 -- dec:a1 dec:a2 next:error ) +err = PyObject_DelItem(a1, a2); + +\ _ , PRINT +PRINT_EXPR ( a1 -- dec:a1 next:on_error ) +a2 = PySys_GetObject("displayhook"); +if (a2 == NULL) { + PyErr_SetString(PyExc_RuntimeError, "lost sys.displayhook"); + err = -1; + x = NULL; +} +if (err == 0) { + x = PyTuple_Pack(1, a1); + if (x == NULL) + err = -1; +} +if (err == 0) { + a2 = PyEval_CallObject(a2, x); + Py_XDECREF(a2); + if (a2 == NULL) + err = -1; +} +Py_XDECREF(x); + +PRINT_ITEM_TO ( a1 -- ) +a2 = stream = a1; +goto print_item_fallthrough; + +PRINT_ITEM ( -- next:error ) +print_item_fallthrough: +a1 = POP(); +if (stream == NULL || stream == Py_None) { + a2 = PySys_GetObject("stdout"); + if (a2 == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "lost sys.stdout"); + err = -1; + } +} +/* PyFile_SoftSpace() can exececute arbitrary code + if sys.stdout is an instance with a __getattr__. + If __getattr__ raises an exception, w will + be freed, so we need to prevent that temporarily. */ +Py_XINCREF(a2); +if (a2 != NULL && PyFile_SoftSpace(a2, 0)) + err = PyFile_WriteString(" ", a2); +if (err == 0) + err = PyFile_WriteObject(a1, a2, Py_PRINT_RAW); +if (err == 0) { + /* XXX move into writeobject() ? */ + if (PyString_Check(a1)) { + char *s = PyString_AS_STRING(a1); + Py_ssize_t len = PyString_GET_SIZE(a1); + if (len == 0 || + !isspace(Py_CHARMASK(s[len-1])) || + s[len-1] == ' ') + PyFile_SoftSpace(a2, 1); + } else + PyFile_SoftSpace(a2, 1); +} +Py_XDECREF(a1); +Py_XDECREF(a2); +Py_XDECREF(stream); +stream = NULL; + +PRINT_NEWLINE_TO ( a1 -- ) +a2 = stream = a1; +goto print_newline_fallthrough; + +PRINT_NEWLINE ( -- next:on_error ) +print_newline_fallthrough: +if (stream == NULL || stream == Py_None) { + a2 = PySys_GetObject("stdout"); + if (a2 == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "lost sys.stdout"); + why = WHY_EXCEPTION; + } +} +if (a2 != NULL) { + /* a2.write() may replace sys.stdout, so we + * have to keep our reference to it */ + Py_INCREF(a2); + err = PyFile_WriteString("\n", a2); + if (err == 0) + PyFile_SoftSpace(a2, 0); + Py_DECREF(a2); +} +Py_XDECREF(stream); +stream = NULL; + +\ _ , RAISE_VARARGS +RAISE_VARARGS_ZERO ( -- next:on_error ) +why = do_raise(NULL, NULL, NULL); + +RAISE_VARARGS_ONE ( a1 -- next:on_error ) +why = do_raise(a1, NULL, NULL); + +RAISE_VARARGS_TWO ( a1 a2 -- next:on_error ) +why = do_raise(a1, a2, NULL); + +RAISE_VARARGS_THREE ( a1 a2 a3 -- next:on_error ) +why = do_raise(a1, a2, a3); + +\ _ , LOAD +LOAD_LOCALS ( -- ) +if ((x = f->f_locals) != NULL) { + Py_INCREF(x); + PUSH(x); + NEXT(); +} +PyErr_SetString(PyExc_SystemError, "no locals"); +ERROR(); + +LOAD_NAME ( #i -- ) +a2 = GETITEM(names, i); +if ((a1 = f->f_locals) == NULL) { + PyErr_Format(PyExc_SystemError, + "no locals when loading %s", + PyObject_REPR(a2)); + why = WHY_EXCEPTION; + ERROR(); +} +if (PyDict_CheckExact(a1)) { + x = PyDict_GetItem(a1, a2); + Py_XINCREF(x); +} else { + x = PyObject_GetItem(a1, a2); + if (x == NULL && PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_KeyError)) + ERROR(); + PyErr_Clear(); + } +} +if (x == NULL) { + x = PyDict_GetItem(f->f_globals, a2); + if (x == NULL) { + x = PyDict_GetItem(f->f_builtins, a2); + if (x == NULL) { + format_exc_check_arg(PyExc_NameError, NAME_ERROR_MSG, a2); + ERROR(); + } + } + Py_INCREF(x); +} +PUSH(x); +NEXT(); + +LOAD_GLOBAL ( #i -- ) +a1 = GETITEM(names, i); +if (PyString_CheckExact(a1)) { + /* Inline the PyDict_GetItem() calls. + WARNING: this is an extreme speed hack. + Do not try this at home. */ + long hash = ((PyStringObject *)a1)->ob_shash; + if (hash != -1) { + PyDictObject *d = (PyDictObject *)(f->f_globals); + PyDictEntry *e = d->ma_lookup(d, a1, hash); + if (e == NULL) { + x = NULL; + ERROR(); + } + x = e->me_value; + if (x != NULL) { + Py_INCREF(x); + PUSH(x); + NEXT(); + } + d = (PyDictObject *)(f->f_builtins); + e = d->ma_lookup(d, a1, hash); + if (e == NULL) { + x = NULL; + ERROR(); + } + x = e->me_value; + if (x != NULL) { + Py_INCREF(x); + PUSH(x); + NEXT(); + } + goto load_global_error; + } +} +/* This is the un-inlined version of the code above */ +x = PyDict_GetItem(f->f_globals, a1); +if (x == NULL) { + x = PyDict_GetItem(f->f_builtins, a1); + if (x == NULL) { + load_global_error: + format_exc_check_arg( + PyExc_NameError, + GLOBAL_NAME_ERROR_MSG, + a1); + ERROR(); + } +} +Py_INCREF(x); +PUSH(x); +NEXT(); + +LOAD_CLOSURE ( #i -- a inc:a next:a ) +a = freevars[i]; + +LOAD_DEREF ( #i -- ) +x = freevars[i]; +a2 = PyCell_Get(x); +if (a2 != NULL) { + PUSH(a2); + NEXT(); +} +err = -1; +/* Don't stomp existing exception */ +if (PyErr_Occurred()) + ERROR(); +if (i < PyTuple_GET_SIZE(co->co_cellvars)) { + a1 = PyTuple_GET_ITEM(co->co_cellvars, i); + format_exc_check_arg( + PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + a1); +} else { + a1 = PyTuple_GET_ITEM( + co->co_freevars, + i - PyTuple_GET_SIZE(co->co_cellvars)); + format_exc_check_arg( + PyExc_NameError, + UNBOUNDFREE_ERROR_MSG, + a1); +} +ERROR(); + +LOAD_ATTR ( #i a1 -- a3 dec:a1 next:a3 ) +a2 = GETITEM(names, i); +a3 = PyObject_GetAttr(a1, a2); + +\ _ , VALUE +RETURN_VALUE ( a -- next:fast_block_end ) +retval = a; +why = WHY_RETURN; + +YIELD_VALUE ( a -- next:fast_yield ) +retval = a; +f->f_stacktop = stack_pointer; +why = WHY_YIELD; + +\ _ , EXEC_STMT +EXEC_STMT ( a1 a2 a3 -- dec:a1 dec:a2 dec:a3 next:on_error ) +err = exec_statement(f, a1, a2, a3); + +\ _ , POP_BLOCK +POP_BLOCK ( -- next:next_opcode ) +PyTryBlock *b = PyFrame_BlockPop(f); +while (STACK_LEVEL() > b->b_level) { + a1 = POP(); + Py_DECREF(a1); +} + +\ _ , END_FINALLY +END_FINALLY ( a1 -- next:on_error ) +if (PyInt_Check(a1)) { + why = (enum why_code) PyInt_AS_LONG(a1); + assert(why != WHY_YIELD); + if (why == WHY_RETURN || why == WHY_CONTINUE) + retval = POP(); +} else if (PyExceptionClass_Check(a1) || PyString_Check(a1)) { + a2 = POP(); + a3 = POP(); + PyErr_Restore(a1, a2, a3); + why = WHY_RERAISE; + ERROR(); +} else if (a1 != Py_None) { + PyErr_SetString(PyExc_SystemError, "'finally' pops bad exception"); + why = WHY_EXCEPTION; +} +Py_DECREF(a1); + +\ _ , BUILD +BUILD_CLASS ( a1 a2 a3 -- a dec:a1 dec:a2 dec:a3 next:on_error ) +x = a = build_class(a3, a2, a1); + +BUILD_TUPLE ( #i -- ) +x = PyTuple_New(i); +if (x != NULL) { + for (; --i >= 0;) { + a1 = POP(); + PyTuple_SET_ITEM(x, i, a1); + } + PUSH(x); + NEXT(); +} +ERROR(); + +BUILD_LIST ( #i -- ) +x = PyList_New(i); +if (x != NULL) { + for (; --i >= 0;) { + a1 = POP(); + PyList_SET_ITEM(x, i, a1); + } + PUSH(x); + NEXT(); +} +ERROR(); + +BUILD_MAP ( #i -- a next:a ) +a = _PyDict_NewPresized(i); + +STORE_MAP ( a1 a2 a3 -- a1 dec:a2 dec:a3 next:error ) +/* a1 == dict, a2 == value, a3 == key */ +assert (PyDict_CheckExact(a1)); +err = PyDict_SetItem(a1, a3, a2); /* a1[a3] = a2 */ + +BUILD_SLICE_TWO ( a1 a2 -- a dec:a1 dec:a2 next:a ) +a = PySlice_New(a1, a2, NULL); + +BUILD_SLICE_THREE ( a1 a2 a3 -- a dec:a1 dec:a2 dec:a3 next:a ) +a = PySlice_New(a1, a2, a3); + +\ _ , STORE +STORE_NAME ( #i -- ) +a2 = GETITEM(names, i); +a1 = POP(); +if ((x = f->f_locals) != NULL) { + if (PyDict_CheckExact(x)) + err = PyDict_SetItem(x, a2, a1); + else + err = PyObject_SetItem(x, a2, a1); + Py_DECREF(a1); + if (err == 0) NEXT(); + ERROR(); +} +PyErr_Format(PyExc_SystemError, + "no locals found when storing %s", + PyObject_REPR(a2)); +ERROR(); + +STORE_ATTR ( #i a1 a2 -- dec:a1 dec:a2 next:error ) +a3 = GETITEM(names, i); +err = PyObject_SetAttr(a2, a3, a1); /* a2.a3 = a1 */ + +STORE_GLOBAL ( #i a1 -- dec:a1 next:error ) +a2 = GETITEM(names, i); +err = PyDict_SetItem(f->f_globals, a2, a1); + +STORE_DEREF ( #i a -- dec:a next:next_opcode ) +x = freevars[i]; +PyCell_Set(x, a); + +\ _ , DELETE +DELETE_NAME ( #i -- ) +a1 = GETITEM(names, i); +if ((x = f->f_locals) != NULL) { + if ((err = PyObject_DelItem(x, a1)) != 0) + format_exc_check_arg(PyExc_NameError, NAME_ERROR_MSG, a1); + ERROR(); +} +PyErr_Format(PyExc_SystemError, + "no locals when deleting %s", + PyObject_REPR(a1)); +ERROR(); + +DELETE_ATTR ( #i a1 -- dec:a1 next:on_error ) +a2 = GETITEM(names, i); +err = PyObject_SetAttr(a1, a2, (PyObject *) NULL); /* del a1.a2 */ + +DELETE_GLOBAL ( #i -- next:on_error ) +a1 = GETITEM(names, i); +if ((err = PyDict_DelItem(f->f_globals, a1)) != 0) + format_exc_check_arg(PyExc_NameError, GLOBAL_NAME_ERROR_MSG, a1); + +DELETE_FAST ( #i -- ) +x = GETLOCAL(i); +if (x != NULL) { + SETLOCAL(i, NULL); + NEXT(); +} +format_exc_check_arg( + PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + PyTuple_GetItem(co->co_varnames, i)); +ERROR(); + +\ _ , UNPACK_SEQUENCE +UNPACK_SEQUENCE ( #i a1 -- ) +if (PyTuple_CheckExact(a1) && PyTuple_GET_SIZE(a1) == i) { + PyObject **items = ((PyTupleObject *)a1)->ob_item; + while (i--) { + a2 = items[i]; + Py_INCREF(a2); + PUSH(a2); + } + Py_DECREF(a1); + NEXT(); +} else if (PyList_CheckExact(a1) && PyList_GET_SIZE(a1) == i) { + PyObject **items = ((PyListObject *)a1)->ob_item; + while (i--) { + a2 = items[i]; + Py_INCREF(a2); + PUSH(a2); + } +} else if (unpack_iterable(a1, i, stack_pointer + i)) { + stack_pointer += i; +} else { + /* unpack_iterable() raised an exception */ + why = WHY_EXCEPTION; +} +Py_DECREF(a1); +ERROR(); + +\ _ , COMPARE_OP +COMPARE_OP ( #i a1 a2 -- a dec:a1 dec:a2 next:a ) +if (PyInt_CheckExact(a2) && PyInt_CheckExact(a1)) { + /* INLINE: cmp(int, int) */ + register long u, v; + register int res; + u = PyInt_AS_LONG(a1); + v = PyInt_AS_LONG(a2); + switch (i) { + case PyCmp_LT: res = u < v; break; + case PyCmp_LE: res = u <= v; break; + case PyCmp_EQ: res = u == v; break; + case PyCmp_NE: res = u != v; break; + case PyCmp_GT: res = u > v; break; + case PyCmp_GE: res = u >= v; break; + case PyCmp_IS: res = a1 == a2; break; + case PyCmp_IS_NOT: res = a1 != a2; break; + default: res = -1; + } + if (res < 0) + a = cmp_outcome(i, a1, a2); + else { + a = res ? Py_True : Py_False; + Py_INCREF(a); + } +} else + a = cmp_outcome(i, a1, a2); + +\ _ , IMPORT +IMPORT_NAME ( #i -- ) +a3 = GETITEM(names, i); +x = PyDict_GetItemString(f->f_builtins, "__import__"); +if (x == NULL) { + PyErr_SetString(PyExc_ImportError, "__import__ not found"); + ERROR(); +} +Py_INCREF(x); +a2 = POP(); +a1 = TOP(); +if (PyInt_AsLong(a1) != -1 || PyErr_Occurred()) + a3 = PyTuple_Pack(5, + a3, + f->f_globals, + f->f_locals == NULL ? Py_None : f->f_locals, + a2, + a1); +else + a3 = PyTuple_Pack(4, + a3, + f->f_globals, + f->f_locals == NULL ? Py_None : f->f_locals, + a2); +Py_DECREF(a2); +Py_DECREF(a1); +if (a3 == NULL) { + a1 = POP(); + Py_DECREF(x); + x = NULL; + ERROR(); +} +a2 = x; +x = PyEval_CallObject(a2, a3); +Py_DECREF(a2); +Py_DECREF(a3); +SET_TOP(x); +if (x != NULL) NEXT(); +ERROR(); + +IMPORT_STAR ( a1 -- ) +PyFrame_FastToLocals(f); +if ((x = f->f_locals) == NULL) { + PyErr_SetString(PyExc_SystemError, "no locals found during 'import *'"); + ERROR(); +} +err = import_all_from(x, a1); +PyFrame_LocalsToFast(f, 0); +Py_DECREF(a1); +if (err == 0) NEXT(); +ERROR(); + +IMPORT_FROM ( #i a1 -- a1 a3 next:a3 ) +a2 = GETITEM(names, i); +a3 = import_from(a1, a2); + +\ _ , JUMP +JUMP_FORWARD ( #i -- ) +JUMPBY(i); + +JUMP_IF_FALSE ( #i a1 -- a1 ) +if (a1 == Py_True) + ; +else if (a1 == Py_False) + JUMPBY(i); +else { + err = PyObject_IsTrue(a1); + if (err > 0) + err = 0; + else if (err == 0) + JUMPBY(i); + else + ERROR(); + NEXT(); +} + +JUMP_IF_TRUE ( #i a1 -- a1 ) +if (a1 == Py_False) + ; +else if (a1 == Py_True) + JUMPBY(i); +else { + err = PyObject_IsTrue(a1); + if (err > 0) { + err = 0; + JUMPBY(i); + } else if (err == 0) + ; + else + ERROR(); + NEXT(); +} + +JUMP_ABSOLUTE ( #i -- next:next_opcode ) +JUMPTO(i); + +\ _ , ITER +GET_ITER ( -- ) +/* before: [obj]; after [getiter(obj)] */ +a1 = TOP(); +x = PyObject_GetIter(a1); +Py_DECREF(a1); +if (x != NULL) { + SET_TOP(x); + NEXT(); +} +STACKADJ(-1); +ERROR(); + +FOR_ITER ( #i -- ) +/* before: [iter]; after: [iter, iter()] *or* [] */ +a1 = TOP(); +x = (*a1->ob_type->tp_iternext)(a1); +if (x != NULL) { + PUSH(x); + NEXT(); +} +if (PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_StopIteration)) + ERROR(); + PyErr_Clear(); +} +/* iterator ended normally */ +x = a1 = POP(); +Py_DECREF(a1); +JUMPBY(i); +NEXT(); + +\ _ , LOOP +BREAK_LOOP ( -- next:fast_block_end ) +why = WHY_BREAK; + +CONTINUE_LOOP ( #i -- ) +retval = PyInt_FromLong(i); +if (!retval) { + x = NULL; + ERROR(); +} +why = WHY_CONTINUE; +goto fast_block_end; + +\ _ , SETUP +\ 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. + +SETUP_LOOP ( #i -- next:next_opcode ) +PyFrame_BlockSetup(f, SETUP_LOOP, INSTR_OFFSET() + i, STACK_LEVEL()); + +SETUP_EXCEPT ( #i -- next:next_opcode ) +PyFrame_BlockSetup(f, SETUP_EXCEPT, INSTR_OFFSET() + i, STACK_LEVEL()); + +SETUP_FINALLY ( #i -- next:next_opcode ) +PyFrame_BlockSetup(f, SETUP_FINALLY, INSTR_OFFSET() + i, STACK_LEVEL()); + +\ _ , WITH_CLEANUP +WITH_CLEANUP ( -- ) +{ + /* At the top of the stack are 1-3 values indicating + how/why we entered the finally clause: + - TOP = None + - (TOP, SECOND) = (WHY_{RETURN,CONTINUE}), retval + - TOP = WHY_*; no retval below it + - (TOP, SECOND, THIRD) = exc_info() + Below them is EXIT, the context.__exit__ bound method. + In the last case, we must call + EXIT(TOP, SECOND, THIRD) + otherwise we must call + EXIT(None, None, None) + + In all cases, we remove EXIT from the stack, leaving + the rest in the same order. + + In addition, if the stack represents an exception, + *and* the function call returns a 'true' value, we + "zap" this information, to prevent END_FINALLY from + re-raising the exception. (But non-local gotos + should still be resumed.) + */ + + PyObject *exit_func; + + a1 = POP(); + if (a1 == Py_None) { + exit_func = TOP(); + SET_TOP(a1); + a2 = a3 = Py_None; + } + else if (PyInt_Check(a1)) { + switch(PyInt_AS_LONG(a1)) { + case WHY_RETURN: + case WHY_CONTINUE: + /* Retval in TOP. */ + exit_func = SECOND(); + SET_SECOND(TOP()); + SET_TOP(a1); + break; + default: + exit_func = TOP(); + SET_TOP(a1); + break; + } + a1 = a2 = a3 = Py_None; + } + else { + a2 = TOP(); + a3 = SECOND(); + exit_func = THIRD(); + SET_TOP(a1); + SET_SECOND(a2); + SET_THIRD(a3); + } + /* XXX Not the fastest way to call it... */ + x = PyObject_CallFunctionObjArgs(exit_func, a1, a2, a3, + NULL); + Py_DECREF(exit_func); + if (x == NULL) + ERROR(); /* Go to error exit */ + if (a1 != Py_None) + err = PyObject_IsTrue(x); + else + err = 0; + Py_DECREF(x); + if (err < 0) + ERROR(); /* Go to error exit */ + else if (err > 0) { + err = 0; + /* There was an exception and a true return */ + STACKADJ(-2); + Py_INCREF(Py_None); + SET_TOP(Py_None); + Py_DECREF(a1); + Py_DECREF(a2); + Py_DECREF(a3); + } else { + /* The stack was rearranged to remove EXIT + above. Let END_FINALLY do its thing */ + } + ERROR(); +} + +\ _ , CALL_FUNCTION +CALL_FUNCTION ( #i -- ) +PyObject **sp = stack_pointer; +x = call_function(&sp, i); +stack_pointer = sp; +PUSH(x); +if (x != NULL) NEXT(); +ERROR(); + +CALL_FUNCTION_VAR_KW ( #i -- ) +int var_kw = i & 0x0000FFFF; +int oparg = i>>16; +int na = oparg & 0xff; +int nk = (oparg>>8) & 0xff; +int flags = var_kw & 3; +int n = na + 2 * nk; +PyObject **pfunc, *func, **sp; +if (flags & CALL_FLAG_VAR) + n++; +if (flags & CALL_FLAG_KW) + n++; +pfunc = stack_pointer - n - 1; +func = *pfunc; +if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { + PyObject *self = PyMethod_GET_SELF(func); + Py_INCREF(self); + func = PyMethod_GET_FUNCTION(func); + Py_INCREF(func); + Py_DECREF(*pfunc); + *pfunc = self; + na++; + n++; +} else + Py_INCREF(func); +sp = stack_pointer; +x = ext_do_call(func, &sp, flags, na, nk); +stack_pointer = sp; +Py_DECREF(func); +while (stack_pointer > pfunc) { + a1 = POP(); + Py_DECREF(a1); +} +PUSH(x); +if (x != NULL) NEXT(); +ERROR(); + +\ _ , MAKE +MAKE_FUNCTION ( #i -- ) +a1 = POP(); /* code object */ +x = PyFunction_New(a1, f->f_globals); +Py_DECREF(a1); +/* XXX Maybe this should be a separate opcode? */ +if (x != NULL && i > 0) { + a1 = PyTuple_New(i); + if (a1 == NULL) { + Py_DECREF(x); + x = NULL; + ERROR(); + } + while (--i >= 0) { + a2 = POP(); + PyTuple_SET_ITEM(a1, i, a2); + } + err = PyFunction_SetDefaults(x, a1); + Py_DECREF(a1); +} +PUSH(x); +ERROR(); + +MAKE_CLOSURE ( #i -- ) +a1 = POP(); /* code object */ +x = PyFunction_New(a1, f->f_globals); +Py_DECREF(a1); +if (x != NULL) { + a1 = POP(); + if (PyFunction_SetClosure(x, a1) != 0) { + /* Can't happen unless bytecode is corrupt. */ + why = WHY_EXCEPTION; + } + Py_DECREF(a1); +} +if (x != NULL && i > 0) { + a1 = PyTuple_New(i); + if (a1 == NULL) { + Py_DECREF(x); + x = NULL; + ERROR(); + } + while (--i >= 0) { + a2 = POP(); + PyTuple_SET_ITEM(a1, i, a2); + } + if (PyFunction_SetDefaults(x, a1) != 0) { + /* Can't happen unless + PyFunction_SetDefaults changes. */ + why = WHY_EXCEPTION; + } + Py_DECREF(a1); +} +PUSH(x); +ERROR(); + +\ _. Superinstructions +CC = LOAD_CONST LOAD_CONST + +CBINARY_POWER = LOAD_CONST BINARY_POWER +CCBINARY_POWER = LOAD_CONST LOAD_CONST BINARY_POWER +CBINARY_MULTIPLY = LOAD_CONST BINARY_MULTIPLY +CCBINARY_MULTIPLY = LOAD_CONST LOAD_CONST BINARY_MULTIPLY +CBINARY_DIVIDE = LOAD_CONST BINARY_DIVIDE +CCBINARY_DIVIDE = LOAD_CONST LOAD_CONST BINARY_DIVIDE +CBINARY_TRUE_DIVIDE = LOAD_CONST BINARY_TRUE_DIVIDE +CCBINARY_TRUE_DIVIDE = LOAD_CONST LOAD_CONST BINARY_TRUE_DIVIDE +CBINARY_FLOOR_DIVIDE = LOAD_CONST BINARY_FLOOR_DIVIDE +CCBINARY_FLOOR_DIVIDE = LOAD_CONST LOAD_CONST BINARY_FLOOR_DIVIDE +CBINARY_MODULO = LOAD_CONST BINARY_MODULO +CCBINARY_MODULO = LOAD_CONST LOAD_CONST BINARY_MODULO +CBINARY_ADD = LOAD_CONST BINARY_ADD +CCBINARY_ADD = LOAD_CONST LOAD_CONST BINARY_ADD +CBINARY_SUBTRACT = LOAD_CONST BINARY_SUBTRACT +CCBINARY_SUBTRACT = LOAD_CONST LOAD_CONST BINARY_SUBTRACT +CBINARY_SUBSCR = LOAD_CONST BINARY_SUBSCR +CCBINARY_SUBSCR = LOAD_CONST LOAD_CONST BINARY_SUBSCR +CBINARY_LSHIFT = LOAD_CONST BINARY_LSHIFT +CCBINARY_LSHIFT = LOAD_CONST LOAD_CONST BINARY_LSHIFT +CBINARY_RSHIFT = LOAD_CONST BINARY_RSHIFT +CCBINARY_RSHIFT = LOAD_CONST LOAD_CONST BINARY_RSHIFT +CBINARY_AND = LOAD_CONST BINARY_AND +CCBINARY_AND = LOAD_CONST LOAD_CONST BINARY_AND +CBINARY_XOR = LOAD_CONST BINARY_XOR +CCBINARY_XOR = LOAD_CONST LOAD_CONST BINARY_XOR +CBINARY_OR = LOAD_CONST BINARY_OR +CCBINARY_OR = LOAD_CONST LOAD_CONST BINARY_OR +CLIST_APPEND = LOAD_CONST LIST_APPEND +CCLIST_APPEND = LOAD_CONST LOAD_CONST LIST_APPEND +CINPLACE_POWER = LOAD_CONST INPLACE_POWER +CCINPLACE_POWER = LOAD_CONST LOAD_CONST INPLACE_POWER +CINPLACE_MULTIPLY = LOAD_CONST INPLACE_MULTIPLY +CCINPLACE_MULTIPLY = LOAD_CONST LOAD_CONST INPLACE_MULTIPLY +CINPLACE_DIVIDE = LOAD_CONST INPLACE_DIVIDE +CCINPLACE_DIVIDE = LOAD_CONST LOAD_CONST INPLACE_DIVIDE +CINPLACE_TRUE_DIVIDE = LOAD_CONST INPLACE_TRUE_DIVIDE +CCINPLACE_TRUE_DIVIDE = LOAD_CONST LOAD_CONST INPLACE_TRUE_DIVIDE +CINPLACE_FLOOR_DIVIDE = LOAD_CONST INPLACE_FLOOR_DIVIDE +CCINPLACE_FLOOR_DIVIDE = LOAD_CONST LOAD_CONST INPLACE_FLOOR_DIVIDE +CINPLACE_MODULO = LOAD_CONST INPLACE_MODULO +CCINPLACE_MODULO = LOAD_CONST LOAD_CONST INPLACE_MODULO +CINPLACE_ADD = LOAD_CONST INPLACE_ADD +CCINPLACE_ADD = LOAD_CONST LOAD_CONST INPLACE_ADD +CINPLACE_SUBTRACT = LOAD_CONST INPLACE_SUBTRACT +CCINPLACE_SUBTRACT = LOAD_CONST LOAD_CONST INPLACE_SUBTRACT +CINPLACE_LSHIFT = LOAD_CONST INPLACE_LSHIFT +CCINPLACE_LSHIFT = LOAD_CONST LOAD_CONST INPLACE_LSHIFT +CINPLACE_RSHIFT = LOAD_CONST INPLACE_RSHIFT +CCINPLACE_RSHIFT = LOAD_CONST LOAD_CONST INPLACE_RSHIFT +CINPLACE_AND = LOAD_CONST INPLACE_AND +CCINPLACE_AND = LOAD_CONST LOAD_CONST INPLACE_AND +CINPLACE_XOR = LOAD_CONST INPLACE_XOR +CCINPLACE_XOR = LOAD_CONST LOAD_CONST INPLACE_XOR +CINPLACE_OR = LOAD_CONST INPLACE_OR +CCINPLACE_OR = LOAD_CONST LOAD_CONST INPLACE_OR +CSLICE_LEFT = LOAD_CONST SLICE_LEFT +CCSLICE_LEFT = LOAD_CONST LOAD_CONST SLICE_LEFT +CSLICE_RIGHT = LOAD_CONST SLICE_RIGHT +CCSLICE_RIGHT = LOAD_CONST LOAD_CONST SLICE_RIGHT +CDELETE_SLICE_LEFT = LOAD_CONST DELETE_SLICE_LEFT +CCDELETE_SLICE_LEFT = LOAD_CONST LOAD_CONST DELETE_SLICE_LEFT +CDELETE_SLICE_RIGHT = LOAD_CONST DELETE_SLICE_RIGHT +CCDELETE_SLICE_RIGHT = LOAD_CONST LOAD_CONST DELETE_SLICE_RIGHT +CDELETE_SUBSCR = LOAD_CONST DELETE_SUBSCR +CCDELETE_SUBSCR = LOAD_CONST LOAD_CONST DELETE_SUBSCR +CSTORE_ATTR = LOAD_CONST STORE_ATTR +CCSTORE_ATTR = LOAD_CONST LOAD_CONST STORE_ATTR +CCOMPARE_OP = LOAD_CONST COMPARE_OP +CCCOMPARE_OP = LOAD_CONST LOAD_CONST COMPARE_OP Index: Python/frozen.c =================================================================== --- Python/frozen.c (revision 68532) +++ Python/frozen.c (working copy) @@ -12,13 +12,16 @@ the appropriate bytes from M___main__.c. */ static unsigned char M___hello__[] = { - 99,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, - 0,115,9,0,0,0,100,0,0,71,72,100,1,0,83,40, - 2,0,0,0,115,14,0,0,0,72,101,108,108,111,32,119, - 111,114,108,100,46,46,46,78,40,0,0,0,0,40,0,0, - 0,0,40,0,0,0,0,40,0,0,0,0,115,8,0,0, - 0,104,101,108,108,111,46,112,121,115,1,0,0,0,63,1, - 0,0,0,115,0,0,0,0, + 99,0,0,0,0,0,0,0,0,1,0,0,0,64,0,0, + 0,91,7,0,0,0,105,4,0,0,0,105,1,0,0,0, + 105,120,0,0,0,105,124,0,0,0,105,4,0,0,0,105, + 3,0,0,0,105,146,0,0,0,40,2,0,0,0,115,14, + 0,0,0,72,101,108,108,111,32,119,111,114,108,100,46,46, + 46,78,40,0,0,0,0,40,0,0,0,0,40,0,0,0, + 0,40,0,0,0,0,115,28,0,0,0,46,46,47,115,114, + 99,47,84,111,111,108,115,47,102,114,101,101,122,101,47,104, + 101,108,108,111,46,112,121,116,8,0,0,0,60,109,111,100, + 117,108,101,62,1,0,0,0,115,0,0,0,0, }; #define SIZE (int)sizeof(M___hello__) Index: Include/instructionsobject.h =================================================================== --- Include/instructionsobject.h (revision 0) +++ Include/instructionsobject.h (revision 0) @@ -0,0 +1,71 @@ +/* Definitions for instructions */ + +#ifndef Py_INSTRUCTIONSOBJECT_H +#define Py_INSTRUCTIONSOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) PyInstructions_Type; + +/* Opcode/arg list in a format that can be persisted to .pyc + files. That is, it contains no pointers. Usually when !is_arg, + opcode_or_arg will be a vmgen operation index, but from the start + of compilation until most of the way through PyCode_Optimize() it's + a value from opcode.h instead. */ +typedef struct { + unsigned int is_arg : 1; + unsigned int opcode_or_arg : 31; +} PyPInst; + +static inline int PyPInst_GET_OPCODE(PyPInst* inst) { + assert(inst->is_arg == 0); + return inst->opcode_or_arg; +} + +static inline int PyPInst_GET_ARG(PyPInst* inst) { + assert(inst->is_arg == 1); + return inst->opcode_or_arg; +} + +static inline void PyPInst_SET_OPCODE(PyPInst* inst, unsigned int opcode) { + inst->is_arg = 0; + inst->opcode_or_arg = opcode; +} + +static inline void PyPInst_SET_ARG(PyPInst* inst, unsigned int arg) { + inst->is_arg = 1; + inst->opcode_or_arg = arg; +} + +typedef struct { + PyObject_VAR_HEAD + PyPInst inst[0]; + /* 'inst' always contains enough space for 'ob_size' + elements. */ +} PyInstructionsObject; + +#define PyInstructions_Check(op) (Py_TYPE(op) == &PyInstructions_Type) + +/* This can also be used to allocate PyPInstVec instances by passing + *vec==NULL. On error, frees *vec, sets it to NULL, and returns -1. */ +PyInstructionsObject *_PyInstructions_New(Py_ssize_t size); + +/* This can also be used to allocate PyPInstVec instances by passing + *vec==NULL. On error, frees *vec, sets it to NULL, and returns -1. */ +int _PyInstructions_Resize(PyInstructionsObject **vec, Py_ssize_t new_size); + +/* Returns a new PyInstructions. On error, returns NULL and sets the + current exception. The sequence is expected to contain integral + elements. Each element 'x' will be converted to a PyPInst as follows: + pinst.is_arg = x & 1; + pinst.opcode_or_arg = x >> 1; + */ +PyObject *PyInstructions_FromSequence(PyObject *seq); + +/* See code.h for the runtime format of the threaded interpreter. */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INSTRUCTIONSOBJECT_H */ Index: Include/opcode.h =================================================================== --- Include/opcode.h (revision 68532) +++ Include/opcode.h (working copy) @@ -4,149 +4,17 @@ extern "C" { #endif - /* Instruction opcodes for compiled code */ -#define STOP_CODE 0 -#define POP_TOP 1 -#define ROT_TWO 2 -#define ROT_THREE 3 -#define DUP_TOP 4 -#define ROT_FOUR 5 -#define NOP 9 +#define INST_ADDR(x) x +enum PyOpcode { +#include "ceval-labels.i" +}; +#undef INST_ADDR -#define UNARY_POSITIVE 10 -#define UNARY_NEGATIVE 11 -#define UNARY_NOT 12 -#define UNARY_CONVERT 13 - -#define UNARY_INVERT 15 - -#define BINARY_POWER 19 - -#define BINARY_MULTIPLY 20 -#define BINARY_DIVIDE 21 -#define BINARY_MODULO 22 -#define BINARY_ADD 23 -#define BINARY_SUBTRACT 24 -#define BINARY_SUBSCR 25 -#define BINARY_FLOOR_DIVIDE 26 -#define BINARY_TRUE_DIVIDE 27 -#define INPLACE_FLOOR_DIVIDE 28 -#define INPLACE_TRUE_DIVIDE 29 - -#define SLICE 30 -/* Also uses 31-33 */ - -#define STORE_SLICE 40 -/* Also uses 41-43 */ - -#define DELETE_SLICE 50 -/* Also uses 51-53 */ - -#define STORE_MAP 54 -#define INPLACE_ADD 55 -#define INPLACE_SUBTRACT 56 -#define INPLACE_MULTIPLY 57 -#define INPLACE_DIVIDE 58 -#define INPLACE_MODULO 59 -#define STORE_SUBSCR 60 -#define DELETE_SUBSCR 61 - -#define BINARY_LSHIFT 62 -#define BINARY_RSHIFT 63 -#define BINARY_AND 64 -#define BINARY_XOR 65 -#define BINARY_OR 66 -#define INPLACE_POWER 67 -#define GET_ITER 68 - -#define PRINT_EXPR 70 -#define PRINT_ITEM 71 -#define PRINT_NEWLINE 72 -#define PRINT_ITEM_TO 73 -#define PRINT_NEWLINE_TO 74 -#define INPLACE_LSHIFT 75 -#define INPLACE_RSHIFT 76 -#define INPLACE_AND 77 -#define INPLACE_XOR 78 -#define INPLACE_OR 79 -#define BREAK_LOOP 80 -#define WITH_CLEANUP 81 -#define LOAD_LOCALS 82 -#define RETURN_VALUE 83 -#define IMPORT_STAR 84 -#define EXEC_STMT 85 -#define YIELD_VALUE 86 -#define POP_BLOCK 87 -#define END_FINALLY 88 -#define BUILD_CLASS 89 - -#define HAVE_ARGUMENT 90 /* Opcodes from here have an argument: */ - -#define STORE_NAME 90 /* Index in name list */ -#define DELETE_NAME 91 /* "" */ -#define UNPACK_SEQUENCE 92 /* Number of sequence items */ -#define FOR_ITER 93 -#define LIST_APPEND 94 - -#define STORE_ATTR 95 /* Index in name list */ -#define DELETE_ATTR 96 /* "" */ -#define STORE_GLOBAL 97 /* "" */ -#define DELETE_GLOBAL 98 /* "" */ -#define DUP_TOPX 99 /* number of items to duplicate */ -#define LOAD_CONST 100 /* Index in const list */ -#define LOAD_NAME 101 /* Index in name list */ -#define BUILD_TUPLE 102 /* Number of tuple items */ -#define BUILD_LIST 103 /* Number of list items */ -#define BUILD_MAP 104 /* Always zero for now */ -#define LOAD_ATTR 105 /* Index in name list */ -#define COMPARE_OP 106 /* Comparison operator */ -#define IMPORT_NAME 107 /* Index in name list */ -#define IMPORT_FROM 108 /* Index in name list */ - -#define JUMP_FORWARD 110 /* Number of bytes to skip */ -#define JUMP_IF_FALSE 111 /* "" */ -#define JUMP_IF_TRUE 112 /* "" */ -#define JUMP_ABSOLUTE 113 /* Target byte offset from beginning of code */ - -#define LOAD_GLOBAL 116 /* Index in name list */ - -#define CONTINUE_LOOP 119 /* Start of loop (absolute) */ -#define SETUP_LOOP 120 /* Target address (relative) */ -#define SETUP_EXCEPT 121 /* "" */ -#define SETUP_FINALLY 122 /* "" */ - -#define LOAD_FAST 124 /* Local variable number */ -#define STORE_FAST 125 /* Local variable number */ -#define DELETE_FAST 126 /* Local variable number */ - -#define RAISE_VARARGS 130 /* Number of raise arguments (1, 2 or 3) */ -/* CALL_FUNCTION_XXX opcodes defined below depend on this definition */ -#define CALL_FUNCTION 131 /* #args + (#kwargs<<8) */ -#define MAKE_FUNCTION 132 /* #defaults */ -#define BUILD_SLICE 133 /* Number of items */ - -#define MAKE_CLOSURE 134 /* #free vars */ -#define LOAD_CLOSURE 135 /* Load free variable from closure */ -#define LOAD_DEREF 136 /* Load and dereference from closure cell */ -#define STORE_DEREF 137 /* Store into cell */ - -/* The next 3 opcodes must be contiguous and satisfy - (CALL_FUNCTION_VAR - CALL_FUNCTION) & 3 == 1 */ -#define CALL_FUNCTION_VAR 140 /* #args + (#kwargs<<8) */ -#define CALL_FUNCTION_KW 141 /* #args + (#kwargs<<8) */ -#define CALL_FUNCTION_VAR_KW 142 /* #args + (#kwargs<<8) */ - -/* Support for opargs more than 16 bits long */ -#define EXTENDED_ARG 143 - - enum cmp_op {PyCmp_LT=Py_LT, PyCmp_LE=Py_LE, PyCmp_EQ=Py_EQ, PyCmp_NE=Py_NE, PyCmp_GT=Py_GT, PyCmp_GE=Py_GE, PyCmp_IN, PyCmp_NOT_IN, PyCmp_IS, PyCmp_IS_NOT, PyCmp_EXC_MATCH, PyCmp_BAD}; -#define HAS_ARG(op) ((op) >= HAVE_ARGUMENT) - #ifdef __cplusplus } #endif Index: Include/code.h =================================================================== --- Include/code.h (revision 68532) +++ Include/code.h (working copy) @@ -6,6 +6,20 @@ extern "C" { #endif +typedef void *Opcode; +typedef int Oparg; + +/* The same information as PyPInst, but optimized for a threaded + interpreter. opcode is now the address of the label in + PyEval_EvalFrameEx that interprets the operation. This struct also + throws away the information about which array elements are + arguments, but you can get that back by looking into c_code. */ +typedef union inst { + Opcode opcode; + Oparg oparg; +} vmgen_Cell, Inst; + + /* Bytecode object */ typedef struct { PyObject_HEAD @@ -13,13 +27,14 @@ int co_nlocals; /* #local variables */ int co_stacksize; /* #entries needed for evaluation stack */ int co_flags; /* CO_..., see below */ - PyObject *co_code; /* instruction opcodes */ + PyObject *co_code; /* PyInstructions object: the actual code */ PyObject *co_consts; /* list (constants used) */ PyObject *co_names; /* list of strings (names used) */ PyObject *co_varnames; /* tuple of strings (local variable names) */ PyObject *co_freevars; /* tuple of strings (free variable names) */ PyObject *co_cellvars; /* tuple of strings (cell variable names) */ /* The rest doesn't count for hash/cmp */ + Inst *co_tcode; /* threaded instructions */ PyObject *co_filename; /* string (where it was loaded from) */ PyObject *co_name; /* string (name, for reference) */ int co_firstlineno; /* first source line number */ @@ -70,6 +85,12 @@ int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *); /* same as struct above */ + +/* Creates a new empty code object so callers don't have to know the + types of most of the arguments. */ +PyAPI_FUNC(PyCodeObject *) +PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno); + PyAPI_FUNC(int) PyCode_Addr2Line(PyCodeObject *, int); /* for internal use only */ Index: configure.in =================================================================== --- configure.in (revision 68532) +++ configure.in (working copy) @@ -755,6 +755,8 @@ AC_MSG_RESULT($LDLIBRARY) +AC_PROG_AWK + AC_PROG_RANLIB AC_SUBST(AR) AC_CHECK_PROGS(AR, ar aal, ar) @@ -3751,7 +3753,7 @@ done AC_SUBST(SRCDIRS) -SRCDIRS="Parser Grammar Objects Python Modules Mac" +SRCDIRS="Parser Grammar Objects Python Modules Mac Include" AC_MSG_CHECKING(for build directories) for dir in $SRCDIRS; do if test ! -d $dir; then Index: setup.py =================================================================== --- setup.py (revision 68532) +++ setup.py (working copy) @@ -419,6 +419,8 @@ # Some modules that are normally always on: exts.append( Extension('_weakref', ['_weakref.c']) ) + # List of opcodes provided by vmgen. + exts.append( Extension('_opcode', ['_opcode.c']) ) # array objects exts.append( Extension('array', ['arraymodule.c']) ) # complex math library functions Index: Objects/codeobject.c =================================================================== --- Objects/codeobject.c (revision 68532) +++ Objects/codeobject.c (working copy) @@ -1,5 +1,6 @@ #include "Python.h" #include "code.h" +#include "instructionsobject.h" #include "structmember.h" #define NAME_CHARS \ @@ -51,7 +52,7 @@ Py_ssize_t i; /* Check argument types */ if (argcount < 0 || nlocals < 0 || - code == NULL || + code == NULL || !PyInstructions_Check(code) || consts == NULL || !PyTuple_Check(consts) || names == NULL || !PyTuple_Check(names) || varnames == NULL || !PyTuple_Check(varnames) || @@ -59,8 +60,7 @@ cellvars == NULL || !PyTuple_Check(cellvars) || name == NULL || !PyString_Check(name) || filename == NULL || !PyString_Check(filename) || - lnotab == NULL || !PyString_Check(lnotab) || - !PyObject_CheckReadBuffer(code)) { + lnotab == NULL || !PyString_Check(lnotab)) { PyErr_BadInternalCall(); return NULL; } @@ -103,11 +103,64 @@ Py_INCREF(lnotab); co->co_lnotab = lnotab; co->co_zombieframe = NULL; + co->co_tcode = NULL; } return co; } +PyCodeObject * +PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) +{ + static PyObject *instructions = NULL; + static PyObject *nulltuple = NULL; + static PyObject *lnotab = NULL; + PyObject *filename_ob = NULL; + PyObject *funcname_ob = NULL; + PyCodeObject *result = NULL; + if (instructions == NULL) { + instructions = (PyObject *)_PyInstructions_New(0); + if (instructions == NULL) + goto failed; + } + if (nulltuple == NULL) { + nulltuple = PyTuple_New(0); + if (nulltuple == NULL) + goto failed; + } + if (lnotab == NULL) { + lnotab = PyString_FromString(""); + if (lnotab == NULL) + goto failed; + } + funcname_ob = PyString_FromString(funcname); + if (funcname_ob == NULL) + goto failed; + filename_ob = PyString_FromString(filename); + if (filename_ob == NULL) + goto failed; + result = PyCode_New(0, /* argcount */ + 0, /* nlocals */ + 0, /* stacksize */ + 0, /* flags */ + instructions, /* code */ + nulltuple, /* consts */ + nulltuple, /* names */ + nulltuple, /* varnames */ + nulltuple, /* freevars */ + nulltuple, /* cellvars */ + filename_ob, /* filename */ + funcname_ob, /* name */ + firstlineno, /* firstlineno */ + lnotab /* lnotab */ + ); + +failed: + Py_XDECREF(funcname_ob); + Py_XDECREF(filename_ob); + return result; +} + #define OFF(x) offsetof(PyCodeObject, x) static PyMemberDef code_memberlist[] = { @@ -186,7 +239,7 @@ int stacksize; int flags; PyObject *co = NULL; - PyObject *code; + PyObject *code; PyObject *ourcode = NULL; PyObject *consts; PyObject *names, *ournames = NULL; PyObject *varnames, *ourvarnames = NULL; @@ -197,7 +250,7 @@ int firstlineno; PyObject *lnotab; - if (!PyArg_ParseTuple(args, "iiiiSO!O!O!SSiS|O!O!:code", + if (!PyArg_ParseTuple(args, "iiiiOO!O!O!SSiS|O!O!:code", &argcount, &nlocals, &stacksize, &flags, &code, &PyTuple_Type, &consts, @@ -223,6 +276,9 @@ goto cleanup; } + ourcode = PyInstructions_FromSequence(code); + if (ourcode == NULL) + goto cleanup; ournames = validate_and_copy_tuple(names); if (ournames == NULL) goto cleanup; @@ -243,10 +299,11 @@ goto cleanup; co = (PyObject *)PyCode_New(argcount, nlocals, stacksize, flags, - code, consts, ournames, ourvarnames, + ourcode, consts, ournames, ourvarnames, ourfreevars, ourcellvars, filename, name, firstlineno, lnotab); cleanup: + Py_XDECREF(ourcode); Py_XDECREF(ournames); Py_XDECREF(ourvarnames); Py_XDECREF(ourfreevars); @@ -268,6 +325,8 @@ Py_XDECREF(co->co_lnotab); if (co->co_zombieframe != NULL) PyObject_GC_Del(co->co_zombieframe); + if (co->co_tcode != NULL) + free(co->co_tcode); PyObject_DEL(co); } Index: Objects/object.c =================================================================== --- Objects/object.c (revision 68532) +++ Objects/object.c (working copy) @@ -2,6 +2,7 @@ /* Generic object operations; and implementation of None (NoObject) */ #include "Python.h" +#include "instructionsobject.h" #ifdef __cplusplus extern "C" { @@ -2043,6 +2044,9 @@ if (PyType_Ready(&PyNotImplemented_Type) < 0) Py_FatalError("Can't initialize type(NotImplemented)"); + + if (PyType_Ready(&PyInstructions_Type) < 0) + Py_FatalError("Can't initialize 'instructions'"); } Index: Objects/frameobject.c =================================================================== --- Objects/frameobject.c (revision 68532) +++ Objects/frameobject.c (working copy) @@ -3,6 +3,7 @@ #include "Python.h" #include "code.h" +#include "instructionsobject.h" #include "frameobject.h" #include "opcode.h" #include "structmember.h" @@ -94,7 +95,7 @@ int new_lineno = 0; /* The new value of f_lineno */ int new_lasti = 0; /* The new value of f_lasti */ int new_iblock = 0; /* The new value of f_iblock */ - unsigned char *code = NULL; /* The bytecode for the frame... */ + PyPInst *code = NULL; /* The bytecode for the frame... */ Py_ssize_t code_len = 0; /* ...and its length */ char *lnotab = NULL; /* Iterating over co_lnotab */ Py_ssize_t lnotab_len = 0; /* (ditto) */ @@ -163,7 +164,8 @@ } /* We're now ready to look at the bytecode. */ - PyString_AsStringAndSize(f->f_code->co_code, (char **)&code, &code_len); + code = ((PyInstructionsObject *)f->f_code->co_code)->inst; + code_len = Py_SIZE(f->f_code->co_code); min_addr = MIN(new_lasti, f->f_lasti); max_addr = MAX(new_lasti, f->f_lasti); @@ -177,7 +179,8 @@ * cases (AFAIK) where a line's code can start with DUP_TOP or * POP_TOP, but if any ever appear, they'll be subject to the same * restriction (but with a different error message). */ - if (code[new_lasti] == DUP_TOP || code[new_lasti] == POP_TOP) { + if (PyPInst_GET_OPCODE(code + new_lasti) == DUP_TOP || + PyPInst_GET_OPCODE(code + new_lasti) == POP_TOP) { PyErr_SetString(PyExc_ValueError, "can't jump to 'except' line as there's no exception"); return -1; @@ -198,7 +201,10 @@ memset(in_finally, '\0', sizeof(in_finally)); blockstack_top = 0; for (addr = 0; addr < code_len; addr++) { - unsigned char op = code[addr]; + if (code[addr].is_arg) { + continue; + } + unsigned char op = PyPInst_GET_OPCODE(code + addr); switch (op) { case SETUP_LOOP: case SETUP_EXCEPT: @@ -209,7 +215,8 @@ case POP_BLOCK: assert(blockstack_top > 0); - setup_op = code[blockstack[blockstack_top-1]]; + setup_op = PyPInst_GET_OPCODE( + code + blockstack[blockstack_top-1]); if (setup_op == SETUP_FINALLY) { in_finally[blockstack_top-1] = 1; } @@ -224,7 +231,8 @@ * 'finally' block. (If blockstack_top is 0, we must * be seeing such an END_FINALLY.) */ if (blockstack_top > 0) { - setup_op = code[blockstack[blockstack_top-1]]; + setup_op = PyPInst_GET_OPCODE( + code + blockstack[blockstack_top-1]); if (setup_op == SETUP_FINALLY) { blockstack_top--; } @@ -255,10 +263,6 @@ } } } - - if (op >= HAVE_ARGUMENT) { - addr += 2; - } } /* Verify that the blockstack tracking code didn't get lost. */ @@ -282,7 +286,10 @@ * again - in that case we raise an exception below. */ delta_iblock = 0; for (addr = min_addr; addr < max_addr; addr++) { - unsigned char op = code[addr]; + if (code[addr].is_arg) { + continue; + } + unsigned char op = PyPInst_GET_OPCODE(code + addr); switch (op) { case SETUP_LOOP: case SETUP_EXCEPT: @@ -296,10 +303,6 @@ } min_delta_iblock = MIN(min_delta_iblock, delta_iblock); - - if (op >= HAVE_ARGUMENT) { - addr += 2; - } } /* Derive the absolute iblock values from the deltas. */ Index: Objects/instructionsobject.c =================================================================== --- Objects/instructionsobject.c (revision 0) +++ Objects/instructionsobject.c (revision 0) @@ -0,0 +1,188 @@ +#include "Python.h" +#include "instructionsobject.h" + +static inline unsigned int pinst_to_int(PyPInst inst) { + return inst.opcode_or_arg << 1 | inst.is_arg; +} + +static int +insts_compare(PyInstructionsObject *l, PyInstructionsObject *r) { + Py_ssize_t min_size = Py_SIZE(l) < Py_SIZE(r) ? Py_SIZE(l) : Py_SIZE(r); + Py_ssize_t i; + for (i = 0; i < min_size; i++) { + unsigned int l_inst = pinst_to_int(l->inst[i]); + unsigned int r_inst = pinst_to_int(r->inst[i]); + if (l_inst < r_inst) return -1; + if (l_inst > r_inst) return 1; + } + if (Py_SIZE(l) < Py_SIZE(r)) return -1; + if (Py_SIZE(l) > Py_SIZE(r)) return 1; + return 0; +} + +static long +insts_hash(PyInstructionsObject *vec) { + long result = Py_SIZE(vec); + Py_ssize_t i; + for (i = 0; i < Py_SIZE(vec); i++) { + result *= 1000003; + result ^= pinst_to_int(vec->inst[i]); + } + return result; +} + +PyInstructionsObject * +_PyInstructions_New(Py_ssize_t num_instructions) +{ + return PyObject_NEW_VAR(PyInstructionsObject, &PyInstructions_Type, + num_instructions); +} + +/* This only works when it's passed the only copy of an Instructions + vector. Otherwise, it would delete the data out from other any + other users. On error, decrefs *vec and sets it to NULL. */ +int +_PyInstructions_Resize(PyInstructionsObject **vec, Py_ssize_t new_size) { + PyInstructionsObject *const old = *vec; + if (!PyInstructions_Check(old) || Py_REFCNT(old) != 1 || new_size < 0) { + *vec = NULL; + Py_DECREF(old); + PyErr_BadInternalCall(); + return -1; + } + _Py_DEC_REFTOTAL; + _Py_ForgetReference((PyObject *)*vec); + *vec = (PyInstructionsObject *)PyObject_REALLOC( + old, sizeof(PyInstructionsObject) + new_size * sizeof(PyPInst)); + if (*vec == NULL) { + PyObject_Del(old); + PyErr_NoMemory(); + return -1; + } + _Py_NewReference((PyObject *)*vec); + Py_SIZE(*vec) = new_size; + return 0; +} + +PyObject * +PyInstructions_FromSequence(PyObject *seq) { + Py_ssize_t codelen; + PyInstructionsObject *code = NULL; + PyObject *item = NULL; + Py_ssize_t i; + if (!PySequence_Check(seq)) { + PyErr_SetString( + PyExc_ValueError, + "code: instructions must be a sequence of integral types."); + return NULL; + } + if ((codelen = PySequence_Size(seq)) < 0 || + (code = _PyInstructions_New(codelen)) == NULL) { + goto error; + } + for (i = 0; i < codelen; i++) { + unsigned int value; + item = PySequence_GetItem(seq, i); + if (item == NULL) { + PyErr_Format(PyExc_ValueError, + "code: Failed to extract %zdth element from" + " 'code' sequence.", i); + goto error; + } + value = PyNumber_AsSsize_t(item, PyExc_OverflowError); + if (value == -1 && PyErr_Occurred()) { + PyErr_Format(PyExc_ValueError, + "code: %zdth element wasn't integral between" + " 0 and 2^32.", i); + goto error; + } + code->inst[i].is_arg = value & 1; + /* Not much checking here. The user can crash us in + plenty of ways even with all valid opcodes. */ + code->inst[i].opcode_or_arg = value >> 1; + Py_CLEAR(item); + } + + return (PyObject *)code; + + error: + Py_XDECREF(item); + Py_XDECREF(code); + return NULL; +} + +Py_ssize_t +insts_length(PyObject *ob) +{ + return Py_SIZE(ob); +} + +PyObject * +insts_item(PyInstructionsObject *ob, Py_ssize_t i) +{ + if (i < 0 || i >= Py_SIZE(ob)) { + PyErr_SetString(PyExc_IndexError, "instruction index out of range"); + return NULL; + } + unsigned int value = pinst_to_int(ob->inst[i]); + return PyInt_FromSize_t(value); +} + +PyDoc_STRVAR(insts_doc, +"instructions stores a sequence of integers, each of which represents either" +" an operation or an operation's argument."); + +static PySequenceMethods instructions_as_sequence = { + (lenfunc)insts_length, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + (ssizeargfunc)insts_item, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + 0, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +PyTypeObject PyInstructions_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "instructions", /* tp_name */ + sizeof(PyInstructionsObject), /* tp_basicsize */ + sizeof(PyPInst), /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)insts_compare, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &instructions_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)insts_hash, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + insts_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + Index: Lib/modulefinder.py =================================================================== --- Lib/modulefinder.py (revision 68532) +++ Lib/modulefinder.py (working copy) @@ -21,7 +21,11 @@ STORE_NAME = chr(dis.opname.index('STORE_NAME')) STORE_GLOBAL = chr(dis.opname.index('STORE_GLOBAL')) STORE_OPS = [STORE_NAME, STORE_GLOBAL] -HAVE_ARGUMENT = chr(dis.HAVE_ARGUMENT) +wordcode = 0 +try: + HAVE_ARGUMENT = chr(dis.HAVE_ARGUMENT) +except AttributeError: + wordcode = 1 # Modulefinder does a good job at simulating Python's, but it can not # handle __path__ modifications packages make at runtime. Therefore there @@ -392,9 +396,46 @@ else: code = code[1:] + def scan_opcodes_wordcode(self, co, + unpack = struct.unpack): + # Scan the code, and yield 'interesting' opcode combinations + # "wordcode" version (has 1-element arguments) + code = list(co.co_code) + names = co.co_names + consts = co.co_consts + LOAD_LOAD_AND_IMPORT = map(lambda x: ord(x) << 1, + [LOAD_CONST, LOAD_CONST, IMPORT_NAME]) + REAL_STORE_OPS = [ord(o) for o in STORE_OPS] + while code: + c = dis.get_opcode(code[0]) + if c in REAL_STORE_OPS: + oparg = dis.get_argument(code[1]) + yield "store", (names[oparg],) + code = code[2:] + continue + if (len(code) >= 5 and + code[0] == dis.make_opcode(dis.opmap['CC']) and + code[3] == dis.make_opcode(ord(IMPORT_NAME))): + oparg_1, oparg_2, oparg_3 = (dis.get_argument(code[x]) + for x in (1,2,4)) + level = consts[oparg_1] + if level == -1: # normal import + yield "import", (consts[oparg_2], names[oparg_3]) + elif level == 0: # absolute import + yield "absolute_import", (consts[oparg_2], names[oparg_3]) + else: # relative import + yield "relative_import", (level, consts[oparg_2], names[oparg_3]) + code = code[5:] + continue + code = code[1:] + while code and dis.is_argument(code[0]): + code = code[1:] + def scan_code(self, co, m): code = co.co_code - if sys.version_info >= (2, 5): + if wordcode: + scanner = self.scan_opcodes_wordcode + elif sys.version_info >= (2, 5): scanner = self.scan_opcodes_25 else: scanner = self.scan_opcodes Index: Lib/compiler/pycodegen.py =================================================================== --- Lib/compiler/pycodegen.py (revision 68532) +++ Lib/compiler/pycodegen.py (working copy) @@ -20,14 +20,6 @@ except AttributeError: VERSION = 1 -callfunc_opcode_info = { - # (Have *args, Have **args) : opcode - (0,0) : "CALL_FUNCTION", - (1,0) : "CALL_FUNCTION_VAR", - (0,1) : "CALL_FUNCTION_KW", - (1,1) : "CALL_FUNCTION_VAR_KW", -} - LOOP = 1 EXCEPT = 2 TRY_FINALLY = 3 @@ -722,9 +714,9 @@ self.emit('LOAD_GLOBAL', 'AssertionError') if node.fail: self.visit(node.fail) - self.emit('RAISE_VARARGS', 2) + self.emit('RAISE_VARARGS_TWO') else: - self.emit('RAISE_VARARGS', 1) + self.emit('RAISE_VARARGS_ONE') self.nextBlock(end) self.emit('POP_TOP') @@ -740,7 +732,7 @@ if node.expr3: self.visit(node.expr3) n = n + 1 - self.emit('RAISE_VARARGS', n) + self.emit('RAISE_VARARGS_' + ('ZERO', 'ONE', 'TWO', 'THREE')[n]) def visitTryExcept(self, node): body = self.newBlock() @@ -1025,7 +1017,7 @@ self.emit('ROT_FOUR') else: self.emit('ROT_THREE') - self.emit('STORE_SLICE+%d' % slice) + self.emit('STORE_SLICE_' + ('NONE', 'LEFT', 'RIGHT', 'BOTH')[slice]) def visitAugSubscript(self, node, mode): if mode == "load": @@ -1063,8 +1055,15 @@ self.visit(node.dstar_args) have_star = node.star_args is not None have_dstar = node.dstar_args is not None - opcode = callfunc_opcode_info[have_star, have_dstar] - self.emit(opcode, kw << 8 | pos) + if not have_star and not have_dstar: + self.emit('CALL_FUNCTION', kw << 8 | pos) + else: + var_kw = 0 + if have_star: + var_kw = var_kw | 1 + if have_dstar: + var_kw = var_kw | 2 + self.emit('CALL_FUNCTION_VAR_KW', (kw << 8 | pos) << 16 | var_kw) def visitPrint(self, node, newline=0): self.set_lineno(node) @@ -1115,15 +1114,15 @@ if slice == 0: self.emit('DUP_TOP') elif slice == 3: - self.emit('DUP_TOPX', 3) + self.emit('DUP_TOP_THREE') else: - self.emit('DUP_TOPX', 2) + self.emit('DUP_TOP_TWO') if node.flags == 'OP_APPLY': - self.emit('SLICE+%d' % slice) + self.emit('SLICE_' + ('NONE', 'LEFT', 'RIGHT', 'BOTH')[slice]) elif node.flags == 'OP_ASSIGN': - self.emit('STORE_SLICE+%d' % slice) + self.emit('STORE_SLICE_' + ('NONE', 'LEFT', 'RIGHT', 'BOTH')[slice]) elif node.flags == 'OP_DELETE': - self.emit('DELETE_SLICE+%d' % slice) + self.emit('DELETE_SLICE_' + ('NONE', 'LEFT', 'RIGHT', 'BOTH')[slice]) else: print "weird slice", node.flags raise @@ -1135,7 +1134,7 @@ if len(node.subs) > 1: self.emit('BUILD_TUPLE', len(node.subs)) if aug_flag: - self.emit('DUP_TOPX', 2) + self.emit('DUP_TOP_TWO') if node.flags == 'OP_APPLY': self.emit('BINARY_SUBSCR') elif node.flags == 'OP_ASSIGN': @@ -1238,7 +1237,8 @@ def visitSliceobj(self, node): for child in node.nodes: self.visit(child) - self.emit('BUILD_SLICE', len(node.nodes)) + # ZERO and ONE aren't actually used. + self.emit('BUILD_SLICE_' + ('ZERO', 'ONE', 'TWO', 'THREE')[len(node.nodes)]) def visitDict(self, node): self.set_lineno(node) Index: Lib/compiler/pyassem.py =================================================================== --- Lib/compiler/pyassem.py (revision 68532) +++ Lib/compiler/pyassem.py (working copy) @@ -387,7 +387,7 @@ pc = pc + 1 else: print "\t", "%3d" % pc, opname, t[1] - pc = pc + 3 + pc = pc + 2 if io: sys.stdout = save @@ -435,8 +435,8 @@ if len(inst) == 1: pc = pc + 1 elif inst[0] != "SET_LINENO": - # arg takes 2 bytes - pc = pc + 3 + # arg takes 1 byte + pc = pc + 2 end[b] = pc pc = 0 for i in range(len(insts)): @@ -444,7 +444,7 @@ if len(inst) == 1: pc = pc + 1 elif inst[0] != "SET_LINENO": - pc = pc + 3 + pc = pc + 2 opname = inst[0] if self.hasjrel.has_elt(opname): oparg = inst[1] @@ -572,12 +572,11 @@ if opname == "SET_LINENO": lnotab.nextLine(oparg) continue - hi, lo = twobyte(oparg) try: - lnotab.addCode(self.opnum[opname], lo, hi) + lnotab.addCode(self.opnum[opname], oparg) except ValueError: print opname, oparg - print self.opnum[opname], lo, hi + print self.opnum[opname], oparg raise self.stage = DONE @@ -638,11 +637,6 @@ argcount = argcount - numNames return argcount -def twobyte(val): - """Convert an int argument into high and low bytes""" - assert isinstance(val, int) - return divmod(val, 256) - class LineAddrTable: """lnotab @@ -667,8 +661,10 @@ self.lnotab = [] def addCode(self, *args): - for arg in args: - self.code.append(chr(arg)) + if args: + self.code.append(args[0] << 1) + for arg in args[1:]: + self.code.append((arg << 1) | 1) self.codeOffset = self.codeOffset + len(args) def nextLine(self, lineno): @@ -704,7 +700,7 @@ self.lastoff = self.codeOffset def getCode(self): - return ''.join(self.code) + return list(self.code) def getTable(self): return ''.join(map(chr, self.lnotab)) @@ -744,18 +740,20 @@ effect = { 'POP_TOP': -1, 'DUP_TOP': 1, + 'DUP_TOP_TWO': 2, + 'DUP_TOP_THREE': 3, 'LIST_APPEND': -2, - 'SLICE+1': -1, - 'SLICE+2': -1, - 'SLICE+3': -2, - 'STORE_SLICE+0': -1, - 'STORE_SLICE+1': -2, - 'STORE_SLICE+2': -2, - 'STORE_SLICE+3': -3, - 'DELETE_SLICE+0': -1, - 'DELETE_SLICE+1': -2, - 'DELETE_SLICE+2': -2, - 'DELETE_SLICE+3': -3, + 'SLICE_LEFT': -1, + 'SLICE_RIGHT': -1, + 'SLICE_BOTH': -2, + 'STORE_SLICE_NONE': -1, + 'STORE_SLICE_LEFT': -2, + 'STORE_SLICE_RIGHT': -2, + 'STORE_SLICE_BOTH': -3, + 'DELETE_SLICE_NONE': -1, + 'DELETE_SLICE_LEFT': -2, + 'DELETE_SLICE_RIGHT': -2, + 'DELETE_SLICE_BOTH': -3, 'STORE_SUBSCR': -3, 'DELETE_SUBSCR': -2, # PRINT_EXPR? @@ -764,6 +762,8 @@ 'YIELD_VALUE': -1, 'EXEC_STMT': -3, 'BUILD_CLASS': -2, + 'BUILD_SLICE_TWO': -1, + 'BUILD_SLICE_THREE': -2, 'STORE_NAME': -1, 'STORE_ATTR': -2, 'DELETE_ATTR': -1, @@ -796,23 +796,15 @@ def CALL_FUNCTION(self, argc): hi, lo = divmod(argc, 256) return -(lo + hi * 2) - def CALL_FUNCTION_VAR(self, argc): - return self.CALL_FUNCTION(argc)-1 - def CALL_FUNCTION_KW(self, argc): - return self.CALL_FUNCTION(argc)-1 def CALL_FUNCTION_VAR_KW(self, argc): - return self.CALL_FUNCTION(argc)-2 + star_effect = -1 + if argc & 0xFFFF == 3: + star_effect = -2 + return self.CALL_FUNCTION(argc >> 16) + star_effect def MAKE_FUNCTION(self, argc): return -argc def MAKE_CLOSURE(self, argc): # XXX need to account for free variables too! return -argc - def BUILD_SLICE(self, argc): - if argc == 2: - return -1 - elif argc == 3: - return -2 - def DUP_TOPX(self, argc): - return argc findDepth = StackDepthTracker().findDepth Index: Lib/inspect.py =================================================================== --- Lib/inspect.py (revision 68532) +++ Lib/inspect.py (working copy) @@ -752,12 +752,12 @@ if args[i][:1] in ('', '.'): stack, remain, count = [], [], [] while step < len(co.co_code): - op = ord(co.co_code[step]) + op = dis.get_opcode(co.co_code[step]) step = step + 1 - if op >= dis.HAVE_ARGUMENT: + if step < len(co.co_code) and dis.is_argument(co.co_code[step]): opname = dis.opname[op] - value = ord(co.co_code[step]) + ord(co.co_code[step+1])*256 - step = step + 2 + value = dis.get_argument(co.co_code[step]) + step = step + 1 if opname in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'): remain.append(value) count.append(value) Index: Lib/opcode.py =================================================================== --- Lib/opcode.py (revision 68532) +++ Lib/opcode.py (working copy) @@ -5,9 +5,14 @@ """ __all__ = ["cmp_op", "hasconst", "hasname", "hasjrel", "hasjabs", - "haslocal", "hascompare", "hasfree", "opname", "opmap", - "HAVE_ARGUMENT", "EXTENDED_ARG"] + "haslocal", "hascompare", "hasfree", "opname", "opmap", "argdesc", + "is_argument", "get_opcode", "get_argument", + "make_opcode", "make_argument", + "prim2super", "super2prim", + "decode_superinstruction"] +import _opcode + cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 'is not', 'exception match', 'BAD') @@ -24,162 +29,135 @@ for op in range(256): opname[op] = '<%r>' % (op,) del op -def def_op(name, op): - opname[op] = name - opmap[name] = op +for i, name in enumerate(_opcode.opcodes): + opname[i] = name + opmap[name] = i +del i, name -def name_op(name, op): - def_op(name, op) +prim2super = _opcode.superinstruction_table +super2prim = dict((super,prims) for prims, super in prim2super.iteritems()) + +argdesc = {} + +def arg_op(name, description): + argdesc[opmap[name]] = description + +def name_op(name, description): + op = opmap[name] hasname.append(op) + argdesc[op] = description -def jrel_op(name, op): - def_op(name, op) +def jrel_op(name, description): + op = opmap[name] hasjrel.append(op) + argdesc[op] = description -def jabs_op(name, op): - def_op(name, op) +def jabs_op(name, description): + op = opmap[name] hasjabs.append(op) + argdesc[op] = description # Instruction opcodes for compiled code -# Blank lines correspond to available opcodes -def_op('STOP_CODE', 0) -def_op('POP_TOP', 1) -def_op('ROT_TWO', 2) -def_op('ROT_THREE', 3) -def_op('DUP_TOP', 4) -def_op('ROT_FOUR', 5) +name_op('STORE_NAME', 'Index in name list') +name_op('DELETE_NAME', 'Index in name list') +arg_op('UNPACK_SEQUENCE', 'Number of tuple items') +jrel_op('FOR_ITER', '???') +arg_op('LIST_APPEND', '???') -def_op('NOP', 9) -def_op('UNARY_POSITIVE', 10) -def_op('UNARY_NEGATIVE', 11) -def_op('UNARY_NOT', 12) -def_op('UNARY_CONVERT', 13) +name_op('STORE_ATTR', 'Index in name list') +name_op('DELETE_ATTR', 'Index in name list') +name_op('STORE_GLOBAL', 'Index in name list') +name_op('DELETE_GLOBAL', 'Index in name list') -def_op('UNARY_INVERT', 15) +arg_op('LOAD_CONST', 'Index in const list') +hasconst.append(opmap['LOAD_CONST']) +name_op('LOAD_NAME', 'Index in name list') +arg_op('BUILD_TUPLE', 'Number of tuple items') +arg_op('BUILD_LIST', 'Number of list items') +arg_op('BUILD_MAP', 'Number of dict entries (upto 255)') +name_op('LOAD_ATTR', 'Index in name list') +arg_op('COMPARE_OP', 'Comparison operator') +hascompare.append(opmap['COMPARE_OP']) +name_op('IMPORT_NAME', 'Index in name list') +name_op('IMPORT_FROM', 'Index in name list') -def_op('BINARY_POWER', 19) -def_op('BINARY_MULTIPLY', 20) -def_op('BINARY_DIVIDE', 21) -def_op('BINARY_MODULO', 22) -def_op('BINARY_ADD', 23) -def_op('BINARY_SUBTRACT', 24) -def_op('BINARY_SUBSCR', 25) -def_op('BINARY_FLOOR_DIVIDE', 26) -def_op('BINARY_TRUE_DIVIDE', 27) -def_op('INPLACE_FLOOR_DIVIDE', 28) -def_op('INPLACE_TRUE_DIVIDE', 29) -def_op('SLICE+0', 30) -def_op('SLICE+1', 31) -def_op('SLICE+2', 32) -def_op('SLICE+3', 33) +jrel_op('JUMP_FORWARD', 'Number of bytes to skip') +jrel_op('JUMP_IF_FALSE', 'Number of bytes to skip') +jrel_op('JUMP_IF_TRUE', 'Number of bytes to skip') +jabs_op('JUMP_ABSOLUTE', 'Target byte offset from beginning of code') -def_op('STORE_SLICE+0', 40) -def_op('STORE_SLICE+1', 41) -def_op('STORE_SLICE+2', 42) -def_op('STORE_SLICE+3', 43) +name_op('LOAD_GLOBAL', 'Index in name list') -def_op('DELETE_SLICE+0', 50) -def_op('DELETE_SLICE+1', 51) -def_op('DELETE_SLICE+2', 52) -def_op('DELETE_SLICE+3', 53) +jabs_op('CONTINUE_LOOP', 'Target address') +jrel_op('SETUP_LOOP', 'Distance to target address') +jrel_op('SETUP_EXCEPT', 'Distance to target address') +jrel_op('SETUP_FINALLY', 'Distance to target address') -def_op('STORE_MAP', 54) -def_op('INPLACE_ADD', 55) -def_op('INPLACE_SUBTRACT', 56) -def_op('INPLACE_MULTIPLY', 57) -def_op('INPLACE_DIVIDE', 58) -def_op('INPLACE_MODULO', 59) -def_op('STORE_SUBSCR', 60) -def_op('DELETE_SUBSCR', 61) -def_op('BINARY_LSHIFT', 62) -def_op('BINARY_RSHIFT', 63) -def_op('BINARY_AND', 64) -def_op('BINARY_XOR', 65) -def_op('BINARY_OR', 66) -def_op('INPLACE_POWER', 67) -def_op('GET_ITER', 68) +arg_op('LOAD_FAST', 'Local variable number') +haslocal.append(opmap['LOAD_FAST']) +arg_op('STORE_FAST', 'Local variable number') +haslocal.append(opmap['STORE_FAST']) +arg_op('DELETE_FAST', 'Local variable number') +haslocal.append(opmap['DELETE_FAST']) -def_op('PRINT_EXPR', 70) -def_op('PRINT_ITEM', 71) -def_op('PRINT_NEWLINE', 72) -def_op('PRINT_ITEM_TO', 73) -def_op('PRINT_NEWLINE_TO', 74) -def_op('INPLACE_LSHIFT', 75) -def_op('INPLACE_RSHIFT', 76) -def_op('INPLACE_AND', 77) -def_op('INPLACE_XOR', 78) -def_op('INPLACE_OR', 79) -def_op('BREAK_LOOP', 80) -def_op('WITH_CLEANUP', 81) -def_op('LOAD_LOCALS', 82) -def_op('RETURN_VALUE', 83) -def_op('IMPORT_STAR', 84) -def_op('EXEC_STMT', 85) -def_op('YIELD_VALUE', 86) -def_op('POP_BLOCK', 87) -def_op('END_FINALLY', 88) -def_op('BUILD_CLASS', 89) +arg_op('CALL_FUNCTION', '#args + (#kwargs << 8)') +arg_op('MAKE_FUNCTION', 'Number of args with default values') +arg_op('MAKE_CLOSURE', '???') +arg_op('LOAD_CLOSURE', '???') +hasfree.append(opmap['LOAD_CLOSURE']) +arg_op('LOAD_DEREF', '???') +hasfree.append(opmap['LOAD_DEREF']) +arg_op('STORE_DEREF', '???') +hasfree.append(opmap['STORE_DEREF']) -HAVE_ARGUMENT = 90 # Opcodes from here have an argument: +arg_op('CALL_FUNCTION_VAR_KW', + '((#args + (#kwargs << 8)) << 16) + code;' + ' where code&1 is true if there\'s a *args parameter,' + ' and code&2 is true if there\'s a **kwargs parameter.') -name_op('STORE_NAME', 90) # Index in name list -name_op('DELETE_NAME', 91) # "" -def_op('UNPACK_SEQUENCE', 92) # Number of tuple items -jrel_op('FOR_ITER', 93) -def_op('LIST_APPEND', 94) -name_op('STORE_ATTR', 95) # Index in name list -name_op('DELETE_ATTR', 96) # "" -name_op('STORE_GLOBAL', 97) # "" -name_op('DELETE_GLOBAL', 98) # "" -def_op('DUP_TOPX', 99) # number of items to duplicate -def_op('LOAD_CONST', 100) # Index in const list -hasconst.append(100) -name_op('LOAD_NAME', 101) # Index in name list -def_op('BUILD_TUPLE', 102) # Number of tuple items -def_op('BUILD_LIST', 103) # Number of list items -def_op('BUILD_MAP', 104) # Number of dict entries (upto 255) -name_op('LOAD_ATTR', 105) # Index in name list -def_op('COMPARE_OP', 106) # Comparison operator -hascompare.append(106) -name_op('IMPORT_NAME', 107) # Index in name list -name_op('IMPORT_FROM', 108) # Index in name list +del arg_op, name_op, jrel_op, jabs_op -jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip -jrel_op('JUMP_IF_FALSE', 111) # "" -jrel_op('JUMP_IF_TRUE', 112) # "" -jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code +def is_argument(instruction): + return bool(instruction & 1) -name_op('LOAD_GLOBAL', 116) # Index in name list +def get_opcode(instruction): + assert not is_argument(instruction), '%d is argument, not opcode' % instruction + return instruction >> 1 -jabs_op('CONTINUE_LOOP', 119) # Target address -jrel_op('SETUP_LOOP', 120) # Distance to target address -jrel_op('SETUP_EXCEPT', 121) # "" -jrel_op('SETUP_FINALLY', 122) # "" +def get_argument(instruction): + assert is_argument(instruction), '%d is opcode, not argument' % instruction + return instruction >> 1 -def_op('LOAD_FAST', 124) # Local variable number -haslocal.append(124) -def_op('STORE_FAST', 125) # Local variable number -haslocal.append(125) -def_op('DELETE_FAST', 126) # Local variable number -haslocal.append(126) +def make_opcode(op): + return op << 1 -def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) -def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8) -def_op('MAKE_FUNCTION', 132) # Number of args with default values -def_op('BUILD_SLICE', 133) # Number of items -def_op('MAKE_CLOSURE', 134) -def_op('LOAD_CLOSURE', 135) -hasfree.append(135) -def_op('LOAD_DEREF', 136) -hasfree.append(136) -def_op('STORE_DEREF', 137) -hasfree.append(137) +def make_argument(arg): + return (arg << 1) | 1 -def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8) -def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8) -def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8) -def_op('EXTENDED_ARG', 143) -EXTENDED_ARG = 143 +def decode_superinstruction(instructions, index): + """Given a sequence of instructions, decodes the superinstruction + at the beginning and returns the list of primitive instructions + and arguments that it executes, and the instruction just after the + superinstruction's arguments. -del def_op, name_op, jrel_op, jabs_op + This function does not change argument values that refer to + instruction indices, so it alone can't convert the whole + instruction sequence back into primitive instructions. + """ + ops = [get_opcode(instructions[index])] + while ops[0] in super2prim: + ops[0:1] = super2prim[ops[0]] + result = [] + next_arg = index + 1 + for op in ops: + result.append(make_opcode(op)) + if op in argdesc: + # The primitive takes an argument, so pull it off of the + # instruction stream. + assert is_argument(instructions[next_arg]) + result.append(instructions[next_arg]) + next_arg += 1 + assert (next_arg == len(instructions) or + not is_argument(instructions[next_arg])) + return result, next_arg Index: Lib/test/test_dis.py =================================================================== --- Lib/test/test_dis.py (revision 68532) +++ Lib/test/test_dis.py (working copy) @@ -13,35 +13,61 @@ dis_f = """\ %-4d 0 LOAD_FAST 0 (a) - 3 PRINT_ITEM - 4 PRINT_NEWLINE + 2 PRINT_ITEM + 3 PRINT_NEWLINE - %-4d 5 LOAD_CONST 1 (1) - 8 RETURN_VALUE + %-4d 4 LOAD_CONST 1 (1) + 6 RETURN_VALUE """%(_f.func_code.co_firstlineno + 1, _f.func_code.co_firstlineno + 2) +def _supertest(a): + return a + 1 + +dis_supertest = """\ + %-4d 0 LOAD_FAST 0 (a) + 2 CBINARY_ADD: + LOAD_CONST 1 (1) + BINARY_ADD + 4 RETURN_VALUE +"""%(_supertest.func_code.co_firstlineno + 1,) + + +def _2arg_supertest(a): + return (a, 1, 2) + +dis_2arg_supertest = """\ + %-4d 0 LOAD_FAST 0 (a) + 2 CC: + LOAD_CONST 1 (1) + LOAD_CONST 2 (2) + 5 BUILD_TUPLE 3 + 7 RETURN_VALUE +"""%(_2arg_supertest.func_code.co_firstlineno + 1,) + + def bug708901(): for res in range(1, 10): pass dis_bug708901 = """\ - %-4d 0 SETUP_LOOP 23 (to 26) - 3 LOAD_GLOBAL 0 (range) - 6 LOAD_CONST 1 (1) + %-4d 0 SETUP_LOOP 15 (to 17) + 2 LOAD_GLOBAL 0 (range) - %-4d 9 LOAD_CONST 2 (10) - 12 CALL_FUNCTION 2 - 15 GET_ITER - >> 16 FOR_ITER 6 (to 25) - 19 STORE_FAST 0 (res) + %-4d 4 CC: + LOAD_CONST 1 (1) + LOAD_CONST 2 (10) + 7 CALL_FUNCTION 2 + 9 GET_ITER + >> 10 FOR_ITER 4 (to 16) + 12 STORE_FAST 0 (res) - %-4d 22 JUMP_ABSOLUTE 16 - >> 25 POP_BLOCK - >> 26 LOAD_CONST 0 (None) - 29 RETURN_VALUE + %-4d 14 JUMP_ABSOLUTE 10 + >> 16 POP_BLOCK + >> 17 LOAD_CONST 0 (None) + 19 RETURN_VALUE """%(bug708901.func_code.co_firstlineno + 1, bug708901.func_code.co_firstlineno + 2, bug708901.func_code.co_firstlineno + 3) @@ -54,34 +80,35 @@ dis_bug1333982 = """\ %-4d 0 LOAD_CONST 1 (0) - 3 JUMP_IF_TRUE 33 (to 39) - 6 POP_TOP - 7 LOAD_GLOBAL 0 (AssertionError) - 10 BUILD_LIST 0 - 13 LOAD_FAST 0 (x) - 16 GET_ITER - >> 17 FOR_ITER 12 (to 32) - 20 STORE_FAST 1 (s) - 23 LOAD_FAST 1 (s) - 26 LIST_APPEND 2 - 29 JUMP_ABSOLUTE 17 + 2 JUMP_IF_TRUE 21 (to 25) + 4 POP_TOP + 5 LOAD_GLOBAL 0 (AssertionError) + 7 BUILD_LIST 0 + 9 LOAD_FAST 0 (x) + 11 GET_ITER + >> 12 FOR_ITER 8 (to 22) + 14 STORE_FAST 1 (s) + 16 LOAD_FAST 1 (s) + 18 LIST_APPEND 2 + 20 JUMP_ABSOLUTE 12 - %-4d >> 32 LOAD_CONST 2 (1) - 35 BINARY_ADD - 36 RAISE_VARARGS 2 - >> 39 POP_TOP + %-4d >> 22 CBINARY_ADD: + LOAD_CONST 2 (1) + BINARY_ADD + 24 RAISE_VARARGS_TWO + >> 25 POP_TOP - %-4d 40 LOAD_CONST 0 (None) - 43 RETURN_VALUE + %-4d 26 LOAD_CONST 0 (None) + 28 RETURN_VALUE """%(bug1333982.func_code.co_firstlineno + 1, bug1333982.func_code.co_firstlineno + 2, bug1333982.func_code.co_firstlineno + 3) _BIG_LINENO_FORMAT = """\ %3d 0 LOAD_GLOBAL 0 (spam) - 3 POP_TOP - 4 LOAD_CONST 0 (None) - 7 RETURN_VALUE + 2 POP_TOP + 3 LOAD_CONST 0 (None) + 5 RETURN_VALUE """ class DisTests(unittest.TestCase): @@ -104,20 +131,21 @@ lines))) def test_opmap(self): - self.assertEqual(dis.opmap["STOP_CODE"], 0) self.assertEqual(dis.opmap["LOAD_CONST"] in dis.hasconst, True) self.assertEqual(dis.opmap["STORE_NAME"] in dis.hasname, True) def test_opname(self): self.assertEqual(dis.opname[dis.opmap["LOAD_FAST"]], "LOAD_FAST") - def test_boundaries(self): - self.assertEqual(dis.opmap["EXTENDED_ARG"], dis.EXTENDED_ARG) - self.assertEqual(dis.opmap["STORE_NAME"], dis.HAVE_ARGUMENT) - def test_dis(self): self.do_disassembly_test(_f, dis_f) + def test_dis_super(self): + self.do_disassembly_test(_supertest, dis_supertest) + + def test_dis_2_arg_super(self): + self.do_disassembly_test(_2arg_supertest, dis_2arg_supertest) + def test_bug_708901(self): self.do_disassembly_test(bug708901, dis_bug708901) Index: Lib/test/test_sys.py =================================================================== --- Lib/test/test_sys.py (revision 68532) +++ Lib/test/test_sys.py (working copy) @@ -484,7 +484,7 @@ # complex check(complex(0,1), size(h + '2d')) # code - check(get_cell().func_code, size(h + '4i8Pi2P')) + check(get_cell().func_code, size(h + '4i9Pi2P')) # BaseException check(BaseException(), size(h + '3P')) # UnicodeEncodeError Index: Lib/dis.py =================================================================== --- Lib/dis.py (revision 68532) +++ Lib/dis.py (working copy) @@ -60,16 +60,17 @@ def disassemble(co, lasti=-1): """Disassemble a code object.""" - code = co.co_code + code = tuple(co.co_code) labels = findlabels(code) linestarts = dict(findlinestarts(co)) n = len(code) - i = 0 extended_arg = 0 free = None - while i < n: + for i in range(n): c = code[i] - op = ord(c) + if is_argument(c): + continue + op = get_opcode(c) if i in linestarts: if i > 0: print @@ -82,30 +83,16 @@ if i in labels: print '>>', else: print ' ', print repr(i).rjust(4), - print opname[op].ljust(20), - i = i+1 - if op >= HAVE_ARGUMENT: - oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg - extended_arg = 0 - i = i+2 - if op == EXTENDED_ARG: - extended_arg = oparg*65536L - print repr(oparg).rjust(5), - if op in hasconst: - print '(' + repr(co.co_consts[oparg]) + ')', - elif op in hasname: - print '(' + co.co_names[oparg] + ')', - elif op in hasjrel: - print '(to ' + repr(i + oparg) + ')', - elif op in haslocal: - print '(' + co.co_varnames[oparg] + ')', - elif op in hascompare: - print '(' + cmp_op[oparg] + ')', - elif op in hasfree: - if free is None: - free = co.co_cellvars + co.co_freevars - print '(' + free[oparg] + ')', - print + if op in super2prim: + print (opname[op] + ':').ljust(20) + insts, new_i = decode_superinstruction(code, i) + for sub_i in range(len(insts)): + if is_argument(insts[sub_i]): + continue + print ' ' * 17, + _print_one_op(co, insts[sub_i:sub_i+2], i) + else: + _print_one_op(co, code[i:i+2], i) def disassemble_string(code, lasti=-1, varnames=None, names=None, constants=None): @@ -114,7 +101,7 @@ i = 0 while i < n: c = code[i] - op = ord(c) + op = get_opcode(c) if i == lasti: print '-->', else: print ' ', if i in labels: print '>>', @@ -122,9 +109,9 @@ print repr(i).rjust(4), print opname[op].ljust(15), i = i+1 - if op >= HAVE_ARGUMENT: - oparg = ord(code[i]) + ord(code[i+1])*256 - i = i+2 + if i < n and is_argument(code[i]): + oparg = get_argument(code[i]) + i = i+1 print repr(oparg).rjust(5), if op in hasconst: if constants: @@ -157,17 +144,16 @@ """ labels = [] n = len(code) - i = 0 - while i < n: + for i in range(n): c = code[i] - op = ord(c) - i = i+1 - if op >= HAVE_ARGUMENT: - oparg = ord(code[i]) + ord(code[i+1])*256 - i = i+2 + if is_argument(c): + continue + op = get_opcode(c) + if i+1 < n and is_argument(code[i+1]): + oparg = get_argument(code[i+1]) label = -1 if op in hasjrel: - label = i+oparg + label = i+2+oparg elif op in hasjabs: label = oparg if label >= 0: @@ -197,6 +183,34 @@ if lineno != lastlineno: yield (addr, lineno) +def _print_one_op(co, insts, i): + """Print the primitive operation in insts, with any argument it has. + + The operation will be considered to start at index i for the + purpose of relative jumps. + + """ + op = get_opcode(insts[0]) + print opname[op].ljust(20), + if len(insts) > 1 and is_argument(insts[1]): + oparg = get_argument(insts[1]) + print repr(oparg).rjust(5), + if op in hasconst: + print '(' + repr(co.co_consts[oparg]) + ')', + elif op in hasname: + print '(' + co.co_names[oparg] + ')', + elif op in hasjrel: + print '(to ' + repr(i + 2 + oparg) + ')', + elif op in haslocal: + print '(' + co.co_varnames[oparg] + ')', + elif op in hascompare: + print '(' + cmp_op[oparg] + ')', + elif op in hasfree: + if free is None: + free = co.co_cellvars + co.co_freevars + print '(' + free[oparg] + ')', + print + def _test(): """Simple test program to disassemble a file.""" if sys.argv[1:]: Index: Makefile.pre.in =================================================================== --- Makefile.pre.in (revision 68532) +++ Makefile.pre.in (working copy) @@ -42,6 +42,9 @@ # Use this to make a link between python$(VERSION) and python in $(BINDIR) LN= @LN@ +# Used to create the ceval .i files. +AWK= @AWK@ + # Portable install script (configure doesn't always guess right) INSTALL= @INSTALL@ INSTALL_PROGRAM=@INSTALL_PROGRAM@ @@ -315,6 +318,7 @@ Objects/floatobject.o \ Objects/frameobject.o \ Objects/funcobject.o \ + Objects/instructionsobject.o \ Objects/intobject.o \ Objects/iterobject.o \ Objects/listobject.o \ @@ -573,6 +577,26 @@ Python/formatter_string.o: $(srcdir)/Python/formatter_string.c \ $(STRINGLIB_HEADERS) +# Vmgen-provided ceval bits + +Python/ceval.o: $(srcdir)/Python/ceval.c \ + Include/ceval-vm.i + $(CC) -c $(PY_CFLAGS) -fno-gcse -o $@ $< + +# Actually generates all of Include/ceval-vm.i, Include/ceval-disasm.i, +# Include/ceval-gen.i, Include/ceval-labels.i, Include/ceval-profile.i, +# and Include/ceval-peephole.i, but I can't figure out how to tell make +# that a single command produces multiple outputs, so I abbreviate +# that whole list with just ceval-vm.i. +Include/ceval-vm.i: $(srcdir)/Python/ceval.vmg $(srcdir)/Python/mangler.awk + vmgen $< + $(AWK) -f $(srcdir)/Python/mangler.awk ceval-vm.i > Include/ceval-vm.i + rm ceval-vm.i + mv ceval-disasm.i ceval-gen.i ceval-labels.i ceval-profile.i ceval-peephole.i Include/ + +# Already built, just telling make. +Include/ceval-disasm.i Include/ceval-gen.i Include/ceval-labels.i Include/ceval-profile.i Include/ceval-peephole.i : Include/ceval-vm.i + ############################################################################ # Header files @@ -589,6 +613,7 @@ Include/bufferobject.h \ Include/cellobject.h \ Include/ceval.h \ + Include/ceval-labels.i \ Include/classobject.h \ Include/cobject.h \ Include/code.h \ @@ -606,6 +631,7 @@ Include/funcobject.h \ Include/genobject.h \ Include/import.h \ + Include/instructionsobject.h \ Include/intobject.h \ Include/intrcheck.h \ Include/iterobject.h \ Index: Modules/_ctypes/callbacks.c =================================================================== --- Modules/_ctypes/callbacks.c (revision 68532) +++ Modules/_ctypes/callbacks.c (working copy) @@ -99,40 +99,13 @@ /* after code that pyrex generates */ void _AddTraceback(char *funcname, char *filename, int lineno) { - PyObject *py_srcfile = 0; - PyObject *py_funcname = 0; PyObject *py_globals = 0; - PyObject *empty_tuple = 0; - PyObject *empty_string = 0; PyCodeObject *py_code = 0; PyFrameObject *py_frame = 0; - - py_srcfile = PyString_FromString(filename); - if (!py_srcfile) goto bad; - py_funcname = PyString_FromString(funcname); - if (!py_funcname) goto bad; + py_globals = PyDict_New(); if (!py_globals) goto bad; - empty_tuple = PyTuple_New(0); - if (!empty_tuple) goto bad; - empty_string = PyString_FromString(""); - if (!empty_string) goto bad; - py_code = PyCode_New( - 0, /*int argcount,*/ - 0, /*int nlocals,*/ - 0, /*int stacksize,*/ - 0, /*int flags,*/ - empty_string, /*PyObject *code,*/ - empty_tuple, /*PyObject *consts,*/ - empty_tuple, /*PyObject *names,*/ - empty_tuple, /*PyObject *varnames,*/ - empty_tuple, /*PyObject *freevars,*/ - empty_tuple, /*PyObject *cellvars,*/ - py_srcfile, /*PyObject *filename,*/ - py_funcname, /*PyObject *name,*/ - lineno, /*int firstlineno,*/ - empty_string /*PyObject *lnotab*/ - ); + py_code = PyCode_NewEmpty(filename, funcname, lineno); if (!py_code) goto bad; py_frame = PyFrame_New( PyThreadState_Get(), /*PyThreadState *tstate,*/ @@ -145,10 +118,6 @@ PyTraceBack_Here(py_frame); bad: Py_XDECREF(py_globals); - Py_XDECREF(py_srcfile); - Py_XDECREF(py_funcname); - Py_XDECREF(empty_tuple); - Py_XDECREF(empty_string); Py_XDECREF(py_code); Py_XDECREF(py_frame); } Index: Modules/pyexpat.c =================================================================== --- Modules/pyexpat.c (revision 68532) +++ Modules/pyexpat.c (working copy) @@ -2,6 +2,7 @@ #include #include "frameobject.h" +#include "instructionsobject.h" #include "expat.h" #include "pyexpat.h" @@ -261,52 +262,11 @@ static PyCodeObject* getcode(enum HandlerTypes slot, char* func_name, int lineno) { - PyObject *code = NULL; - PyObject *name = NULL; - PyObject *nulltuple = NULL; - PyObject *filename = NULL; - if (handler_info[slot].tb_code == NULL) { - code = PyString_FromString(""); - if (code == NULL) - goto failed; - name = PyString_FromString(func_name); - if (name == NULL) - goto failed; - nulltuple = PyTuple_New(0); - if (nulltuple == NULL) - goto failed; - filename = PyString_FromString(__FILE__); handler_info[slot].tb_code = - PyCode_New(0, /* argcount */ - 0, /* nlocals */ - 0, /* stacksize */ - 0, /* flags */ - code, /* code */ - nulltuple, /* consts */ - nulltuple, /* names */ - nulltuple, /* varnames */ -#if PYTHON_API_VERSION >= 1010 - nulltuple, /* freevars */ - nulltuple, /* cellvars */ -#endif - filename, /* filename */ - name, /* name */ - lineno, /* firstlineno */ - code /* lnotab */ - ); - if (handler_info[slot].tb_code == NULL) - goto failed; - Py_DECREF(code); - Py_DECREF(nulltuple); - Py_DECREF(filename); - Py_DECREF(name); + PyCode_NewEmpty(__FILE__, func_name, lineno); } return handler_info[slot].tb_code; - failed: - Py_XDECREF(code); - Py_XDECREF(name); - return NULL; } #ifdef FIX_TRACE Index: Modules/_opcode.c =================================================================== --- Modules/_opcode.c (revision 0) +++ Modules/_opcode.c (revision 0) @@ -0,0 +1,88 @@ +#include "Python.h" + +#define INST_ADDR(CODE) #CODE +static const char *const opcode_names[] = { +#include "ceval-labels.i" +NULL +}; +#undef INST_ADDR + +static PyObject * +init_opcode_names(void) +{ + const char *const*opcode_name; + PyObject *opcode_list = PyList_New(0); + if (opcode_list == NULL) + return NULL; + for (opcode_name = opcode_names; *opcode_name != NULL; opcode_name++) { + PyObject *pyname = PyString_FromString(*opcode_name); + if (pyname == NULL) + goto err; + if (PyList_Append(opcode_list, pyname) == -1) + goto err; + } + return opcode_list; + +err: + Py_DECREF(opcode_list); + return NULL; +} + +typedef struct idx_combination { + int prefix; /* instruction or superinstruction prefix index */ + int lastprim; /* most recently added instruction index */ + int combination; /* resulting superinstruction index */ +} IdxCombination; + +static IdxCombination peephole_table[] = { +#include "ceval-peephole.i" +}; + +static PyObject * +init_superinstruction_table(void) +{ + Py_ssize_t i; + PyObject *table = NULL, *key = NULL, *value = NULL; + + table = PyDict_New(); + if (table == NULL) + goto err; + + for (i = 0; i < sizeof(peephole_table)/sizeof(peephole_table[0]); i++) { + IdxCombination *c = &(peephole_table[i]); + + key = Py_BuildValue("ii", c->prefix, c->lastprim); + value = PyInt_FromLong(c->combination); + if (key == NULL || value == NULL) + goto err; + if (PyDict_SetItem(table, key, value) != 0) + goto err; + Py_CLEAR(key); + Py_CLEAR(value); + } + return table; + +err: + Py_XDECREF(table); + Py_XDECREF(key); + Py_XDECREF(value); + return NULL; +} + +PyMODINIT_FUNC +init_opcode(void) +{ + PyObject *m; + + m = Py_InitModule3("_opcode", NULL, "Opcode definition module."); + if (m != NULL) { + PyObject *opcode_list, *superinstruction_table; + opcode_list = init_opcode_names(); + if (opcode_list != NULL) + PyModule_AddObject(m, "opcodes", opcode_list); + + superinstruction_table = init_superinstruction_table(); + if (superinstruction_table != NULL) + PyModule_AddObject(m, "superinstruction_table", superinstruction_table); + } +} Index: README.vmgen =================================================================== --- README.vmgen (revision 0) +++ README.vmgen (revision 0) @@ -0,0 +1,16 @@ +This branch uses vmgen, part of gforth, to generate the core eval +loop. It assumes vmgen is on the path, and that you've applied the +patch below to prims2x.fs, which usually gets installed to +/usr/share/gforth/0.6.2: + +--- prims2x.fs Sun Aug 24 11:17:19 2003 ++++ prims2x.fs Sun Oct 5 19:38:37 2008 +@@ -71,7 +71,7 @@ + + include ./gray.fs + 128 constant max-effect \ number of things on one side of a stack effect +-4 constant max-stacks \ the max. number of stacks (including inst-stream). ++9 constant max-stacks \ the max. number of stacks (including inst-stream). + 255 constant maxchar + maxchar 1+ constant eof-char + #tab constant tab-char