diff options
Diffstat (limited to 'Lib/compiler')
-rw-r--r-- | Lib/compiler/misc.py | 6 | ||||
-rw-r--r-- | Lib/compiler/pyassem.py | 120 | ||||
-rw-r--r-- | Lib/compiler/pycodegen.py | 28 |
3 files changed, 133 insertions, 21 deletions
diff --git a/Lib/compiler/misc.py b/Lib/compiler/misc.py index e57acae..29ff866 100644 --- a/Lib/compiler/misc.py +++ b/Lib/compiler/misc.py @@ -14,6 +14,8 @@ class Set: self.elts = {} def __len__(self): return len(self.elts) + def __contains__(self, elt): + return self.elts.has_key(elt) def add(self, elt): self.elts[elt] = elt def elements(self): @@ -22,6 +24,10 @@ class Set: return self.elts.has_key(elt) def remove(self, elt): del self.elts[elt] + def copy(self): + c = Set() + c.elts.update(self.elts) + return c class Stack: def __init__(self): diff --git a/Lib/compiler/pyassem.py b/Lib/compiler/pyassem.py index dcc8bc0..0b6701e 100644 --- a/Lib/compiler/pyassem.py +++ b/Lib/compiler/pyassem.py @@ -3,10 +3,18 @@ import dis import new import string +import sys import types from compiler import misc +def xxx_sort(l): + l = l[:] + def sorter(a, b): + return cmp(a.bid, b.bid) + l.sort(sorter) + return l + class FlowGraph: def __init__(self): self.current = self.entry = Block() @@ -16,13 +24,18 @@ class FlowGraph: self.blocks.add(self.exit) def startBlock(self, block): + if self._debug: + if self.current: + print "end", repr(self.current) + print " ", self.current.get_children() + print repr(block) self.current = block - def nextBlock(self, block=None): - if block is None: - block = self.newBlock() + def nextBlock(self, block=None, force=0): # XXX think we need to specify when there is implicit transfer - # from one block to the next + # from one block to the next. might be better to represent this + # with explicit JUMP_ABSOLUTE instructions that are optimized + # out when they are unnecessary. # # I think this strategy works: each block has a child # designated as "next" which is returned as the last of the @@ -30,6 +43,16 @@ class FlowGraph: # reverse post order, the "next" block will always be emitted # immediately after its parent. # Worry: maintaining this invariant could be tricky + if block is None: + block = self.newBlock() + + # 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(). + self.current.addNext(block) self.startBlock(block) @@ -41,13 +64,24 @@ class FlowGraph: def startExitBlock(self): self.startBlock(self.exit) + _debug = 0 + + def _enable_debug(self): + self._debug = 1 + + def _disable_debug(self): + self._debug = 0 + def emit(self, *inst): - # XXX should jump instructions implicitly call nextBlock? + if self._debug: + print "\t", inst if inst[0] == 'RETURN_VALUE': self.current.addOutEdge(self.exit) + if len(inst) == 2 and isinstance(inst[1], Block): + self.current.addOutEdge(inst[1]) self.current.emit(inst) - def getBlocks(self): + def getBlocksInOrder(self): """Return the blocks in reverse postorder i.e. each node appears before all of its successors @@ -64,13 +98,57 @@ class FlowGraph: # hack alert if not self.exit in order: order.append(self.exit) + +## for b in order: +## print repr(b) +## print "\t", b.get_children() +## print b +## print + return order + def getBlocks(self): + return self.blocks.elements() + + def getRoot(self): + """Return nodes appropriate for use with dominator""" + return self.entry + + def getContainedGraphs(self): + l = [] + for b in self.getBlocks(): + l.extend(b.getContainedGraphs()) + return l + + _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS', + 'JUMP_ABSOLUTE', 'JUMP_FORWARD') + + def pruneNext(self): + """Remove bogus edge for unconditional transfers + + Each block has a next edge that accounts for implicit control + transfers, e.g. from a JUMP_IF_FALSE to the block that will be + executed if the test is true. + + These edges must remain for the current assembler code to + work. If they are removed, the dfs_postorder gets things in + weird orders. However, they shouldn't be there for other + purposes, e.g. conversion to SSA form. This method will + remove the next edge when it follows an unconditional control + transfer. + """ + try: + op, arg = self.insts[-1] + except (IndexError, TypeError): + return + if op in self._uncond_transfer: + self.next = [] + def dfs_postorder(b, seen): """Depth-first search of tree rooted at b, return in postorder""" order = [] seen[b] = b - for c in b.children(): + for c in b.get_children(): if seen.has_key(c): continue order = order + dfs_postorder(c, seen) @@ -91,10 +169,9 @@ class Block: def __repr__(self): if self.label: - return "<block %s id=%d len=%d>" % (self.label, self.bid, - len(self.insts)) + return "<block %s id=%d>" % (self.label, self.bid) else: - return "<block id=%d len=%d>" % (self.bid, len(self.insts)) + return "<block id=%d>" % (self.bid) def __str__(self): insts = map(str, self.insts) @@ -120,9 +197,26 @@ class Block: self.next.append(block) assert len(self.next) == 1, map(str, self.next) - def children(self): + 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 + def getContainedGraphs(self): + """Return all graphs contained within this block. + + For example, a MAKE_FUNCTION block will contain a reference to + the graph for the function body. + """ + contained = [] + for inst in self.insts: + if len(inst) == 1: + continue + op = inst[1] + if hasattr(op, 'graph'): + contained.append(op.graph) + return contained + # flags for code objects CO_OPTIMIZED = 0x0001 CO_NEWLOCALS = 0x0002 @@ -204,7 +298,7 @@ class PyFlowGraph(FlowGraph): pc = 0 begin = {} end = {} - for b in self.getBlocks(): + for b in self.getBlocksInOrder(): begin[b] = pc for inst in b.getInstructions(): insts.append(inst) @@ -274,6 +368,8 @@ class PyFlowGraph(FlowGraph): _converters = {} def _convert_LOAD_CONST(self, arg): + if hasattr(arg, 'getCode'): + arg = arg.getCode() return self._lookupName(arg, self.consts) def _convert_LOAD_FAST(self, arg): diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py index bf54c32..a1b5d1d 100644 --- a/Lib/compiler/pycodegen.py +++ b/Lib/compiler/pycodegen.py @@ -160,7 +160,7 @@ class CodeGenerator: self.set_lineno(node) for default in node.defaults: self.visit(default) - self.emit('LOAD_CONST', gen.getCode()) + self.emit('LOAD_CONST', gen) self.emit('MAKE_FUNCTION', len(node.defaults)) def visitClass(self, node): @@ -195,7 +195,7 @@ class CodeGenerator: self.emit('POP_TOP') self.visit(suite) self.emit('JUMP_FORWARD', end) - self.nextBlock(nextTest) + self.startBlock(nextTest) self.emit('POP_TOP') if node.else_: self.visit(node.else_) @@ -243,10 +243,11 @@ class CodeGenerator: self.nextBlock(start) self.set_lineno(node) self.emit('FOR_LOOP', anchor) + self.nextBlock() self.visit(node.assign) self.visit(node.body) self.emit('JUMP_ABSOLUTE', start) - self.nextBlock(anchor) + self.startBlock(anchor) self.emit('POP_BLOCK') if node.else_: self.visit(node.else_) @@ -304,7 +305,7 @@ class CodeGenerator: if len(node.ops) > 1: end = self.newBlock() self.emit('JUMP_FORWARD', end) - self.nextBlock(cleanup) + self.startBlock(cleanup) self.emit('ROT_TWO') self.emit('POP_TOP') self.nextBlock(end) @@ -344,11 +345,11 @@ class CodeGenerator: if cont: skip_one = self.newBlock() self.emit('JUMP_FORWARD', skip_one) - self.nextBlock(cont) + self.startBlock(cont) self.emit('POP_TOP') self.nextBlock(skip_one) self.emit('JUMP_ABSOLUTE', start) - self.nextBlock(anchor) + self.startBlock(anchor) self.delName(append) self.__list_count = self.__list_count - 1 @@ -363,6 +364,7 @@ class CodeGenerator: self.emit('SET_LINENO', node.lineno) self.nextBlock(start) self.emit('FOR_LOOP', anchor) + self.nextBlock() self.visit(node.assign) return start, anchor @@ -390,9 +392,13 @@ class CodeGenerator: self.visit(node.test) self.emit('JUMP_IF_TRUE', end) self.nextBlock() + self.emit('POP_TOP') self.emit('LOAD_GLOBAL', 'AssertionError') - self.visit(node.fail) - self.emit('RAISE_VARARGS', 2) + if node.fail: + self.visit(node.fail) + self.emit('RAISE_VARARGS', 2) + else: + self.emit('RAISE_VARARGS', 1) self.nextBlock(end) self.emit('POP_TOP') @@ -419,10 +425,11 @@ class CodeGenerator: lElse = end self.set_lineno(node) self.emit('SETUP_EXCEPT', handlers) + self.nextBlock() self.visit(node.body) self.emit('POP_BLOCK') self.emit('JUMP_FORWARD', lElse) - self.nextBlock(handlers) + self.startBlock(handlers) last = len(node.handlers) - 1 for i in range(len(node.handlers)): @@ -446,6 +453,8 @@ class CodeGenerator: self.emit('JUMP_FORWARD', end) if expr: self.nextBlock(next) + else: + self.nextBlock() self.emit('POP_TOP') self.emit('END_FINALLY') if node.else_: @@ -457,6 +466,7 @@ class CodeGenerator: final = self.newBlock() self.set_lineno(node) self.emit('SETUP_FINALLY', final) + self.nextBlock() self.visit(node.body) self.emit('POP_BLOCK') self.emit('LOAD_CONST', None) |