[Python-checkins] r86724 - in python/branches/dmalcolm-ast-optimization-branch: Include/Python-ast.h Lib/__optimizer__.py Lib/test/test_optimize.py Parser/Python.asdl Python/Python-ast.c Python/compile.c

david.malcolm python-checkins at python.org
Wed Nov 24 00:46:30 CET 2010


Author: david.malcolm
Date: Wed Nov 24 00:46:30 2010
New Revision: 86724

Log:
Get more of the machinery for methodcall inlining working

Introduce "identifier expected_value" field within ast.Specialize (for supporting method calls and nested functions)



Modified:
   python/branches/dmalcolm-ast-optimization-branch/Include/Python-ast.h
   python/branches/dmalcolm-ast-optimization-branch/Lib/__optimizer__.py
   python/branches/dmalcolm-ast-optimization-branch/Lib/test/test_optimize.py
   python/branches/dmalcolm-ast-optimization-branch/Parser/Python.asdl
   python/branches/dmalcolm-ast-optimization-branch/Python/Python-ast.c
   python/branches/dmalcolm-ast-optimization-branch/Python/compile.c

Modified: python/branches/dmalcolm-ast-optimization-branch/Include/Python-ast.h
==============================================================================
--- python/branches/dmalcolm-ast-optimization-branch/Include/Python-ast.h	(original)
+++ python/branches/dmalcolm-ast-optimization-branch/Include/Python-ast.h	Wed Nov 24 00:46:30 2010
@@ -281,6 +281,7 @@
                 
                 struct {
                         expr_ty name;
+                        identifier expected_value;
                         asdl_seq *specialized_body;
                         expr_ty specialized_result;
                         expr_ty generalized;
@@ -513,10 +514,10 @@
 expr_ty _Py_Bytes(string s, int lineno, int col_offset, PyArena *arena);
 #define Ellipsis(a0, a1, a2) _Py_Ellipsis(a0, a1, a2)
 expr_ty _Py_Ellipsis(int lineno, int col_offset, PyArena *arena);
-#define Specialize(a0, a1, a2, a3, a4, a5, a6) _Py_Specialize(a0, a1, a2, a3, a4, a5, a6)
-expr_ty _Py_Specialize(expr_ty name, asdl_seq * specialized_body, expr_ty
-                       specialized_result, expr_ty generalized, int lineno, int
-                       col_offset, PyArena *arena);
+#define Specialize(a0, a1, a2, a3, a4, a5, a6, a7) _Py_Specialize(a0, a1, a2, a3, a4, a5, a6, a7)
+expr_ty _Py_Specialize(expr_ty name, identifier expected_value, asdl_seq *
+                       specialized_body, expr_ty specialized_result, expr_ty
+                       generalized, int lineno, int col_offset, PyArena *arena);
 #define Attribute(a0, a1, a2, a3, a4, a5) _Py_Attribute(a0, a1, a2, a3, a4, a5)
 expr_ty _Py_Attribute(expr_ty value, identifier attr, expr_context_ty ctx, int
                       lineno, int col_offset, PyArena *arena);

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	Wed Nov 24 00:46:30 2010
@@ -293,6 +293,7 @@
                     parent_nsp = NamespacePath.from_node_path(path).get_parent_path()
                     return parent_nsp.as_dotted_str() + '.' + attr.attr
 # FIXME: only makes sense to traverse within this class and within subclasses
+# FIXME: fake the MRO and pick an appropriate class
 
         # Don't try to inline where the function is a non-trivial
         # expression e.g. "f()()", or for other awkward cases
@@ -315,18 +316,17 @@
         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('Not inlining attribute %s' % ast.dump(call.func))
-            return call
-
         if not isinstance(call.func, (ast.Name, ast.Attribute)):
             # Don't try to inline where the function is a non-trivial
             # expression e.g. "f()()"
             print('foo!')
             return call
 
-        # FIXME
+        if isinstance(call.func, ast.Attribute):
+            # Emergency cutoff for method inlining:
+            if 0:
+                self.log('Not inlining attribute %s' % ast.dump(call.func))
+                return call
 
         self.log('Considering call to: %s' % ast.dump(call.func))
         self.log('NodePath: %r' % path)
@@ -347,7 +347,7 @@
         funcdef = self.def_dict[dotted_name]
 
         self.log(ast.dump(funcdef))
-        varprefix = '__inline_%s%x__' % (dotted_name, id(call))
+        varprefix = '__internal__inline_%s%x__' % (dotted_name.replace('.', '_'), id(call))
         self.log('varprefix: %s' % varprefix)
 
         # Generate a body of specialized statements that can replace the call:
@@ -399,6 +399,7 @@
                                                call)
 
         return ast.copy_location(ast.Specialize(name=call.func,
+                                                expected_value='__internal__.saved.' + dotted_name,
                                                 generalized=call,
                                                 specialized_body=specialized,
                                                 specialized_result=specialized_result),
@@ -792,7 +793,7 @@
 
 # For now restrict ourselves to just a few places:
 def is_test_code(t, filename):
-    if filename.endswith('test_optimize.py'):
+    if filename == 'optimizable.py':
         return True
     for n in ast.walk(t):
         if isinstance(n, ast.FunctionDef):
@@ -800,6 +801,14 @@
                 return True
     return False
 
+def dump_dot(t, filename):
+    return False
+    for n in ast.walk(t):
+        if isinstance(n, ast.FunctionDef):
+            if n.name == 'simple_method':
+                return True
+    return False
+
 #class OptimizationError(Exception):
 #    def __init__(self
 
@@ -815,9 +824,10 @@
             # pprint(t)
             # log(ast.dump(t))
 
-            #dot_to_png(to_dot(t), 'before.png')
-
             if isinstance(t, ast.Module):
+                if dump_dot(t, filename):
+                    dot_to_png(to_dot(t), 'before.png')
+
                 t = _inline_function_calls(t)
                 #cfg = CFG.from_ast(t)
                 #print(cfg.to_dot())
@@ -827,11 +837,14 @@
 
                 t = _remove_redundant_locals(t)
 
-            #dot_to_png(to_dot(t), 'after.png')
+                if dump_dot(t, filename):
+                    dot_to_png(to_dot(t), 'after.png')
 
         except:
             print('Exception during optimization of %r' % filename)
             # dot_to_png(dot_before, 'before.png')
             raise
     #print('finished optimizing')
+    #if filename == 'optimizable.py':
+    #    print(ast.dump(t))
     return t

Modified: python/branches/dmalcolm-ast-optimization-branch/Lib/test/test_optimize.py
==============================================================================
--- python/branches/dmalcolm-ast-optimization-branch/Lib/test/test_optimize.py	(original)
+++ python/branches/dmalcolm-ast-optimization-branch/Lib/test/test_optimize.py	Wed Nov 24 00:46:30 2010
@@ -88,7 +88,7 @@
 
     def compile_to_code(self, src, fnname):
         # Compile the given source code, returning the code object for the given function name
-        co = compile(src, 'test_optimize.py', 'exec')
+        co = compile(src, 'optimizable.py', 'exec')
         return get_code_for_fn(co, fnname)
 
     def test_simple_inlining(self):
@@ -233,7 +233,7 @@
 def h(x, y):
     return g(x, y, 0)
 '''
-        co = compile(src, 'test_optimize.py', 'exec')
+        co = compile(src, 'optimizable.py', 'exec')
         g = get_code_for_fn(co, 'g')
         h = get_code_for_fn(co, 'h')
         co_asm = disassemble(co)
@@ -407,9 +407,7 @@
          return self.bar + a + self.baz
 
     def user_of_method(self):
-         print(self.simple_method(1))
-         print(self.simple_method(2))
-         print(self.simple_method(3))
+         return self.simple_method(10)
 '''
 
         # "Foo.simple_method" should be inlinable
@@ -426,6 +424,11 @@
         fn = self.compile_to_code(src, 'Foo.user_of_method')
         asm = disassemble(fn)
         #print(asm)
+        self.assertHasLineWith(asm,
+                               ('LOAD_GLOBAL', '(__internal__.saved.Foo.simple_method)'))
+        self.assertIn('JUMP_IF_SPECIALIZABLE', asm)
+
+
 
 
     def test_namespaces(self):

Modified: python/branches/dmalcolm-ast-optimization-branch/Parser/Python.asdl
==============================================================================
--- python/branches/dmalcolm-ast-optimization-branch/Parser/Python.asdl	(original)
+++ python/branches/dmalcolm-ast-optimization-branch/Parser/Python.asdl	Wed Nov 24 00:46:30 2010
@@ -74,6 +74,7 @@
 
 	     -- Generated by the optimizer:
 	     | Specialize(expr name,
+                          identifier expected_value,
                           stmt* specialized_body,
                           expr specialized_result,
                           expr generalized) -- must be a Call

Modified: python/branches/dmalcolm-ast-optimization-branch/Python/Python-ast.c
==============================================================================
--- python/branches/dmalcolm-ast-optimization-branch/Python/Python-ast.c	(original)
+++ python/branches/dmalcolm-ast-optimization-branch/Python/Python-ast.c	Wed Nov 24 00:46:30 2010
@@ -244,6 +244,7 @@
 static PyTypeObject *Specialize_type;
 static char *Specialize_fields[]={
         "name",
+        "expected_value",
         "specialized_body",
         "specialized_result",
         "generalized",
@@ -758,7 +759,7 @@
         Ellipsis_type = make_type("Ellipsis", expr_type, NULL, 0);
         if (!Ellipsis_type) return 0;
         Specialize_type = make_type("Specialize", expr_type, Specialize_fields,
-                                    4);
+                                    5);
         if (!Specialize_type) return 0;
         Attribute_type = make_type("Attribute", expr_type, Attribute_fields, 3);
         if (!Attribute_type) return 0;
@@ -1831,9 +1832,9 @@
 }
 
 expr_ty
