Skip to content

Commit 12f8bf9

Browse files
committed
pythongh-150587: Factor non-negative compact int subscript guards
1 parent 2413bac commit 12f8bf9

7 files changed

Lines changed: 105 additions & 195 deletions

File tree

‎Include/internal/pycore_opcode_metadata.h‎

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Lib/test/test_opcache.py‎

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1879,19 +1879,6 @@ def binary_subscr_list_int():
18791879
"BINARY_OP_SUBSCR_LIST_INT")
18801880
self.assert_no_opcode(binary_subscr_list_int, "BINARY_OP")
18811881

1882-
def binary_subscr_list_int_noncompact(index):
1883-
a = [1, 2, 3]
1884-
return a[index]
1885-
1886-
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
1887-
self.assertEqual(binary_subscr_list_int_noncompact(0), 1)
1888-
self.assert_specialized(binary_subscr_list_int_noncompact,
1889-
"BINARY_OP_SUBSCR_LIST_INT")
1890-
# 2**30 + 2 is non-compact, but its low digit is 2. The specialized
1891-
# path must fall back instead of reading the low digit as the index.
1892-
with self.assertRaises(IndexError):
1893-
binary_subscr_list_int_noncompact(2**30 + 2)
1894-
18951882
def binary_subscr_tuple_int():
18961883
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
18971884
a = (1, 2, 3)
@@ -2008,19 +1995,6 @@ def __getitem__(self, item):
20081995
@cpython_only
20091996
@requires_specialization
20101997
def test_store_subscr(self):
2011-
def store_subscr_list_int(index):
2012-
a = [None]
2013-
a[index] = 1
2014-
return a
2015-
2016-
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
2017-
self.assertEqual(store_subscr_list_int(0), [1])
2018-
self.assert_specialized(store_subscr_list_int, "STORE_SUBSCR_LIST_INT")
2019-
# 2**30 + 2 is non-compact, but its low digit is 2. The specialized
2020-
# path must fall back instead of writing through the low digit index.
2021-
with self.assertRaises(IndexError):
2022-
store_subscr_list_int(2**30 + 2)
2023-
20241998
def store_subscr_dict():
20251999
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
20262000
a = {1: 2, 2: 3}

‎Misc/NEWS.d/next/Core_and_Builtins/2026-07-04-02-30-00.gh-issue-150587.list-int-compact-guard.rst‎

Lines changed: 0 additions & 3 deletions
This file was deleted.

‎Modules/_testinternalcapi/test_cases.c.h‎

Lines changed: 31 additions & 42 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Python/bytecodes.c‎

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,20 +1134,17 @@ dummy_func(
11341134
macro(STORE_SLICE) = _SPECIALIZE_STORE_SLICE + _STORE_SLICE;
11351135

11361136
macro(BINARY_OP_SUBSCR_LIST_INT) =
1137-
_GUARD_TOS_INT + _GUARD_NOS_LIST + unused/5 + _BINARY_OP_SUBSCR_LIST_INT + _POP_TOP_INT + POP_TOP;
1137+
_GUARD_TOS_NON_NEGATIVE_COMPACT_INT + _GUARD_NOS_LIST +
1138+
unused/5 + _BINARY_OP_SUBSCR_LIST_INT + _POP_TOP_INT + POP_TOP;
11381139

11391140
op(_BINARY_OP_SUBSCR_LIST_INT, (list_st, sub_st -- res, ls, ss)) {
11401141
PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st);
11411142
PyObject *list = PyStackRef_AsPyObjectBorrow(list_st);
11421143

11431144
assert(PyLong_CheckExact(sub));
11441145
assert(PyList_CheckExact(list));
1145-
EXIT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub));
11461146

11471147
Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub);
1148-
if (index < 0) {
1149-
index += PyList_GET_SIZE(list);
1150-
}
11511148
#ifdef Py_GIL_DISABLED
11521149
PyObject *res_o = _PyList_GetItemRef((PyListObject*)list, index);
11531150
EXIT_IF(res_o == NULL);
@@ -1186,15 +1183,15 @@ dummy_func(
11861183
}
11871184

11881185
macro(BINARY_OP_SUBSCR_STR_INT) =
1189-
_GUARD_TOS_INT + _GUARD_NOS_COMPACT_ASCII + unused/5 + _BINARY_OP_SUBSCR_STR_INT + _POP_TOP_INT + _POP_TOP_UNICODE;
1186+
_GUARD_TOS_NON_NEGATIVE_COMPACT_INT + _GUARD_NOS_COMPACT_ASCII +
1187+
unused/5 + _BINARY_OP_SUBSCR_STR_INT + _POP_TOP_INT + _POP_TOP_UNICODE;
11901188

