Commit bf03077f authored by Stefan Behnel's avatar Stefan Behnel

extend PyLong optimisations to everything that fits into a C long (Py2.7 uses...

extend PyLong optimisations to everything that fits into a C long (Py2.7 uses 15 bit digits even on 64 bit platforms, i.e. C long covers 4 digits)
parent 2acc6660
......@@ -1000,7 +1000,7 @@ class GlobalState(object):
# utility_code_def
#
code = self.parts['utility_code_def']
code.put(UtilityCode.load_as_string("TypeConversions", "TypeConversion.c")[1])
code.put(TempitaUtilityCode.load_cached("TypeConversions", "TypeConversion.c").impl)
code.putln("")
def __getitem__(self, key):
......
......@@ -493,6 +493,7 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long
//@requires: TypeConversion.c::PyLongInternals
#if CYTHON_COMPILING_IN_CPYTHON
{{py: from Cython.Utility import pylong_join }}
{{py: pyval, ival = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }}
{{py: c_op = {'Add': '+', 'Subtract': '-', 'Remainder': '%', 'Or': '|', 'Xor': '^', 'And': '&', 'Rshift': '>>'}[op] }}
......@@ -532,27 +533,26 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO
if (likely(PyLong_CheckExact({{pyval}}))) {
const long {{'a' if order == 'CObj' else 'b'}} = intval;
long x, {{ival}};
const digit* digits = ((PyLongObject*){{pyval}})->ob_digit;
const Py_ssize_t size = Py_SIZE({{pyval}});
switch (size) {
case 0: {{ival}} = 0; break;
case -1: {{if c_op != '%'}}
{{ival}} = -(sdigit)((PyLongObject*){{pyval}})->ob_digit[0]; break;
{{ival}} = -(sdigit)digits[0]; break;
{{endif}}
// fall through to positive calculation for '%'
case 1: {{ival}} = ((PyLongObject*){{pyval}})->ob_digit[0]; break;
case -2: {{if c_op != '%'}}
if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
{{ival}} = -(long) ((((unsigned long)((PyLongObject*){{pyval}})->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*){{pyval}})->ob_digit[0]);
case 1: {{ival}} = digits[0]; break;
{{for _size in (2, 3, 4)}}
{{for _case in (-_size, _size)}}
case {{_case}}: {{if c_op != '%' or _case > 0}}
if (8 * sizeof(long) - 1 > {{_size}} * PyLong_SHIFT) {
{{ival}} = {{'-' if _case < 0 else ''}}(long) {{pylong_join(_size, 'digits')}};
break;
}
{{endif}}
// fall through to positive calculation for '%'
case 2:
if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
{{ival}} = (long) ((((unsigned long)((PyLongObject*){{pyval}})->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*){{pyval}})->ob_digit[0]);
break;
}
// fall through if two platform digits don't fit into a long
// in negative case, fall through to positive calculation for '%'
{{endfor}}
{{endfor}}
default: return PyLong_Type.tp_as_number->nb_{{op.lower()}}(op1, op2);
}
{{if c_op == '%'}}
......@@ -596,6 +596,7 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou
//@requires: TypeConversion.c::PyLongInternals
#if CYTHON_COMPILING_IN_CPYTHON
{{py: from Cython.Utility import pylong_join }}
{{py: pyval, fval = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }}
{{py: c_op = '+' if op == 'Add' else '-' }}
......@@ -615,22 +616,25 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou
if (likely(PyLong_CheckExact({{pyval}}))) {
#if CYTHON_USE_PYLONG_INTERNALS
const digit* digits = ((PyLongObject*){{pyval}})->ob_digit;
const Py_ssize_t size = Py_SIZE({{pyval}});
switch (size) {
case -1: {{fval}} = -(double)((PyLongObject*){{pyval}})->ob_digit[0]; break;
case 0: {{fval}} = 0.0; break;
case 1: {{fval}} = (double)((PyLongObject*){{pyval}})->ob_digit[0]; break;
case 2:
case -2:
if (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT) {
{{fval}} = (double) ((((unsigned long)((PyLongObject*){{pyval}})->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*){{pyval}})->ob_digit[0]);
case -1: {{fval}} = -(double) digits[0]; break;
case 1: {{fval}} = (double) digits[0]; break;
{{for _size in (2, 3, 4)}}
case -{{_size}}:
case {{_size}}:
if (8 * sizeof(unsigned long) > {{_size}} * PyLong_SHIFT && ((8 * sizeof(unsigned long) < 53) || ({{_size-1}} * PyLong_SHIFT < 53))) {
{{fval}} = (double) {{pylong_join(_size, 'digits')}};
// let CPython do its own float rounding from 2**53 on (max. consecutive integer in double float)
if ((8 * sizeof(unsigned long) < 53) || (2 * PyLong_SHIFT < 53) || ({{fval}} < (double) (1L<<53))) {
if (size == -2)
if ((8 * sizeof(unsigned long) < 53) || ({{_size}} * PyLong_SHIFT < 53) || ({{fval}} < (double) (1L<<53))) {
if (size == {{-_size}})
{{fval}} = -{{fval}};
break;
}
}
{{endfor}}
// fall through if two platform digits don't fit into a double
default: {{fval}} = PyLong_AsDouble({{pyval}});
if (unlikely({{fval}} == -1 && PyErr_Occurred())) return NULL;
......
......@@ -280,6 +280,8 @@ static CYTHON_INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x) {
return res;
}
{{py: from Cython.Utility import pylong_join }}
static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {
Py_ssize_t ival;
PyObject *x;
......@@ -293,20 +295,25 @@ static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {
#endif
if (likely(PyLong_CheckExact(b))) {
#if CYTHON_USE_PYLONG_INTERNALS
switch (Py_SIZE(b)) {
const digit* digits = ((PyLongObject*)b)->ob_digit;
const Py_ssize_t size = Py_SIZE(b);
switch (size) {
case 0: return 0;
case 1: return ((PyLongObject*)b)->ob_digit[0];
case -1: return -(sdigit)((PyLongObject*)b)->ob_digit[0];
case 2:
if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) {
return (Py_ssize_t) ((((size_t)((PyLongObject*)b)->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*)b)->ob_digit[0]);
}
break;
case -2:
if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) {
return -(Py_ssize_t) ((((size_t)((PyLongObject*)b)->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*)b)->ob_digit[0]);
}
break;
case 1: return digits[0];
case -1: return -(sdigit) digits[0];
default:
// move the substantially less common cases out of the way
switch (size) {
{{for _size in (2, 3, 4)}}
{{for _case in (_size, -_size)}}
case {{_case}}:
if (8 * sizeof(Py_ssize_t) > {{_size}} * PyLong_SHIFT) {
return (Py_ssize_t) {{pylong_join(_size, 'digits', 'size_t')}};
}
break;
{{endfor}}
{{endfor}}
}
}
#endif
return PyLong_AsSsize_t(b);
......@@ -540,6 +547,8 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *);
//@requires: CIntFromPyVerify
//@requires: PyLongInternals
{{py: from Cython.Utility import pylong_join }}
static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) {
const {{TYPE}} neg_one = ({{TYPE}}) -1, const_zero = 0;
const int is_unsigned = neg_one > const_zero;
......@@ -559,15 +568,17 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) {
if (likely(PyLong_Check(x))) {
if (is_unsigned) {
#if CYTHON_USE_PYLONG_INTERNALS
const digit* digits = ((PyLongObject*)x)->ob_digit;
switch (Py_SIZE(x)) {
case 0: return 0;
case 1: __PYX_VERIFY_RETURN_INT({{TYPE}}, digit, ((PyLongObject*)x)->ob_digit[0])
case 2:
if ((8 * sizeof({{TYPE}}) > PyLong_SHIFT) && (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
__PYX_VERIFY_RETURN_INT({{TYPE}}, unsigned long,
(((unsigned long)((PyLongObject*)x)->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*)x)->ob_digit[0])
case 1: __PYX_VERIFY_RETURN_INT({{TYPE}}, digit, digits[0])
{{for _size in (2, 3, 4)}}
case {{_size}}:
if ((8 * sizeof({{TYPE}}) > {{_size-1}} * PyLong_SHIFT) && (8 * sizeof(unsigned long) > {{_size}} * PyLong_SHIFT)) {
__PYX_VERIFY_RETURN_INT({{TYPE}}, unsigned long, {{pylong_join(_size, 'digits')}})
}
break;
{{endfor}}
}
#endif
#if CYTHON_COMPILING_IN_CPYTHON
......@@ -583,22 +594,20 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) {
} else {
// signed
#if CYTHON_USE_PYLONG_INTERNALS
const digit* digits = ((PyLongObject*)x)->ob_digit;
switch (Py_SIZE(x)) {
case 0: return 0;
case -1: __PYX_VERIFY_RETURN_INT({{TYPE}}, sdigit, -(sdigit) ((PyLongObject*)x)->ob_digit[0])
case 1: __PYX_VERIFY_RETURN_INT({{TYPE}}, digit, +(((PyLongObject*)x)->ob_digit[0]))
case 2:
if ((8 * sizeof({{TYPE}}) > PyLong_SHIFT) && (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
__PYX_VERIFY_RETURN_INT({{TYPE}}, unsigned long,
(((unsigned long)((PyLongObject*)x)->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*)x)->ob_digit[0])
}
break;
case -2:
if ((8 * sizeof({{TYPE}}) > 2 * PyLong_SHIFT) && (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
__PYX_VERIFY_RETURN_INT({{TYPE}}, long,
-(long) (((unsigned long)((PyLongObject*)x)->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*)x)->ob_digit[0])
case -1: __PYX_VERIFY_RETURN_INT({{TYPE}}, sdigit, -(sdigit) digits[0])
case 1: __PYX_VERIFY_RETURN_INT({{TYPE}}, digit, +digits[0])
{{for _size in (2, 3, 4)}}
{{for _case in (-_size, _size)}}
case {{_case}}:
if ((8 * sizeof({{TYPE}}) > {{_size if _case < 0 else _size-1}} * PyLong_SHIFT) && (8 * sizeof(unsigned long) > {{_size}} * PyLong_SHIFT)) {
__PYX_VERIFY_RETURN_INT({{TYPE}}, {{'long' if _case < 0 else 'unsigned long'}}, {{'-(long) ' if _case < 0 else ''}}{{pylong_join(_size, 'digits')}})
}
break;
{{endfor}}
{{endfor}}
}
#endif
if (sizeof({{TYPE}}) <= sizeof(long)) {
......
def pylong_join(count, digits_ptr='digits', join_type='unsigned long'):
"""
Generate an unrolled shift-then-or loop over the first 'count' digits.
Assumes that they fit into 'join_type'.
"""
return ('(' * (count * 2) + "(%s)" % join_type + ' | '.join(
"%s[%d])%s)" % (digits_ptr, _i, " << PyLong_SHIFT" if _i else '')
for _i in range(count-1, -1, -1)))
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