[pypy-svn] r6143 - pypy/trunk/src/pypy/translator/tool

rxe at codespeak.net rxe at codespeak.net
Tue Aug 24 01:00:06 CEST 2004


Author: rxe
Date: Tue Aug 24 01:00:06 2004
New Revision: 6143

Added:
   pypy/trunk/src/pypy/translator/tool/flowtrace.py
Log:
See the __doc__ string. :-)

Where best to check in hacks like these?




Added: pypy/trunk/src/pypy/translator/tool/flowtrace.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/translator/tool/flowtrace.py	Tue Aug 24 01:00:06 2004
@@ -0,0 +1,391 @@
+"""
+
+This is an experimental hack - use it at your peril!  I (rxe) needed some sanity
+check when (attempting) to write gencpp.py and this ended up being a useful
+exercise.  I doubt it much use to anyone else or the rest of the project, but it
+might have some interesting possiblities later on once we have full translation.
+:-)
+
+The idea is simple - take the basic blocks output via the flow object space and
+evaluate them against the standard object space.  It is effectively the same as
+creating a C extension to the CPython interpreter to use the CPython API instead
+of interpreting bytecodes - except in this case the we don't need to compile
+anything and all space operations are emitted on the fly.  One might call this a
+flow interpreter - I wouldn't go that far!
+
+"""
+import autopath
+from pypy.objspace.flow import FlowObjSpace
+from pypy.objspace.flow.model import traverse, Constant, Variable, Block, Link
+from pypy.translator.simplify import simplify_graph
+from pypy.interpreter.baseobjspace import OperationError
+
+
+class FlowTracer(object):
+
+    def __init__(self, flow_space, space, debug = False, trace = True):
+        self.space = space
+        self.flow_space = flow_space
+        self.trace = trace
+        self.debug = debug
+
+    def pprint(self, v):        
+        s = self.space.unwrap(self.space.repr(v.e_value))
+        if len(s) > 30:
+            s = s[:27] + "..."
+
+            if isinstance(v, Constant):
+                s = "Const(%s)" % s
+
+            elif isinstance(v, Variable):
+                s = "%s(%s)" % (v, s)
+
+            else:
+                assert False, "really?"
+        return s
+
+
+    def get_blocknames(self, graph):
+        blocknames = {}
+        def visit(n):        
+            if isinstance(n, Block):
+                blocknames[n] = 'block%d' % len(blocknames)
+
+        traverse(visit, graph)
+        return blocknames
+
+    
+    def wrap_constants(self, graph):
+        all = []
+        
+        def visit(n):
+            # Note - be careful with uniqueitems and constants, as there can
+            # multiple constant of the same value (XXX why?)
+            if isinstance(n, Block):
+                values = []
+                values += n.inputargs
+                for op in n.operations:
+                    values += op.args
+
+                for ii in values:
+                    all.append(ii)
+
+                all.append(n.exitswitch)
+                
+            if isinstance(n, Link):
+                values = n.args
+                for ii in values:
+                    all.append(ii)
+                
+        traverse(visit, graph)
+        for ii in all:
+            
+            if isinstance(ii, Constant) :
+                ii.e_value = self.space.wrap(ii.value)
+
+            else:
+                assert (isinstance(ii, Variable) or ii is None)
+
+    def wrap_linkexits(self, graph):
+        all = []
+        
+        def visit(n):                        
+            if isinstance(n, Link):
+                all.append(n)
+
+        traverse(visit, graph)
+
+        for l in all:
+            l.exitcase = self.space.wrap(l.exitcase)
+
+        
+    def execute_function(self, graph, *args_w):
+
+        curblock = graph.startblock
+        assert len(curblock.inputargs) == len(args_w)        
+
+        # We add value as evaluated values during interpretation
+        # to variables, constants and exit switches.
+        # Variables are done on assignment (ie lazily)
+        for input, arg in zip(curblock.inputargs, args_w):
+            input.e_value = arg
+
+        # Here we add value attribute with wrap
+        self.wrap_constants(graph)
+        self.wrap_linkexits(graph)
+        
+        blocknames = self.get_blocknames(graph)
+        last_exception = None
+        while True:
+            
+            if self.trace:
+                print 'Entering %s:' % blocknames[curblock]
+                print '  Input args :- %s' % (", ".join(map(self.pprint, curblock.inputargs)))
+                print '  Operations :-'
+
+            for op in curblock.operations:
+                
+                # Why does op.args have a list in it?
+                opargs = [a.e_value for a in op.args]
+                if self.trace:
+                    print '    %s  = space.%s(%s)' % (op.result, 
+                                                   op.opname,
+                                                   ", ".join(map(self.pprint, op.args)))
+
+                if op.opname == "exception":
+                    assert (len(opargs) == 1)
+                    # XXX What we suppose to do with argument???
+                    if last_exception is not None:
+                        res = last_exception
+                        last_exception = None
+                    else:
+                        res = self.space.w_None
+
+                elif op.opname == "simple_call":
+                    assert (len(opargs) >= 1)
+                    res = self.simple_call(opargs[0], *opargs[1:])
+
+                else:
+                    # More special cases
+                    spaceop = getattr(self.space, op.opname)
+                    
+                    if op.opname in ("newlist", "newdict", "newtuple"):
+                        # These expect a list, not a *args
+                        res = spaceop(opargs)
+
+                    else:
+                        try:
+                            res = spaceop(*opargs)
+
+                            # More special case
+                            if op.opname == "is_true":
+                                # Rewrap it!
+                                res = self.space.wrap(res)
+                                
+                        except OperationError, exc:
+                            last_exception = exc.w_type
+                            res = self.space.w_None
+                        
+                op.result.e_value = res
+                
+                if self.trace:
+                    # Cases will likely not be a space object
+                    if curblock.exits and curblock.exitswitch == op.result:
+                        print '    %s := exit(%s)' % (op.result, op.result.e_value)
+                    else:
+                        print '    %s := %s' % (op.result, self.pprint(op.result))
+
+            # Switch to next block
+            if curblock.exits:
+
+                # exits (safe code)                
+                exit_link = None
+                if len(curblock.exits) == 1:
+                    exit_link = curblock.exits[0]
+
+                else:
+                    exit_res = curblock.exitswitch.e_value
+                    for link in curblock.exits:
+                        if self.space.is_true(self.space.eq(exit_res, link.exitcase)):
+                            exit_link = link
+                            break
+
+                assert exit_link is not None
+                
+                if self.trace:
+                    print '  Exit to %s :- ' % blocknames[exit_link.target]
+
+                sourceargs = exit_link.args
+                targetargs = exit_link.target.inputargs
+                assert len(sourceargs) == len(targetargs)
+
+                for s, t in zip(sourceargs, targetargs):                    
+                    if self.trace:
+                        print "    %s = %s" % (t, s)
+
+                    t.e_value = s.e_value
+
+                curblock = exit_link.target
+
+                if self.trace:
+                    print
+                
+            elif hasattr(curblock, 'exc_type'):
+                raise curblock.exc_type
+
+            else:
+                result = curblock.inputargs[0]
+                if self.trace:
+                    print "Returning -", self.pprint(result)
+
+                return result.e_value
+
+
+    def simple_call(self, w_func, *args_w):
+        
+        func = self.space.unwrap(w_func)
+        if hasattr(func, "func_code"):        
+            graph = self.flow_space.build_flow(func)
+            graph = simplify_graph(graph)
+            if self.debug:
+                debug(func) 
+            return self.execute_function(graph, *args_w)
+
+        else:
+            # XXX We could try creating the flow graph by runnning another
+            # flow objspace under self.space.  Hmmm - if only I had
+            # bigger computer. 
+
+            # Instead we cheat (this is great fun when it is a fake type :-))
+            if self.trace:
+                print "WOA! Cheating!", w_func
+
+            return self.space.call_function(w_func, *args_w)  
+            
+
+    def call(self, f, *args):
+        w = self.space.wrap
+        args_w = [w(ii) for ii in args]
+        w_func = w(f)
+
+        res = self.simple_call(w_func, *args_w)
+        return self.space.unwrap(res)
+            
+
+def debug(func):
+    """Shows the control flow graph with annotations if computed.
+    Requires 'dot' and pygame."""
+    from pypy.translator.tool.pygame.graphdisplay import GraphDisplay
+    from pypy.translator.tool.pygame.flowviewer import FlowGraphLayout
+    from pypy.translator.translator import Translator
+    t = Translator(func)
+    t.simplify()
+    #t.annotate([int])
+    GraphDisplay(FlowGraphLayout(t)).run()
+
+def timeit(num, func, *args):
+    from time import time as now
+    start = now()
+    for i in xrange(num):
+        print func(*args)
+    return now() - start
+
+if __name__ == '__main__':
+    from earthenware.common import chunk
+    from earthenware.utils.stacktrace import init_module
+    init_module(True)
+
+    from pypy.objspace.std import Space
+    space = Space()
+
+    def create_std_func(app_func):
+
+        import new 
+        from pypy.interpreter.gateway import app2interp
+        from pypy.interpreter.argument import Arguments    
+
+        # Horrible hack (ame needs to start with "app_")
+        app_func = new.function(app_func.func_code,
+                                app_func.func_globals,
+                                "app_" + app_func.__name__)
+        
+        # Create our function
+        func_gw = app2interp(app_func)
+        func = func_gw.get_function(space)
+        w_func = space.wrap(func)
+          
+        def f(*args):
+            args_w = [space.wrap(ii) for ii in args]
+            args_ = Arguments(space, args_w)
+            w_result = space.call_args(w_func, args_)
+            return space.unwrap(w_result) 
+        return f
+    
+    def create_flow_func(f):
+        flow_space = FlowObjSpace()
+        interpreter = FlowTracer(flow_space, space)
+        def func(*args):
+            return interpreter.call(f, *args)
+        return func
+    
+    def do(f, *args):
+        print "doing %s(%s)" % (f.__name__, ", ".join(map(str, args)))
+        f_flow = create_flow_func(f)
+        res = f_flow(*args)
+        f_norm = create_std_func(f)
+        res_norm = f_norm(*args)
+        assert res == res_norm 
+        return res
+
+    def do_flow_only(f, *args):
+        print "doing %s(%s)" % (f.__name__, ", ".join(map(str, args)))
+        f_flow = create_flow_func(f)
+        res = f_flow(*args)
+        return res
+
+    #/////////////////////////////////////////////////////////////////////////////
+
+    def tests():
+        from pypy.translator.test import snippet
+        
+        tests = [
+            (snippet.if_then_else, 1, 2, 3),
+            (snippet.if_then_else, 0, 2, 3),
+            (snippet.my_gcd, 256, 192),
+            (snippet.is_perfect_number, 81),
+            (snippet.my_bool, 1),
+            (snippet.my_bool, 0),
+            (snippet.two_plus_two,),
+            #(snippet.sieve_of_eratosthenes,),
+            (snippet.simple_func, 10),
+            (snippet.nested_whiles, 1, 10),
+            (snippet.simple_func, 10),
+            (snippet.builtinusage,),
+            (snippet.poor_man_range, 10),
+            (snippet.poor_man_rev_range, 10),
+            (snippet.simple_id, 2) ,
+            (snippet.branch_id, 1, "k", 1.0) ,
+            (snippet.branch_id, False, "k", 1.0) ,
+            (snippet.builtinusage,),
+            (snippet.yast, [1,2,3,4,5]),
+            (snippet.time_waster, 5),
+            (snippet.half_of_n, 20),
+            (snippet.int_id, 20),
+            (snippet.greet, "world"),
+            (snippet.choose_last,),
+            #(snippet.choose_last,), XXX Why does repeating this break?
+            (snippet.poly_branch, 1),
+            (snippet.s_and, 1, 1),
+            (snippet.s_and, 0, 1),
+            (snippet.s_and, 1, 0),
+            (snippet.s_and, 0, 0),
+            (snippet.break_continue, 15),
+            (snippet.reverse_3, ("k", 1, 1.0)),
+            (snippet.finallys, ("k", 1, 1.0)),
+            (snippet.finallys, ("k",)),
+            (snippet.finallys, []),
+            (snippet._append_five, []),
+            (snippet._append_five, [1,2,3]),
+            ]
+        for ii in tests:
+            print do(*ii) 
+
+        tests = [
+            (snippet.factorial, 4),
+            (snippet.factorial2, 4),
+            (snippet.call_five,),
+            (snippet.build_instance,),
+            (snippet.set_attr,),
+            (snippet.merge_setattr, 0),        
+            (snippet.merge_setattr, 1),        
+            # XXX These don't work from test.snippet (haven't tried anymore)
+            #(snippet.inheritance1,),        
+            #(snippet.inheritance2,),        
+
+            ]
+
+        for ii in tests:
+            print do_flow_only(*ii) 
+
+    tests()
+



More information about the Pypy-commit mailing list