[pypy-svn] rev 2049 - in pypy/trunk/src/pypy: interpreter objspace/flowobjspace/flow/test

hpk at codespeak.net hpk at codespeak.net
Sat Oct 25 15:14:50 CEST 2003


Author: hpk
Date: Sat Oct 25 15:14:49 2003
New Revision: 2049

Added:
   pypy/trunk/src/pypy/objspace/flow/framestate.py   (contents, props changed)
   pypy/trunk/src/pypy/objspace/flow/test/test_framestate.py   (contents, props changed)
Modified:
   pypy/trunk/src/pypy/interpreter/pyframe.py
   pypy/trunk/src/pypy/objspace/flow/flowcontext.py
   pypy/trunk/src/pypy/objspace/flow/model.py   (contents, props changed)
   pypy/trunk/src/pypy/objspace/flow/objspace.py
   pypy/trunk/src/pypy/objspace/flow/test/test_objspace.py
Log:
note: this commit will break all translators

- a new flowmodel according to our discussions at the berlin sprint

- a new class FrameState now encapsulates operations on the
  (mergeable) state of frames.

- PyFrame no longer carries flowobjspace specific stuff

Armin, Holger


Modified: pypy/trunk/src/pypy/interpreter/pyframe.py
==============================================================================
--- pypy/trunk/src/pypy/interpreter/pyframe.py	(original)
+++ pypy/trunk/src/pypy/interpreter/pyframe.py	Sat Oct 25 15:14:49 2003
@@ -94,33 +94,6 @@
         if attr == 'f_code':     return self.space.wrap(self.code)
         raise OperationError(self.space.w_AttributeError, w_attr)
 
-    ### cloning (for FlowObjSpace) ###
-
-    def getflowstate(self):
-        mergeablestate = self.getfastscope() + self.valuestack.items
-        nonmergeablestate = (
-            self.blockstack.items[:],
-            self.last_exception,
-            self.next_instr,
-            )
-        return mergeablestate, nonmergeablestate
-
-    def setflowstate(self, (mergeablestate, nonmergeablestate)):
-        self.setfastscope(mergeablestate[:len(self.fastlocals_w)])
-        self.valuestack.items[:] = mergeablestate[len(self.fastlocals_w):]
-        (
-            self.blockstack.items[:],
-            self.last_exception,
-            self.next_instr,
-            ) = nonmergeablestate
-        
-    def clone(self):
-        # Clone the frame, making a copy of the mutable state
-        cls = self.__class__
-        f = cls(self.space, self.code, self.w_globals, self.getclosure())
-        f.setflowstate(self.getflowstate())
-
-
 ### Frame Blocks ###
 
 class FrameBlock:

Modified: pypy/trunk/src/pypy/objspace/flow/flowcontext.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/flow/flowcontext.py	(original)
+++ pypy/trunk/src/pypy/objspace/flow/flowcontext.py	Sat Oct 25 15:14:49 2003
@@ -2,69 +2,29 @@
 from pypy.interpreter.miscutils import Stack
 from pypy.interpreter.pyframe \
      import ControlFlowException, ExitFrame, PyFrame
-from pypy.objspace.flow.wrapper import W_Variable, W_Constant, UnwrapException
-from pypy.translator.flowmodel import *
+from pypy.objspace.flow.model import *
+from pypy.objspace.flow.framestate import FrameState
 
 
-def constantsof(lst):
-    result = {}
-    for i in range(len(lst)):
-        if isinstance(lst[i], W_Constant):
-            result[i] = lst[i]
-    return result
-
-class SpamBlock(BasicBlock):
+class SpamBlock(Block):
     dead = False
-    
+      
     def __init__(self, framestate):
-        mergeablestate, unmergeablestate = framestate
-        newstate = []
-        inputargs = []
-        for w in mergeablestate:
-            if isinstance(w, W_Variable):
-                w = W_Variable()  # make fresh variables
-                inputargs.append(w)   # collects all variables
-            newstate.append(w)
-        self.framestate = newstate, unmergeablestate
-        #import sys; print >> sys.stderr, "** creating SpamBlock", self.framestate
-        BasicBlock.__init__(self, inputargs, inputargs, [], None)
+        Block.__init__(self, framestate.getvariables())
+        self.framestate = framestate
 
     def patchframe(self, frame, executioncontext):
         if self.dead:
             raise ExitFrame(None)
