[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