summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Behnel <stefan_ml@behnel.de>2018-10-14 14:57:06 +0200
committerStefan Behnel <stefan_ml@behnel.de>2018-10-14 14:57:06 +0200
commit788fb7332935a5f7695a6dea3ccf0a5e01df2756 (patch)
tree7d082a3fd988dca1f5327a14ce2035eadb4cf066
parentd5aef03683f77400cd56160852c650de09d0a8bb (diff)
downloadcython-smarter_bounds_checks.tar.gz
Optimise internal bounds checks like "0 <= i <= limit" using a single unsigned comparison.smarter_bounds_checks
See https://bugs.python.org/issue28397
-rw-r--r--Cython/Utility/MemoryView_C.c2
-rw-r--r--Cython/Utility/ObjectHandling.c8
-rw-r--r--Cython/Utility/Optimize.c2
-rw-r--r--Cython/Utility/StringTools.c16
-rw-r--r--Cython/Utility/TypeConversion.c43
5 files changed, 43 insertions, 28 deletions
diff --git a/Cython/Utility/MemoryView_C.c b/Cython/Utility/MemoryView_C.c
index b1bd74aff..b7bd6a04e 100644
--- a/Cython/Utility/MemoryView_C.c
+++ b/Cython/Utility/MemoryView_C.c
@@ -857,7 +857,7 @@ if (unlikely(__pyx_memoryview_slice_memviewslice(
if ({{wraparound}} && (__pyx_tmp_idx < 0))
__pyx_tmp_idx += __pyx_tmp_shape;
- if ({{boundscheck}} && (__pyx_tmp_idx < 0 || __pyx_tmp_idx >= __pyx_tmp_shape)) {
+ if ({{boundscheck}} && !__Pyx_is_valid_index(__pyx_tmp_idx, __pyx_tmp_shape)) {
{{if not have_gil}}
#ifdef WITH_THREAD
PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();
diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c
index e388410a6..b147cb33e 100644
--- a/Cython/Utility/ObjectHandling.c
+++ b/Cython/Utility/ObjectHandling.c
@@ -398,7 +398,7 @@ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_{{type}}_Fast(PyObject *o, Py_ss
if (wraparound & unlikely(i < 0)) {
wrapped_i += Py{{type}}_GET_SIZE(o);
}
- if ((!boundscheck) || likely((0 <= wrapped_i) & (wrapped_i < Py{{type}}_GET_SIZE(o)))) {
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(wrapped_i, Py{{type}}_GET_SIZE(o)))) {
PyObject *r = Py{{type}}_GET_ITEM(o, wrapped_i);
Py_INCREF(r);
return r;
@@ -416,7 +416,7 @@ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i,
#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS
if (is_list || PyList_CheckExact(o)) {
Py_ssize_t n = ((!wraparound) | likely(i >= 0)) ? i : i + PyList_GET_SIZE(o);
- if ((!boundscheck) || (likely((n >= 0) & (n < PyList_GET_SIZE(o))))) {
+ if ((!boundscheck) || (likely(__Pyx_is_valid_index(n, PyList_GET_SIZE(o))))) {
PyObject *r = PyList_GET_ITEM(o, n);
Py_INCREF(r);
return r;
@@ -424,7 +424,7 @@ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i,
}
else if (PyTuple_CheckExact(o)) {
Py_ssize_t n = ((!wraparound) | likely(i >= 0)) ? i : i + PyTuple_GET_SIZE(o);
- if ((!boundscheck) || likely((n >= 0) & (n < PyTuple_GET_SIZE(o)))) {
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(n, PyTuple_GET_SIZE(o)))) {
PyObject *r = PyTuple_GET_ITEM(o, n);
Py_INCREF(r);
return r;
@@ -482,7 +482,7 @@ static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObje
#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS
if (is_list || PyList_CheckExact(o)) {
Py_ssize_t n = (!wraparound) ? i : ((likely(i >= 0)) ? i : i + PyList_GET_SIZE(o));
- if ((!boundscheck) || likely((n >= 0) & (n < PyList_GET_SIZE(o)))) {
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(n, PyList_GET_SIZE(o)))) {
PyObject* old = PyList_GET_ITEM(o, n);
Py_INCREF(v);
PyList_SET_ITEM(o, n, v);
diff --git a/Cython/Utility/Optimize.c b/Cython/Utility/Optimize.c
index 4f4061a63..3d5be8f00 100644
--- a/Cython/Utility/Optimize.c
+++ b/Cython/Utility/Optimize.c
@@ -165,7 +165,7 @@ static PyObject* __Pyx__PyList_PopIndex(PyObject* L, PyObject* py_ix, Py_ssize_t
if (cix < 0) {
cix += size;
}
- if (likely(0 <= cix && cix < size)) {
+ if (likely(__Pyx_is_valid_index(cix, size))) {
PyObject* v = PyList_GET_ITEM(L, cix);
Py_SIZE(L) -= 1;
size -= 1;
diff --git a/Cython/Utility/StringTools.c b/Cython/Utility/StringTools.c
index 2489e6630..4862a25a5 100644
--- a/Cython/Utility/StringTools.c
+++ b/Cython/Utility/StringTools.c
@@ -331,7 +331,7 @@ static CYTHON_INLINE int __Pyx_GetItemInt_ByteArray_Fast(PyObject* string, Py_ss
if (wraparound | boundscheck) {
length = PyByteArray_GET_SIZE(string);
if (wraparound & unlikely(i < 0)) i += length;
- if ((!boundscheck) || likely((0 <= i) & (i < length))) {
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(i, length))) {
return (unsigned char) (PyByteArray_AS_STRING(string)[i]);
} else {
PyErr_SetString(PyExc_IndexError, "bytearray index out of range");
@@ -361,7 +361,7 @@ static CYTHON_INLINE int __Pyx_SetItemInt_ByteArray_Fast(PyObject* string, Py_ss
if (wraparound | boundscheck) {
length = PyByteArray_GET_SIZE(string);
if (wraparound & unlikely(i < 0)) i += length;
- if ((!boundscheck) || likely((0 <= i) & (i < length))) {
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(i, length))) {
PyByteArray_AS_STRING(string)[i] = (char) v;
return 0;
} else {
@@ -394,7 +394,7 @@ static CYTHON_INLINE Py_UCS4 __Pyx_GetItemInt_Unicode_Fast(PyObject* ustring, Py
if (wraparound | boundscheck) {
length = __Pyx_PyUnicode_GET_LENGTH(ustring);
if (wraparound & unlikely(i < 0)) i += length;
- if ((!boundscheck) || likely((0 <= i) & (i < length))) {
+ if ((!boundscheck) || likely(__Pyx_is_valid_index(i, length))) {
return __Pyx_PyUnicode_READ_CHAR(ustring, i);
} else {
PyErr_SetString(PyExc_IndexError, "string index out of range");
@@ -752,15 +752,15 @@ static CYTHON_INLINE char __Pyx_PyBytes_GetItemInt(PyObject* bytes, Py_ssize_t i
/////////////// bytes_index ///////////////
static CYTHON_INLINE char __Pyx_PyBytes_GetItemInt(PyObject* bytes, Py_ssize_t index, int check_bounds) {
+ if (index < 0)
+ index += PyBytes_GET_SIZE(bytes);
if (check_bounds) {
Py_ssize_t size = PyBytes_GET_SIZE(bytes);
- if (unlikely(index >= size) | ((index < 0) & unlikely(index < -size))) {
+ if (unlikely(!__Pyx_is_valid_index(index, size))) {
PyErr_SetString(PyExc_IndexError, "string index out of range");
return (char) -1;
}
}
- if (index < 0)
- index += PyBytes_GET_SIZE(bytes);
return PyBytes_AS_STRING(bytes)[index];
}
@@ -990,7 +990,7 @@ static CYTHON_INLINE int __Pyx_PyByteArray_AppendObject(PyObject* bytearray, PyO
{
// CPython calls PyNumber_Index() internally
ival = __Pyx_PyIndex_AsSsize_t(value);
- if (unlikely((ival < 0) | (ival > 255))) {
+ if (unlikely(!__Pyx_is_valid_index(ival, 256))) {
if (ival == -1 && PyErr_Occurred())
return -1;
goto bad_range;
@@ -1012,7 +1012,7 @@ static CYTHON_INLINE int __Pyx_PyByteArray_Append(PyObject* bytearray, int value
static CYTHON_INLINE int __Pyx_PyByteArray_Append(PyObject* bytearray, int value) {
PyObject *pyval, *retval;
#if CYTHON_COMPILING_IN_CPYTHON
- if (likely((value >= 0) & (value <= 255))) {
+ if (likely(__Pyx_is_valid_index(value, 256))) {
Py_ssize_t n = Py_SIZE(bytearray);
if (likely(n != PY_SSIZE_T_MAX)) {
if (unlikely(PyByteArray_Resize(bytearray, n + 1) < 0))
diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c
index aa38d7da1..796c8bed9 100644
--- a/Cython/Utility/TypeConversion.c
+++ b/Cython/Utility/TypeConversion.c
@@ -16,6 +16,14 @@
(is_signed || likely(v < (type)PY_SSIZE_T_MAX || \
v == (type)PY_SSIZE_T_MAX))) )
+static CYTHON_INLINE int __Pyx_is_valid_index(Py_ssize_t i, Py_ssize_t limit) {
+ // Optimisation from Section 14.2 "Bounds Checking" in
+ // https://www.agner.org/optimize/optimizing_cpp.pdf
+ // See https://bugs.python.org/issue28397
+ // The cast to unsigned effectively tests for "0 <= i < limit".
+ return (size_t) i < (size_t) limit;
+}
+
// fast and unsafe abs(Py_ssize_t) that ignores the overflow for (-PY_SSIZE_T_MAX-1)
#if defined (__cplusplus) && __cplusplus >= 201103L
#include <cstdlib>
@@ -529,18 +537,23 @@ static Py_UCS4 __Pyx__PyObject_AsPy_UCS4(PyObject*);
/////////////// ObjectAsUCS4 ///////////////
-static Py_UCS4 __Pyx__PyObject_AsPy_UCS4(PyObject* x) {
- long ival;
- ival = __Pyx_PyInt_As_long(x);
- if (unlikely(ival < 0)) {
+static Py_UCS4 __Pyx__PyObject_AsPy_UCS4_raise_error(long ival) {
+ if (ival < 0) {
if (!PyErr_Occurred())
PyErr_SetString(PyExc_OverflowError,
"cannot convert negative value to Py_UCS4");
- return (Py_UCS4)-1;
- } else if (unlikely(ival > 1114111)) {
+ } else {
PyErr_SetString(PyExc_OverflowError,
"value too large to convert to Py_UCS4");
- return (Py_UCS4)-1;
+ }
+ return (Py_UCS4)-1;
+}
+
+static Py_UCS4 __Pyx__PyObject_AsPy_UCS4(PyObject* x) {
+ long ival;
+ ival = __Pyx_PyInt_As_long(x);
+ if (unlikely(!__Pyx_is_valid_index(ival, 1114111 + 1))) {
+ return __Pyx__PyObject_AsPy_UCS4_raise_error(ival);
}
return (Py_UCS4)ival;
}
@@ -582,14 +595,16 @@ static CYTHON_INLINE Py_UNICODE __Pyx_PyObject_AsPy_UNICODE(PyObject* x) {
#endif
ival = __Pyx_PyInt_As_long(x);
}
- if (unlikely(ival < 0)) {
- if (!PyErr_Occurred())
+ if (unlikely(!__Pyx_is_valid_index(ival, maxval + 1))) {
+ if (ival < 0) {
+ if (!PyErr_Occurred())
+ PyErr_SetString(PyExc_OverflowError,
+ "cannot convert negative value to Py_UNICODE");
+ return (Py_UNICODE)-1;
+ } else {
PyErr_SetString(PyExc_OverflowError,
- "cannot convert negative value to Py_UNICODE");
- return (Py_UNICODE)-1;
- } else if (unlikely(ival > maxval)) {
- PyErr_SetString(PyExc_OverflowError,
- "value too large to convert to Py_UNICODE");
+ "value too large to convert to Py_UNICODE");
+ }
return (Py_UNICODE)-1;
}
return (Py_UNICODE)ival;