Commit 8e1e2932 authored by Stefan Behnel's avatar Stefan Behnel

optimise some more cases where int() is called on C values

parent 5dc357fe
...@@ -2040,28 +2040,46 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ...@@ -2040,28 +2040,46 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
return node return node
return coerce_node return coerce_node
float_float_func_types = dict(
(float_type, PyrexTypes.CFuncType(
float_type, [
PyrexTypes.CFuncTypeArg("arg", float_type, None)
]))
for float_type in (PyrexTypes.c_float_type, PyrexTypes.c_double_type, PyrexTypes.c_longdouble_type))
def _optimise_numeric_cast_call(self, node, arg): def _optimise_numeric_cast_call(self, node, arg):
function = arg.function function = arg.function
if not isinstance(function, ExprNodes.NameNode) \ args = None
or not function.type.is_builtin_type \ if isinstance(arg, ExprNodes.PythonCapiCallNode):
or not isinstance(arg.arg_tuple, ExprNodes.TupleNode): args = arg.args
return node elif isinstance(function, ExprNodes.NameNode):
args = arg.arg_tuple.args if function.type.is_builtin_type and isinstance(arg.arg_tuple, ExprNodes.TupleNode):
if len(args) != 1: args = arg.arg_tuple.args
if args is None or len(args) != 1:
return node return node
func_arg = args[0] func_arg = args[0]
if isinstance(func_arg, ExprNodes.CoerceToPyTypeNode): if isinstance(func_arg, ExprNodes.CoerceToPyTypeNode):
func_arg = func_arg.arg func_arg = func_arg.arg
elif func_arg.type.is_pyobject: elif func_arg.type.is_pyobject:
# play safe: Python conversion might work on all sorts of things # play it safe: Python conversion might work on all sorts of things
return node return node
if function.name == 'int': if function.name == 'int':
if func_arg.type.is_int or node.type.is_int: if func_arg.type.is_int or node.type.is_int:
if func_arg.type == node.type: if func_arg.type == node.type:
return func_arg return func_arg
elif node.type.assignable_from(func_arg.type) or func_arg.type.is_float: elif node.type.assignable_from(func_arg.type) or func_arg.type.is_float:
return ExprNodes.TypecastNode( return ExprNodes.TypecastNode(node.pos, operand=func_arg, type=node.type)
node.pos, operand=func_arg, type=node.type) elif func_arg.type.is_float and node.type.is_numeric:
return ExprNodes.PythonCapiCallNode(
node.pos, 'trunc' + func_arg.type.math_h_modifier,
func_type=self.float_float_func_types[func_arg.type],
args=[func_arg],
py_name='int',
is_temp=node.is_temp,
result_is_used=node.result_is_used,
).coerce_to(node.type, self.current_env())
elif function.name == 'float': elif function.name == 'float':
if func_arg.type.is_float or node.type.is_float: if func_arg.type.is_float or node.type.is_float:
if func_arg.type == node.type: if func_arg.type == node.type:
...@@ -2295,6 +2313,11 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ...@@ -2295,6 +2313,11 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
PyrexTypes.CFuncTypeArg("o", PyrexTypes.py_object_type, None) PyrexTypes.CFuncTypeArg("o", PyrexTypes.py_object_type, None)
]) ])
PyInt_FromDouble_func_type = PyrexTypes.CFuncType(
PyrexTypes.py_object_type, [
PyrexTypes.CFuncTypeArg("value", PyrexTypes.c_double_type, None)
])
def _handle_simple_function_int(self, node, function, pos_args): def _handle_simple_function_int(self, node, function, pos_args):
"""Transform int() into a faster C function call. """Transform int() into a faster C function call.
""" """
...@@ -2305,11 +2328,17 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ...@@ -2305,11 +2328,17 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
return node # int(x, base) return node # int(x, base)
func_arg = pos_args[0] func_arg = pos_args[0]
if isinstance(func_arg, ExprNodes.CoerceToPyTypeNode): if isinstance(func_arg, ExprNodes.CoerceToPyTypeNode):
return node # handled in visit_CoerceFromPyTypeNode() if func_arg.arg.type.is_float:
return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_PyInt_FromDouble", self.PyInt_FromDouble_func_type,
args=[func_arg.arg], is_temp=True, py_name='int',
utility_code=UtilityCode.load_cached("PyIntFromDouble", "TypeConversion.c"))
else:
return node # handled in visit_CoerceFromPyTypeNode()
if func_arg.type.is_pyobject and node.type.is_pyobject: if func_arg.type.is_pyobject and node.type.is_pyobject:
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
node.pos, "PyNumber_Int", self.PyNumber_Int_func_type, node.pos, "PyNumber_Int", self.PyNumber_Int_func_type,
args=pos_args, is_temp=True) args=pos_args, is_temp=True, py_name='int')
return node return node
def _handle_simple_function_bool(self, node, function, pos_args): def _handle_simple_function_bool(self, node, function, pos_args):
......
...@@ -561,6 +561,26 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) { ...@@ -561,6 +561,26 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) {
} }
/////////////// PyIntFromDouble.proto ///////////////
#if PY_MAJOR_VERSION < 3
static CYTHON_INLINE PyObject* __Pyx_PyInt_FromDouble(double value);
#else
#define __Pyx_PyInt_FromDouble(value) PyLong_FromDouble(value)
#endif
/////////////// PyIntFromDouble ///////////////
#if PY_MAJOR_VERSION < 3
static CYTHON_INLINE PyObject* __Pyx_PyInt_FromDouble(double value) {
if (value >= (double)LONG_MIN && value <= (double)LONG_MAX) {
return PyInt_FromLong((long)value);
}
return PyLong_FromDouble(value);
}
#endif
/////////////// CIntFromPyVerify /////////////// /////////////// CIntFromPyVerify ///////////////
// see CIntFromPy // see CIntFromPy
......
...@@ -102,7 +102,7 @@ def float_to_float_float(float x): ...@@ -102,7 +102,7 @@ def float_to_float_float(float x):
return r return r
@cython.test_fail_if_path_exists("//SimpleCallNode", @cython.test_fail_if_path_exists("//SimpleCallNode",
"//SingleAssignmentNode/TypecastNode") "//SingleAssignmentNode//TypecastNode")
def double_to_double_float(double x): def double_to_double_float(double x):
""" """
>>> 4.05 < double_to_double_float(4.1) < 4.15 >>> 4.05 < double_to_double_float(4.1) < 4.15
...@@ -115,8 +115,8 @@ def double_to_double_float(double x): ...@@ -115,8 +115,8 @@ def double_to_double_float(double x):
# tests that cannot be optimised # tests that cannot be optimised
@cython.test_fail_if_path_exists("//SingleAssignmentNode/TypecastNode") @cython.test_fail_if_path_exists("//TypecastNode")
@cython.test_assert_path_exists("//SimpleCallNode") @cython.test_assert_path_exists("//PythonCapiCallNode")
def double_to_py_int(double x): def double_to_py_int(double x):
""" """
>>> double_to_py_int(4.1) >>> double_to_py_int(4.1)
...@@ -126,8 +126,8 @@ def double_to_py_int(double x): ...@@ -126,8 +126,8 @@ def double_to_py_int(double x):
""" """
return int(x) return int(x)
@cython.test_fail_if_path_exists("//SingleAssignmentNode/TypecastNode") @cython.test_fail_if_path_exists("//SingleAssignmentNode//TypecastNode")
@cython.test_assert_path_exists("//SimpleCallNode") @cython.test_assert_path_exists("//PythonCapiCallNode")
def double_to_double_int(double x): def double_to_double_int(double x):
""" """
>>> double_to_double_int(4.1) >>> double_to_double_int(4.1)
...@@ -138,6 +138,73 @@ def double_to_double_int(double x): ...@@ -138,6 +138,73 @@ def double_to_double_int(double x):
cdef double r = int(x) cdef double r = int(x)
return r return r
@cython.test_fail_if_path_exists("//SingleAssignmentNode//TypecastNode")
@cython.test_assert_path_exists(
"//PythonCapiCallNode",
"//PythonCapiCallNode/PythonCapiFunctionNode/@cname = 'truncf'",
)
def float_to_float_int(float x):
"""
>>> float_to_float_int(4.1)
4.0
>>> float_to_float_int(4)
4.0
"""
cdef float r = int(x)
return r
@cython.test_fail_if_path_exists("//SingleAssignmentNode//TypecastNode")
@cython.test_assert_path_exists(
"//PythonCapiCallNode",
"//PythonCapiCallNode/PythonCapiFunctionNode/@cname = 'truncf'",
)
def float_to_double_int(float x):
"""
>>> float_to_double_int(4.1)
4.0
>>> float_to_double_int(4)
4.0
"""
cdef double r = int(x)
return r
@cython.test_fail_if_path_exists("//SingleAssignmentNode//TypecastNode")
@cython.test_assert_path_exists(
"//PythonCapiCallNode",
"//PythonCapiCallNode/PythonCapiFunctionNode/@cname = 'trunc'",
)
def double_to_float_int(double x):
"""
>>> double_to_float_int(4.1)
4.0
>>> double_to_float_int(4)
4.0
"""
cdef float r = int(x)
return r
@cython.test_fail_if_path_exists("//SingleAssignmentNode//TypecastNode")
@cython.test_assert_path_exists(
"//PythonCapiCallNode",
"//PythonCapiCallNode/PythonCapiFunctionNode/@cname = 'truncl'",
)
def long_double_to_float_int(long double x):
"""
>>> long_double_to_float_int(4.1)
4.0
>>> long_double_to_float_int(-4.1)
-4.0
>>> long_double_to_float_int(4)
4.0
"""
cdef float r = int(x)
return r
@cython.test_fail_if_path_exists("//SimpleCallNode") @cython.test_fail_if_path_exists("//SimpleCallNode")
@cython.test_assert_path_exists("//PythonCapiCallNode") @cython.test_assert_path_exists("//PythonCapiCallNode")
def object_float(x): def object_float(x):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment