[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