11911189
op(_BINARY_OP_SUBSCR_STR_INT, (str_st, sub_st -- res, s, i)) {
11921190
PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st);
11931191
PyObject *str = PyStackRef_AsPyObjectBorrow(str_st);
11941192

11951193
assert(PyLong_CheckExact(sub));
11961194
assert(PyUnicode_CheckExact(str));
1197-
EXIT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject*)sub));
11981195
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
11991196
EXIT_IF(PyUnicode_GET_LENGTH(str) <= index);
12001197
uint8_t c = PyUnicode_1BYTE_DATA(str)[index];
@@ -1208,15 +1205,15 @@ dummy_func(
12081205
}
12091206

12101207
macro(BINARY_OP_SUBSCR_USTR_INT) =
1211-
_GUARD_TOS_INT + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_SUBSCR_USTR_INT + _POP_TOP_INT + _POP_TOP_UNICODE;
1208+
_GUARD_TOS_NON_NEGATIVE_COMPACT_INT + _GUARD_NOS_UNICODE +
1209+
unused/5 + _BINARY_OP_SUBSCR_USTR_INT + _POP_TOP_INT + _POP_TOP_UNICODE;
12121210

12131211
op(_BINARY_OP_SUBSCR_USTR_INT, (str_st, sub_st -- res, s, i)) {
12141212
PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st);
12151213
PyObject *str = PyStackRef_AsPyObjectBorrow(str_st);
12161214

12171215
assert(PyLong_CheckExact(sub));
12181216
assert(PyUnicode_CheckExact(str));
1219-
EXIT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject*)sub));
12201217
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
12211218
EXIT_IF(PyUnicode_GET_LENGTH(str) <= index);
12221219
// Specialize for reading an ASCII character from any string:
@@ -1241,7 +1238,7 @@ dummy_func(
12411238
}
12421239

12431240
macro(BINARY_OP_SUBSCR_TUPLE_INT) =
1244-
_GUARD_TOS_INT +
1241+
_GUARD_TOS_NON_NEGATIVE_COMPACT_INT +
12451242
_GUARD_NOS_TUPLE +
12461243
_GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS +
12471244
unused/5 +
@@ -1257,8 +1254,7 @@ dummy_func(
12571254
assert(PyLong_CheckExact(sub));
12581255
assert(PyTuple_CheckExact(tuple));
12591256

1260-
// Deopt unless 0 <= sub < PyTuple_Size(list)
1261-
EXIT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub));
1257+
// Deopt unless sub < PyTuple_Size(list)
12621258
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
12631259
EXIT_IF(index >= PyTuple_GET_SIZE(tuple));
12641260
}
@@ -1414,12 +1410,12 @@ dummy_func(
14141410

14151411
op(_GUARD_TOS_NON_NEGATIVE_COMPACT_INT, (value -- value)) {
14161412
PyObject *value_o = PyStackRef_AsPyObjectBorrow(value);
1417-
assert(PyLong_CheckExact(value_o));
1413+
EXIT_IF(!PyLong_CheckExact(value_o));
14181414
EXIT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)value_o));
14191415
}
14201416

14211417
macro(STORE_SUBSCR_LIST_INT) =
1422-
_GUARD_TOS_INT + _GUARD_TOS_NON_NEGATIVE_COMPACT_INT + _GUARD_NOS_LIST +
1418+
_GUARD_TOS_NON_NEGATIVE_COMPACT_INT + _GUARD_NOS_LIST +
14231419
unused/1 + _STORE_SUBSCR_LIST_INT + _POP_TOP_INT + POP_TOP;
14241420

14251421
op(_STORE_SUBSCR_LIST_INT, (value, list_st, sub_st -- ls, ss)) {
@@ -1432,11 +1428,7 @@ dummy_func(
14321428
Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub);
14331429
DEOPT_IF(!LOCK_OBJECT(list));
14341430
Py_ssize_t len = PyList_GET_SIZE(list);
1435-
// Ensure index < len(list)
1436-
if (index < 0) {
1437-
index += len;
1438-
}
1439-
if (index < 0 || index >= len) {
1431+
if (index >= len) {
14401432
UNLOCK_OBJECT(list);
14411433
DEOPT_IF(true);
14421434
}

0 commit comments

Comments
 (0)