-        frame.setflowstate(self.framestate)
+        self.framestate.restoreframe(frame)
         executioncontext.crnt_block = self
         executioncontext.crnt_ops = self.operations
 
-    def union(self, other):
-        mergeablestate1, unmergeablestate1 = self.framestate
-        mergeablestate2, unmergeablestate2 = other.framestate
-##        XXX reintroduce me
-##        assert unmergeablestate1 == unmergeablestate2, (
-##            "non mergeable states reached:\n%r\n%r" % (
-##            unmergeablestate1, unmergeablestate2))
-        assert len(mergeablestate1) == len(mergeablestate2), (
-            "non mergeable states (different value stack depth)")
-
-        newstate = []
-        for w1, w2 in zip(mergeablestate1, mergeablestate2):
-            if w1 == w2 or isinstance(w1, W_Variable):
-                w = w1
-            else:
-                w = W_Variable()
-            newstate.append(w)
-        if constantsof(newstate) == constantsof(mergeablestate1):
-            return self
-        elif constantsof(newstate) == constantsof(mergeablestate2):
-            return other
-        else:
-            return SpamBlock((newstate, unmergeablestate1))
 
-class EggBlock(BasicBlock):
-    has_renaming = False
+class EggBlock(Block):
 
-    def __init__(self, prevblock, booloutcome):
-        BasicBlock.__init__(self, [], prevblock.locals, [], None)
+    def __init__(self, inputargs, prevblock, booloutcome):
+        Block.__init__(self, inputargs)
         self.prevblock = prevblock
         self.booloutcome = booloutcome
 
@@ -110,19 +70,17 @@
         self.w_globals = w_globals = space.wrap(globals)
         frame = code.create_frame(space, w_globals)
         formalargcount = code.getformalargcount()
-        dummy = W_Constant(None)
+        dummy = Constant(None)
         dummy.dummy = True
