Commit 0c5fa933 authored by Stefan Behnel's avatar Stefan Behnel

properly handle C integer wrap-around when optimising abs()

parent 813b5a9e
...@@ -23,6 +23,20 @@ proto = """ ...@@ -23,6 +23,20 @@ proto = """
""" """
) )
abs_int_utility_code = UtilityCode(
proto = '''
#if HAVE_LONG_LONG
#define __Pyx_abs_int(x) \
((sizeof(x) <= sizeof(int)) ? ((unsigned int)abs(x)) : \
((sizeof(x) <= sizeof(long)) ? ((unsigned long)labs(x)) : \
((unsigned PY_LONG_LONG)llabs(x))))
#else
#define __Pyx_abs_int(x) \
((sizeof(x) <= sizeof(int)) ? ((unsigned int)abs(x)) : ((unsigned long)labs(x)))
#endif
#define __Pyx_abs_long(x) __Pyx_abs_int(x)
''')
iter_next_utility_code = UtilityCode( iter_next_utility_code = UtilityCode(
proto = """ proto = """
#define __Pyx_PyIter_Next(obj) __Pyx_PyIter_Next2(obj, NULL); #define __Pyx_PyIter_Next(obj) __Pyx_PyIter_Next2(obj, NULL);
...@@ -330,6 +344,8 @@ class BuiltinMethod(_BuiltinOverride): ...@@ -330,6 +344,8 @@ class BuiltinMethod(_BuiltinOverride):
self_arg.not_none = True self_arg.not_none = True
self_arg.accept_builtin_subtypes = True self_arg.accept_builtin_subtypes = True
method_type = sig.function_type(self_arg) method_type = sig.function_type(self_arg)
if self.is_strict_signature:
method_type.is_strict_signature = True
self_type.scope.declare_builtin_cfunction( self_type.scope.declare_builtin_cfunction(
self.py_name, method_type, self.cname, utility_code = self.utility_code) self.py_name, method_type, self.cname, utility_code = self.utility_code)
...@@ -340,8 +356,20 @@ builtin_function_table = [ ...@@ -340,8 +356,20 @@ builtin_function_table = [
is_strict_signature = True), is_strict_signature = True),
BuiltinFunction('abs', "f", "f", "fabsf", BuiltinFunction('abs', "f", "f", "fabsf",
is_strict_signature = True), is_strict_signature = True),
BuiltinFunction('abs', "i", "l", "labs", BuiltinFunction('abs', None, None, "__Pyx_abs_int",
is_strict_signature = True), utility_code = abs_int_utility_code,
func_type = PyrexTypes.CFuncType(
PyrexTypes.c_uint_type, [
PyrexTypes.CFuncTypeArg("arg", PyrexTypes.c_int_type, None)
],
is_strict_signature = True)),
BuiltinFunction('abs', None, None, "__Pyx_abs_long",
utility_code = abs_int_utility_code,
func_type = PyrexTypes.CFuncType(
PyrexTypes.c_ulong_type, [
PyrexTypes.CFuncTypeArg("arg", PyrexTypes.c_long_type, None)
],
is_strict_signature = True)),
BuiltinFunction('abs', "O", "O", "PyNumber_Absolute"), BuiltinFunction('abs', "O", "O", "PyNumber_Absolute"),
#('chr', "", "", ""), #('chr', "", "", ""),
#('cmp', "", "", "", ""), # int PyObject_Cmp(PyObject *o1, PyObject *o2, int *result) #('cmp', "", "", "", ""), # int PyObject_Cmp(PyObject *o1, PyObject *o2, int *result)
......
...@@ -3233,6 +3233,8 @@ class SimpleCallNode(CallNode): ...@@ -3233,6 +3233,8 @@ class SimpleCallNode(CallNode):
self.type = PyrexTypes.CPtrType(self.function.class_type) self.type = PyrexTypes.CPtrType(self.function.class_type)
else: else:
self.type = func_type.return_type self.type = func_type.return_type
if self.function.entry and self.function.entry.utility_code:
self.is_temp = 1 # currently doesn't work for self.calculate_result_code()
if self.type.is_pyobject: if self.type.is_pyobject:
self.result_ctype = py_object_type self.result_ctype = py_object_type
self.is_temp = 1 self.is_temp = 1
...@@ -3285,6 +3287,8 @@ class SimpleCallNode(CallNode): ...@@ -3285,6 +3287,8 @@ class SimpleCallNode(CallNode):
def generate_result_code(self, code): def generate_result_code(self, code):
func_type = self.function_type() func_type = self.function_type()
if self.function.entry and self.function.entry.utility_code:
code.globalstate.use_utility_code(self.function.entry.utility_code)
if func_type.is_pyobject: if func_type.is_pyobject:
arg_code = self.arg_tuple.py_result() arg_code = self.arg_tuple.py_result()
code.putln( code.putln(
......
...@@ -1702,12 +1702,11 @@ class CFuncType(CType): ...@@ -1702,12 +1702,11 @@ class CFuncType(CType):
is_cfunction = 1 is_cfunction = 1
original_sig = None original_sig = None
is_strict_signature = False
def __init__(self, return_type, args, has_varargs = 0, def __init__(self, return_type, args, has_varargs = 0,
exception_value = None, exception_check = 0, calling_convention = "", exception_value = None, exception_check = 0, calling_convention = "",
nogil = 0, with_gil = 0, is_overridable = 0, optional_arg_count = 0, nogil = 0, with_gil = 0, is_overridable = 0, optional_arg_count = 0,
templates = None): templates = None, is_strict_signature = False):
self.return_type = return_type self.return_type = return_type
self.args = args self.args = args
self.has_varargs = has_varargs self.has_varargs = has_varargs
...@@ -1719,6 +1718,7 @@ class CFuncType(CType): ...@@ -1719,6 +1718,7 @@ class CFuncType(CType):
self.with_gil = with_gil self.with_gil = with_gil
self.is_overridable = is_overridable self.is_overridable = is_overridable
self.templates = templates self.templates = templates
self.is_strict_signature = is_strict_signature
def __repr__(self): def __repr__(self):
arg_reprs = map(repr, self.args) arg_reprs = map(repr, self.args)
......
# mode: run
# ticket: 698
cdef extern from *:
int INT_MAX
long LONG_MAX
long long PY_LLONG_MAX
max_int = INT_MAX
max_long = LONG_MAX
max_long_long = PY_LLONG_MAX
cimport cython
def py_abs(a):
"""
>>> py_abs(-5)
5
>>> py_abs(-5.5)
5.5
"""
return abs(a)
@cython.test_assert_path_exists("//ReturnStatNode//NameNode[@entry.name = 'abs']",
"//ReturnStatNode//NameNode[@entry.cname = '__Pyx_abs_int']")
def int_abs(int a):
"""
>>> int_abs(-5) == 5
True
>>> int_abs(-5.1) == 5
True
>>> long_abs(-max_int-1) > 0
True
>>> int_abs(-max_int-1) == abs(-max_int-1)
True
>>> int_abs(max_int) == abs(max_int)
True
"""
return abs(a)
@cython.test_assert_path_exists("//ReturnStatNode//NameNode[@entry.name = 'abs']",
"//ReturnStatNode//NameNode[@entry.cname = '__Pyx_abs_long']")
def long_abs(long a):
"""
>>> long_abs(-5) == 5
True
>>> long_abs(-5.1) == 5
True
>>> long_abs(-max_long-1) > 0
True
>>> long_abs(-max_long-1) == abs(-max_long-1)
True
>>> long_abs(max_long) == abs(max_long)
True
"""
return abs(a)
def long_long_abs(long long a):
"""
>>> long_long_abs(-(2**33)) == 2**33
True
>>> long_long_abs(-max_long_long-1) > 0
True
>>> long_long_abs(-max_long_long-1) == abs(-max_long_long-1)
True
>>> long_long_abs(max_long_long) == abs(max_long_long)
True
"""
return abs(a)
@cython.test_assert_path_exists("//ReturnStatNode//NameNode[@entry.name = 'abs']",
"//ReturnStatNode//NameNode[@entry.cname = 'fabs']")
def double_abs(double a):
"""
>>> double_abs(-5)
5.0
>>> double_abs(-5.5)
5.5
"""
return abs(a)
@cython.test_assert_path_exists("//ReturnStatNode//NameNode[@entry.name = 'abs']",
"//ReturnStatNode//NameNode[@entry.cname = 'fabsf']")
def float_abs(float a):
"""
>>> float_abs(-5)
5.0
>>> float_abs(-5.5)
5.5
"""
return abs(a)
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