optimise tp_new() for module-internally defined types by calling the tp_new slot...
authorStefan Behnel <stefan_ml@behnel.de>
Sat, 23 Feb 2013 17:20:53 +0000 (18:20 +0100)
committerStefan Behnel <stefan_ml@behnel.de>
Sat, 23 Feb 2013 17:20:53 +0000 (18:20 +0100)
Cython/Compiler/ModuleNode.py
Cython/Compiler/Optimize.py
Cython/Utility/ObjectHandling.c

index f431a70..1451aa6 100644 (file)
@@ -1033,11 +1033,15 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
         else:
             unused_marker = 'CYTHON_UNUSED '
 
-        need_self_cast = type.vtabslot_cname or have_entries or cpp_class_attrs
+        decls = code.globalstate['decls']
+        decls.putln("static PyObject *%s(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/" %
+                    slot_func)
         code.putln("")
         code.putln(
             "static PyObject *%s(PyTypeObject *t, %sPyObject *a, %sPyObject *k) {"
-                % (scope.mangle_internal("tp_new"), unused_marker, unused_marker))
+                % (slot_func, unused_marker, unused_marker))
+
+        need_self_cast = type.vtabslot_cname or have_entries or cpp_class_attrs
         if need_self_cast:
             code.putln(
                 "%s;"
index 2d03eef..a3e686d 100644 (file)
@@ -2142,17 +2142,32 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
 
     Pyx_tp_new_func_type = PyrexTypes.CFuncType(
         PyrexTypes.py_object_type, [
-            PyrexTypes.CFuncTypeArg("type",   Builtin.type_type, None),
+            PyrexTypes.CFuncTypeArg("type",   PyrexTypes.py_object_type, None),
             PyrexTypes.CFuncTypeArg("args",   Builtin.tuple_type, None),
             ])
 
     Pyx_tp_new_kwargs_func_type = PyrexTypes.CFuncType(
         PyrexTypes.py_object_type, [
-            PyrexTypes.CFuncTypeArg("type",   Builtin.type_type, None),
+            PyrexTypes.CFuncTypeArg("type",   PyrexTypes.py_object_type, None),
             PyrexTypes.CFuncTypeArg("args",   Builtin.tuple_type, None),
             PyrexTypes.CFuncTypeArg("kwargs", Builtin.dict_type, None),
         ])
 
+    Pyx_call_tp_new_func_type = PyrexTypes.CFuncType(
+        PyrexTypes.py_object_type, [
+            PyrexTypes.CFuncTypeArg("tpnew_func", PyrexTypes.c_void_ptr_type, None),
+            PyrexTypes.CFuncTypeArg("type",   PyrexTypes.py_object_type, None),
+            PyrexTypes.CFuncTypeArg("args",   Builtin.tuple_type, None),
+            ])
+
+    Pyx_call_tp_new_kwargs_func_type = PyrexTypes.CFuncType(
+        PyrexTypes.py_object_type, [
+            PyrexTypes.CFuncTypeArg("tpnew_func", PyrexTypes.c_void_ptr_type, None),
+            PyrexTypes.CFuncTypeArg("type",   PyrexTypes.py_object_type, None),
+            PyrexTypes.CFuncTypeArg("args",   Builtin.tuple_type, None),
+            PyrexTypes.CFuncTypeArg("kwargs", Builtin.dict_type, None),
+            ])
+
     def _handle_any_slot__new__(self, node, args, is_unbound_method, kwargs=None):
         """Replace 'exttype.__new__(exttype, ...)' by a call to exttype->tp_new()
         """
@@ -2175,20 +2190,41 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
             # different types - may or may not lead to an error at runtime
             return node
 
-        # FIXME: we could potentially look up the actual tp_new C
-        # method of the extension type and call that instead of the
-        # generic slot. That would also allow us to pass parameters
-        # efficiently.
+        args_tuple = ExprNodes.TupleNode(node.pos, args=args[1:])
+        args_tuple = args_tuple.analyse_types(
+            self.current_env(), skip_children=True)
 
-        if not type_arg.type_entry:
+        if type_arg.type_entry:
+            ext_type = type_arg.type_entry.type
+            if ext_type.is_extension_type and not ext_type.is_external:
+                utility_code = UtilityCode.load_cached(
+                    'call_tp_new', 'ObjectHandling.c')
+                slot_func_cname = ext_type.scope.mangle_internal("tp_new")
+                slot_func = ExprNodes.RawCNameExprNode(
+                    node.pos, cname=slot_func_cname,
+                    type=PyrexTypes.c_void_ptr_type)
+
+                if kwargs:
+                    return ExprNodes.PythonCapiCallNode(
+                        node.pos, "__Pyx_call_tp_new_kwargs",
+                        self.Pyx_call_tp_new_kwargs_func_type,
+                        args=[slot_func, type_arg, args_tuple, kwargs],
+                        utility_code=utility_code,
+                        is_temp=node.is_temp
+                    )
+                else:
+                    return ExprNodes.PythonCapiCallNode(
+                        node.pos, "__Pyx_call_tp_new",
+                        self.Pyx_call_tp_new_func_type,
+                        args=[slot_func, type_arg, args_tuple],
+                        utility_code=utility_code,
+                        is_temp=node.is_temp
+                    )
+        else:
             # arbitrary variable, needs a None check for safety
             type_arg = type_arg.as_none_safe_node(
                 "object.__new__(X): X is not a type object (NoneType)")
 
-        args_tuple = ExprNodes.TupleNode(node.pos, args=args[1:])
-        args_tuple = args_tuple.analyse_types(
-            self.current_env(), skip_children=True)
-
         utility_code = UtilityCode.load_cached('tp_new', 'ObjectHandling.c')
         if kwargs:
             return ExprNodes.PythonCapiCallNode(
index 15e7efd..8cb0375 100644 (file)
@@ -659,3 +659,9 @@ static CYTHON_INLINE PyObject* __Pyx_tp_new_kwargs(PyObject* type_obj, PyObject*
     return (PyObject*) (((PyTypeObject*)(type_obj))->tp_new(
         (PyTypeObject*)(type_obj), args, kwargs));
 }
+
+
+/////////////// call_tp_new.proto ///////////////
+
+#define __Pyx_call_tp_new(tp_new_func, type_obj, args) __Pyx_call_tp_new_kwargs(tp_new_func, type_obj, args, NULL)
+#define __Pyx_call_tp_new_kwargs(tp_new_func, type_obj, args, kwargs) tp_new_func((PyTypeObject*)type_obj, args, kwargs)