cython-devel
changeset 2194:b9d8cecc8975
general optimisation support for calls to builtin types and their methods
currently providing optimisations for
- getattr(o,a)
- getattr(o,a,d)
- X.append(o)
- L.append(o)
- list.append(L,x)
currently providing optimisations for
- getattr(o,a)
- getattr(o,a,d)
- X.append(o)
- L.append(o)
- list.append(L,x)
| author | Stefan Behnel <scoder@users.berlios.de> |
|---|---|
| date | Sun Mar 29 13:27:55 2009 +0200 (2 years ago) |
| parents | ceb0a35ea490 |
| children | c3ab9cc6856f |
| files | Cython/Compiler/Builtin.py Cython/Compiler/ExprNodes.py Cython/Compiler/Main.py Cython/Compiler/Optimize.py Cython/Compiler/PyrexTypes.py tests/run/getattr3call.pyx |
line diff
1.1 --- a/Cython/Compiler/Builtin.py Sun Mar 29 12:24:01 2009 +0200
1.2 +++ b/Cython/Compiler/Builtin.py Sun Mar 29 13:27:55 2009 +0200
1.3 @@ -21,7 +21,7 @@
1.4 #('eval', "", "", ""),
1.5 #('execfile', "", "", ""),
1.6 #('filter', "", "", ""),
1.7 - ('getattr', "OO", "O", "PyObject_GetAttr"),
1.8 + #('getattr', "OO", "O", "PyObject_GetAttr"), # optimised later on
1.9 ('getattr3', "OOO", "O", "__Pyx_GetAttr3", "getattr"),
1.10 ('hasattr', "OO", "b", "PyObject_HasAttr"),
1.11 ('hash', "O", "l", "PyObject_Hash"),
2.1 --- a/Cython/Compiler/ExprNodes.py Sun Mar 29 12:24:01 2009 +0200
2.2 +++ b/Cython/Compiler/ExprNodes.py Sun Mar 29 13:27:55 2009 +0200
2.3 @@ -2315,15 +2315,6 @@
2.4 function = self.function
2.5 function.is_called = 1
2.6 self.function.analyse_types(env)
2.7 - if function.is_attribute and function.is_py_attr and \
2.8 - function.attribute == "append" and len(self.args) == 1:
2.9 - # L.append(x) is almost always applied to a list
2.10 - self.py_func = self.function
2.11 - self.function = NameNode(pos=self.function.pos, name="__Pyx_PyObject_Append")
2.12 - self.function.analyse_types(env)
2.13 - self.self = self.py_func.obj
2.14 - function.obj = CloneNode(self.self)
2.15 - env.use_utility_code(append_utility_code)
2.16 if function.is_attribute and function.entry and function.entry.is_cmethod:
2.17 # Take ownership of the object from which the attribute
2.18 # was obtained, because we need to pass it as 'self'.
2.19 @@ -2516,7 +2507,37 @@
2.20 code.putln("%s%s; %s" % (lhs, rhs, goto_error))
2.21 if self.type.is_pyobject and self.result():
2.22 code.put_gotref(self.py_result())
2.23 -
2.24 +
2.25 +
2.26 +class PythonCapiFunctionNode(ExprNode):
2.27 + subexprs = []
2.28 + def __init__(self, pos, name, func_type, utility_code = None):
2.29 + self.pos = pos
2.30 + self.name = name
2.31 + self.type = func_type
2.32 + self.utility_code = utility_code
2.33 +
2.34 + def generate_result_code(self, code):
2.35 + if self.utility_code:
2.36 + code.globalstate.use_utility_code(self.utility_code)
2.37 +
2.38 + def calculate_result_code(self):
2.39 + return self.name
2.40 +
2.41 +class PythonCapiCallNode(SimpleCallNode):
2.42 + # Python C-API Function call (only created in transforms)
2.43 +
2.44 + def __init__(self, pos, function_name, func_type,
2.45 + utility_code = None, **kwargs):
2.46 + self.type = func_type.return_type
2.47 + self.result_ctype = self.type
2.48 + self.function = PythonCapiFunctionNode(
2.49 + pos, function_name, func_type,
2.50 + utility_code = utility_code)
2.51 + # call this last so that we can override the constructed
2.52 + # attributes above with explicit keyword arguments if required
2.53 + SimpleCallNode.__init__(self, pos, **kwargs)
2.54 +
2.55
2.56 class GeneralCallNode(CallNode):
2.57 # General Python function call, including keyword,
2.58 @@ -5419,29 +5440,6 @@
2.59
2.60 #------------------------------------------------------------------------------------
2.61
2.62 -append_utility_code = UtilityCode(
2.63 -proto = """
2.64 -static INLINE PyObject* __Pyx_PyObject_Append(PyObject* L, PyObject* x) {
2.65 - if (likely(PyList_CheckExact(L))) {
2.66 - if (PyList_Append(L, x) < 0) return NULL;
2.67 - Py_INCREF(Py_None);
2.68 - return Py_None; /* this is just to have an accurate signature */
2.69 - }
2.70 - else {
2.71 - PyObject *r, *m;
2.72 - m = __Pyx_GetAttrString(L, "append");
2.73 - if (!m) return NULL;
2.74 - r = PyObject_CallFunctionObjArgs(m, x, NULL);
2.75 - Py_DECREF(m);
2.76 - return r;
2.77 - }
2.78 -}
2.79 -""",
2.80 -impl = ""
2.81 -)
2.82 -
2.83 -#------------------------------------------------------------------------------------
2.84 -
2.85 # If the is_unsigned flag is set, we need to do some extra work to make
2.86 # sure the index doesn't become negative.
2.87
3.1 --- a/Cython/Compiler/Main.py Sun Mar 29 12:24:01 2009 +0200
3.2 +++ b/Cython/Compiler/Main.py Sun Mar 29 13:27:55 2009 +0200
3.3 @@ -84,7 +84,7 @@
3.4 from ParseTreeTransforms import GilCheck
3.5 from AutoDocTransforms import EmbedSignature
3.6 from Optimize import FlattenInListTransform, SwitchTransform, IterationTransform
3.7 - from Optimize import FlattenBuiltinTypeCreation, ConstantFolding, FinalOptimizePhase
3.8 + from Optimize import OptimiseBuiltinCalls, ConstantFolding, FinalOptimizePhase
3.9 from Buffer import IntroduceBufferAuxiliaryVars
3.10 from ModuleNode import check_c_declarations
3.11
3.12 @@ -125,7 +125,7 @@
3.13 IntroduceBufferAuxiliaryVars(self),
3.14 _check_c_declarations,
3.15 AnalyseExpressionsTransform(self),
3.16 - FlattenBuiltinTypeCreation(),
3.17 + OptimiseBuiltinCalls(),
3.18 # ComprehensionTransform(),
3.19 IterationTransform(),
3.20 SwitchTransform(),
4.1 --- a/Cython/Compiler/Optimize.py Sun Mar 29 12:24:01 2009 +0200
4.2 +++ b/Cython/Compiler/Optimize.py Sun Mar 29 13:27:55 2009 +0200
4.3 @@ -7,8 +7,10 @@
4.4 import TypeSlots
4.5 import Symtab
4.6 import Options
4.7 +
4.8 +from Cython.Utils import UtilityCode
4.9 from StringEncoding import EncodedString
4.10 -
4.11 +from Errors import error
4.12 from ParseTreeTransforms import SkipDeclarations
4.13
4.14 #def unwrap_node(node):
4.15 @@ -414,18 +416,12 @@
4.16 visit_Node = Visitor.VisitorTransform.recurse_to_children
4.17
4.18
4.19 -class FlattenBuiltinTypeCreation(Visitor.VisitorTransform):
4.20 - """Optimise some common instantiation patterns for builtin types.
4.21 +class OptimiseBuiltinCalls(Visitor.VisitorTransform):
4.22 + """Optimise some common methods calls and instantiation patterns
4.23 + for builtin types.
4.24 """
4.25 - PyList_AsTuple_func_type = PyrexTypes.CFuncType(
4.26 - PyrexTypes.py_object_type, [
4.27 - PyrexTypes.CFuncTypeArg("list", Builtin.list_type, None)
4.28 - ])
4.29 -
4.30 - PyList_AsTuple_name = EncodedString("PyList_AsTuple")
4.31 -
4.32 - PyList_AsTuple_entry = Symtab.Entry(
4.33 - PyList_AsTuple_name, PyList_AsTuple_name, PyList_AsTuple_func_type)
4.34 + # only intercept on call nodes
4.35 + visit_Node = Visitor.VisitorTransform.recurse_to_children
4.36
4.37 def visit_GeneralCallNode(self, node):
4.38 self.visitchildren(node)
4.39 @@ -441,16 +437,38 @@
4.40 node = handler(node, node.arg_tuple)
4.41 return node
4.42
4.43 + def visit_PyTypeTestNode(self, node):
4.44 + """Flatten redundant type checks after tree changes.
4.45 + """
4.46 + old_arg = node.arg
4.47 + self.visitchildren(node)
4.48 + if old_arg is node.arg or node.arg.type != node.type:
4.49 + return node
4.50 + return node.arg
4.51 +
4.52 def _find_handler(self, call_type, function):
4.53 - if not function.type.is_builtin_type:
4.54 + if not function.type.is_pyobject:
4.55 return None
4.56 - if not isinstance(function, ExprNodes.NameNode):
4.57 + if function.is_name:
4.58 + if not function.type.is_builtin_type and '_' in function.name:
4.59 + # not interesting anyway, so let's play safe here
4.60 + return None
4.61 + match_name = function.name
4.62 + elif isinstance(function, ExprNodes.AttributeNode):
4.63 + if not function.obj.type.is_builtin_type:
4.64 + type_name = "object" # safety measure
4.65 + else:
4.66 + type_name = function.obj.type.name
4.67 + match_name = "%s_%s" % (type_name, function.attribute)
4.68 + else:
4.69 return None
4.70 - handler = getattr(self, '_handle_%s_%s' % (call_type, function.name), None)
4.71 + handler = getattr(self, '_handle_%s_%s' % (call_type, match_name), None)
4.72 if handler is None:
4.73 - handler = getattr(self, '_handle_any_%s' % function.name, None)
4.74 + handler = getattr(self, '_handle_any_%s' % match_name, None)
4.75 return handler
4.76
4.77 + ### builtin types
4.78 +
4.79 def _handle_general_dict(self, node, pos_args, kwargs):
4.80 """Replace dict(a=b,c=d,...) by the underlying keyword dict
4.81 construction which is done anyway.
4.82 @@ -491,6 +509,11 @@
4.83 else:
4.84 return node
4.85
4.86 + PyList_AsTuple_func_type = PyrexTypes.CFuncType(
4.87 + Builtin.tuple_type, [
4.88 + PyrexTypes.CFuncTypeArg("list", Builtin.list_type, None)
4.89 + ])
4.90 +
4.91 def _handle_simple_tuple(self, node, pos_args):
4.92 """Replace tuple([...]) by a call to PyList_AsTuple.
4.93 """
4.94 @@ -506,27 +529,139 @@
4.95 # everything else may be None => take the safe path
4.96 return node
4.97
4.98 - node.args = pos_args.args
4.99 - node.arg_tuple = None
4.100 - node.type = Builtin.tuple_type
4.101 - node.result_ctype = Builtin.tuple_type
4.102 - node.function = ExprNodes.NameNode(
4.103 - pos = node.pos,
4.104 - name = self.PyList_AsTuple_name,
4.105 - type = self.PyList_AsTuple_func_type,
4.106 - entry = self.PyList_AsTuple_entry)
4.107 + return ExprNodes.PythonCapiCallNode(
4.108 + node.pos, "PyList_AsTuple", self.PyList_AsTuple_func_type,
4.109 + args = pos_args.args,
4.110 + is_temp = node.is_temp
4.111 + )
4.112 +
4.113 + ### builtin functions
4.114 +
4.115 + PyObject_GetAttr2_func_type = PyrexTypes.CFuncType(
4.116 + PyrexTypes.py_object_type, [
4.117 + PyrexTypes.CFuncTypeArg("object", PyrexTypes.py_object_type, None),
4.118 + PyrexTypes.CFuncTypeArg("attr_name", PyrexTypes.py_object_type, None),
4.119 + ])
4.120 +
4.121 + PyObject_GetAttr3_func_type = PyrexTypes.CFuncType(
4.122 + PyrexTypes.py_object_type, [
4.123 + PyrexTypes.CFuncTypeArg("object", PyrexTypes.py_object_type, None),
4.124 + PyrexTypes.CFuncTypeArg("attr_name", PyrexTypes.py_object_type, None),
4.125 + PyrexTypes.CFuncTypeArg("default", PyrexTypes.py_object_type, None),
4.126 + ])
4.127 +
4.128 + def _handle_simple_getattr(self, node, pos_args):
4.129 + # not really a builtin *type*, but worth optimising anyway
4.130 + if not isinstance(pos_args, ExprNodes.TupleNode):
4.131 + return node
4.132 + args = pos_args.args
4.133 + if len(args) == 2:
4.134 + node = ExprNodes.PythonCapiCallNode(
4.135 + node.pos, "PyObject_GetAttr", self.PyObject_GetAttr2_func_type,
4.136 + args = args,
4.137 + is_temp = node.is_temp
4.138 + )
4.139 + elif len(args) == 3:
4.140 + node = ExprNodes.PythonCapiCallNode(
4.141 + node.pos, "__Pyx_GetAttr3", self.PyObject_GetAttr3_func_type,
4.142 + utility_code = Builtin.getattr3_utility_code,
4.143 + args = args,
4.144 + is_temp = node.is_temp
4.145 + )
4.146 + else:
4.147 + error(node.pos, "getattr() called with wrong number of args, "
4.148 + "expected 2 or 3, found %d" %
4.149 + len(pos_args.args))
4.150 return node
4.151
4.152 - def visit_PyTypeTestNode(self, node):
4.153 - """Flatten redundant type checks after tree changes.
4.154 - """
4.155 - old_arg = node.arg
4.156 - self.visitchildren(node)
4.157 - if old_arg is node.arg or node.arg.type != node.type:
4.158 + ### methods of builtin types
4.159 +
4.160 + PyObject_Append_func_type = PyrexTypes.CFuncType(
4.161 + PyrexTypes.py_object_type, [
4.162 + PyrexTypes.CFuncTypeArg("list", PyrexTypes.py_object_type, None),
4.163 + PyrexTypes.CFuncTypeArg("item", PyrexTypes.py_object_type, None),
4.164 + ])
4.165 +
4.166 + def _handle_simple_object_append(self, node, pos_args):
4.167 + # X.append() is almost always referring to a list
4.168 + if not isinstance(pos_args, ExprNodes.TupleNode):
4.169 return node
4.170 - return node.arg
4.171 + if len(pos_args.args) != 1:
4.172 + return node
4.173
4.174 - visit_Node = Visitor.VisitorTransform.recurse_to_children
4.175 + args = [node.function.obj] + pos_args.args
4.176 + return ExprNodes.PythonCapiCallNode(
4.177 + node.pos, "__Pyx_PyObject_Append", self.PyObject_Append_func_type,
4.178 + args = args,
4.179 + is_temp = node.is_temp,
4.180 + utility_code = append_utility_code # FIXME: move to Builtin.py
4.181 + )
4.182 +
4.183 + PyList_Append_func_type = PyrexTypes.CFuncType(
4.184 + PyrexTypes.c_int_type, [
4.185 + PyrexTypes.CFuncTypeArg("list", PyrexTypes.py_object_type, None),
4.186 + PyrexTypes.CFuncTypeArg("item", PyrexTypes.py_object_type, None),
4.187 + ],
4.188 + exception_value = "-1")
4.189 +
4.190 + def _handle_simple_list_append(self, node, pos_args):
4.191 + if not isinstance(pos_args, ExprNodes.TupleNode):
4.192 + return node
4.193 + if len(pos_args.args) != 1:
4.194 + error(node.pos, "list.append(x) called with wrong number of args, found %d" %
4.195 + len(pos_args.args))
4.196 + return node
4.197 +
4.198 + obj = node.function.obj
4.199 + # FIXME: obj may need a None check (ticket #166)
4.200 + args = [obj] + pos_args.args
4.201 + return ExprNodes.PythonCapiCallNode(
4.202 + node.pos, "PyList_Append", self.PyList_Append_func_type,
4.203 + args = args,
4.204 + is_temp = node.is_temp
4.205 + )
4.206 +
4.207 + def _handle_simple_type_append(self, node, pos_args):
4.208 + # unbound method call to list.append(L, x) ?
4.209 + if node.function.obj.name != 'list':
4.210 + return node
4.211 + if not isinstance(pos_args, ExprNodes.TupleNode):
4.212 + return node
4.213 +
4.214 + args = pos_args.args
4.215 + if len(args) != 2:
4.216 + error(node.pos, "list.append(x) called with wrong number of args, found %d" %
4.217 + len(pos_args.args))
4.218 + return node
4.219 +
4.220 + # FIXME: this may need a type check on the first operand
4.221 + return ExprNodes.PythonCapiCallNode(
4.222 + node.pos, "PyList_Append", self.PyList_Append_func_type,
4.223 + args = args,
4.224 + is_temp = node.is_temp
4.225 + )
4.226 +
4.227 +
4.228 +append_utility_code = UtilityCode(
4.229 +proto = """
4.230 +static INLINE PyObject* __Pyx_PyObject_Append(PyObject* L, PyObject* x) {
4.231 + if (likely(PyList_CheckExact(L))) {
4.232 + if (PyList_Append(L, x) < 0) return NULL;
4.233 + Py_INCREF(Py_None);
4.234 + return Py_None; /* this is just to have an accurate signature */
4.235 + }
4.236 + else {
4.237 + PyObject *r, *m;
4.238 + m = __Pyx_GetAttrString(L, "append");
4.239 + if (!m) return NULL;
4.240 + r = PyObject_CallFunctionObjArgs(m, x, NULL);
4.241 + Py_DECREF(m);
4.242 + return r;
4.243 + }
4.244 +}
4.245 +""",
4.246 +impl = ""
4.247 +)
4.248
4.249
4.250 class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
5.1 --- a/Cython/Compiler/PyrexTypes.py Sun Mar 29 12:24:01 2009 +0200
5.2 +++ b/Cython/Compiler/PyrexTypes.py Sun Mar 29 13:27:55 2009 +0200
5.3 @@ -234,7 +234,8 @@
5.4 # Base class for all Python object types (reference-counted).
5.5 #
5.6 # buffer_defaults dict or None Default options for bu
5.7 -
5.8 +
5.9 + name = "object"
5.10 is_pyobject = 1
5.11 default_value = "0"
5.12 pymemberdef_typecode = "T_OBJECT"
6.1 --- a/tests/run/getattr3call.pyx Sun Mar 29 12:24:01 2009 +0200
6.2 +++ b/tests/run/getattr3call.pyx Sun Mar 29 13:27:55 2009 +0200
6.3 @@ -12,9 +12,7 @@
6.4 1
6.5 >>> g(t, 'b', 2)
6.6 2
6.7 -"""
6.8
6.9 -BROKEN = """
6.10 >>> h(t, 'a', 2)
6.11 1
6.12 >>> h(t, 'b', 2)
6.13 @@ -27,5 +25,5 @@
6.14 def g(a, b, c):
6.15 return getattr3(a, b, c)
6.16
6.17 -#def h(a, b, c):
6.18 -# return getattr(a, b, c)
6.19 +def h(a, b, c):
6.20 + return getattr(a, b, c)