-Specialize(expr_ty name, asdl_seq * specialized_body, expr_ty
-           specialized_result, expr_ty generalized, int lineno, int col_offset,
-           PyArena *arena)
+Specialize(expr_ty name, identifier expected_value, asdl_seq *
+           specialized_body, expr_ty specialized_result, expr_ty generalized,
+           int lineno, int col_offset, PyArena *arena)
 {
         expr_ty p;
         if (!name) {
@@ -1841,6 +1842,11 @@
                                 "field name is required for Specialize");
                 return NULL;
         }
+        if (!expected_value) {
+                PyErr_SetString(PyExc_ValueError,
+                                "field expected_value is required for Specialize");
+                return NULL;
+        }
         if (!specialized_result) {
                 PyErr_SetString(PyExc_ValueError,
                                 "field specialized_result is required for Specialize");
@@ -1856,6 +1862,7 @@
                 return NULL;
         p->kind = Specialize_kind;
         p->v.Specialize.name = name;
+        p->v.Specialize.expected_value = expected_value;
         p->v.Specialize.specialized_body = specialized_body;
         p->v.Specialize.specialized_result = specialized_result;
         p->v.Specialize.generalized = generalized;
@@ -2975,6 +2982,12 @@
                 if (PyObject_SetAttrString(result, "name", value) == -1)
                         goto failed;
                 Py_DECREF(value);
+                value = ast2obj_identifier(o->v.Specialize.expected_value, st);
+                if (!value) goto failed;
+                if (PyObject_SetAttrString(result, "expected_value", value) ==
+                    -1)
+                        goto failed;
+                Py_DECREF(value);
                 value = ast2obj_list(o->v.Specialize.specialized_body,
                                      ast2obj_stmt, st);
                 if (!value) goto failed;
@@ -5760,6 +5773,7 @@
         }
         if (isinstance) {
                 expr_ty name;
+                identifier expected_value;
                 asdl_seq* specialized_body;
                 expr_ty specialized_result;
                 expr_ty generalized;
@@ -5776,6 +5790,18 @@
                         PyErr_SetString(PyExc_TypeError, "required field \"name\" missing from Specialize");
                         return 1;
                 }
