diff -r aa480ecc8d3a Include/opcode.h --- a/Include/opcode.h Sat Mar 22 14:07:06 2008 +0100 +++ b/Include/opcode.h Wed Mar 26 23:48:56 2008 +0100 @@ -88,7 +88,7 @@ extern "C" { #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 FOR_ITER 93 /* Byte index of beginning of loop body */ #define STORE_ATTR 95 /* Index in name list */ #define DELETE_ATTR 96 /* "" */ @@ -109,6 +109,8 @@ extern "C" { #define JUMP_IF_FALSE 111 /* "" */ #define JUMP_IF_TRUE 112 /* "" */ #define JUMP_ABSOLUTE 113 /* Target byte offset from beginning of code */ +#define JUMP_ABS_IF_TRUE 114 /* "" */ +#define JUMP_ABS_IF_FALSE 115 /* "" */ #define LOAD_GLOBAL 116 /* Index in name list */ diff -r aa480ecc8d3a Lib/compiler/pyassem.py --- a/Lib/compiler/pyassem.py Sat Mar 22 14:07:06 2008 +0100 +++ b/Lib/compiler/pyassem.py Wed Mar 26 23:48:56 2008 +0100 @@ -42,10 +42,8 @@ class FlowGraph: # Note: If the current block ends with an unconditional # control transfer, then it is incorrect to add an implicit - # transfer to the block graph. The current code requires - # these edges to get the blocks emitted in the right order, - # however. :-( If a client needs to remove these edges, call - # pruneEdges(). + # transfer to the block graph. These edges are removed later by + # calling pruneEdges() (see getBlocksInOrder()). self.current.addNext(block) self.startBlock(block) @@ -69,10 +67,12 @@ class FlowGraph: def emit(self, *inst): if self._debug: print "\t", inst - if inst[0] in ['RETURN_VALUE', 'YIELD_VALUE']: - self.current.addOutEdge(self.exit) if len(inst) == 2 and isinstance(inst[1], Block): self.current.addOutEdge(inst[1]) + # Record block emission order dependencies for relative jumps + # (which can only have positive offsets). + if self.hasjrel.has_elt(inst[0]): + self.current.followers.add(inst[1]) self.current.emit(inst) def getBlocksInOrder(self): @@ -80,117 +80,11 @@ class FlowGraph: i.e. each node appears before all of its successors """ - # XXX make sure every node that doesn't have an explicit next - # is set so that next points to exit + # Remove false edges. for b in self.blocks.elements(): - if b is self.exit: - continue - if not b.next: - b.addNext(self.exit) - order = dfs_postorder(self.entry, {}) - order.reverse() - self.fixupOrder(order, self.exit) - # hack alert - if not self.exit in order: - order.append(self.exit) - + b.pruneNext() + order = order_blocks(self.entry, self.exit) return order - - def fixupOrder(self, blocks, default_next): - """Fixup bad order introduced by DFS.""" - - # XXX This is a total mess. There must be a better way to get - # the code blocks in the right order. - - self.fixupOrderHonorNext(blocks, default_next) - self.fixupOrderForward(blocks, default_next) - - def fixupOrderHonorNext(self, blocks, default_next): - """Fix one problem with DFS. - - The DFS uses child block, but doesn't know about the special - "next" block. As a result, the DFS can order blocks so that a - block isn't next to the right block for implicit control - transfers. - """ - index = {} - for i in range(len(blocks)): - index[blocks[i]] = i - - for i in range(0, len(blocks) - 1): - b = blocks[i] - n = blocks[i + 1] - if not b.next or b.next[0] == default_next or b.next[0] == n: - continue - # The blocks are in the wrong order. Find the chain of - # blocks to insert where they belong. - cur = b - chain = [] - elt = cur - while elt.next and elt.next[0] != default_next: - chain.append(elt.next[0]) - elt = elt.next[0] - # Now remove the blocks in the chain from the current - # block list, so that they can be re-inserted. - l = [] - for b in chain: - assert index[b] > i - l.append((index[b], b)) - l.sort() - l.reverse() - for j, b in l: - del blocks[index[b]] - # Insert the chain in the proper location - blocks[i:i + 1] = [cur] + chain - # Finally, re-compute the block indexes - for i in range(len(blocks)): - index[blocks[i]] = i - - def fixupOrderForward(self, blocks, default_next): - """Make sure all JUMP_FORWARDs jump forward""" - index = {} - chains = [] - cur = [] - for b in blocks: - index[b] = len(chains) - cur.append(b) - if b.next and b.next[0] == default_next: - chains.append(cur) - cur = [] - chains.append(cur) - - while 1: - constraints = [] - - for i in range(len(chains)): - l = chains[i] - for b in l: - for c in b.get_children(): - if index[c] < i: - forward_p = 0 - for inst in b.insts: - if inst[0] == 'JUMP_FORWARD': - if inst[1] == c: - forward_p = 1 - if not forward_p: - continue - constraints.append((index[c], i)) - - if not constraints: - break - - # XXX just do one for now - # do swaps to get things in the right order - goes_before, a_chain = constraints[0] - assert a_chain > goes_before - c = chains[a_chain] - chains.remove(c) - chains.insert(goes_before, c) - - del blocks[:] - for c in chains: - for b in c: - blocks.append(b) def getBlocks(self): return self.blocks.elements() @@ -205,16 +99,60 @@ class FlowGraph: l.extend(b.getContainedGraphs()) return l -def dfs_postorder(b, seen): - """Depth-first search of tree rooted at b, return in postorder""" + +def order_blocks(start_block, exit_block): + """Order blocks so that they are emitted in the right order""" + # Rules: + # - when a block has a next block, the next block must be emitted just after + # - when a block has followers (relative jumps), it must be emitted before + # them + # - all reachable blocks must be emitted order = [] - seen[b] = b - for c in b.get_children(): - if seen.has_key(c): - continue - order = order + dfs_postorder(c, seen) - order.append(b) + + reachable = set() + blocks = [start_block] + while blocks: + reachable.update(blocks) + blocks = [c for b in blocks for c in b.get_children() + if not c in reachable] + + remaining = set(reachable) + + def fill_todo(): + todo = set(remaining) + if not todo: + return todo + for b in remaining: + # Followers must be processed after their preceders + for c in b.get_followers(): + while 1: + todo.discard(c) + # We must also discard any other block whose next block + # is the block we just discarded! + if c.prev and c.prev[0] is not b: + c = c.prev[0] + else: + break + # If todo is empty, then we have a circular dependency! + assert todo + return todo + + def fill_order(b): + remaining.discard(b) + order.append(b) + if b.next: + fill_order(b.next[0]) + # Append the default exit block if necessary + elif b is not exit_block and not b.has_unconditional_transfer(): + order.append(exit_block) + + fill_order(start_block) + while remaining: + todo = fill_todo() + for b in todo: + fill_order(b) return order + class Block: _count = 0 @@ -222,10 +160,18 @@ class Block: def __init__(self, label=''): self.insts = [] self.inEdges = misc.Set() - self.outEdges = misc.Set() + self.outEdges = set() self.label = label self.bid = Block._count self.next = [] + self.prev = [] + # Blocks that must be emitted *after* this one, because of + # bytecode offsets (e.g. relative jumps) pointing to them. + # NOTE: this might also include the next block if it is explicitly + # referenced by an instruction inside the block. Yet, the next block + # is treated separately from self.followers so that pruneNext() + # can work properly. + self.followers = set() Block._count = Block._count + 1 def __repr__(self): @@ -241,8 +187,6 @@ class Block: def emit(self, inst): op = inst[0] - if op[:4] == 'JUMP': - self.outEdges.add(inst[1]) self.insts.append(inst) def getInstructions(self): @@ -254,12 +198,26 @@ class Block: def addOutEdge(self, block): self.outEdges.add(block) - def addNext(self, block): + def addNext(self, block, add_prev=True): self.next.append(block) assert len(self.next) == 1, map(str, self.next) + if add_prev: + block.prev.append(self) + assert len(block.prev) == 1, map(str, block.prev) - _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS', 'YIELD_VALUE', - 'JUMP_ABSOLUTE', 'JUMP_FORWARD', 'CONTINUE_LOOP') + _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS', + 'JUMP_ABSOLUTE', 'JUMP_FORWARD', 'CONTINUE_LOOP', + ) + + def has_unconditional_transfer(self): + """Returns True if there is an unconditional transfer to an other block + at the end of this block. This means there is no risk for the bytecode + executer to go past this block's bytecode.""" + try: + op, arg = self.insts[-1] + except (IndexError, ValueError): + return + return op in self._uncond_transfer def pruneNext(self): """Remove bogus edge for unconditional transfers @@ -275,17 +233,17 @@ class Block: remove the next edge when it follows an unconditional control transfer. """ - try: - op, arg = self.insts[-1] - except (IndexError, ValueError): - return - if op in self._uncond_transfer: + if self.next and self.has_unconditional_transfer(): + self.outEdges.add(self.next[0]) + self.next[0].prev = [] self.next = [] def get_children(self): - if self.next and self.next[0] in self.outEdges: - self.outEdges.remove(self.next[0]) - return self.outEdges.elements() + self.next + return list(self.outEdges) + self.next + + def get_followers(self): + """Get the whole list of followers, including the next block.""" + return self.followers | set(self.next) def getContainedGraphs(self): """Return all graphs contained within this block. diff -r aa480ecc8d3a Lib/compiler/pycodegen.py --- a/Lib/compiler/pycodegen.py Sat Mar 22 14:07:06 2008 +0100 +++ b/Lib/compiler/pycodegen.py Wed Mar 26 23:48:56 2008 +0100 @@ -410,7 +410,7 @@ class CodeGenerator: def visitIf(self, node): end = self.newBlock() numtests = len(node.tests) - for i in range(numtests): + for i in range(numtests - 1): test, suite = node.tests[i] if is_constant_false(test): # XXX will need to check generator stuff here @@ -418,40 +418,54 @@ class CodeGenerator: self.set_lineno(test) self.visit(test) nextTest = self.newBlock() - self.emit('JUMP_IF_FALSE', nextTest) + self.emit('JUMP_ABS_IF_FALSE', nextTest) self.nextBlock() - self.emit('POP_TOP') self.visit(suite) self.emit('JUMP_FORWARD', end) self.startBlock(nextTest) - self.emit('POP_TOP') - if node.else_: - self.visit(node.else_) + # The last test can optimize a few things + test, suite = node.tests[numtests - 1] + if is_constant_false(test): + if node.else_: + self.visit(node.else_) + else: + self.set_lineno(test) + self.visit(test) + else_block = self.newBlock() if node.else_ else end + self.emit('JUMP_ABS_IF_FALSE', else_block) + self.nextBlock() + self.visit(suite) + if node.else_: + self.emit('JUMP_FORWARD', end) + self.startBlock(else_block) + self.visit(node.else_) self.nextBlock(end) def visitWhile(self, node): self.set_lineno(node) loop = self.newBlock() + body = self.newBlock() + tail = self.newBlock() else_ = self.newBlock() + after = self.newBlock() - after = self.newBlock() self.emit('SETUP_LOOP', after) self.nextBlock(loop) self.setups.push((LOOP, loop)) + self.emit('JUMP_FORWARD', tail) + self.startBlock(body) self.set_lineno(node, force=True) + + self.visit(node.body) + + self.nextBlock(tail) self.visit(node.test) - self.emit('JUMP_IF_FALSE', else_ or after) + self.emit('JUMP_ABS_IF_TRUE', body) - self.nextBlock() - self.emit('POP_TOP') - self.visit(node.body) - self.emit('JUMP_ABSOLUTE', loop) - - self.startBlock(else_) # or just the POPs if not else clause - self.emit('POP_TOP') + self.nextBlock(else_) # or just the POPs if not else clause self.emit('POP_BLOCK') self.setups.pop() if node.else_: @@ -460,6 +474,8 @@ class CodeGenerator: def visitFor(self, node): start = self.newBlock() + body = self.newBlock() + tail = self.newBlock() anchor = self.newBlock() after = self.newBlock() self.setups.push((LOOP, start)) @@ -471,10 +487,12 @@ class CodeGenerator: self.nextBlock(start) self.set_lineno(node, force=1) - self.emit('FOR_ITER', anchor) + self.emit('JUMP_FORWARD', tail) + self.startBlock(body) self.visit(node.assign) self.visit(node.body) - self.emit('JUMP_ABSOLUTE', start) + self.nextBlock(tail) + self.emit('FOR_ITER', body) self.nextBlock(anchor) self.emit('POP_BLOCK') self.setups.pop() @@ -536,12 +554,10 @@ class CodeGenerator: endblock = self.newBlock() elseblock = self.newBlock() self.visit(node.test) - self.emit('JUMP_IF_FALSE', elseblock) - self.emit('POP_TOP') + self.emit('JUMP_ABS_IF_FALSE', elseblock) self.visit(node.then) self.emit('JUMP_FORWARD', endblock) self.nextBlock(elseblock) - self.emit('POP_TOP') self.visit(node.else_) self.nextBlock(endblock) @@ -583,50 +599,42 @@ class CodeGenerator: stack = [] for i, for_ in zip(range(len(node.quals)), node.quals): - start, anchor = self.visit(for_) + start, body, tail = self.visit(for_) cont = None for if_ in for_.ifs: - if cont is None: - cont = self.newBlock() - self.visit(if_, cont) - stack.insert(0, (start, cont, anchor)) + self.visit(if_, tail) + stack.insert(0, (start, body, cont, tail)) self._implicitNameOp('LOAD', tmpname) self.visit(node.expr) self.emit('LIST_APPEND') - for start, cont, anchor in stack: - if cont: - skip_one = self.newBlock() - self.emit('JUMP_FORWARD', skip_one) - self.startBlock(cont) - self.emit('POP_TOP') - self.nextBlock(skip_one) - self.emit('JUMP_ABSOLUTE', start) - self.startBlock(anchor) + for start, body, cont, tail in stack: + self.nextBlock(tail) + self.emit('FOR_ITER', body) + self.nextBlock() self._implicitNameOp('DELETE', tmpname) self.__list_count = self.__list_count - 1 def visitListCompFor(self, node): start = self.newBlock() - anchor = self.newBlock() + body = self.newBlock() + tail = self.newBlock() self.visit(node.list) self.emit('GET_ITER') self.nextBlock(start) self.set_lineno(node, force=True) - self.emit('FOR_ITER', anchor) - self.nextBlock() + self.emit('JUMP_FORWARD', tail) + self.nextBlock(body) self.visit(node.assign) - return start, anchor + return start, body, tail def visitListCompIf(self, node, branch): self.set_lineno(node, force=True) self.visit(node.test) - self.emit('JUMP_IF_FALSE', branch) - self.newBlock() - self.emit('POP_TOP') + self.emit('JUMP_ABS_IF_FALSE', branch) def _makeClosure(self, gen, args): frees = gen.scope.get_free_vars() @@ -658,36 +666,30 @@ class CodeGenerator: stack = [] for i, for_ in zip(range(len(node.quals)), node.quals): - start, anchor, end = self.visit(for_) + start, body, tail, end = self.visit(for_) cont = None for if_ in for_.ifs: - if cont is None: - cont = self.newBlock() - self.visit(if_, cont) - stack.insert(0, (start, cont, anchor, end)) + self.visit(if_, tail) + stack.insert(0, (start, body, tail, end)) self.visit(node.expr) self.emit('YIELD_VALUE') self.emit('POP_TOP') - for start, cont, anchor, end in stack: - if cont: - skip_one = self.newBlock() - self.emit('JUMP_FORWARD', skip_one) - self.startBlock(cont) - self.emit('POP_TOP') - self.nextBlock(skip_one) - self.emit('JUMP_ABSOLUTE', start) - self.startBlock(anchor) + for start, body, tail, end in stack: + self.nextBlock(tail) + self.emit('FOR_ITER', body) + self.nextBlock() self.emit('POP_BLOCK') self.setups.pop() - self.startBlock(end) + self.nextBlock(end) self.emit('LOAD_CONST', None) def visitGenExprFor(self, node): start = self.newBlock() - anchor = self.newBlock() + body = self.newBlock() + tail = self.newBlock() end = self.newBlock() self.setups.push((LOOP, start)) @@ -701,17 +703,16 @@ class CodeGenerator: self.nextBlock(start) self.set_lineno(node, force=True) - self.emit('FOR_ITER', anchor) - self.nextBlock() + self.emit('JUMP_FORWARD', tail) + self.nextBlock(body) self.visit(node.assign) - return start, anchor, end + return start, body, tail, end def visitGenExprIf(self, node, branch): self.set_lineno(node, force=True) self.visit(node.test) - self.emit('JUMP_IF_FALSE', branch) + self.emit('JUMP_ABS_IF_FALSE', branch) self.newBlock() - self.emit('POP_TOP') # exception related diff -r aa480ecc8d3a Lib/opcode.py --- a/Lib/opcode.py Sat Mar 22 14:07:06 2008 +0100 +++ b/Lib/opcode.py Wed Mar 26 23:48:56 2008 +0100 @@ -127,7 +127,7 @@ name_op('STORE_NAME', 90) # Index 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) +jabs_op('FOR_ITER', 93) name_op('STORE_ATTR', 95) # Index in name list name_op('DELETE_ATTR', 96) # "" @@ -150,6 +150,8 @@ jrel_op('JUMP_IF_FALSE', 111) # "" jrel_op('JUMP_IF_FALSE', 111) # "" jrel_op('JUMP_IF_TRUE', 112) # "" jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code +jabs_op('JUMP_ABS_IF_TRUE', 114) # "" +jabs_op('JUMP_ABS_IF_FALSE', 115) # "" name_op('LOAD_GLOBAL', 116) # Index in name list diff -r aa480ecc8d3a Lib/test/test_dis.py --- a/Lib/test/test_dis.py Sat Mar 22 14:07:06 2008 +0100 +++ b/Lib/test/test_dis.py Wed Mar 26 23:48:56 2008 +0100 @@ -35,11 +35,11 @@ dis_bug708901 = """\ %-4d 9 LOAD_CONST 2 (10) 12 CALL_FUNCTION 2 15 GET_ITER - >> 16 FOR_ITER 6 (to 25) - 19 STORE_FAST 0 (res) + 16 JUMP_FORWARD 3 (to 22) + >> 19 STORE_FAST 0 (res) - %-4d 22 JUMP_ABSOLUTE 16 - >> 25 POP_BLOCK + %-4d >> 22 FOR_ITER 19 + 25 POP_BLOCK >> 26 LOAD_CONST 0 (None) 29 RETURN_VALUE """%(bug708901.func_code.co_firstlineno + 1, @@ -62,13 +62,13 @@ dis_bug1333982 = """\ 14 STORE_FAST 1 (_[1]) 17 LOAD_FAST 0 (x) 20 GET_ITER - >> 21 FOR_ITER 13 (to 37) - 24 STORE_FAST 2 (s) + 21 JUMP_FORWARD 10 (to 34) + >> 24 STORE_FAST 2 (s) 27 LOAD_FAST 1 (_[1]) 30 LOAD_FAST 2 (s) 33 LIST_APPEND - 34 JUMP_ABSOLUTE 21 - >> 37 DELETE_FAST 1 (_[1]) + >> 34 FOR_ITER 24 + 37 DELETE_FAST 1 (_[1]) %-4d 40 LOAD_CONST 2 (1) 43 BINARY_ADD diff -r aa480ecc8d3a Lib/test/test_peepholer.py --- a/Lib/test/test_peepholer.py Sat Mar 22 14:07:06 2008 +0100 +++ b/Lib/test/test_peepholer.py Wed Mar 26 23:48:56 2008 +0100 @@ -19,14 +19,14 @@ class TestTranforms(unittest.TestCase): class TestTranforms(unittest.TestCase): def test_unot(self): - # UNARY_NOT JUMP_IF_FALSE POP_TOP --> JUMP_IF_TRUE POP_TOP' + # UNARY_NOT JUMP_ABS_IF_FALSE --> JUMP_ABS_IF_TRUE def unot(x): if not x == 2: del x asm = disassemble(unot) - for elem in ('UNARY_NOT', 'JUMP_IF_FALSE'): + for elem in ('UNARY_NOT', 'JUMP_ABS_IF_FALSE'): self.assert_(elem not in asm) - for elem in ('JUMP_IF_TRUE', 'POP_TOP'): + for elem in ('JUMP_ABS_IF_TRUE'): self.assert_(elem in asm) def test_elim_inversion_of_is_or_in(self): diff -r aa480ecc8d3a Python/ceval.c --- a/Python/ceval.c Sat Mar 22 14:07:06 2008 +0100 +++ b/Python/ceval.c Wed Mar 26 23:48:56 2008 +0100 @@ -631,8 +631,11 @@ PyEval_EvalFrameEx(PyFrameObject *f, int #ifdef DYNAMIC_EXECUTION_PROFILE #define PREDICT(op) if (0) goto PRED_##op +#define PREDICT_WITH_SIGNALS(op) if (0) goto PRED_##op #else #define PREDICT(op) if (*next_instr == op) goto PRED_##op +#define PREDICT_WITH_SIGNALS(op) \ + if (*next_instr == op && --_Py_Ticker > 0) goto PRED_##op #endif #define PREDICTED(op) PRED_##op: next_instr++ @@ -754,8 +757,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int 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.) + JUMP_FORWARD is effectively a single opcode and f->f_lasti will + point at to the beginning of the combined pair.) */ next_instr = first_instr + f->f_lasti + 1; stack_pointer = f->f_stacktop; @@ -1268,7 +1271,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int Py_DECREF(v); Py_DECREF(w); if (err == 0) { - PREDICT(JUMP_ABSOLUTE); + PREDICT(FOR_ITER); continue; } break; @@ -2055,6 +2058,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int Py_DECREF(w); SET_TOP(x); if (x == NULL) break; + PREDICT(JUMP_ABS_IF_FALSE); PREDICT(JUMP_IF_FALSE); PREDICT(JUMP_IF_TRUE); continue; @@ -2129,6 +2133,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int if (x != NULL) continue; break; + PREDICTED_WITH_ARG(JUMP_FORWARD); case JUMP_FORWARD: JUMPBY(oparg); goto fast_next_opcode; @@ -2175,7 +2180,56 @@ PyEval_EvalFrameEx(PyFrameObject *f, int break; continue; - PREDICTED_WITH_ARG(JUMP_ABSOLUTE); + PREDICTED_WITH_ARG(JUMP_ABS_IF_FALSE); + case JUMP_ABS_IF_FALSE: + w = POP(); + if (w == Py_True) { + goto fast_next_opcode; + } + if (w == Py_False) { + JUMPTO(oparg); +#if FAST_LOOPS + goto fast_next_opcode; +#endif + } + err = PyObject_IsTrue(w); + if (err > 0) + err = 0; + else if (err == 0) { + JUMPTO(oparg); +#if FAST_LOOPS + goto fast_next_opcode; +#endif + } + else + break; + continue; + + case JUMP_ABS_IF_TRUE: + w = POP(); + if (w == Py_False) { + goto fast_next_opcode; + } + if (w == Py_True) { + JUMPTO(oparg); +#if FAST_LOOPS + goto fast_next_opcode; +#endif + } + err = PyObject_IsTrue(w); + if (err > 0) { + err = 0; + JUMPTO(oparg); +#if FAST_LOOPS + goto fast_next_opcode; +#endif + } + else if (err == 0) + ; + else + break; + continue; + case JUMP_ABSOLUTE: JUMPTO(oparg); #if FAST_LOOPS @@ -2198,7 +2252,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int Py_DECREF(v); if (x != NULL) { SET_TOP(x); - PREDICT(FOR_ITER); + PREDICT(JUMP_FORWARD); continue; } STACKADJ(-1); @@ -2211,7 +2265,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int x = (*v->ob_type->tp_iternext)(v); if (x != NULL) { PUSH(x); - PREDICT(STORE_FAST); + JUMPTO(oparg); + /* STORE_FAST is a fast_next_opcode so we must be careful + about not blocking signals in an empty loop. */ + PREDICT_WITH_SIGNALS(STORE_FAST); PREDICT(UNPACK_SEQUENCE); continue; } @@ -2222,9 +2279,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int PyErr_Clear(); } /* iterator ended normally */ - x = v = POP(); - Py_DECREF(v); - JUMPBY(oparg); + x = v = POP(); + Py_DECREF(v); continue; case BREAK_LOOP: diff -r aa480ecc8d3a Python/compile.c --- a/Python/compile.c Sat Mar 22 14:07:06 2008 +0100 +++ b/Python/compile.c Wed Mar 26 23:48:56 2008 +0100 @@ -830,6 +830,9 @@ opcode_stack_effect(int opcode, int opar case JUMP_IF_TRUE: case JUMP_ABSOLUTE: return 0; + case JUMP_ABS_IF_TRUE: + case JUMP_ABS_IF_FALSE: + return -1; case LOAD_GLOBAL: return 1; @@ -1488,12 +1491,10 @@ compiler_ifexp(struct compiler *c, expr_ if (next == NULL) return 0; VISIT(c, expr, e->v.IfExp.test); - ADDOP_JREL(c, JUMP_IF_FALSE, next); - ADDOP(c, POP_TOP); + ADDOP_JABS(c, JUMP_ABS_IF_FALSE, next); VISIT(c, expr, e->v.IfExp.body); ADDOP_JREL(c, JUMP_FORWARD, end); compiler_use_next_block(c, next); - ADDOP(c, POP_TOP); VISIT(c, expr, e->v.IfExp.orelse); compiler_use_next_block(c, end); return 1; @@ -1581,10 +1582,7 @@ compiler_if(struct compiler *c, stmt_ty end = compiler_new_block(c); if (end == NULL) return 0; - next = compiler_new_block(c); - if (next == NULL) - return 0; - + constant = expr_constant(s->v.If.test); /* constant = 0: "if 0" * constant = 1: "if 1", "if 2", ... @@ -1596,14 +1594,20 @@ compiler_if(struct compiler *c, stmt_ty VISIT_SEQ(c, stmt, s->v.If.body); } else { VISIT(c, expr, s->v.If.test); - ADDOP_JREL(c, JUMP_IF_FALSE, next); - ADDOP(c, POP_TOP); + if (s->v.If.orelse) { + next = compiler_new_block(c); + if (next == NULL) + return 0; + } + else + next = end; + ADDOP_JABS(c, JUMP_ABS_IF_FALSE, next); VISIT_SEQ(c, stmt, s->v.If.body); - ADDOP_JREL(c, JUMP_FORWARD, end); - compiler_use_next_block(c, next); - ADDOP(c, POP_TOP); - if (s->v.If.orelse) + if (s->v.If.orelse) { + ADDOP_JREL(c, JUMP_FORWARD, end); + compiler_use_next_block(c, next); VISIT_SEQ(c, stmt, s->v.If.orelse); + } } compiler_use_next_block(c, end); return 1; @@ -1612,12 +1616,15 @@ static int static int compiler_for(struct compiler *c, stmt_ty s) { - basicblock *start, *cleanup, *end; + basicblock *start, *body, *tail, *cleanup, *end; start = compiler_new_block(c); + body = compiler_new_block(c); + tail = compiler_new_block(c); cleanup = compiler_new_block(c); end = compiler_new_block(c); - if (start == NULL || end == NULL || cleanup == NULL) + if (start == NULL || body == NULL || tail == NULL + || end == NULL || cleanup == NULL) return 0; ADDOP_JREL(c, SETUP_LOOP, end); if (!compiler_push_fblock(c, LOOP, start)) @@ -1628,10 +1635,12 @@ compiler_for(struct compiler *c, stmt_ty /* for expressions must be traced on each iteration, so we need to set an extra line number. */ c->u->u_lineno_set = false; - ADDOP_JREL(c, FOR_ITER, cleanup); + ADDOP_JREL(c, JUMP_FORWARD, tail); + compiler_use_next_block(c, body); VISIT(c, expr, s->v.For.target); VISIT_SEQ(c, stmt, s->v.For.body); - ADDOP_JABS(c, JUMP_ABSOLUTE, start); + compiler_use_next_block(c, tail); + ADDOP_JABS(c, FOR_ITER, body); compiler_use_next_block(c, cleanup); ADDOP(c, POP_BLOCK); compiler_pop_fblock(c, LOOP, start); @@ -1643,7 +1652,7 @@ static int static int compiler_while(struct compiler *c, stmt_ty s) { - basicblock *loop, *orelse, *end, *anchor = NULL; + basicblock *loop, *body, *tail, *orelse, *end, *anchor = NULL; int constant = expr_constant(s->v.While.test); if (constant == 0) { @@ -1654,8 +1663,10 @@ compiler_while(struct compiler *c, stmt_ loop = compiler_new_block(c); end = compiler_new_block(c); if (constant == -1) { + body = compiler_new_block(c); + tail = compiler_new_block(c); anchor = compiler_new_block(c); - if (anchor == NULL) + if (body == NULL || tail == NULL || anchor == NULL) return 0; } if (loop == NULL || end == NULL) @@ -1676,12 +1687,18 @@ compiler_while(struct compiler *c, stmt_ /* while expressions must be traced on each iteration, so we need to set an extra line number. */ c->u->u_lineno_set = false; - VISIT(c, expr, s->v.While.test); - ADDOP_JREL(c, JUMP_IF_FALSE, anchor); - ADDOP(c, POP_TOP); + ADDOP_JREL(c, JUMP_FORWARD, tail); + compiler_use_next_block(c, body); } VISIT_SEQ(c, stmt, s->v.While.body); - ADDOP_JABS(c, JUMP_ABSOLUTE, loop); + if (constant == -1) { + compiler_use_next_block(c, tail); + VISIT(c, expr, s->v.While.test); + ADDOP_JABS(c, JUMP_ABS_IF_TRUE, body); + } + else { + ADDOP_JABS(c, JUMP_ABSOLUTE, loop); + } /* XXX should the two POP instructions be in a separate block if there is no else clause ? @@ -1689,7 +1706,6 @@ compiler_while(struct compiler *c, stmt_ if (constant == -1) { compiler_use_next_block(c, anchor); - ADDOP(c, POP_TOP); ADDOP(c, POP_BLOCK); } compiler_pop_fblock(c, LOOP, loop); @@ -2594,24 +2610,23 @@ compiler_listcomp_generator(struct compi and then write to the element */ comprehension_ty l; - basicblock *start, *anchor, *skip, *if_cleanup; + basicblock *start, *body, *tail, *anchor; int i, n; start = compiler_new_block(c); - skip = compiler_new_block(c); - if_cleanup = compiler_new_block(c); + body = compiler_new_block(c); + tail = compiler_new_block(c); anchor = compiler_new_block(c); - if (start == NULL || skip == NULL || if_cleanup == NULL || - anchor == NULL) + if (start == NULL || body == NULL || tail == NULL || anchor == NULL) return 0; l = (comprehension_ty)asdl_seq_GET(generators, gen_index); VISIT(c, expr, l->iter); ADDOP(c, GET_ITER); compiler_use_next_block(c, start); - ADDOP_JREL(c, FOR_ITER, anchor); - NEXT_BLOCK(c); + ADDOP_JREL(c, JUMP_FORWARD, tail); + compiler_use_next_block(c, body); VISIT(c, expr, l->target); /* XXX this needs to be cleaned up...a lot! */ @@ -2619,10 +2634,8 @@ compiler_listcomp_generator(struct compi for (i = 0; i < n; i++) { expr_ty e = (expr_ty)asdl_seq_GET(l->ifs, i); VISIT(c, expr, e); - ADDOP_JREL(c, JUMP_IF_FALSE, if_cleanup); - NEXT_BLOCK(c); - ADDOP(c, POP_TOP); - } + ADDOP_JABS(c, JUMP_ABS_IF_FALSE, tail); + } if (++gen_index < asdl_seq_LEN(generators)) if (!compiler_listcomp_generator(c, tmpname, @@ -2635,16 +2648,9 @@ compiler_listcomp_generator(struct compi return 0; VISIT(c, expr, elt); ADDOP(c, LIST_APPEND); - - compiler_use_next_block(c, skip); } - for (i = 0; i < n; i++) { - ADDOP_I(c, JUMP_FORWARD, 1); - if (i == 0) - compiler_use_next_block(c, if_cleanup); - ADDOP(c, POP_TOP); - } - ADDOP_JABS(c, JUMP_ABSOLUTE, start); + compiler_use_next_block(c, tail); + ADDOP_JABS(c, FOR_ITER, body); compiler_use_next_block(c, anchor); /* delete the temporary list name added to locals */ if (gen_index == 1) @@ -2683,17 +2689,15 @@ compiler_genexp_generator(struct compile and then write to the element */ comprehension_ty ge; - basicblock *start, *anchor, *skip, *if_cleanup, *end; + basicblock *start, *body, *tail, *end; int i, n; start = compiler_new_block(c); - skip = compiler_new_block(c); - if_cleanup = compiler_new_block(c); - anchor = compiler_new_block(c); + body = compiler_new_block(c); + tail = compiler_new_block(c); end = compiler_new_block(c); - if (start == NULL || skip == NULL || if_cleanup == NULL || - anchor == NULL || end == NULL) + if (start == NULL || body == NULL || tail == NULL || end == NULL) return 0; ge = (comprehension_ty)asdl_seq_GET(generators, gen_index); @@ -2712,8 +2716,8 @@ compiler_genexp_generator(struct compile ADDOP(c, GET_ITER); } compiler_use_next_block(c, start); - ADDOP_JREL(c, FOR_ITER, anchor); - NEXT_BLOCK(c); + ADDOP_JREL(c, JUMP_FORWARD, tail); + compiler_use_next_block(c, body); VISIT(c, expr, ge->target); /* XXX this needs to be cleaned up...a lot! */ @@ -2721,10 +2725,9 @@ compiler_genexp_generator(struct compile for (i = 0; i < n; i++) { expr_ty e = (expr_ty)asdl_seq_GET(ge->ifs, i); VISIT(c, expr, e); - ADDOP_JREL(c, JUMP_IF_FALSE, if_cleanup); + ADDOP_JABS(c, JUMP_ABS_IF_FALSE, tail); NEXT_BLOCK(c); - ADDOP(c, POP_TOP); - } + } if (++gen_index < asdl_seq_LEN(generators)) if (!compiler_genexp_generator(c, generators, gen_index, elt)) @@ -2735,18 +2738,9 @@ compiler_genexp_generator(struct compile VISIT(c, expr, elt); ADDOP(c, YIELD_VALUE); ADDOP(c, POP_TOP); - - compiler_use_next_block(c, skip); } - for (i = 0; i < n; i++) { - ADDOP_I(c, JUMP_FORWARD, 1); - if (i == 0) - compiler_use_next_block(c, if_cleanup); - - ADDOP(c, POP_TOP); - } - ADDOP_JABS(c, JUMP_ABSOLUTE, start); - compiler_use_next_block(c, anchor); + compiler_use_next_block(c, tail); + ADDOP_JABS(c, FOR_ITER, body); ADDOP(c, POP_BLOCK); compiler_pop_fblock(c, LOOP, start); compiler_use_next_block(c, end); diff -r aa480ecc8d3a Python/import.c --- a/Python/import.c Sat Mar 22 14:07:06 2008 +0100 +++ b/Python/import.c Wed Mar 26 23:48:56 2008 +0100 @@ -73,9 +73,11 @@ extern time_t PyOS_GetLastModificationTi Python 2.5c2: 62131 (fix wrong code: for x, in ... in listcomp/genexp) Python 2.6a0: 62151 (peephole optimizations and STORE_MAP opcode) Python 2.6a1: 62161 (WITH_CLEANUP optimization) + Python 2.6a1: 62171 (FOR_ITER optimization, + introduction of JUMP_ABS_IF_TRUE, JUMP_ABS_IF_FALSE) . */ -#define MAGIC (62161 | ((long)'\r'<<16) | ((long)'\n'<<24)) +#define MAGIC (62171 | ((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 diff -r aa480ecc8d3a Python/peephole.c --- a/Python/peephole.c Sat Mar 22 14:07:06 2008 +0100 +++ b/Python/peephole.c Wed Mar 26 23:48:56 2008 +0100 @@ -13,7 +13,8 @@ #define GETARG(arr, i) ((int)((arr[i+2]<<8) + arr[i+1])) #define UNCONDITIONAL_JUMP(op) (op==JUMP_ABSOLUTE || op==JUMP_FORWARD) -#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP) +#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP || \ + op==FOR_ITER || op==JUMP_ABS_IF_TRUE || op==JUMP_ABS_IF_FALSE) #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) @@ -248,6 +249,8 @@ markblocks(unsigned char *code, int len) case JUMP_IF_FALSE: case JUMP_IF_TRUE: case JUMP_ABSOLUTE: + case JUMP_ABS_IF_TRUE: + case JUMP_ABS_IF_FALSE: case CONTINUE_LOOP: case SETUP_LOOP: case SETUP_EXCEPT: @@ -345,21 +348,31 @@ PyCode_Optimize(PyObject *code, PyObject switch (opcode) { - /* Replace UNARY_NOT JUMP_IF_FALSE POP_TOP with - with JUMP_IF_TRUE POP_TOP */ + /* Replace UNARY_NOT JUMP_IF_FALSE POP_TOP + with JUMP_IF_TRUE POP_TOP + and UNARY_NOT JUMP_ABS_IF_FALSE + with JUMP_ABS_IF_TRUE + */ case UNARY_NOT: - if (codestr[i+1] != JUMP_IF_FALSE || - codestr[i+4] != POP_TOP || - !ISBASICBLOCK(blocks,i,5)) - continue; - tgt = GETJUMPTGT(codestr, (i+1)); - if (codestr[tgt] != POP_TOP) - continue; - j = GETARG(codestr, i+1) + 1; - codestr[i] = JUMP_IF_TRUE; - SETARG(codestr, i, j); - codestr[i+3] = POP_TOP; - codestr[i+4] = NOP; + if (codestr[i+1] == JUMP_IF_FALSE && + codestr[i+4] == POP_TOP && + ISBASICBLOCK(blocks,i,5)) { + tgt = GETJUMPTGT(codestr, (i+1)); + if (codestr[tgt] != POP_TOP) + continue; + j = GETARG(codestr, i+1) + 1; + codestr[i] = JUMP_IF_TRUE; + SETARG(codestr, i, j); + codestr[i+3] = POP_TOP; + codestr[i+4] = NOP; + } + else if (codestr[i+1] == JUMP_ABS_IF_FALSE && + ISBASICBLOCK(blocks,i,4)) { + j = GETARG(codestr, i+1); + codestr[i] = JUMP_ABS_IF_TRUE; + SETARG(codestr, i, j); + codestr[i+3] = NOP; + } break; /* not a is b --> a is not b @@ -590,12 +603,14 @@ PyCode_Optimize(PyObject *code, PyObject continue; case JUMP_ABSOLUTE: + case JUMP_ABS_IF_TRUE: + case JUMP_ABS_IF_FALSE: case CONTINUE_LOOP: + case FOR_ITER: j = addrmap[GETARG(codestr, i)]; SETARG(codestr, i, j); break; - case FOR_ITER: case JUMP_FORWARD: case JUMP_IF_FALSE: case JUMP_IF_TRUE: