[pypy-svn] r50429 - in pypy/branch/simpler-pyassem/pypy/interpreter/astcompiler: . test

arigo at codespeak.net arigo at codespeak.net
Mon Jan 7 18:23:35 CET 2008


Author: arigo
Date: Mon Jan  7 18:23:34 2008
New Revision: 50429

Modified:
   pypy/branch/simpler-pyassem/pypy/interpreter/astcompiler/pyassem.py
   pypy/branch/simpler-pyassem/pypy/interpreter/astcompiler/test/test_compiler.py
Log:
Stack depth computation.  Probably still buggy, but hopefully
more robust that the older version.


Modified: pypy/branch/simpler-pyassem/pypy/interpreter/astcompiler/pyassem.py
==============================================================================
--- pypy/branch/simpler-pyassem/pypy/interpreter/astcompiler/pyassem.py	(original)
+++ pypy/branch/simpler-pyassem/pypy/interpreter/astcompiler/pyassem.py	Mon Jan  7 18:23:34 2008
@@ -1,3 +1,4 @@
+import sys
 from pypy.interpreter.astcompiler.consts \
      import CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS
 from pypy.interpreter.pycode import PyCode
@@ -196,6 +197,9 @@
                                             (opname,))
         self.emitop_int(opname, target)
 
+    hasjrel = {}
+    for i in pythonopcode.hasjrel:
+        hasjrel[pythonopcode.opname[i]] = True
     hasjabs = {}
     for i in pythonopcode.hasjabs:
         hasjabs[pythonopcode.opname[i]] = True
@@ -204,12 +208,90 @@
     # ____________________________________________________________
 
     def getCode(self):
-        self.computeStackDepth()
         self.fixLabelTargets()
+        self.computeStackDepth()
         return self.newCodeObject()
 
+    def _setdepth(self, i, stackdepth):
+        if stackdepth < 0:
+            raise InternalCompilerError("negative stack depth")
+        depths = self._stackdepths
+        previous_value = depths[i]
+        if previous_value < 0:
+            if i <= self._stackdepth_seen_until:
+                raise InternalCompilerError("back jump to code that is "
+                                            "otherwise not reachable")
+            depths[i] = stackdepth
+        else:
+            if previous_value != stackdepth:
+                raise InternalCompilerError("inconsistent stack depth")
+
     def computeStackDepth(self):
-        self.stacksize = 20  # XXX!
+        UNREACHABLE = -1
+        co_code = self.co_code
+        self._stackdepths = [UNREACHABLE] * len(co_code)
+        self._stackdepths[0] = 0
+        just_loaded_const = None
+        consts_w = self.getConsts()
+        largestsize = 0
+        i = 0
+
+        while i < len(co_code):
+            curstackdepth = self._stackdepths[i]
+            if curstackdepth > largestsize:
+                largestsize = curstackdepth
+            self._stackdepth_seen_until = i
+
+            # decode the next instruction
+            opcode = ord(co_code[i])
+            if opcode >= pythonopcode.HAVE_ARGUMENT:
+                oparg = ord(co_code[i+1]) | (ord(co_code[i+2]) << 8)
+                i += 3
+                if opcode == pythonopcode.opmap['EXTENDED_ARG']:
+                    opcode = ord(co_code[i])
+                    assert opcode >= pythonopcode.HAVE_ARGUMENT
+                    oparg = ((oparg << 16) |
+                             ord(co_code[i+1]) | (ord(co_code[i+2]) << 8))
+                    i += 3
+            else:
+                oparg = sys.maxint
+                i += 1
+
+            if curstackdepth == UNREACHABLE:
+                just_loaded_const = None
+                continue    # ignore unreachable instructions
+
+            if opcode in DEPTH_OP_EFFECT_ALONG_JUMP:
+                if opcode in pythonopcode.hasjabs:
+                    target_i = oparg
+                else:
+                    target_i = i + oparg
+                effect = DEPTH_OP_EFFECT_ALONG_JUMP[opcode]
+                self._setdepth(target_i, curstackdepth + effect)
+
+            try:
+                tracker = DEPTH_OP_TRACKER[opcode]
+            except KeyError:
+                pass
+            else:
+                if opcode == pythonopcode.opmap['MAKE_CLOSURE']:
+                    # only supports "LOAD_CONST co / MAKE_CLOSURE n"
+                    if just_loaded_const is None:
+                        raise InternalCompilerError("MAKE_CLOSURE not "
+                                                    "following LOAD_CONST")
+                    codeobj = self.space.interp_w(PyCode, just_loaded_const)
+                    nfreevars = len(codeobj.co_freevars)
+                    effect = - nfreevars - oparg
+                else:
+                    effect = tracker(oparg)
+                self._setdepth(i, curstackdepth + effect)
+
+            if opcode == pythonopcode.opmap['LOAD_CONST']:
+                just_loaded_const = consts_w[oparg]
+            else:
+                just_loaded_const = None
+
+        self.stacksize = largestsize
 
     def fixLabelTargets(self):
         for label, i, absolute in self.pending_label_fixes:
@@ -266,3 +348,165 @@
 
 class Label(object):
     position = -1
+
+# ____________________________________________________________
+# Stack depth tracking
+
+def depth_UNPACK_SEQUENCE(count):
+    return count-1
+def depth_BUILD_TUPLE(count):
+    return -count+1
+def depth_BUILD_LIST(count):
+    return -count+1
+def depth_CALL_FUNCTION(argc):
+    hi = argc//256
+    lo = argc%256
+    return -(lo + hi * 2)
+def depth_CALL_FUNCTION_VAR(argc):
+    return depth_CALL_FUNCTION(argc)-1
+def depth_CALL_FUNCTION_KW(argc):
+    return depth_CALL_FUNCTION(argc)-1
+def depth_CALL_FUNCTION_VAR_KW(argc):
+    return depth_CALL_FUNCTION(argc)-2
+def depth_CALL_METHOD(argc):
+    return -argc-1
+def depth_CALL_LIKELY_BUILTIN(argc):
+    nargs = argc & 0xFF
+    return -nargs+1
+def depth_MAKE_FUNCTION(argc):
+    return -argc
+def depth_MAKE_CLOSURE(argc):
+    raise InternalCompilerError("must special-case this in order to account"
+                                " for the free variables")
+def depth_BUILD_SLICE(argc):
+    if argc == 2:
+        return -1
+    elif argc == 3:
+        return -2
+    assert False, 'Unexpected argument %s to depth_BUILD_SLICE' % argc
+    
+def depth_DUP_TOPX(argc):
+    return argc
+
+def setup_stack_depth_tracker():
+    effect = {
+        'STOP_CODE': 0,
+        'NOP': 0,
+        'EXTENDED_ARG': 0,
+        'POP_TOP': -1,
+        'DUP_TOP': 1,
+        'SLICE+0': 0,
+        '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,
+        'STORE_SUBSCR': -3,
+        'DELETE_SUBSCR': -2,
+        'PRINT_EXPR': -1,
+        'PRINT_ITEM': -1,
+        'PRINT_ITEM_TO': -2,
+        'PRINT_NEWLINE': 0,
+        'PRINT_NEWLINE_TO': -1,
+        'YIELD_VALUE': -1,
+        'EXEC_STMT': -3,
+        'BUILD_CLASS': -2,
+        'STORE_NAME': -1,
+        'DELETE_NAME': 0,
+        'STORE_ATTR': -2,
+        'DELETE_ATTR': -1,
+        'STORE_GLOBAL': -1,
+        'DELETE_GLOBAL': 0,
+        'STORE_DEREF': -1,
+        'BUILD_MAP': 1,
+        'COMPARE_OP': -1,
+        'STORE_FAST': -1,
+        'DELETE_FAST': 0,
+        'IMPORT_STAR': -1,
+        'IMPORT_NAME': 0,
+        'IMPORT_FROM': 1,
+        'LOAD_ATTR': 0, # unlike other loads
+        'GET_ITER': 0,
+        'FOR_ITER': 1,
+        'BREAK_LOOP': 0,
+        'CONTINUE_LOOP': 0,
+        'POP_BLOCK': 0,
+        'END_FINALLY': -3,
+        'WITH_CLEANUP': -1,
+        'LOOKUP_METHOD': 1,
+        'LIST_APPEND': -2,
+        }
+    # use pattern match
+    patterns = [
+        ('ROT_', 0),
+        ('UNARY_', 0),
+        ('BINARY_', -1),
+        ('INPLACE_', -1),
+        ('LOAD_', 1),
+        ('SETUP_', 0),
+        ('JUMP_IF_', 0),
+        ]
+
+    def gettracker(opname):
+        # first look for an explicit tracker
+        try:
+            return globals()['depth_' + opname]
+        except KeyError:
+            pass
+        # then look for an explicit constant effect
+        try:
+            delta = effect[opname]
+        except KeyError:
+            # then do pattern matching
+            for pat, delta in patterns:
+                if opname.startswith(pat):
+                    break
+            else:
+                raise InternalCompilerError("no stack effect registered for "
+                                            + opname)
+        def tracker(argc):
+            return delta
+        return tracker
+
+    effect_along_jump = {
+        'JUMP_FORWARD': 0,
+        'JUMP_ABSOLUTE': 0,
+        'JUMP_IF_TRUE': 0,
+        'JUMP_IF_FALSE': 0,
+        'FOR_ITER': -1,
+        'SETUP_LOOP': 0,
+        'SETUP_EXCEPT': 3,
+        'SETUP_FINALLY': 3,
+        }
+    def geteffect_jump(opname):
+        try:
+            return effect_along_jump[opname]
+        except KeyError:
+            raise InternalCompilerError("no stack effect registered for "
+                                        "the branch of " + opname)
+
+    for opname, opcode in pythonopcode.opmap.items():
+        if opname in ops_interrupt_unconditionally:
+            continue
+        if opname not in ops_jump_unconditionally:
+            # the effect on the stack depth when execution goes from
+            # this instruction to the next one
+            DEPTH_OP_TRACKER[opcode] = gettracker(opname)
+        if opname in ops_jumps:
+            DEPTH_OP_EFFECT_ALONG_JUMP[opcode] = geteffect_jump(opname)
+
+
+ops_interrupt_unconditionally = ('RETURN_VALUE', 'RAISE_VARARGS',
+                                 'CONTINUE_LOOP', 'BREAK_LOOP')
+ops_jump_unconditionally = ('JUMP_ABSOLUTE', 'JUMP_FORWARD')
+ops_jumps = list(PyFlowGraph.hasjrel) + list(PyFlowGraph.hasjabs)
+
+DEPTH_OP_TRACKER = {}
+DEPTH_OP_EFFECT_ALONG_JUMP = {}
+setup_stack_depth_tracker()

Modified: pypy/branch/simpler-pyassem/pypy/interpreter/astcompiler/test/test_compiler.py
==============================================================================
--- pypy/branch/simpler-pyassem/pypy/interpreter/astcompiler/test/test_compiler.py	(original)
+++ pypy/branch/simpler-pyassem/pypy/interpreter/astcompiler/test/test_compiler.py	Mon Jan  7 18:23:34 2008
@@ -363,6 +363,16 @@
         yield self.st, "k=2; x = sum(n+2 for n in [6, 1, k])", 'x', 15
         yield self.st, "k=2; x = sum(n+2 for n in (6, 1, k))", 'x', 15
 
+    def test_closure(self):
+        decl = py.code.Source("""
+            def make_adder(n):
+                def add(m):
+                    return n + m
+                return add
+        """)
+        decl = str(decl) + "\n"
+        yield self.st, decl + "x = make_adder(40)(2)", 'x', 42
+
     def test_pprint(self):
         # a larger example that showed a bug with jumps
         # over more than 256 bytes



More information about the Pypy-commit mailing list