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

Avoid useless "div by zero" checks in optimised constant division code. Also...

Avoid useless "div by zero" checks in optimised constant division code. Also avoid using a (named) C macro where code generation will do.
See GH-2820.
parent 13617b61
......@@ -805,7 +805,6 @@ static {{c_ret_type}} __Pyx_PyInt_{{'' if ret_type.is_pyobject else 'Bool'}}{{op
{{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: cfunc_name = '__Pyx_PyInt_%s%s%s' % ('' if ret_type.is_pyobject else 'Bool', op, order)}}
{{py: zerodiv_check = lambda operand, _cfunc_name=cfunc_name: '%s_ZeroDivisionError(%s)' % (_cfunc_name, operand)}}
{{py:
c_op = {
'Add': '+', 'Subtract': '-', 'Multiply': '*', 'Remainder': '%', 'TrueDivide': '/', 'FloorDivide': '/',
......@@ -813,21 +812,19 @@ c_op = {
'Eq': '==', 'Ne': '!=',
}[op]
}}
{{if op in ('TrueDivide', 'FloorDivide', 'Remainder')}}
#if PY_MAJOR_VERSION < 3 || CYTHON_USE_PYLONG_INTERNALS
#define {{zerodiv_check('operand')}} \
if (unlikely(zerodivision_check && ((operand) == 0))) { \
PyErr_SetString(PyExc_ZeroDivisionError, "integer division{{if op == 'Remainder'}} or modulo{{endif}} by zero"); \
return NULL; \
}
#endif
{{endif}}
{{py:
def zerodiv_check(operand, optype='integer', _is_mod=op == 'Remainder', _needs_check=(order == 'CObj' and c_op in '%/')):
return (((
'if (unlikely(zerodivision_check && ((%s) == 0))) {'
' PyErr_SetString(PyExc_ZeroDivisionError, "%s division%s by zero");'
' return NULL;'
'}') % (operand, optype, ' or modulo' if _is_mod else '')
) if _needs_check else '')
}}
static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, CYTHON_UNUSED long intval, int inplace, int zerodivision_check) {
// Prevent "unused" warnings.
(void)inplace;
(void)zerodivision_check;
(void)inplace; (void)zerodivision_check;
{{if op in ('Eq', 'Ne')}}
if (op1 == op2) {
......@@ -927,7 +924,10 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, CYTHON_UNUSED
{{endif}}
// special cases for 0: + - * % / // | ^ & >> <<
if (unlikely(size == 0)) {
{{if order == 'CObj' and c_op in '+-|^>><<'}}
{{if order == 'CObj' and c_op in '%/'}}
// division by zero!
{{zerodiv_check('0')}}
{{elif order == 'CObj' and c_op in '+-|^>><<'}}
// x == x+0 == x-0 == x|0 == x^0 == x>>0 == x<<0
return __Pyx_NewRef(op1);
{{elif order == 'CObj' and c_op in '*&'}}
......@@ -999,19 +999,16 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, CYTHON_UNUSED
return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2);
#endif
{{elif c_op == '%'}}
{{zerodiv_check('b')}}
// see ExprNodes.py :: mod_int_utility_code
x = a % b;
x += ((x != 0) & ((x ^ b) < 0)) * b;
{{elif op == 'TrueDivide'}}
{{zerodiv_check('b')}}
if ((8 * sizeof(long) <= 53 || likely(labs({{ival}}) <= ((PY_LONG_LONG)1 << 53)))
|| __Pyx_sst_abs(size) <= 52 / PyLong_SHIFT) {
return PyFloat_FromDouble((double)a / (double)b);
}
return PyLong_Type.tp_as_number->nb_{{slot_name}}(op1, op2);
{{elif op == 'FloorDivide'}}
{{zerodiv_check('b')}}
{
long q, r;
// see ExprNodes.py :: div_int_utility_code
......@@ -1076,12 +1073,7 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, CYTHON_UNUSED
}
{{else}}
double result;
{{if op == 'TrueDivide'}}
if (unlikely(zerodivision_check && b == 0)) {
PyErr_SetString(PyExc_ZeroDivisionError, "float division by zero");
return NULL;
}
{{endif}}
{{zerodiv_check('b', 'float')}}
// copied from floatobject.c in Py3.5:
PyFPE_START_PROTECT("{{op.lower() if not op.endswith('Divide') else 'divide'}}", return NULL)
result = ((double)a) {{c_op}} (double)b;
......@@ -1122,27 +1114,27 @@ static {{c_ret_type}} __Pyx_PyFloat_{{'' if ret_type.is_pyobject else 'Bool'}}{{
{{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: cfunc_name = '__Pyx_PyFloat_%s%s%s' % ('' if ret_type.is_pyobject else 'Bool', op, order) }}
{{py: zerodiv_check = lambda operand, _cfunc_name=cfunc_name: '%s_ZeroDivisionError(%s)' % (_cfunc_name, operand)}}
{{py:
c_op = {
'Add': '+', 'Subtract': '-', 'TrueDivide': '/', 'Divide': '/', 'Remainder': '%',
'Eq': '==', 'Ne': '!=',
}[op]
}}
{{if order == 'CObj' and c_op in '%/'}}
#define {{zerodiv_check('operand')}} if (unlikely(zerodivision_check && ((operand) == 0))) { \
PyErr_SetString(PyExc_ZeroDivisionError, "float division{{if op == 'Remainder'}} or modulo{{endif}} by zero"); \
return NULL; \
}
{{endif}}
{{py:
def zerodiv_check(operand, _is_mod=op == 'Remainder', _needs_check=(order == 'CObj' and c_op in '%/')):
return (((
'if (unlikely(zerodivision_check && ((%s) == 0.0))) {'
' PyErr_SetString(PyExc_ZeroDivisionError, "float division%s by zero");'
' return NULL;'
'}') % (operand, ' or modulo' if _is_mod else '')
) if _needs_check else '')
}}
static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, double floatval, int inplace, int zerodivision_check) {
const double {{'a' if order == 'CObj' else 'b'}} = floatval;
double {{fval}}{{if op not in ('Eq', 'Ne')}}, result{{endif}};
// Prevent "unused" warnings.
(void)inplace;
(void)zerodivision_check;
(void)inplace; (void)zerodivision_check;
{{if op in ('Eq', 'Ne')}}
if (op1 == op2) {
......@@ -1152,13 +1144,13 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, double floatv
if (likely(PyFloat_CheckExact({{pyval}}))) {
{{fval}} = PyFloat_AS_DOUBLE({{pyval}});
{{if order == 'CObj' and c_op in '%/'}}{{zerodiv_check(fval)}}{{endif}}
{{zerodiv_check(fval)}}
} else
#if PY_MAJOR_VERSION < 3
if (likely(PyInt_CheckExact({{pyval}}))) {
{{fval}} = (double) PyInt_AS_LONG({{pyval}});
{{if order == 'CObj' and c_op in '%/'}}{{zerodiv_check(fval)}}{{endif}}
{{zerodiv_check(fval)}}
} else
#endif
......@@ -1167,7 +1159,7 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, double floatv
const digit* digits = ((PyLongObject*){{pyval}})->ob_digit;
const Py_ssize_t size = Py_SIZE({{pyval}});
switch (size) {
case 0: {{if order == 'CObj' and c_op in '%/'}}{{zerodiv_check('0')}}{{else}}{{fval}} = 0.0;{{endif}} break;
case 0: {{fval}} = 0.0; {{zerodiv_check(fval)}} break;
case -1: {{fval}} = -(double) digits[0]; break;
case 1: {{fval}} = (double) digits[0]; break;
{{for _size in (2, 3, 4)}}
......@@ -1199,7 +1191,11 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, double floatv
{{else}}
{{fval}} = PyLong_AsDouble({{pyval}});
if (unlikely({{fval}} == -1.0 && PyErr_Occurred())) return NULL;
{{if order == 'CObj' and c_op in '%/'}}{{zerodiv_check(fval)}}{{endif}}
{{if zerodiv_check(fval)}}
#if !CYTHON_USE_PYLONG_INTERNALS
{{zerodiv_check(fval)}}
#endif
{{endif}}
{{endif}}
}
} else {
......@@ -1221,7 +1217,6 @@ static {{c_ret_type}} {{cfunc_name}}(PyObject *op1, PyObject *op2, double floatv
}
{{else}}
// copied from floatobject.c in Py3.5:
{{if order == 'CObj' and c_op in '%/'}}{{zerodiv_check('b')}}{{endif}}
PyFPE_START_PROTECT("{{op.lower() if not op.endswith('Divide') else 'divide'}}", return NULL)
{{if c_op == '%'}}
result = fmod(a, b);
......
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