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