]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Backport bugfixes since rev 1.19 on the trunk.
authorJeremy Hylton <jeremy@alum.mit.edu>
Mon, 17 Dec 2001 23:53:10 +0000 (23:53 +0000)
committerJeremy Hylton <jeremy@alum.mit.edu>
Mon, 17 Dec 2001 23:53:10 +0000 (23:53 +0000)
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.

Tools/compiler/compiler/pyassem.py

index 06846233be4d0254deb67e31f223a191281c294e..7b4176365ab06811b94a7f4f1b514c041b629258 100644 (file)
@@ -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 "<block %s %d:\n%s>" % (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