Commit b74716a2 authored by scoder's avatar scoder Committed by GitHub

Merge pull request #2188 from scoder/faster_int_comparison

Further optimise int/float comparison
parents 440c51b2 55cdc9f0
...@@ -3159,21 +3159,17 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ...@@ -3159,21 +3159,17 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
may_return_none=True, may_return_none=True,
utility_code=load_c_utility('py_dict_pop')) utility_code=load_c_utility('py_dict_pop'))
Pyx_PyInt_BinopInt_func_type = PyrexTypes.CFuncType( Pyx_BinopInt_func_types = dict(
PyrexTypes.py_object_type, [ ((ctype, ret_type), PyrexTypes.CFuncType(
PyrexTypes.CFuncTypeArg("op1", PyrexTypes.py_object_type, None), ret_type, [
PyrexTypes.CFuncTypeArg("op2", PyrexTypes.py_object_type, None), PyrexTypes.CFuncTypeArg("op1", PyrexTypes.py_object_type, None),
PyrexTypes.CFuncTypeArg("intval", PyrexTypes.c_long_type, None), PyrexTypes.CFuncTypeArg("op2", PyrexTypes.py_object_type, None),
PyrexTypes.CFuncTypeArg("inplace", PyrexTypes.c_bint_type, None), PyrexTypes.CFuncTypeArg("cval", ctype, None),
]) PyrexTypes.CFuncTypeArg("inplace", PyrexTypes.c_bint_type, None),
], exception_value=None if ret_type.is_pyobject else ret_type.exception_value))
Pyx_PyFloat_BinopInt_func_type = PyrexTypes.CFuncType( for ctype in (PyrexTypes.c_long_type, PyrexTypes.c_double_type)
PyrexTypes.py_object_type, [ for ret_type in (PyrexTypes.py_object_type, PyrexTypes.c_bint_type)
PyrexTypes.CFuncTypeArg("op1", PyrexTypes.py_object_type, None), )
PyrexTypes.CFuncTypeArg("op2", PyrexTypes.py_object_type, None),
PyrexTypes.CFuncTypeArg("fval", PyrexTypes.c_double_type, None),
PyrexTypes.CFuncTypeArg("inplace", PyrexTypes.c_bint_type, None),
])
def _handle_simple_method_object___add__(self, node, function, args, is_unbound_method): def _handle_simple_method_object___add__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Add', node, function, args, is_unbound_method) return self._optimise_num_binop('Add', node, function, args, is_unbound_method)
...@@ -3184,7 +3180,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ...@@ -3184,7 +3180,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
def _handle_simple_method_object___eq__(self, node, function, args, is_unbound_method): def _handle_simple_method_object___eq__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Eq', node, function, args, is_unbound_method) return self._optimise_num_binop('Eq', node, function, args, is_unbound_method)
def _handle_simple_method_object___neq__(self, node, function, args, is_unbound_method): def _handle_simple_method_object___ne__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Ne', node, function, args, is_unbound_method) return self._optimise_num_binop('Ne', node, function, args, is_unbound_method)
def _handle_simple_method_object___and__(self, node, function, args, is_unbound_method): def _handle_simple_method_object___and__(self, node, function, args, is_unbound_method):
...@@ -3253,7 +3249,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ...@@ -3253,7 +3249,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
def _handle_simple_method_float___eq__(self, node, function, args, is_unbound_method): def _handle_simple_method_float___eq__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Eq', node, function, args, is_unbound_method) return self._optimise_num_binop('Eq', node, function, args, is_unbound_method)
def _handle_simple_method_float___neq__(self, node, function, args, is_unbound_method): def _handle_simple_method_float___ne__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Ne', node, function, args, is_unbound_method) return self._optimise_num_binop('Ne', node, function, args, is_unbound_method)
def _optimise_num_binop(self, operator, node, function, args, is_unbound_method): def _optimise_num_binop(self, operator, node, function, args, is_unbound_method):
...@@ -3262,7 +3258,15 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ...@@ -3262,7 +3258,15 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
""" """
if len(args) != 2: if len(args) != 2:
return node return node
if not node.type.is_pyobject:
if node.type.is_pyobject:
if operator in ('Eq', 'Ne'):
ret_type = PyrexTypes.c_bint_type
else:
ret_type = PyrexTypes.py_object_type
elif node.type is PyrexTypes.c_bint_type and operator in ('Eq', 'Ne'):
ret_type = PyrexTypes.c_bint_type
else:
return node return node
# When adding IntNode/FloatNode to something else, assume other operand is also numeric. # When adding IntNode/FloatNode to something else, assume other operand is also numeric.
...@@ -3285,6 +3289,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ...@@ -3285,6 +3289,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
return node return node
is_float = isinstance(numval, ExprNodes.FloatNode) is_float = isinstance(numval, ExprNodes.FloatNode)
num_type = PyrexTypes.c_double_type if is_float else PyrexTypes.c_long_type
if is_float: if is_float:
if operator not in ('Add', 'Subtract', 'Remainder', 'TrueDivide', 'Divide', 'Eq', 'Ne'): if operator not in ('Add', 'Subtract', 'Remainder', 'TrueDivide', 'Divide', 'Eq', 'Ne'):
return node return node
...@@ -3292,27 +3297,38 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ...@@ -3292,27 +3297,38 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
# mixed old-/new-style division is not currently optimised for integers # mixed old-/new-style division is not currently optimised for integers
return node return node
elif abs(numval.constant_result) > 2**30: elif abs(numval.constant_result) > 2**30:
# Cut off at an integer border that is still safe for all operations.
return node return node
args = list(args) args = list(args)
args.append((ExprNodes.FloatNode if is_float else ExprNodes.IntNode)( args.append((ExprNodes.FloatNode if is_float else ExprNodes.IntNode)(
numval.pos, value=numval.value, constant_result=numval.constant_result, numval.pos, value=numval.value, constant_result=numval.constant_result,
type=PyrexTypes.c_double_type if is_float else PyrexTypes.c_long_type)) type=num_type))
inplace = node.inplace if isinstance(node, ExprNodes.NumBinopNode) else False inplace = node.inplace if isinstance(node, ExprNodes.NumBinopNode) else False
args.append(ExprNodes.BoolNode(node.pos, value=inplace, constant_result=inplace)) args.append(ExprNodes.BoolNode(node.pos, value=inplace, constant_result=inplace))
utility_code = TempitaUtilityCode.load_cached( utility_code = TempitaUtilityCode.load_cached(
"PyFloatBinop" if is_float else "PyIntBinop", "Optimize.c", "PyFloatBinop" if is_float else "PyIntCompare" if operator in ('Eq', 'Ne') else "PyIntBinop",
context=dict(op=operator, order=arg_order)) "Optimize.c",
context=dict(op=operator, order=arg_order, ret_type=ret_type))
return self._substitute_method_call( call_node = self._substitute_method_call(
node, function, "__Pyx_Py%s_%s%s" % ('Float' if is_float else 'Int', operator, arg_order), node, function,
self.Pyx_PyFloat_BinopInt_func_type if is_float else self.Pyx_PyInt_BinopInt_func_type, "__Pyx_Py%s_%s%s%s" % (
'Float' if is_float else 'Int',
'' if ret_type.is_pyobject else 'Bool',
operator,
arg_order),
self.Pyx_BinopInt_func_types[(num_type, ret_type)],
'__%s__' % operator[:3].lower(), is_unbound_method, args, '__%s__' % operator[:3].lower(), is_unbound_method, args,
may_return_none=True, may_return_none=True,
with_none_check=False, with_none_check=False,
utility_code=utility_code) utility_code=utility_code)
if node.type.is_pyobject and not ret_type.is_pyobject:
call_node = ExprNodes.CoerceToPyTypeNode(call_node, self.current_env(), node.type)
return call_node
### unicode type methods ### unicode type methods
PyUnicode_uchar_predicate_func_type = PyrexTypes.CFuncType( PyUnicode_uchar_predicate_func_type = PyrexTypes.CFuncType(
......
...@@ -698,13 +698,99 @@ fallback: ...@@ -698,13 +698,99 @@ fallback:
} }
/////////////// PyIntCompare.proto ///////////////
{{py: c_ret_type = 'PyObject*' if ret_type.is_pyobject else 'int'}}
static CYTHON_INLINE {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(PyObject *op1, PyObject *op2, long intval, long inplace); /*proto*/
/////////////// PyIntCompare ///////////////
{{py: pyval, ival = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }}
{{py: c_ret_type = 'PyObject*' if ret_type.is_pyobject else 'int'}}
{{py: return_true = 'Py_RETURN_TRUE' if ret_type.is_pyobject else 'return 1'}}
{{py: return_false = 'Py_RETURN_FALSE' if ret_type.is_pyobject else 'return 0'}}
{{py: slot_name = op.lower() }}
{{py: c_op = {'Eq': '==', 'Ne': '!='}[op] }}
{{py:
return_compare = (
(lambda a,b,c_op: "if ({a} {c_op} {b}) {return_true}; else {return_false};".format(
a=a, b=b, c_op=c_op, return_true=return_true, return_false=return_false))
if ret_type.is_pyobject else
(lambda a,b,c_op: "return ({a} {c_op} {b});".format(a=a, b=b, c_op=c_op))
)
}}
static CYTHON_INLINE {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHON_UNUSED long intval, CYTHON_UNUSED long inplace) {
if (op1 == op2) {
{{return_true if op == 'Eq' else return_false}};
}
#if PY_MAJOR_VERSION < 3
if (likely(PyInt_CheckExact({{pyval}}))) {
const long {{'a' if order == 'CObj' else 'b'}} = intval;
long {{ival}} = PyInt_AS_LONG({{pyval}});
{{return_compare('a', 'b', c_op)}}
}
#endif
#if CYTHON_USE_PYLONG_INTERNALS
if (likely(PyLong_CheckExact({{pyval}}))) {
int unequal;
unsigned long uintval;
Py_ssize_t size = Py_SIZE({{pyval}});
const digit* digits = ((PyLongObject*){{pyval}})->ob_digit;
if (intval == 0) {
// == 0 => Py_SIZE(pyval) == 0
{{return_compare('size', '0', c_op)}}
} else if (intval < 0) {
// < 0 => Py_SIZE(pyval) < 0
if (size >= 0)
{{return_false if op == 'Eq' else return_true}};
// both are negative => can use absolute values now.
intval = -intval;
size = -size;
} else {
// > 0 => Py_SIZE(pyval) > 0
if (size <= 0)
{{return_false if op == 'Eq' else return_true}};
}
// After checking that the sign is the same (and excluding 0), now compare the absolute values.
// When inlining, the C compiler should select exactly one line from this unrolled loop.
uintval = (unsigned long) intval;
{{for _size in range(4, 0, -1)}}
#if PyLong_SHIFT * {{_size}} < SIZEOF_LONG*8
if (uintval >> (PyLong_SHIFT * {{_size}})) {
// The C integer value is between (PyLong_BASE ** _size) and MIN(PyLong_BASE ** _size, LONG_MAX).
unequal = (size != {{_size+1}}) || (digits[0] != (uintval & (unsigned long) PyLong_MASK))
{{for _i in range(1, _size+1)}} | (digits[{{_i}}] != ((uintval >> ({{_i}} * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)){{endfor}};
} else
#endif
{{endfor}}
unequal = (size != 1) || (((unsigned long) digits[0]) != (uintval & (unsigned long) PyLong_MASK));
{{return_compare('unequal', '0', c_op)}}
}
#endif
if (PyFloat_CheckExact({{pyval}})) {
const long {{'a' if order == 'CObj' else 'b'}} = intval;
double {{ival}} = PyFloat_AS_DOUBLE({{pyval}});
{{return_compare('(double)a', '(double)b', c_op)}}
}
return {{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(
PyObject_RichCompare(op1, op2, Py_{{op.upper()}}));
}
/////////////// PyIntBinop.proto /////////////// /////////////// PyIntBinop.proto ///////////////
{{py: c_ret_type = 'PyObject*' if ret_type.is_pyobject else 'int'}}
#if !CYTHON_COMPILING_IN_PYPY #if !CYTHON_COMPILING_IN_PYPY
static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long intval, int inplace); /*proto*/ static {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(PyObject *op1, PyObject *op2, long intval, int inplace); /*proto*/
#else #else
#define __Pyx_PyInt_{{op}}{{order}}(op1, op2, intval, inplace) \ #define __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(op1, op2, intval, inplace) \
{{if op in ('Eq', 'Ne')}}PyObject_RichCompare(op1, op2, Py_{{op.upper()}}) {{if op in ('Eq', 'Ne')}}{{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(PyObject_RichCompare(op1, op2, Py_{{op.upper()}}))
{{else}}(inplace ? PyNumber_InPlace{{op}}(op1, op2) : PyNumber_{{op}}(op1, op2)) {{else}}(inplace ? PyNumber_InPlace{{op}}(op1, op2) : PyNumber_{{op}}(op1, op2))
{{endif}} {{endif}}
#endif #endif
...@@ -714,6 +800,9 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long ...@@ -714,6 +800,9 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long
#if !CYTHON_COMPILING_IN_PYPY #if !CYTHON_COMPILING_IN_PYPY
{{py: from Cython.Utility import pylong_join }} {{py: from Cython.Utility import pylong_join }}
{{py: pyval, ival = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }} {{py: pyval, ival = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }}
{{py: c_ret_type = 'PyObject*' if ret_type.is_pyobject else 'int'}}
{{py: return_true = 'Py_RETURN_TRUE' if ret_type.is_pyobject else 'return 1'}}
{{py: return_false = 'Py_RETURN_FALSE' if ret_type.is_pyobject else 'return 0'}}
{{py: slot_name = {'TrueDivide': 'true_divide', 'FloorDivide': 'floor_divide'}.get(op, op.lower()) }} {{py: slot_name = {'TrueDivide': 'true_divide', 'FloorDivide': 'floor_divide'}.get(op, op.lower()) }}
{{py: {{py:
c_op = { c_op = {
...@@ -723,10 +812,10 @@ c_op = { ...@@ -723,10 +812,10 @@ c_op = {
}[op] }[op]
}} }}
static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHON_UNUSED long intval, CYTHON_UNUSED int inplace) { static {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHON_UNUSED long intval, CYTHON_UNUSED int inplace) {
{{if op in ('Eq', 'Ne')}} {{if op in ('Eq', 'Ne')}}
if (op1 == op2) { if (op1 == op2) {
Py_RETURN_{{'TRUE' if op == 'Eq' else 'FALSE'}}; {{return_true if op == 'Eq' else return_false}};
} }
{{endif}} {{endif}}
...@@ -740,9 +829,9 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO ...@@ -740,9 +829,9 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO
{{if op in ('Eq', 'Ne')}} {{if op in ('Eq', 'Ne')}}
if (a {{c_op}} b) { if (a {{c_op}} b) {
Py_RETURN_TRUE; {{return_true}};
} else { } else {
Py_RETURN_FALSE; {{return_false}};
} }
{{elif c_op in '+-'}} {{elif c_op in '+-'}}
// adapted from intobject.c in Py2.7: // adapted from intobject.c in Py2.7:
...@@ -826,10 +915,11 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO ...@@ -826,10 +915,11 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO
{{if op in ('Eq', 'Ne')}} {{if op in ('Eq', 'Ne')}}
#if PyLong_SHIFT < 30 && PyLong_SHIFT != 15 #if PyLong_SHIFT < 30 && PyLong_SHIFT != 15
// unusual setup - your fault // unusual setup - your fault
default: return PyLong_Type.tp_richcompare({{'op1, op2' if order == 'ObjC' else 'op2, op1'}}, Py_{{op.upper()}}); default: return {{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(
PyLong_Type.tp_richcompare({{'op1, op2' if order == 'ObjC' else 'op2, op1'}}, Py_{{op.upper()}}));
#else #else
// too large for the long values we allow => definitely not equal // too large for the long values we allow => definitely not equal
default: Py_RETURN_{{'FALSE' if op == 'Eq' else 'TRUE'}}; default: {{return_false if op == 'Eq' else return_true}};
#endif #endif
{{else}} {{else}}
default: return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2); default: return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2);
...@@ -838,9 +928,9 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO ...@@ -838,9 +928,9 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO
} }
{{if op in ('Eq', 'Ne')}} {{if op in ('Eq', 'Ne')}}
if (a {{c_op}} b) { if (a {{c_op}} b) {
Py_RETURN_TRUE; {{return_true}};
} else { } else {
Py_RETURN_FALSE; {{return_false}};
} }
{{else}} {{else}}
{{if c_op == '%'}} {{if c_op == '%'}}
...@@ -912,9 +1002,9 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO ...@@ -912,9 +1002,9 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO
double {{ival}} = PyFloat_AS_DOUBLE({{pyval}}); double {{ival}} = PyFloat_AS_DOUBLE({{pyval}});
{{if op in ('Eq', 'Ne')}} {{if op in ('Eq', 'Ne')}}
if ((double)a {{c_op}} (double)b) { if ((double)a {{c_op}} (double)b) {
Py_RETURN_TRUE; {{return_true}};
} else { } else {
Py_RETURN_FALSE; {{return_false}};
} }
{{else}} {{else}}
double result; double result;
...@@ -928,7 +1018,8 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO ...@@ -928,7 +1018,8 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO
{{endif}} {{endif}}
{{if op in ('Eq', 'Ne')}} {{if op in ('Eq', 'Ne')}}
return PyObject_RichCompare(op1, op2, Py_{{op.upper()}}); return {{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(
PyObject_RichCompare(op1, op2, Py_{{op.upper()}}));
{{else}} {{else}}
return (inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2); return (inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2);
{{endif}} {{endif}}
...@@ -937,11 +1028,12 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO ...@@ -937,11 +1028,12 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO
/////////////// PyFloatBinop.proto /////////////// /////////////// PyFloatBinop.proto ///////////////
{{py: c_ret_type = 'PyObject*' if ret_type.is_pyobject else 'int'}}
#if !CYTHON_COMPILING_IN_PYPY #if !CYTHON_COMPILING_IN_PYPY
static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, double floatval, int inplace); /*proto*/ static {{c_ret_type}} __Pyx_PyFloat_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(PyObject *op1, PyObject *op2, double floatval, int inplace); /*proto*/
#else #else
#define __Pyx_PyFloat_{{op}}{{order}}(op1, op2, floatval, inplace) \ #define __Pyx_PyFloat_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(op1, op2, floatval, inplace) \
{{if op in ('Eq', 'Ne')}}PyObject_RichCompare(op1, op2, Py_{{op.upper()}}) {{if op in ('Eq', 'Ne')}}{{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(PyObject_RichCompare(op1, op2, Py_{{op.upper()}}))
{{elif op == 'Divide'}}((inplace ? __Pyx_PyNumber_InPlaceDivide(op1, op2) : __Pyx_PyNumber_Divide(op1, op2))) {{elif op == 'Divide'}}((inplace ? __Pyx_PyNumber_InPlaceDivide(op1, op2) : __Pyx_PyNumber_Divide(op1, op2)))
{{else}}(inplace ? PyNumber_InPlace{{op}}(op1, op2) : PyNumber_{{op}}(op1, op2)) {{else}}(inplace ? PyNumber_InPlace{{op}}(op1, op2) : PyNumber_{{op}}(op1, op2))
{{endif}} {{endif}}
...@@ -951,6 +1043,9 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou ...@@ -951,6 +1043,9 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou
#if !CYTHON_COMPILING_IN_PYPY #if !CYTHON_COMPILING_IN_PYPY
{{py: from Cython.Utility import pylong_join }} {{py: from Cython.Utility import pylong_join }}
{{py: c_ret_type = 'PyObject*' if ret_type.is_pyobject else 'int'}}
{{py: return_true = 'Py_RETURN_TRUE' if ret_type.is_pyobject else 'return 1'}}
{{py: return_false = 'Py_RETURN_FALSE' if ret_type.is_pyobject else 'return 0'}}
{{py: pyval, fval = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }} {{py: pyval, fval = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }}
{{py: {{py:
c_op = { c_op = {
...@@ -959,13 +1054,13 @@ c_op = { ...@@ -959,13 +1054,13 @@ c_op = {
}[op] }[op]
}} }}
static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, double floatval, CYTHON_UNUSED int inplace) { static {{c_ret_type}} __Pyx_PyFloat_{{'' if ret_type.is_pyobject else 'Bool'}}{{op}}{{order}}(PyObject *op1, PyObject *op2, double floatval, CYTHON_UNUSED int inplace) {
const double {{'a' if order == 'CObj' else 'b'}} = floatval; const double {{'a' if order == 'CObj' else 'b'}} = floatval;
double {{fval}}{{if op not in ('Eq', 'Ne')}}, result{{endif}}; double {{fval}}{{if op not in ('Eq', 'Ne')}}, result{{endif}};
{{if op in ('Eq', 'Ne')}} {{if op in ('Eq', 'Ne')}}
if (op1 == op2) { if (op1 == op2) {
Py_RETURN_{{'TRUE' if op == 'Eq' else 'FALSE'}}; {{return_true if op == 'Eq' else return_false}};
} }
{{endif}} {{endif}}
...@@ -1011,7 +1106,8 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou ...@@ -1011,7 +1106,8 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou
{ {
#endif #endif
{{if op in ('Eq', 'Ne')}} {{if op in ('Eq', 'Ne')}}
return PyFloat_Type.tp_richcompare({{'op1, op2' if order == 'CObj' else 'op2, op1'}}, Py_{{op.upper()}}); return {{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(
PyFloat_Type.tp_richcompare({{'op1, op2' if order == 'CObj' else 'op2, op1'}}, Py_{{op.upper()}}));
{{else}} {{else}}
{{fval}} = PyLong_AsDouble({{pyval}}); {{fval}} = PyLong_AsDouble({{pyval}});
if (unlikely({{fval}} == -1.0 && PyErr_Occurred())) return NULL; if (unlikely({{fval}} == -1.0 && PyErr_Occurred())) return NULL;
...@@ -1019,7 +1115,8 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou ...@@ -1019,7 +1115,8 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou
} }
} else { } else {
{{if op in ('Eq', 'Ne')}} {{if op in ('Eq', 'Ne')}}
return PyObject_RichCompare(op1, op2, Py_{{op.upper()}}); return {{'' if ret_type.is_pyobject else '__Pyx_PyObject_IsTrueAndDecref'}}(
PyObject_RichCompare(op1, op2, Py_{{op.upper()}}));
{{elif op == 'Divide'}} {{elif op == 'Divide'}}
return (inplace ? __Pyx_PyNumber_InPlaceDivide(op1, op2) : __Pyx_PyNumber_Divide(op1, op2)); return (inplace ? __Pyx_PyNumber_InPlaceDivide(op1, op2) : __Pyx_PyNumber_Divide(op1, op2));
{{else}} {{else}}
...@@ -1029,9 +1126,9 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou ...@@ -1029,9 +1126,9 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou
{{if op in ('Eq', 'Ne')}} {{if op in ('Eq', 'Ne')}}
if (a {{c_op}} b) { if (a {{c_op}} b) {
Py_RETURN_TRUE; {{return_true}};
} else { } else {
Py_RETURN_FALSE; {{return_false}};
} }
{{else}} {{else}}
// copied from floatobject.c in Py3.5: // copied from floatobject.c in Py3.5:
......
...@@ -86,6 +86,7 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) { ...@@ -86,6 +86,7 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) {
#define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None) #define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None)
static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b); static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b);
static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*); static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*);
static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject*);
static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x); static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x);
#define __Pyx_PySequence_Tuple(obj) \ #define __Pyx_PySequence_Tuple(obj) \
...@@ -285,6 +286,14 @@ static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) { ...@@ -285,6 +286,14 @@ static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) {
else return PyObject_IsTrue(x); else return PyObject_IsTrue(x);
} }
static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject* x) {
int retval;
if (unlikely(!x)) return -1;
retval = __Pyx_PyObject_IsTrue(x);
Py_DECREF(x);
return retval;
}
static PyObject* __Pyx_PyNumber_IntOrLongWrongResultType(PyObject* result, const char* type_name) { static PyObject* __Pyx_PyNumber_IntOrLongWrongResultType(PyObject* result, const char* type_name) {
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
if (PyLong_Check(result)) { if (PyLong_Check(result)) {
......
"""
Non-test that prints debug information about the current build environment.
"""
from __future__ import print_function
import sys
cdef extern from *:
"""
#ifndef PyLong_SHIFT
#define PyLong_SHIFT 0
typedef int digit;
typedef int sdigit;
#endif
#ifndef PyLong_BASE
#define PyLong_BASE 0
#endif
#ifndef PyLong_MASK
#define PyLong_MASK 0
#endif
"""
# Python runtime
cdef long PY_VERSION_HEX
# Cython config
cdef int CYTHON_COMPILING_IN_CPYTHON
cdef int CYTHON_COMPILING_IN_PYPY
cdef int CYTHON_COMPILING_IN_PYSTON
cdef int CYTHON_USE_PYLONG_INTERNALS
cdef int CYTHON_USE_PYLIST_INTERNALS
cdef int CYTHON_USE_UNICODE_INTERNALS
cdef int CYTHON_USE_UNICODE_WRITER
cdef int CYTHON_AVOID_BORROWED_REFS
cdef int CYTHON_ASSUME_SAFE_MACROS
cdef int CYTHON_UNPACK_METHODS
cdef int CYTHON_FAST_THREAD_STATE
cdef int CYTHON_FAST_PYCALL
cdef int CYTHON_PEP489_MULTI_PHASE_INIT
cdef int CYTHON_USE_TP_FINALIZE
# C and platform specifics
cdef int SIZEOF_INT
cdef int SIZEOF_LONG
cdef int SIZEOF_SIZE_T
cdef int SIZEOF_LONG_LONG
cdef int SIZEOF_VOID_P
# PyLong internals
cdef long PyLong_BASE
cdef long PyLong_MASK
cdef int PyLong_SHIFT
cdef int digit
cdef int sdigit
print(f"""Python build environment:
Python {sys.version_info}
PY_VERSION_HEX 0x{PY_VERSION_HEX:X}
CYTHON_COMPILING_IN_CPYTHON {CYTHON_COMPILING_IN_CPYTHON}
CYTHON_COMPILING_IN_PYPY {CYTHON_COMPILING_IN_PYPY}
CYTHON_COMPILING_IN_PYSTON {CYTHON_COMPILING_IN_PYSTON}
CYTHON_USE_PYLONG_INTERNALS {CYTHON_USE_PYLONG_INTERNALS}
CYTHON_USE_PYLIST_INTERNALS {CYTHON_USE_PYLIST_INTERNALS}
CYTHON_USE_UNICODE_INTERNALS {CYTHON_USE_UNICODE_INTERNALS}
CYTHON_USE_UNICODE_WRITER {CYTHON_USE_UNICODE_WRITER}
CYTHON_AVOID_BORROWED_REFS {CYTHON_AVOID_BORROWED_REFS}
CYTHON_ASSUME_SAFE_MACROS {CYTHON_ASSUME_SAFE_MACROS}
CYTHON_UNPACK_METHODS {CYTHON_UNPACK_METHODS}
CYTHON_FAST_THREAD_STATE {CYTHON_FAST_THREAD_STATE}
CYTHON_FAST_PYCALL {CYTHON_FAST_PYCALL}
CYTHON_PEP489_MULTI_PHASE_INIT {CYTHON_PEP489_MULTI_PHASE_INIT}
CYTHON_USE_TP_FINALIZE {CYTHON_USE_TP_FINALIZE}
PyLong_BASE 0x{PyLong_BASE:X}
PyLong_MASK {PyLong_MASK:X}
PyLong_SHIFT {PyLong_SHIFT}
sizeof(digit) {sizeof(digit)}
sizeof(sdigit) {sizeof(sdigit)}
SIZEOF_INT {SIZEOF_INT} ({sizeof(int)})
SIZEOF_LONG {SIZEOF_LONG} ({sizeof(long)})
SIZEOF_SIZE_T {SIZEOF_SIZE_T} ({sizeof(Py_ssize_t)}, {getattr(sys, 'maxsize', getattr(sys, 'maxint', None))})
SIZEOF_LONG_LONG {SIZEOF_LONG_LONG} ({sizeof(long long)})
SIZEOF_VOID_P {SIZEOF_VOID_P} ({sizeof(void*)})
""")
# mode: run # mode: run
cimport cython
def bigint(x): def bigint(x):
# avoid 'L' postfix in Py2.x # avoid 'L' postfix in Py2.x
...@@ -10,6 +12,7 @@ def bigints(x): ...@@ -10,6 +12,7 @@ def bigints(x):
print(str(x).replace('L', '')) print(str(x).replace('L', ''))
@cython.test_assert_path_exists('//IntBinopNode')
def or_obj(obj2, obj3): def or_obj(obj2, obj3):
""" """
>>> or_obj(2, 3) >>> or_obj(2, 3)
...@@ -19,6 +22,7 @@ def or_obj(obj2, obj3): ...@@ -19,6 +22,7 @@ def or_obj(obj2, obj3):
return obj1 return obj1
@cython.test_fail_if_path_exists('//IntBinopNode')
def or_int(obj2): def or_int(obj2):
""" """
>>> or_int(1) >>> or_int(1)
...@@ -30,6 +34,7 @@ def or_int(obj2): ...@@ -30,6 +34,7 @@ def or_int(obj2):
return obj1 return obj1
@cython.test_assert_path_exists('//IntBinopNode')
def xor_obj(obj2, obj3): def xor_obj(obj2, obj3):
""" """
>>> xor_obj(2, 3) >>> xor_obj(2, 3)
...@@ -39,6 +44,7 @@ def xor_obj(obj2, obj3): ...@@ -39,6 +44,7 @@ def xor_obj(obj2, obj3):
return obj1 return obj1
@cython.test_fail_if_path_exists('//IntBinopNode')
def xor_int(obj2): def xor_int(obj2):
""" """
>>> xor_int(2) >>> xor_int(2)
...@@ -50,6 +56,7 @@ def xor_int(obj2): ...@@ -50,6 +56,7 @@ def xor_int(obj2):
return obj1 return obj1
@cython.test_assert_path_exists('//IntBinopNode')
def and_obj(obj2, obj3): def and_obj(obj2, obj3):
""" """
>>> and_obj(2, 3) >>> and_obj(2, 3)
...@@ -59,6 +66,7 @@ def and_obj(obj2, obj3): ...@@ -59,6 +66,7 @@ def and_obj(obj2, obj3):
return obj1 return obj1
@cython.test_fail_if_path_exists('//IntBinopNode')
def and_int(obj2): def and_int(obj2):
""" """
>>> and_int(1) >>> and_int(1)
...@@ -70,6 +78,7 @@ def and_int(obj2): ...@@ -70,6 +78,7 @@ def and_int(obj2):
return obj1 return obj1
@cython.test_assert_path_exists('//IntBinopNode')
def lshift_obj(obj2, obj3): def lshift_obj(obj2, obj3):
""" """
>>> lshift_obj(2, 3) >>> lshift_obj(2, 3)
...@@ -79,6 +88,7 @@ def lshift_obj(obj2, obj3): ...@@ -79,6 +88,7 @@ def lshift_obj(obj2, obj3):
return obj1 return obj1
@cython.test_assert_path_exists('//IntBinopNode')
def rshift_obj(obj2, obj3): def rshift_obj(obj2, obj3):
""" """
>>> rshift_obj(2, 3) >>> rshift_obj(2, 3)
...@@ -88,6 +98,7 @@ def rshift_obj(obj2, obj3): ...@@ -88,6 +98,7 @@ def rshift_obj(obj2, obj3):
return obj1 return obj1
@cython.test_fail_if_path_exists('//IntBinopNode')
def rshift_int(obj2): def rshift_int(obj2):
""" """
>>> rshift_int(2) >>> rshift_int(2)
...@@ -134,6 +145,10 @@ def rshift_int(obj2): ...@@ -134,6 +145,10 @@ def rshift_int(obj2):
return obj1 return obj1
@cython.test_assert_path_exists(
'//SingleAssignmentNode//IntBinopNode',
'//SingleAssignmentNode//PythonCapiCallNode',
)
def lshift_int(obj): def lshift_int(obj):
""" """
>>> lshift_int(0) >>> lshift_int(0)
...@@ -189,6 +204,10 @@ def lshift_int(obj): ...@@ -189,6 +204,10 @@ def lshift_int(obj):
return r1, r2, r3, r4 return r1, r2, r3, r4
@cython.test_assert_path_exists(
'//IntBinopNode',
'//IntBinopNode//IntBinopNode',
)
def mixed_obj(obj2, obj3): def mixed_obj(obj2, obj3):
""" """
>>> mixed_obj(2, 3) >>> mixed_obj(2, 3)
...@@ -198,6 +217,13 @@ def mixed_obj(obj2, obj3): ...@@ -198,6 +217,13 @@ def mixed_obj(obj2, obj3):
return obj1 return obj1
@cython.test_assert_path_exists(
'//IntBinopNode',
'//IntBinopNode//PythonCapiCallNode',
)
@cython.test_fail_if_path_exists(
'//IntBinopNode//IntBinopNode',
)
def mixed_int(obj2): def mixed_int(obj2):
""" """
>>> mixed_int(2) >>> mixed_int(2)
...@@ -209,3 +235,244 @@ def mixed_int(obj2): ...@@ -209,3 +235,244 @@ def mixed_int(obj2):
""" """
obj1 = (obj2 ^ 0x10) | (obj2 & 0x01) obj1 = (obj2 ^ 0x10) | (obj2 & 0x01)
return obj1 return obj1
@cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_fail_if_path_exists(
'//IntBinopNode',
'//PrimaryCmpNode',
)
def equals(obj2):
"""
>>> equals(2)
True
>>> equals(0)
False
>>> equals(-1)
False
"""
result = obj2 == 2
return result
@cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_fail_if_path_exists(
'//IntBinopNode',
'//PrimaryCmpNode',
)
def not_equals(obj2):
"""
>>> not_equals(2)
False
>>> not_equals(0)
True
>>> not_equals(-1)
True
"""
result = obj2 != 2
return result
@cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_assert_path_exists('//PrimaryCmpNode')
def equals_many(obj2):
"""
>>> equals_many(-2)
(False, False, False, False, False, False, False, False, False, False, False, False, False, False, False)
>>> equals_many(0)
(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False)
>>> equals_many(1)
(False, True, False, False, False, False, False, False, False, False, False, False, False, False, False)
>>> equals_many(-1)
(False, False, True, False, False, False, False, False, False, False, False, False, False, False, False)
>>> equals_many(2**30)
(False, False, False, True, False, False, False, False, False, False, False, False, False, False, False)
>>> equals_many(-2**30)
(False, False, False, False, True, False, False, False, False, False, False, False, False, False, False)
>>> equals_many(2**30-1)
(False, False, False, False, False, True, False, False, False, False, False, False, False, False, False)
>>> equals_many(-2**30+1)
(False, False, False, False, False, False, True, False, False, False, False, False, False, False, False)
>>> equals_many(2**32)
(False, False, False, False, False, False, False, True, False, False, False, False, False, False, False)
>>> equals_many(-2**32)
(False, False, False, False, False, False, False, False, True, False, False, False, False, False, False)
>>> equals_many(2**45-1)
(False, False, False, False, False, False, False, False, False, True, False, False, False, False, False)
>>> equals_many(-2**45+1)
(False, False, False, False, False, False, False, False, False, False, True, False, False, False, False)
>>> equals_many(2**64)
(False, False, False, False, False, False, False, False, False, False, False, True, False, False, False)
>>> equals_many(-2**64)
(False, False, False, False, False, False, False, False, False, False, False, False, True, False, False)
>>> equals_many(2**64-1)
(False, False, False, False, False, False, False, False, False, False, False, False, False, True, False)
>>> equals_many(-2**64+1)
(False, False, False, False, False, False, False, False, False, False, False, False, False, False, True)
"""
cdef bint x, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o
a = obj2 == 0
x = 0 == obj2
assert a == x
b = obj2 == 1
x = 1 == obj2
assert b == x
c = obj2 == -1
x = -1 == obj2
assert c == x
d = obj2 == 2**30
x = 2**30 == obj2
assert d == x
e = obj2 == -2**30
x = -2**30 == obj2
assert e == x
f = obj2 == 2**30-1
x = 2**30-1 == obj2
assert f == x
g = obj2 == -2**30+1
x = -2**30+1 == obj2
assert g == x
h = obj2 == 2**32
x = 2**32 == obj2
assert h == x
i = obj2 == -2**32
x = -2**32 == obj2
assert i == x
j = obj2 == 2**45-1
x = 2**45-1 == obj2
assert j == x
k = obj2 == -2**45+1
x = -2**45+1 == obj2
assert k == x
l = obj2 == 2**64
x = 2**64 == obj2
assert l == x
m = obj2 == -2**64
x = -2**64 == obj2
assert m == x
n = obj2 == 2**64-1
x = 2**64-1 == obj2
assert n == x
o = obj2 == -2**64+1
x = -2**64+1 == obj2
assert o == x
return (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)
@cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_assert_path_exists('//PrimaryCmpNode')
def not_equals_many(obj2):
"""
>>> not_equals_many(-2)
(False, False, False, False, False, False, False, False, False, False, False, False, False, False, False)
>>> not_equals_many(0)
(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False)
>>> not_equals_many(1)
(False, True, False, False, False, False, False, False, False, False, False, False, False, False, False)
>>> not_equals_many(-1)
(False, False, True, False, False, False, False, False, False, False, False, False, False, False, False)
>>> not_equals_many(2**30)
(False, False, False, True, False, False, False, False, False, False, False, False, False, False, False)
>>> not_equals_many(-2**30)
(False, False, False, False, True, False, False, False, False, False, False, False, False, False, False)
>>> not_equals_many(2**30-1)
(False, False, False, False, False, True, False, False, False, False, False, False, False, False, False)
>>> not_equals_many(-2**30+1)
(False, False, False, False, False, False, True, False, False, False, False, False, False, False, False)
>>> not_equals_many(2**32)
(False, False, False, False, False, False, False, True, False, False, False, False, False, False, False)
>>> not_equals_many(-2**32)
(False, False, False, False, False, False, False, False, True, False, False, False, False, False, False)
>>> not_equals_many(2**45-1)
(False, False, False, False, False, False, False, False, False, True, False, False, False, False, False)
>>> not_equals_many(-2**45+1)
(False, False, False, False, False, False, False, False, False, False, True, False, False, False, False)
>>> not_equals_many(2**64)
(False, False, False, False, False, False, False, False, False, False, False, True, False, False, False)
>>> not_equals_many(-2**64)
(False, False, False, False, False, False, False, False, False, False, False, False, True, False, False)
>>> not_equals_many(2**64-1)
(False, False, False, False, False, False, False, False, False, False, False, False, False, True, False)
>>> not_equals_many(-2**64+1)
(False, False, False, False, False, False, False, False, False, False, False, False, False, False, True)
"""
cdef bint a, b, c, d, e, f, g, h, i, j, k, l, m, n, o
a = obj2 != 0
x = 0 != obj2
assert a == x
b = obj2 != 1
x = 1 != obj2
assert b == x
c = obj2 != -1
x = -1 != obj2
assert c == x
d = obj2 != 2**30
x = 2**30 != obj2
assert d == x
e = obj2 != -2**30
x = -2**30 != obj2
assert e == x
f = obj2 != 2**30-1
x = 2**30-1 != obj2
assert f == x
g = obj2 != -2**30+1
x = -2**30+1 != obj2
assert g == x
h = obj2 != 2**32
x = 2**32 != obj2
assert h == x
i = obj2 != -2**32
x = -2**32 != obj2
assert i == x
j = obj2 != 2**45-1
x = 2**45-1 != obj2
assert j == x
k = obj2 != -2**45+1
x = -2**45+1 != obj2
assert k == x
l = obj2 != 2**64
x = 2**64 != obj2
assert l == x
m = obj2 != -2**64
x = -2**64 != obj2
assert m == x
n = obj2 != 2**64-1
x = 2**64-1 != obj2
assert n == x
o = obj2 != -2**64+1
x = -2**64+1 != obj2
assert o == x
return tuple(not x for x in (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o))
@cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_fail_if_path_exists(
'//IntBinopNode',
'//PrimaryCmpNode',
)
def equals_zero(obj2):
"""
>>> equals_zero(2)
False
>>> equals_zero(0)
True
>>> equals_zero(-1)
False
"""
result = obj2 == 0
return result
def truthy(obj2):
"""
>>> truthy(2)
True
>>> truthy(0)
False
>>> truthy(-1)
True
"""
if obj2:
return True
else:
return False
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