From: Jeremy Hylton Date: Mon, 17 Dec 2001 23:53:10 +0000 (+0000) Subject: Backport bugfixes since rev 1.19 on the trunk. X-Git-Tag: v2.1.2c1~47 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c2b05c098ed3b434b13f5d5025745b899c574188;p=thirdparty%2FPython%2Fcpython.git Backport bugfixes since rev 1.19 on the trunk. Brief summary: Improve stack depth calculation, 1.24, 1.25, 1.28 Wrong co_lntob, 1.20. XXX_NMAE ops should affect varnames, 1.21. Fix list comp code gen, 1.26. Fix _convert_NAME() for class bodies, 1.26. --- diff --git a/Tools/compiler/compiler/pyassem.py b/Tools/compiler/compiler/pyassem.py index 06846233be4d..7b4176365ab0 100644 --- a/Tools/compiler/compiler/pyassem.py +++ b/Tools/compiler/compiler/pyassem.py @@ -1,5 +1,7 @@ """A flow graph representation for Python bytecode""" +from __future__ import nested_scopes + import dis import new import string @@ -7,6 +9,8 @@ import sys import types from compiler import misc +from compiler.consts import CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, \ + CO_VARKEYWORDS def xxx_sort(l): l = l[:] @@ -53,7 +57,7 @@ class FlowGraph: # 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) @@ -108,13 +112,13 @@ class FlowGraph: # 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 @@ -194,19 +198,18 @@ class FlowGraph: 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() def getRoot(self): """Return nodes appropriate for use with dominator""" return self.entry - + def getContainedGraphs(self): l = [] for b in self.getBlocks(): @@ -245,7 +248,7 @@ class Block: def __str__(self): insts = map(str, self.insts) return "" % (self.label, self.bid, - string.join(insts, '\n')) + string.join(insts, '\n')) def emit(self, inst): op = inst[0] @@ -267,7 +270,7 @@ class Block: assert len(self.next) == 1, map(str, self.next) _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS', - 'JUMP_ABSOLUTE', 'JUMP_FORWARD') + 'JUMP_ABSOLUTE', 'JUMP_FORWARD', 'CONTINUE_LOOP') def pruneNext(self): """Remove bogus edge for unconditional transfers @@ -311,11 +314,6 @@ class Block: return contained # flags for code objects -CO_OPTIMIZED = 0x0001 -CO_NEWLOCALS = 0x0002 -CO_VARARGS = 0x0004 -CO_VARKEYWORDS = 0x0008 -CO_NESTED = 0x0010 # the FlowGraph is transformed in place; it exists in one of these states RAW = "RAW" @@ -326,15 +324,17 @@ DONE = "DONE" class PyFlowGraph(FlowGraph): super_init = FlowGraph.__init__ - def __init__(self, name, filename, args=(), optimized=0): + def __init__(self, name, filename, args=(), optimized=0, klass=None): self.super_init() self.name = name + assert isinstance(filename, types.StringType) self.filename = filename self.docstring = None self.args = args # XXX self.argcount = getArgCount(args) + self.klass = klass if optimized: - self.flags = CO_OPTIMIZED | CO_NEWLOCALS + self.flags = CO_OPTIMIZED | CO_NEWLOCALS else: self.flags = 0 self.consts = [] @@ -363,6 +363,10 @@ class PyFlowGraph(FlowGraph): if flag == CO_VARARGS: self.argcount = self.argcount - 1 + def checkFlag(self, flag): + if self.flags & flag: + return 1 + def setFreeVars(self, names): self.freevars = list(names) @@ -372,6 +376,7 @@ class PyFlowGraph(FlowGraph): def getCode(self): """Get a Python code object""" if self.stage == RAW: + self.computeStackDepth() self.flattenGraph() if self.stage == FLAT: self.convertArgs() @@ -399,6 +404,36 @@ class PyFlowGraph(FlowGraph): if io: sys.stdout = save + def computeStackDepth(self): + """Compute the max stack depth. + + Approach is to compute the stack effect of each basic block. + Then find the path through the code with the largest total + effect. + """ + depth = {} + exit = None + for b in self.getBlocks(): + depth[b] = findDepth(b.getInstructions()) + + seen = {} + + def max_depth(b, d): + if seen.has_key(b): + return d + seen[b] = 1 + d = d + depth[b] + children = b.get_children() + if children: + return max([max_depth(c, d) for c in children]) + else: + if not b.label == "exit": + return max_depth(self.exit, d) + else: + return d + + self.stacksize = max_depth(self.entry, 0) + def flattenGraph(self): """Arrange the blocks in order and resolve jumps""" assert self.stage == RAW @@ -430,7 +465,6 @@ class PyFlowGraph(FlowGraph): insts[i] = opname, offset elif self.hasjabs.has_elt(opname): insts[i] = opname, begin[inst[1]] - self.stacksize = findDepth(self.insts) self.stage = FLAT hasjrel = misc.Set() @@ -448,8 +482,7 @@ class PyFlowGraph(FlowGraph): for i in range(len(self.insts)): t = self.insts[i] if len(t) == 2: - opname = t[0] - oparg = t[1] + opname, oparg = t conv = self._converters.get(opname, None) if conv: self.insts[i] = opname, conv(self, oparg) @@ -469,10 +502,16 @@ class PyFlowGraph(FlowGraph): self.closure = self.cellvars + self.freevars def _lookupName(self, name, list): - """Return index of name in list, appending if necessary""" + """Return index of name in list, appending if necessary + + This routine uses a list instead of a dictionary, because a + dictionary can't store two different keys if the keys have the + same value but different types, e.g. 2 and 2L. The compiler + must treat these two separately, so it does an explicit type + comparison before comparing the values. + """ t = type(name) for i in range(len(list)): - # must do a comparison on type first to prevent UnicodeErrors if t == type(list[i]) and list[i] == name: return i end = len(list) @@ -491,9 +530,15 @@ class PyFlowGraph(FlowGraph): _convert_STORE_FAST = _convert_LOAD_FAST _convert_DELETE_FAST = _convert_LOAD_FAST + def _convert_LOAD_NAME(self, arg): + if self.klass is None: + self._lookupName(arg, self.varnames) + return self._lookupName(arg, self.names) + def _convert_NAME(self, arg): + if self.klass is None: + self._lookupName(arg, self.varnames) return self._lookupName(arg, self.names) - _convert_LOAD_NAME = _convert_NAME _convert_STORE_NAME = _convert_NAME _convert_DELETE_NAME = _convert_NAME _convert_IMPORT_NAME = _convert_NAME @@ -525,7 +570,7 @@ class PyFlowGraph(FlowGraph): for name, obj in locals().items(): if name[:9] == "_convert_": opname = name[9:] - _converters[opname] = obj + _converters[opname] = obj del name, obj, opname def makeByteCode(self): @@ -555,13 +600,14 @@ class PyFlowGraph(FlowGraph): def newCodeObject(self): assert self.stage == DONE - if self.flags == 0: + if (self.flags & CO_NEWLOCALS) == 0: nlocals = 0 else: nlocals = len(self.varnames) argcount = self.argcount if self.flags & CO_VARKEYWORDS: argcount = argcount - 1 + return new.code(argcount, nlocals, self.stacksize, self.flags, self.lnotab.getCode(), self.getConsts(), tuple(self.names), tuple(self.varnames), @@ -581,7 +627,7 @@ class PyFlowGraph(FlowGraph): elt = elt.getCode() l.append(elt) return tuple(l) - + def isJump(opname): if opname[:4] == 'JUMP': return 1 @@ -675,26 +721,22 @@ class LineAddrTable: def getTable(self): return string.join(map(chr, self.lnotab), '') - + class StackDepthTracker: # XXX 1. need to keep track of stack depth on jumps # XXX 2. at least partly as a result, this code is broken - def findDepth(self, insts): + def findDepth(self, insts, debug=0): depth = 0 maxDepth = 0 for i in insts: opname = i[0] - delta = self.effect.get(opname, 0) - if delta > 1: - depth = depth + delta - elif delta < 0: - if depth > maxDepth: - maxDepth = depth + if debug: + print i, + delta = self.effect.get(opname, None) + if delta is not None: depth = depth + delta else: - if depth > maxDepth: - maxDepth = depth # now check patterns for pat, pat_delta in self.patterns: if opname[:len(pat)] == pat: @@ -702,12 +744,14 @@ class StackDepthTracker: depth = depth + delta break # if we still haven't found a match - if delta == 0: + if delta is None: meth = getattr(self, opname, None) if meth is not None: depth = depth + meth(i[1]) - if depth < 0: - depth = 0 + if depth > maxDepth: + maxDepth = depth + if debug: + print depth, maxDepth return maxDepth effect = { @@ -728,9 +772,8 @@ class StackDepthTracker: 'DELETE_SUBSCR': -2, # PRINT_EXPR? 'PRINT_ITEM': -1, - 'LOAD_LOCALS': 1, 'RETURN_VALUE': -1, - 'EXEC_STMT': -2, + 'EXEC_STMT': -3, 'BUILD_CLASS': -2, 'STORE_NAME': -1, 'STORE_ATTR': -2, @@ -742,37 +785,44 @@ class StackDepthTracker: 'IMPORT_STAR': -1, 'IMPORT_NAME': 0, 'IMPORT_FROM': 1, + 'LOAD_ATTR': 0, # unlike other loads + # close enough... + 'SETUP_EXCEPT': 3, + 'SETUP_FINALLY': 3, + 'FOR_ITER': 1, } # use pattern match patterns = [ ('BINARY_', -1), ('LOAD_', 1), ] - - # special cases: - # UNPACK_SEQUENCE, BUILD_TUPLE, - # BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE + def UNPACK_SEQUENCE(self, count): - return count + return count-1 def BUILD_TUPLE(self, count): - return -count + return -count+1 def BUILD_LIST(self, count): - return -count + return -count+1 def CALL_FUNCTION(self, argc): hi, lo = divmod(argc, 256) - return lo + hi * 2 + return -(lo + hi * 2) def CALL_FUNCTION_VAR(self, argc): - return self.CALL_FUNCTION(argc)+1 + return self.CALL_FUNCTION(argc)-1 def CALL_FUNCTION_KW(self, argc): - return self.CALL_FUNCTION(argc)+1 + return self.CALL_FUNCTION(argc)-1 def CALL_FUNCTION_VAR_KW(self, argc): - return self.CALL_FUNCTION(argc)+2 + return self.CALL_FUNCTION(argc)-2 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