Skip to content

Commit 2b82c36

Browse files
authored
gh-99761: Add _PyLong_IsPositiveSingleDigit function to check for single digit integers (#100064)
1 parent 6898157 commit 2b82c36

File tree

3 files changed

+25
-10
lines changed

3 files changed

+25
-10
lines changed

Include/internal/pycore_long.h

+19
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,25 @@ PyAPI_FUNC(char*) _PyLong_FormatBytesWriter(
110110
int base,
111111
int alternate);
112112

113+
/* Return 1 if the argument is positive single digit int */
114+
static inline int
115+
_PyLong_IsPositiveSingleDigit(PyObject* sub) {
116+
/* For a positive single digit int, the value of Py_SIZE(sub) is 0 or 1.
117+
118+
We perform a fast check using a single comparison by casting from int
119+
to uint which casts negative numbers to large positive numbers.
120+
For details see Section 14.2 "Bounds Checking" in the Agner Fog
121+
optimization manual found at:
122+
https://www.agner.org/optimize/optimizing_cpp.pdf
123+
124+
The function is not affected by -fwrapv, -fno-wrapv and -ftrapv
125+
compiler options of GCC and clang
126+
*/
127+
assert(PyLong_CheckExact(sub));
128+
Py_ssize_t signed_size = Py_SIZE(sub);
129+
return ((size_t)signed_size) <= 1;
130+
}
131+
113132
#ifdef __cplusplus
114133
}
115134
#endif

Python/bytecodes.c

+3-5
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,7 @@ dummy_func(
391391
DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR);
392392

393393
// Deopt unless 0 <= sub < PyList_Size(list)
394-
Py_ssize_t signed_magnitude = Py_SIZE(sub);
395-
DEOPT_IF(((size_t)signed_magnitude) > 1, BINARY_SUBSCR);
394+
DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR);
396395
assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0);
397396
Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0];
398397
DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR);
@@ -410,8 +409,7 @@ dummy_func(
410409
DEOPT_IF(!PyTuple_CheckExact(tuple), BINARY_SUBSCR);
411410

412411
// Deopt unless 0 <= sub < PyTuple_Size(list)
413-
Py_ssize_t signed_magnitude = Py_SIZE(sub);
414-
DEOPT_IF(((size_t)signed_magnitude) > 1, BINARY_SUBSCR);
412+
DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR);
415413
assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0);
416414
Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0];
417415
DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR);
@@ -508,7 +506,7 @@ dummy_func(
508506
DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR);
509507

510508
// Ensure nonnegative, zero-or-one-digit ints.
511-
DEOPT_IF(((size_t)Py_SIZE(sub)) > 1, STORE_SUBSCR);
509+
DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), STORE_SUBSCR);
512510
Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0];
513511
// Ensure index < len(list)
514512
DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR);

Python/generated_cases.c.h

+3-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)