+                if (PyObject_HasAttrString(obj, "expected_value")) {
+                        int res;
+                        tmp = PyObject_GetAttrString(obj, "expected_value");
+                        if (tmp == NULL) goto failed;
+                        res = obj2ast_identifier(tmp, &expected_value, arena);
+                        if (res != 0) goto failed;
+                        Py_XDECREF(tmp);
+                        tmp = NULL;
+                } else {
+                        PyErr_SetString(PyExc_TypeError, "required field \"expected_value\" missing from Specialize");
+                        return 1;
+                }
                 if (PyObject_HasAttrString(obj, "specialized_body")) {
                         int res;
                         Py_ssize_t len;
@@ -5825,8 +5851,9 @@
                         PyErr_SetString(PyExc_TypeError, "required field \"generalized\" missing from Specialize");
                         return 1;
                 }
-                *out = Specialize(name, specialized_body, specialized_result,
-                                  generalized, lineno, col_offset, arena);
+                *out = Specialize(name, expected_value, specialized_body,
+                                  specialized_result, generalized, lineno,
+                                  col_offset, arena);
                 if (*out == NULL) goto failed;
                 return 0;
         }

Modified: python/branches/dmalcolm-ast-optimization-branch/Python/compile.c
==============================================================================
--- python/branches/dmalcolm-ast-optimization-branch/Python/compile.c	(original)
+++ python/branches/dmalcolm-ast-optimization-branch/Python/compile.c	Wed Nov 24 00:46:30 2010
@@ -398,7 +398,7 @@
                                               NULL);
     if (!py_ast_out)
         goto finally;
-     
+
     /* 4. Convert the python objects back to an AST: */
     new_mod = PyAST_obj2mod(py_ast_out, c->c_arena, mode);
     if (!new_mod)
@@ -3252,7 +3252,7 @@
 {
     basicblock *specialized, *generalized, *end;
     expr_ty call;
-    identifier id;
+    identifier expected_value;
     PyObject *saved_name = NULL;
 
     assert(e->kind == Specialize_kind);
@@ -3267,12 +3267,9 @@
     VISIT(c, expr, e->v.Specialize.name);
 
     /* Push the expected version of the name, it will be the new TOS */
-    /* For now, look for a global named "__saved__" + funcname */
-    if (e->v.Specialize.name->kind != Name_kind)
-        return 0;
-    id = e->v.Specialize.name->v.Name.id;
-    assert(id);
-    saved_name = PyUnicode_FromFormat("__internal__.saved.%U", id);
+    expected_value = e->v.Specialize.expected_value;
+    assert(expected_value);
+    saved_name = PyUnicode_FromFormat("%U", expected_value); // FIXME
     if (!saved_name)
         return 0;
     ADDOP_O(c, LOAD_GLOBAL, saved_name, names); /* takes ownership of the reference */


More information about the Python-checkins mailing list