Skip to content

Commit 613223c

Browse files
committed
gh-153486: Preserve negative compact subscript specializations
1 parent 9205927 commit 613223c

7 files changed

Lines changed: 219 additions & 147 deletions

File tree

Include/internal/pycore_opcode_metadata.h

Lines changed: 4 additions & 4 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: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,17 @@ def binary_subscr_tuple_int():
18901890
"BINARY_OP_SUBSCR_TUPLE_INT")
18911891
self.assert_no_opcode(binary_subscr_tuple_int, "BINARY_OP")
18921892

1893+
def binary_subscr_tuple_negative_int():
1894+
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
1895+
a = (1, 2, 3)
1896+
idx = -1
1897+
self.assertEqual(a[idx], 3)
1898+
1899+
binary_subscr_tuple_negative_int()
1900+
self.assert_specialized(binary_subscr_tuple_negative_int,
1901+
"BINARY_OP_SUBSCR_TUPLE_INT")
1902+
self.assert_no_opcode(binary_subscr_tuple_negative_int, "BINARY_OP")
1903+
18931904
def binary_subscr_dict():
18941905
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
18951906
a = {1: 2, 2: 3}
@@ -1967,6 +1978,16 @@ def binary_subscr_str_int():
19671978
self.assert_specialized(binary_subscr_str_int, "BINARY_OP_SUBSCR_STR_INT")
19681979
self.assert_no_opcode(binary_subscr_str_int, "BINARY_OP")
19691980

1981+
def binary_subscr_str_negative_int():
1982+
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
1983+
a = "foobar"
1984+
idx = -1
1985+
self.assertEqual(a[idx], "r")
1986+
1987+
binary_subscr_str_negative_int()
1988+
self.assert_specialized(binary_subscr_str_negative_int, "BINARY_OP_SUBSCR_STR_INT")
1989+
self.assert_no_opcode(binary_subscr_str_negative_int, "BINARY_OP")
1990+
19701991
def binary_subscr_str_int_non_compact():
19711992
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
19721993
a = "바이트코드_특수화"
@@ -1977,6 +1998,16 @@ def binary_subscr_str_int_non_compact():
19771998
self.assert_specialized(binary_subscr_str_int_non_compact, "BINARY_OP_SUBSCR_USTR_INT")
19781999
self.assert_no_opcode(binary_subscr_str_int_non_compact, "BINARY_OP_SUBSCR_STR_INT")
19792000

2001+
def binary_subscr_str_negative_int_non_compact():
2002+
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
2003+
a = "바이트코드_특수화"
2004+
idx = -1
2005+
self.assertEqual(a[idx], "화")
2006+
2007+
binary_subscr_str_negative_int_non_compact()
2008+
self.assert_specialized(binary_subscr_str_negative_int_non_compact, "BINARY_OP_SUBSCR_USTR_INT")
2009+
self.assert_no_opcode(binary_subscr_str_negative_int_non_compact, "BINARY_OP_SUBSCR_STR_INT")
2010+
19802011
def binary_subscr_getitems():
19812012
class C:
19822013
def __init__(self, val):
@@ -2026,6 +2057,17 @@ def store_subscr_defaultdict():
20262057
self.assert_specialized(store_subscr_defaultdict, "STORE_SUBSCR_DICT")
20272058
self.assert_no_opcode(store_subscr_defaultdict, "STORE_SUBSCR")
20282059

2060+
def store_subscr_list_negative_int():
2061+
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
2062+
a = [1, 2, 3]
2063+
idx = -1
2064+
a[idx] = 4
2065+
self.assertEqual(a, [1, 2, 4])
2066+
2067+
store_subscr_list_negative_int()
2068+
self.assert_specialized(store_subscr_list_negative_int, "STORE_SUBSCR_LIST_INT")
2069+
self.assert_no_opcode(store_subscr_list_negative_int, "STORE_SUBSCR")
2070+
20292071
def store_subscr_dict_subclass_override():
20302072
class MyDict(dict):
20312073
def __setitem__(self, key, value):

Modules/_testinternalcapi/test_cases.c.h

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

Python/bytecodes.c

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,7 +1186,7 @@ dummy_func(
11861186
}
11871187

11881188
macro(BINARY_OP_SUBSCR_STR_INT) =
1189-
_GUARD_TOS_NON_NEGATIVE_COMPACT_INT + _GUARD_NOS_COMPACT_ASCII +
1189+
_GUARD_TOS_INT + _GUARD_NOS_COMPACT_ASCII +
11901190
unused/5 + _BINARY_OP_SUBSCR_STR_INT + _POP_TOP_INT + _POP_TOP_UNICODE;
11911191