-        arg_list = ([W_Variable() for i in range(formalargcount)] +
+        arg_list = ([Variable() for i in range(formalargcount)] +
                     [dummy] * (len(frame.fastlocals_w) - formalargcount))
         frame.setfastscope(arg_list)
         self.joinpoints = {}
         for joinpoint in code.getjoinpoints():
             self.joinpoints[joinpoint] = None
-        initialblock = SpamBlock(frame.getflowstate())
-        # only keep arguments of the function in initialblock.input_args
-        del initialblock.input_args[formalargcount:]
+        initialblock = SpamBlock(FrameState(frame).copy())
         self.pendingblocks = [initialblock]
-        self.graph = FunctionGraph(initialblock, code.co_name)
+        self.graph = FunctionGraph(code.co_name, initialblock)
 
     def bytecode_trace(self, frame):
         if isinstance(self.crnt_ops, ReplayList):
@@ -130,44 +88,51 @@
         next_instr = frame.next_instr
         if next_instr in self.joinpoints:
             block = self.joinpoints[next_instr]
-            currentframestate = frame.getflowstate()
-            newblock = SpamBlock(currentframestate)
-            if block is not None:
-                newblock = block.union(newblock)
-            finished = newblock is block
-            outputargs = []
-            for w_output, w_target in zip(currentframestate[0],
-                                          newblock.framestate[0]):
-                if isinstance(w_target, W_Variable):
-                    outputargs.append(w_output)
-            self.crnt_block.closeblock(Branch(outputargs, newblock))
+            currentstate = FrameState(frame)
+            if block is None:
+                newstate = currentstate.copy()
+                finished = False
+            else:
+                # there is already a block for this bytecode position,
+                # we merge its state with the new (current) state.
+                newstate = block.framestate.union(currentstate)
+                finished = newstate == block.framestate
+            if finished:
+                newblock = block
+            else:
+                newblock = SpamBlock(newstate)
+            # unconditionally link the current block to the newblock
+            outputargs = currentstate.getoutputargs(newstate)
+            self.crnt_block.closeblock(Link(outputargs, newblock))
             # phew
             if finished:
                 raise ExitFrame(None)
-            if block is not None and isinstance(block.operations, tuple):
-                # patch the old block to point directly at the new block
+            if block is not None and block.exits:
+                # to simplify the graph, we patch the old block to point
+                # directly at the new block which is its generalization
                 block.dead = True
                 block.operations = ()
-                outputargs = []
-                for w_output, w_target in zip(block.framestate[0],
-                                              newblock.framestate[0]):
-                    if isinstance(w_target, W_Variable):
-                        outputargs.append(w_output)
-                block.branch = Branch(outputargs, newblock)
+                outputargs = block.framestate.getoutputargs(newstate)
+                block.exits = (Link(outputargs, newblock),)
             newblock.patchframe(frame, self)
             self.joinpoints[next_instr] = newblock
 
     def guessbool(self, w_condition):
         if not isinstance(self.crnt_ops, ReplayList):
             block = self.crnt_block
-            ifegg = EggBlock(block, True)
-            elseegg = EggBlock(block, False)
-            ifbranch = Branch([], ifegg)
-            elsebranch = Branch([], elseegg)
-            branch = ConditionalBranch(w_condition, ifbranch, elsebranch)
-            block.closeblock(branch)
-            self.pendingblocks.append(ifegg)
-            self.pendingblocks.append(elseegg)
+            vars = block.getvariables()
+            ifEgg = EggBlock(vars, block, True)
+            elseEgg = EggBlock(vars, block, False)
+            ifLink = Link(vars, ifEgg, True)
+            elseLink = Link(vars, elseEgg, False)
+            block.exitswitch = w_condition
+            block.closeblock(elseLink, ifLink)
+            # forked the graph. Note that elseLink comes before ifLink
+            # in the exits tuple so that (just in case we need it) we
+            # actually have block.exits[False] = elseLink and
+            # block.exits[True] = ifLink.
+            self.pendingblocks.append(ifEgg)
+            self.pendingblocks.append(elseEgg)
             raise ExitFrame(None)
         replaylist = self.crnt_ops
         assert replaylist.finished()
@@ -185,4 +150,6 @@
                 continue   # restarting a dead SpamBlock
             w_result = frame.eval(self)
             if w_result is not None:
-                self.crnt_block.closeblock(EndBranch(w_result))
+                link = Link([w_result], self.graph.returnblock)
+                self.crnt_block.closeblock(link)
+

Added: pypy/trunk/src/pypy/objspace/flow/framestate.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/objspace/flow/framestate.py	Sat Oct 25 15:14:49 2003
@@ -0,0 +1,94 @@
+from pypy.interpreter.pyframe import PyFrame
+from pypy.objspace.flow.model import *
+
+class FrameState:
+    # XXX this class depends on the internal state of PyFrame objects
+
+    def __init__(self, state):
+        if isinstance(state, PyFrame):
+            self.mergeable = state.getfastscope() + state.valuestack.items
+            self.nonmergeable = (
+                state.blockstack.items[:],
+                state.last_exception,
+                state.next_instr,
+            )
+        elif isinstance(state, tuple):
+            self.mergeable, self.nonmergeable = state
+        else:
+            raise TypeError("can't get framestate for %r" % 
+                            state.__class__.__name__)
+
+    def restoreframe(self, frame):
+        if isinstance(frame, PyFrame):
+            fastlocals = len(frame.fastlocals_w)
+            frame.setfastscope(self.mergeable[:fastlocals])
+            frame.valuestack.items[:] = self.mergeable[fastlocals:]
+            (
+                frame.blockstack.items[:],
+                frame.last_exception,
+                frame.next_instr,
+            ) = self.nonmergeable
+        else:
+            raise TypeError("can't set framestate for %r" % 
+                            frame.__class__.__name__)
+
+    def copy(self):
+        "Make a copy of this state in which all Variables are fresh."
+        newstate = []
+        for w in self.mergeable:
+            if isinstance(w, Variable):
+                w = Variable()
+            newstate.append(w)
+        return FrameState((newstate, self.nonmergeable))
+
+    def getvariables(self):
+        return [w for w in self.mergeable if isinstance(w, Variable)]
+
+    def __eq__(self, other):
+        """Two states are equal
+        if they only use different Variables at the same place"""
+        # safety check, don't try to compare states with different
+        # nonmergeable states
+        assert isinstance(other, FrameState)
+        assert len(self.mergeable) == len(other.mergeable)
+        # XXX assert self.nonmergeable == other.nonmergeable
+        # XXX this requires a proper __eq__ on the blockstack items
+        for w1, w2 in zip(self.mergeable, other.mergeable):
+            if not (w1 == w2 or (isinstance(w1, Variable) and
+                                 isinstance(w2, Variable))):
+                return False
+        return True
+
+    def __ne__(self, other):
+        return not (self == other)
+
+    def union(self, other):
+        """Compute a state that is at least as general as both self and other.
+           A state 'a' is more general than a state 'b' if all Variables in 'b'
+           are also Variables in 'a', but 'a' may have more Variables.
+        """
+        newstate = []
+        for w1, w2 in zip(self.mergeable, other.mergeable):
+            if isinstance(w1, Variable):
+                w = Variable()  # new fresh Variable
+            elif isinstance(w1, Constant):
+                if w1 == w2:
+                    w = w1
+                else:
+                    w = Variable()  # generalize different constants
+            else:
+                raise TypeError('union of %r and %r' % (w1.__class__.__name__,
+                                                        w2.__class__.__name__))
+            newstate.append(w)
+        return FrameState((newstate, self.nonmergeable))
+
+    def getoutputargs(self, targetstate):
+        "Return the output arguments needed to link self to targetstate."
+        result = []
+        for w_output, w_target in zip(self.mergeable, targetstate.mergeable):
+            if isinstance(w_target, Variable):
+                result.append(w_output)
+            elif not isinstance(w_target, Constant):
+                raise TypeError('output arg %r' % w_target.__class__.__name__)
+        return result
+

Modified: pypy/trunk/src/pypy/objspace/flow/model.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/flow/model.py	(original)
+++ pypy/trunk/src/pypy/objspace/flow/model.py	Sat Oct 25 15:14:49 2003
@@ -5,35 +5,76 @@
 # a discussion in Berlin, 4th of october 2003
 
 class FunctionGraph:
-    startblock  # 
-    returnblock # 
-    name        # function name (possibly mangled already)
+    def __init__(self, name, startblock, return_var=None):
+        self.name        = name    # function name (possibly mangled already)
+        self.startblock  = startblock
+        # build default returnblock
+        self.returnblock = Block([return_var or Variable()])
+        self.returnblock.operations = ()
+        self.returnblock.exits      = ()
 
 class Link:
-    exitcase    # Constant (or so)
-    args        # mixed list of variable/const
-    target      # block 
+    def __init__(self, args, target, exitcase=None):
+        self.args = args           # mixed list of var/const
+        self.target = target       # block
+        self.exitcase = exitcase   # this is a concrete value
 
 class Block:
-    input_args  # mixed list of variable/const 
-    operations  # list of SpaceOperation(s)
-    exitswitch  # variable
-    exits       # list of Link(s)
-
-class ReturnBlock:
-    input_args  # a single variable 
-    operations = None # for uniformity
-    exits = ()  # ?
 
-class Variable:
-    name
+    def __init__(self, inputargs):
+        self.inputargs = inputargs    # mixed list of variable/const 
+        self.operations = []          # list of SpaceOperation(s)
+        self.exitswitch = None        # variable
+        self.exits      = []          # list of Link(s)
+
+    def getvariables(self):
+        "Return all variables mentionned in this Block."
+        result = self.inputargs[:]
+        for op in self.operations:
+            result += op.args
+            result.append(result)
+        return uniqueitems([w for w in result if isinstance(w, Variable)])
+
+    def closeblock(self, *exits):
+        assert self.exits == [], "block already closed"
+        self.exits = exits
 
-class Const:
-    value     # a concrete value
+class Variable:
+    counter = 0
+    def __init__(self, name=None):
+        if name is None:
+            name = 'v%d' % Variable.counter
+            Variable.counter += 1
+        self.name = name
+
+class Constant:
+    def __init__(self, value):
+        self.value = value     # a concrete value
+    def __eq__(self, other):
+        return isinstance(other, Constant) and self.value == other.value
+    def __ne__(self, other):
+        return not (self == other)
 
 class SpaceOperation:
-    opname    # operation name
-    args      # mixed list of variables/Constants (can be mixed)
-    result    # either Variable or Constant instance
-
+    def __init__(self, opname, args, result): 
+        self.opname = opname      # operation name
+        self.args   = list(args)  # mixed list of var/const
+        self.result = result      # either Variable or Constant instance
+    def __eq__(self, other):
+        return (self.__class__ is other.__class__ and 
+                self.opname == other.opname and
+                self.args == other.args and
+                self.result == other.result)
+    def __ne__(self, other):
+        return not (self == other)
+
+def uniqueitems(lst):
+    "Returns a list with duplicate elements removed."
+    result = []
+    seen = {}
+    for item in lst:
+        if item not in seen:
+            result.append(item)
+            seen[item] = True
+    return result
 

Modified: pypy/trunk/src/pypy/objspace/flow/objspace.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/flow/objspace.py	(original)
+++ pypy/trunk/src/pypy/objspace/flow/objspace.py	Sat Oct 25 15:14:49 2003
@@ -4,17 +4,20 @@
 from pypy.interpreter.baseobjspace import ObjSpace, NoValue
 from pypy.interpreter.pycode import PyCode
 from pypy.interpreter.error import OperationError
-from pypy.objspace.flow.wrapper import *
-from pypy.translator.flowmodel import *
+from pypy.objspace.flow.model import *
 from pypy.objspace.flow import flowcontext
 
 debug = 0
+
+class UnwrapException(Exception):
+    "Attempted to unwrap a Variable."
+
 # ______________________________________________________________________
 class FlowObjSpace(ObjSpace):
     def initialize(self):
         import __builtin__
-        self.w_builtins = W_Constant(__builtin__.__dict__)
-        self.w_KeyError = W_Constant(KeyError)
+        self.w_builtins = Constant(__builtin__.__dict__)
+        self.w_KeyError = Constant(KeyError)
         #self.make_builtins()
         #self.make_sys()
 
@@ -35,13 +38,15 @@
         return self.do_operation('newslice', w_start, w_stop, w_step)
 
     def wrap(self, obj):
-        if isinstance(obj, W_Object):
+        if isinstance(obj, (Variable, Constant)):
             raise TypeError("already wrapped: " + repr(obj))
-        return W_Constant(obj)
+        return Constant(obj)
 
     def unwrap(self, w_obj):
-        if isinstance(w_obj, W_Object):
-            return w_obj.unwrap()
+        if isinstance(w_obj, Variable):
+            raise UnwrapException
+        elif isinstance(w_obj, Constant):
+            return w_obj.value
         else:
             raise TypeError("not wrapped: " + repr(w_obj))
 
@@ -65,7 +70,7 @@
 
     # ____________________________________________________________
     def do_operation(self, name, *args_w):
-        spaceop = SpaceOperation(name, args_w, W_Variable())
+        spaceop = SpaceOperation(name, args_w, Variable())
         self.executioncontext.crnt_ops.append(spaceop)
         return spaceop.result
     
@@ -81,10 +86,10 @@
 
     def next(self, w_iter):
         w_tuple = self.do_operation("next_and_flag", w_iter)
-        w_flag = self.do_operation("getitem", w_tuple, W_Constant(1))
+        w_flag = self.do_operation("getitem", w_tuple, Constant(1))
         context = self.getexecutioncontext()
         if context.guessbool(w_flag):
-            return self.do_operation("getitem", w_tuple, W_Constant(0))
+            return self.do_operation("getitem", w_tuple, Constant(0))
         else:
             raise NoValue
 

Added: pypy/trunk/src/pypy/objspace/flow/test/test_framestate.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/objspace/flow/test/test_framestate.py	Sat Oct 25 15:14:49 2003
@@ -0,0 +1,109 @@
+
+import autopath
+from pypy.tool import test
+
+from pypy.objspace.flow.flowcontext import *
+from pypy.objspace.flow.model import *
+from pypy.interpreter.pycode import PyCode
+
+class TestFrameState(test.TestCase):
+    def setUp(self):
+        self.space = test.objspace('flow')
+
+    def getframe(self, func):
+        space = self.space
+        try:
+            func = func.im_func
+        except AttributeError:
+            pass
+        code = func.func_code
+        code = PyCode()._from_code(code)
+        w_globals = Constant({}) # space.newdict([])
+        frame = code.create_frame(space, w_globals)
+
+        formalargcount = code.getformalargcount()
+        dummy = Constant(None)
+        #dummy.dummy = True
+        arg_list = ([Variable() for i in range(formalargcount)] +
+                    [dummy] * (len(frame.fastlocals_w) - formalargcount))
+        frame.setfastscope(arg_list)
+        return frame
+
+    def func_simple(x):
+        spam = 5
+        return spam
+
+    def test_eq_framestate(self):
+        frame = self.getframe(self.func_simple)
+        fs1 = FrameState(frame)
+        fs2 = FrameState(frame)
+        self.assertEquals(fs1, fs2)
+
+    def test_neq_hacked_framestate(self):
+        frame = self.getframe(self.func_simple)
+        fs1 = FrameState(frame)
+        frame.fastlocals_w[-1] = Variable()
+        fs2 = FrameState(frame)
+        self.assertNotEquals(fs1, fs2)
+
+    def test_union_on_equal_framestates(self):
+        frame = self.getframe(self.func_simple)
+        fs1 = FrameState(frame)
+        fs2 = FrameState(frame)
+        self.assertEquals(fs1.union(fs2), fs1)
+
+    def test_union_on_hacked_framestates(self):
+        frame = self.getframe(self.func_simple)
+        fs1 = FrameState(frame)
+        frame.fastlocals_w[-1] = Variable()
+        fs2 = FrameState(frame)
+        self.assertEquals(fs1.union(fs2), fs2)  # fs2 is more general
+        self.assertEquals(fs2.union(fs1), fs2)  # fs2 is more general
+
+    def test_restore_frame(self):
+        frame = self.getframe(self.func_simple)
+        fs1 = FrameState(frame)
+        frame.fastlocals_w[-1] = Variable()
+        fs1.restoreframe(frame)
+        self.assertEquals(fs1, FrameState(frame))
+
+    def test_copy(self):
+        frame = self.getframe(self.func_simple)
+        fs1 = FrameState(frame)
+        fs2 = fs1.copy()
+        self.assertEquals(fs1, fs2)
+
+    def test_getvariables(self):
+        frame = self.getframe(self.func_simple)
+        fs1 = FrameState(frame)
+        vars = fs1.getvariables()
+        self.assertEquals(len(vars), 1) 
+
+    def test_getoutputargs(self):
+        frame = self.getframe(self.func_simple)
+        fs1 = FrameState(frame)
+        frame.fastlocals_w[-1] = Variable()
+        fs2 = FrameState(frame)
+        outputargs = fs1.getoutputargs(fs2)
+        # 'x' -> 'x' is a Variable
+        # fastlocals_w[-1] -> fastlocals_w[-1] is Constant(None)
+        self.assertEquals(outputargs, [frame.fastlocals_w[0], Constant(None)])
+
+    def test_union_different_constants(self):
+        frame = self.getframe(self.func_simple)
+        fs1 = FrameState(frame)
+        frame.fastlocals_w[-1] = Constant(42)
+        fs2 = FrameState(frame)
+        fs3 = fs1.union(fs2)
+        fs3.restoreframe(frame)
+        self.assert_(isinstance(frame.fastlocals_w[-1], Variable)) # generalized
+
+if __name__ == '__main__':
+    test.main()
+        
+
+        
+        
+        
+        
+

Modified: pypy/trunk/src/pypy/objspace/flow/test/test_objspace.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/flow/test/test_objspace.py	(original)
+++ pypy/trunk/src/pypy/objspace/flow/test/test_objspace.py	Sat Oct 25 15:14:49 2003
@@ -35,7 +35,9 @@
 
     def test_nothing(self):
         x = self.codetest(self.nothing)
-        self.assertEquals(x.startblock.branch.__class__, EndBranch)
+        self.assertEquals(len(x.startblock.exits), 1)
+        link, = x.startblock.exits
+        self.assertEquals(link.target, x.returnblock)
         self.show(x)
 
     #__________________________________________________________


More information about the Pypy-commit mailing list