[Python-checkins] r86722 - python/branches/dmalcolm-ast-optimization-branch/Lib/__optimizer__.py

david.malcolm python-checkins at python.org
Tue Nov 23 23:23:26 CET 2010


Author: david.malcolm
Date: Tue Nov 23 23:23:26 2010
New Revision: 86722

Log:
Rework FunctionInliner to cover all inlinable functions, rather than having one visitor per inlinable function, so that we can do all inlining in a single traversal of the tree.


Modified:
   python/branches/dmalcolm-ast-optimization-branch/Lib/__optimizer__.py

Modified: python/branches/dmalcolm-ast-optimization-branch/Lib/__optimizer__.py
==============================================================================
--- python/branches/dmalcolm-ast-optimization-branch/Lib/__optimizer__.py	(original)
+++ python/branches/dmalcolm-ast-optimization-branch/Lib/__optimizer__.py	Tue Nov 23 23:23:26 2010
@@ -103,10 +103,13 @@
         self.index = index
 
     def __str__(self):
+        result = self.node.__class__.__name__
+        if hasattr(self.node, 'ste'):
+            result += '<%s>' % repr(self.node.ste.name)
+        result += '.%s' % self.field
         if self.index is not None:
-            return '%s.%s[%i]' % (self.node, self.field, self.index)
-        else:
-            return '%s.%s' % (self.node, self.field)
+            result += '[%i]' % self.index
+        return result
 
 class NodePath:
     '''
@@ -250,28 +253,33 @@
         # replace (the final) return with "__returnval__ = expr":
         return make_assignment(self.varprefix + "__returnval__", node.value, node)
 
+
 class FunctionInliner(PathTransformer):
-    def __init__(self, tree, funcdef, dotted_name):
+    def __init__(self, tree, def_dict):
         self.tree = tree
-        self.funcdef = funcdef
-        self.dotted_name = dotted_name
-        assert hasattr(funcdef, 'ste')
+        self.def_dict = def_dict
+        # dict from dottedname to ast.FunctionDef
+
+        #self.funcdef = funcdef
+        #self.dotted_name = dotted_name
+        #assert hasattr(funcdef, 'ste')
+
         self.num_callsites = 0
-        self.log('inlining calls to %r' % dotted_name)
+        self.log('inlining calls to %r' % def_dict)
         #self.log('ste for body: %r' % funcdef.ste)
 
     def log(self, msg):
         if 0:
             print('%s: %s' % (self.__class__.__name__, msg))
 
-    def is_inlinable_callsite(self, call, path):
+    def guess_dotted_name_for_def_from_call(self, call, path):
         # Return the name of the stored global if this is inlinable, otherwise None
         assert isinstance(call, ast.Call)
 
         if isinstance(call.func, ast.Name):
             # Name must match:
-            if call.func.id == self.dotted_name:
-                return self.dotted_name
+            if call.func.id in self.def_dict:
+                return call.func.id
 
         if isinstance(call.func, ast.Attribute):
             # Handle simple "self.METHOD_NAME" case:
@@ -294,21 +302,22 @@
     def visit_Call(self, call, path):
         # Stop inlining beyond an arbitrary cutoff
         # (bm_simplecall was exploding):
-        if self.num_callsites > 10:
+        if self.num_callsites > 1000:
             return call
 
         # Visit children:
         self.generic_visit(call, path)
 
-        stored_name = self.is_inlinable_callsite(call, path)
-        if stored_name is None:
+        dotted_name = self.guess_dotted_name_for_def_from_call(call, path)
+        if dotted_name is None:
             return call
 
-        self.log('Got inlinable callsite of %r' % stored_name)
+        self.log('Got inlinable callsite of:\n  dotted_name: %r\n  path: %s\n  node:%s'
+                 % (dotted_name, path, ast.dump(call)))
 
         if isinstance(call.func, ast.Attribute):
             # Don't try to inline method calls yet:
-            self.log('Got attribute %s' % ast.dump(call.func))
+            self.log('Not inlining attribute %s' % ast.dump(call.func))
             return call
 
         if not isinstance(call.func, (ast.Name, ast.Attribute)):
@@ -324,7 +333,7 @@
         nsp = NamespacePath.from_node_path(path)
         self.log('NamespacePath for callsite: %r' % nsp)
 
-        if stored_name != self.dotted_name:
+        if dotted_name not in self.def_dict:
             return call
         #if call.func.id != self.funcdef.name:
         #    return call
@@ -332,11 +341,13 @@
         # Locate innermost scope at callsite:
         ste = nsp.get_innermost_scope()
 
-        self.log('Inlining call to: %r within %r' % (self.dotted_name, ste.name))
+        self.log('Inlining call to: %r within %r' % (dotted_name, ste.name))
         self.num_callsites += 1
 
-        self.log(ast.dump(self.funcdef))
-        varprefix = '__inline_%s%x__' % (self.dotted_name, id(call))
+        funcdef = self.def_dict[dotted_name]
+
+        self.log(ast.dump(funcdef))
+        varprefix = '__inline_%s%x__' % (dotted_name, id(call))
         self.log('varprefix: %s' % varprefix)
 
         # Generate a body of specialized statements that can replace the call:
@@ -346,7 +357,7 @@
         #    __inline__x = expr for x
         # for each parameter
         # We will insert before the callsite
-        for formal, actual in zip(self.funcdef.args.args, call.args):
+        for formal, actual in zip(funcdef.args.args, call.args):
             #log('formal: %s' % ast.dump(formal))
             #log('actual: %s' % ast.dump(actual))
             # FIXME: ste
@@ -372,8 +383,8 @@
         # ending with:
         #    __inline____returnval = expr
         inline_body = []
-        fixer = InlineBodyFixups(varprefix, self.funcdef.ste)
-        for stmt in self.funcdef.body:
+        fixer = InlineBodyFixups(varprefix, funcdef.ste)
+        for stmt in funcdef.body:
             assert isinstance(stmt, ast.AST)
             inline_body.append(fixer.visit(ast_clone(stmt)))
         #log('inline_body:', inline_body)
@@ -401,6 +412,7 @@
     pass
 
 class CheckInlinableVisitor(PathTransformer):
+    # Walk an ast.FunctionDef subtree, determining if it's inlinable
     def __init__(self, funcdef):
         self.funcdef = funcdef
         self.returns = []
@@ -531,15 +543,13 @@
 def _inline_function_calls(t):
     v = InlinableFunctionFinder(t)
     v.visit(t)
-    inlinable_function_defs = v.funcdefs
+    def_dict = v.funcdefs
 
-    # print('inlinable_function_defs:%r' % inlinable_function_defs)
+    # print('def_dict:%r' % def_dict)
 
     # Locate call sites:
-    for dotted_name in inlinable_function_defs:
-        inliner = FunctionInliner(t, inlinable_function_defs[dotted_name],
-                                  dotted_name)
-        inliner.visit(t)
+    inliner = FunctionInliner(t, def_dict)
+    inliner.visit(t)
 
     return t
 


More information about the Python-checkins mailing list