11921192
op(_BINARY_OP_SUBSCR_STR_INT, (str_st, sub_st -- res, s, i)) {
@@ -1195,8 +1195,12 @@ dummy_func(
11951195

11961196
assert(PyLong_CheckExact(sub));
11971197
assert(PyUnicode_CheckExact(str));
1198-
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
1199-
EXIT_IF(PyUnicode_GET_LENGTH(str) <= index);
1198+
Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub);
1199+
Py_ssize_t len = PyUnicode_GET_LENGTH(str);
1200+
if (index < 0) {
1201+
index += len;
1202+
}
1203+
EXIT_IF(index < 0 || len <= index);
12001204
uint8_t c = PyUnicode_1BYTE_DATA(str)[index];
12011205
assert(c < 128);
12021206
STAT_INC(BINARY_OP, hit);
@@ -1208,7 +1212,7 @@ dummy_func(
12081212
}
12091213

12101214
macro(BINARY_OP_SUBSCR_USTR_INT) =
1211-
_GUARD_TOS_NON_NEGATIVE_COMPACT_INT + _GUARD_NOS_UNICODE +
1215+
_GUARD_TOS_INT + _GUARD_NOS_UNICODE +
12121216
unused/5 + _BINARY_OP_SUBSCR_USTR_INT + _POP_TOP_INT + _POP_TOP_UNICODE;
12131217

12141218
op(_BINARY_OP_SUBSCR_USTR_INT, (str_st, sub_st -- res, s, i)) {
@@ -1217,8 +1221,12 @@ dummy_func(
12171221

12181222
assert(PyLong_CheckExact(sub));
12191223
assert(PyUnicode_CheckExact(str));
1220-
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
1221-
EXIT_IF(PyUnicode_GET_LENGTH(str) <= index);
1224+
Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub);
1225+
Py_ssize_t len = PyUnicode_GET_LENGTH(str);
1226+
if (index < 0) {
1227+
index += len;
1228+
}
1229+
EXIT_IF(index < 0 || len <= index);
12221230
// Specialize for reading an ASCII character from any string:
12231231
Py_UCS4 c = PyUnicode_READ_CHAR(str, index);
12241232
EXIT_IF(Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c);
@@ -1241,7 +1249,7 @@ dummy_func(
12411249
}
12421250

12431251
macro(BINARY_OP_SUBSCR_TUPLE_INT) =
1244-
_GUARD_TOS_NON_NEGATIVE_COMPACT_INT +
1252+
_GUARD_TOS_INT +
12451253
_GUARD_NOS_TUPLE +
12461254
_GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS +
12471255
unused/5 +
@@ -1257,9 +1265,12 @@ dummy_func(
12571265
assert(PyLong_CheckExact(sub));
12581266
assert(PyTuple_CheckExact(tuple));
12591267

1260-
// Deopt unless sub < PyTuple_Size(list)
1261-
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
1262-
EXIT_IF(index >= PyTuple_GET_SIZE(tuple));
1268+
Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub);
1269+
Py_ssize_t len = PyTuple_GET_SIZE(tuple);
1270+
if (index < 0) {
1271+
index += len;
1272+
}
1273+
EXIT_IF(index < 0 || index >= len);
12631274
}
12641275

12651276
op(_BINARY_OP_SUBSCR_TUPLE_INT, (tuple_st, sub_st -- res, ts, ss)) {
@@ -1270,7 +1281,10 @@ dummy_func(
12701281
assert(PyTuple_CheckExact(tuple));
12711282

12721283
STAT_INC(BINARY_OP, hit);
1273-
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
1284+
Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub);
1285+
if (index < 0) {
1286+
index += PyTuple_GET_SIZE(tuple);
1287+
}
12741288
PyObject *res_o = PyTuple_GET_ITEM(tuple, index);
12751289
assert(res_o != NULL);
12761290
res = PyStackRef_FromPyObjectNew(res_o);
@@ -1413,13 +1427,13 @@ dummy_func(
14131427

14141428
op(_GUARD_TOS_NON_NEGATIVE_COMPACT_INT, (value -- value)) {
14151429
PyObject *value_o = PyStackRef_AsPyObjectBorrow(value);
1416-
EXIT_IF(!PyLong_CheckExact(value_o));
1430+
assert(PyLong_CheckExact(value_o));
14171431
EXIT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)value_o));
14181432
}
14191433

14201434
macro(STORE_SUBSCR_LIST_INT) =
1421-
_GUARD_TOS_NON_NEGATIVE_COMPACT_INT + _GUARD_NOS_LIST +
1422-
unused/1 + _STORE_SUBSCR_LIST_INT + _POP_TOP_INT + POP_TOP;
1435+
_GUARD_TOS_INT + _GUARD_NOS_LIST + unused/1 +
1436+
_STORE_SUBSCR_LIST_INT + _POP_TOP_INT + POP_TOP;
14231437

14241438
op(_STORE_SUBSCR_LIST_INT, (value, list_st, sub_st -- ls, ss)) {
14251439
PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st);
@@ -1431,7 +1445,10 @@ dummy_func(
14311445
Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub);
14321446
DEOPT_IF(!LOCK_OBJECT(list));
14331447
Py_ssize_t len = PyList_GET_SIZE(list);
1434-
if (index >= len) {
1448+
if (index < 0) {
1449+
index += len;
1450+
}
1451+
if (index < 0 || index >= len) {
14351452
UNLOCK_OBJECT(list);
14361453
DEOPT_IF(true);
14371454
}

0 commit comments

Comments
 (0)