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,
return 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):
function = arg.function
if not isinstance(function, ExprNodes.NameNode) \
or not function.type.is_builtin_type \
or not isinstance(arg.arg_tuple, ExprNodes.TupleNode):
return node
args = arg.arg_tuple.args
if len(args) != 1:
args = None
if isinstance(arg, ExprNodes.PythonCapiCallNode):
args = arg.args
elif isinstance(function, ExprNodes.NameNode):
if function.type.is_builtin_type and isinstance(arg.arg_tuple, ExprNodes.TupleNode):
args = arg.arg_tuple.args
if args is None or len(args) != 1:
return node
func_arg = args[0]
if isinstance(func_arg, ExprNodes.CoerceToPyTypeNode):
func_arg = func_arg.arg
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
if function.name == 'int':
if func_arg.type.is_int or node.type.is_int:
if func_arg.type == node.type:
return func_arg
elif node.type.assignable_from(func_arg.type) or func_arg.type.is_float:
return ExprNodes.TypecastNode(
node.pos, operand=func_arg, type=node.type)
return ExprNodes.TypecastNode(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':
if func_arg.type.is_float or node.type.is_float:
if func_arg.type == node.type:
......@@ -2295,6 +2313,11 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
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):
"""Transform int() into a faster C function call.
"""
......@@ -2305,11 +2328,17 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
return node # int(x, base)
func_arg = pos_args[0]
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:
return ExprNodes.PythonCapiCallNode(
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
def _handle_simple_function_bool(self, node, function, pos_args):
......
......@@ -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 ///////////////
// see CIntFromPy
......
......@@ -102,7 +102,7 @@ def float_to_float_float(float x):
return r
@cython.test_fail_if_path_exists("//SimpleCallNode",
"//SingleAssignmentNode/TypecastNode")
"//SingleAssignmentNode//TypecastNode")
def double_to_double_float(double x):
"""
>>> 4.05 < double_to_double_float(4.1) < 4.15
......@@ -115,8 +115,8 @@ def double_to_double_float(double x):
# tests that cannot be optimised
@cython.test_fail_if_path_exists("//SingleAssignmentNode/TypecastNode")
@cython.test_assert_path_exists("//SimpleCallNode")
@cython.test_fail_if_path_exists("//TypecastNode")
@cython.test_assert_path_exists("//PythonCapiCallNode")
def double_to_py_int(double x):
"""
>>> double_to_py_int(4.1)
......@@ -126,8 +126,8 @@ def double_to_py_int(double x):
"""
return int(x)
@cython.test_fail_if_path_exists("//SingleAssignmentNode/TypecastNode")
@cython.test_assert_path_exists("//SimpleCallNode")
@cython.test_fail_if_path_exists("//SingleAssignmentNode//TypecastNode")
@cython.test_assert_path_exists("//PythonCapiCallNode")
def double_to_double_int(double x):
"""
>>> double_to_double_int(4.1)
......@@ -138,6 +138,73 @@ def double_to_double_int(double x):
cdef double 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_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_assert_path_exists("//PythonCapiCallNode")
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