Skip to content

Commit 08b2deb

Browse files
corona10picnixz
authored andcommitted
pythongh-115999: Add partial free-thread specialization for BINARY_SUBSCR (pythongh-127227)
1 parent a250d3f commit 08b2deb

10 files changed

+128
-71
lines changed

Include/internal/pycore_list.h

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ extern "C" {
1010

1111
PyAPI_FUNC(PyObject*) _PyList_Extend(PyListObject *, PyObject *);
1212
extern void _PyList_DebugMallocStats(FILE *out);
13+
// _PyList_GetItemRef should be used only when the object is known as a list
14+
// because it doesn't raise TypeError when the object is not a list, whereas PyList_GetItemRef does.
15+
extern PyObject* _PyList_GetItemRef(PyListObject *, Py_ssize_t i);
1316

1417
#define _PyList_ITEMS(op) _Py_RVALUE(_PyList_CAST(op)->ob_item)
1518

Include/internal/pycore_opcode_metadata.h

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

Include/internal/pycore_uop_metadata.h

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

Lib/test/test_dis.py

-21
Original file line numberDiff line numberDiff line change
@@ -1260,27 +1260,6 @@ def test_super_instructions(self):
12601260
got = self.get_disassembly(load_test, adaptive=True)
12611261
self.do_disassembly_compare(got, dis_load_test_quickened_code)
12621262

1263-
@cpython_only
1264-
@requires_specialization
1265-
def test_binary_subscr_specialize(self):
1266-
binary_subscr_quicken = """\
1267-
0 RESUME_CHECK 0
1268-
1269-
1 LOAD_NAME 0 (a)
1270-
LOAD_SMALL_INT 0
1271-
%s
1272-
RETURN_VALUE
1273-
"""
1274-
co_list = compile('a[0]', "<list>", "eval")
1275-
self.code_quicken(lambda: exec(co_list, {}, {'a': [0]}))
1276-
got = self.get_disassembly(co_list, adaptive=True)
1277-
self.do_disassembly_compare(got, binary_subscr_quicken % "BINARY_SUBSCR_LIST_INT")
1278-
1279-
co_dict = compile('a[0]', "<dict>", "eval")
1280-
self.code_quicken(lambda: exec(co_dict, {}, {'a': {0: '1'}}))
1281-
got = self.get_disassembly(co_dict, adaptive=True)
1282-
self.do_disassembly_compare(got, binary_subscr_quicken % "BINARY_SUBSCR_DICT")
1283-
12841263
@cpython_only
12851264
@requires_specialization
12861265
def test_load_attr_specialize(self):

Lib/test/test_opcache.py

+77-30
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ def write(items):
617617
opname = "BINARY_SUBSCR_GETITEM"
618618
self.assert_races_do_not_crash(opname, get_items, read, write)
619619

620-
@requires_specialization
620+
@requires_specialization_ft
621621
def test_binary_subscr_list_int(self):
622622
def get_items():
623623
items = []
@@ -1023,7 +1023,7 @@ def write(items):
10231023
opname = "STORE_ATTR_WITH_HINT"
10241024
self.assert_races_do_not_crash(opname, get_items, read, write)
10251025

1026-
@requires_specialization
1026+
@requires_specialization_ft
10271027
def test_store_subscr_list_int(self):
10281028
def get_items():
10291029
items = []
@@ -1229,48 +1229,48 @@ class TestSpecializer(TestBase):
12291229
@cpython_only
12301230
@requires_specialization_ft
12311231
def test_binary_op(self):
1232-
def f():
1232+
def binary_op_add_int():
12331233
for _ in range(100):
12341234
a, b = 1, 2
12351235
c = a + b
12361236
self.assertEqual(c, 3)
12371237

1238-
f()
1239-
self.assert_specialized(f, "BINARY_OP_ADD_INT")
1240-
self.assert_no_opcode(f, "BINARY_OP")
1238+
binary_op_add_int()
1239+
self.assert_specialized(binary_op_add_int, "BINARY_OP_ADD_INT")
1240+
self.assert_no_opcode(binary_op_add_int, "BINARY_OP")
12411241

1242-
def g():
1242+
def binary_op_add_unicode():
12431243
for _ in range(100):
12441244
a, b = "foo", "bar"
12451245
c = a + b
12461246
self.assertEqual(c, "foobar")
12471247

1248-
g()
1249-
self.assert_specialized(g, "BINARY_OP_ADD_UNICODE")
1250-
self.assert_no_opcode(g, "BINARY_OP")
1248+
binary_op_add_unicode()
1249+
self.assert_specialized(binary_op_add_unicode, "BINARY_OP_ADD_UNICODE")
1250+
self.assert_no_opcode(binary_op_add_unicode, "BINARY_OP")
12511251

12521252
@cpython_only
12531253
@requires_specialization_ft
12541254
def test_contain_op(self):
1255-
def f():
1255+
def contains_op_dict():
12561256
for _ in range(100):
12571257
a, b = 1, {1: 2, 2: 5}
12581258
self.assertTrue(a in b)
12591259
self.assertFalse(3 in b)
12601260

1261-
f()
1262-
self.assert_specialized(f, "CONTAINS_OP_DICT")
1263-
self.assert_no_opcode(f, "CONTAINS_OP")
1261+
contains_op_dict()
1262+
self.assert_specialized(contains_op_dict, "CONTAINS_OP_DICT")
1263+
self.assert_no_opcode(contains_op_dict, "CONTAINS_OP")
12641264

1265-
def g():
1265+
def contains_op_set():
12661266
for _ in range(100):
12671267
a, b = 1, {1, 2}
12681268
self.assertTrue(a in b)
12691269
self.assertFalse(3 in b)
12701270

1271-
g()
1272-
self.assert_specialized(g, "CONTAINS_OP_SET")
1273-
self.assert_no_opcode(g, "CONTAINS_OP")
1271+
contains_op_set()
1272+
self.assert_specialized(contains_op_set, "CONTAINS_OP_SET")
1273+
self.assert_no_opcode(contains_op_set, "CONTAINS_OP")
12741274

12751275
@cpython_only
12761276
@requires_specialization_ft
@@ -1342,34 +1342,81 @@ def to_bool_str():
13421342
@cpython_only
13431343
@requires_specialization_ft
13441344
def test_unpack_sequence(self):
1345-
def f():
1345+
def unpack_sequence_two_tuple():
13461346
for _ in range(100):
13471347
a, b = 1, 2
13481348
self.assertEqual(a, 1)
13491349
self.assertEqual(b, 2)
13501350

1351-
f()
1352-
self.assert_specialized(f, "UNPACK_SEQUENCE_TWO_TUPLE")
1353-
self.assert_no_opcode(f, "UNPACK_SEQUENCE")
1351+
unpack_sequence_two_tuple()
1352+
self.assert_specialized(unpack_sequence_two_tuple,
1353+
"UNPACK_SEQUENCE_TWO_TUPLE")
1354+
self.assert_no_opcode(unpack_sequence_two_tuple, "UNPACK_SEQUENCE")
13541355

1355-
def g():
1356+
def unpack_sequence_tuple():
13561357
for _ in range(100):
13571358
a, = 1,
13581359
self.assertEqual(a, 1)
13591360

1360-
g()
1361-
self.assert_specialized(g, "UNPACK_SEQUENCE_TUPLE")
1362-
self.assert_no_opcode(g, "UNPACK_SEQUENCE")
1361+
unpack_sequence_tuple()
1362+
self.assert_specialized(unpack_sequence_tuple, "UNPACK_SEQUENCE_TUPLE")
1363+
self.assert_no_opcode(unpack_sequence_tuple, "UNPACK_SEQUENCE")
13631364

1364-
def x():
1365+
def unpack_sequence_list():
13651366
for _ in range(100):
13661367
a, b = [1, 2]
13671368
self.assertEqual(a, 1)
13681369
self.assertEqual(b, 2)
13691370

1370-
x()
1371-
self.assert_specialized(x, "UNPACK_SEQUENCE_LIST")
1372-
self.assert_no_opcode(x, "UNPACK_SEQUENCE")
1371+
unpack_sequence_list()
1372+
self.assert_specialized(unpack_sequence_list, "UNPACK_SEQUENCE_LIST")
1373+
self.assert_no_opcode(unpack_sequence_list, "UNPACK_SEQUENCE")
1374+
1375+
@cpython_only
1376+
@requires_specialization_ft
1377+
def test_binary_subscr(self):
1378+
def binary_subscr_list_int():
1379+
for _ in range(100):
1380+
a = [1, 2, 3]
1381+
for idx, expected in enumerate(a):
1382+
self.assertEqual(a[idx], expected)
1383+
1384+
binary_subscr_list_int()
1385+
self.assert_specialized(binary_subscr_list_int,
1386+
"BINARY_SUBSCR_LIST_INT")
1387+
self.assert_no_opcode(binary_subscr_list_int, "BINARY_SUBSCR")
1388+
1389+
def binary_subscr_tuple_int():
1390+
for _ in range(100):
1391+
a = (1, 2, 3)
1392+
for idx, expected in enumerate(a):
1393+
self.assertEqual(a[idx], expected)
1394+
1395+
binary_subscr_tuple_int()
1396+
self.assert_specialized(binary_subscr_tuple_int,
1397+
"BINARY_SUBSCR_TUPLE_INT")
1398+
self.assert_no_opcode(binary_subscr_tuple_int, "BINARY_SUBSCR")
1399+
1400+
def binary_subscr_dict():
1401+
for _ in range(100):
1402+
a = {1: 2, 2: 3}
1403+
self.assertEqual(a[1], 2)
1404+
self.assertEqual(a[2], 3)
1405+
1406+
binary_subscr_dict()
1407+
self.assert_specialized(binary_subscr_dict, "BINARY_SUBSCR_DICT")
1408+
self.assert_no_opcode(binary_subscr_dict, "BINARY_SUBSCR")
1409+
1410+
def binary_subscr_str_int():
1411+
for _ in range(100):
1412+
a = "foobar"
1413+
for idx, expected in enumerate(a):
1414+
self.assertEqual(a[idx], expected)
1415+
1416+
binary_subscr_str_int()
1417+
self.assert_specialized(binary_subscr_str_int, "BINARY_SUBSCR_STR_INT")
1418+
self.assert_no_opcode(binary_subscr_str_int, "BINARY_SUBSCR")
1419+
13731420

13741421
if __name__ == "__main__":
13751422
unittest.main()

Objects/listobject.c

+6
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,12 @@ PyList_GetItemRef(PyObject *op, Py_ssize_t i)
391391
return item;
392392
}
393393

394+
PyObject *
395+
_PyList_GetItemRef(PyListObject *list, Py_ssize_t i)
396+
{
397+
return list_get_item_ref(list, i);
398+
}
399+
394400
int
395401
PyList_SetItem(PyObject *op, Py_ssize_t i,
396402
PyObject *newitem)

Python/bytecodes.c

+8-2
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ dummy_func(
704704
};
705705

706706
specializing op(_SPECIALIZE_BINARY_SUBSCR, (counter/1, container, sub -- container, sub)) {
707-
#if ENABLE_SPECIALIZATION
707+
#if ENABLE_SPECIALIZATION_FT
708708
assert(frame->stackpointer == NULL);
709709
if (ADAPTIVE_COUNTER_TRIGGERS(counter)) {
710710
next_instr = this_instr;
@@ -713,7 +713,7 @@ dummy_func(
713713
}
714714
OPCODE_DEFERRED_INC(BINARY_SUBSCR);
715715
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
716-
#endif /* ENABLE_SPECIALIZATION */
716+
#endif /* ENABLE_SPECIALIZATION_FT */
717717
}
718718

719719
op(_BINARY_SUBSCR, (container, sub -- res)) {
@@ -790,11 +790,17 @@ dummy_func(
790790
// Deopt unless 0 <= sub < PyList_Size(list)
791791
DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub));
792792
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
793+
#ifdef Py_GIL_DISABLED
794+
PyObject *res_o = _PyList_GetItemRef((PyListObject*)list, index);
795+
DEOPT_IF(res_o == NULL);
796+
STAT_INC(BINARY_SUBSCR, hit);
797+
#else
793798
DEOPT_IF(index >= PyList_GET_SIZE(list));
794799
STAT_INC(BINARY_SUBSCR, hit);
795800
PyObject *res_o = PyList_GET_ITEM(list, index);
796801
assert(res_o != NULL);
797802
Py_INCREF(res_o);
803+
#endif
798804
PyStackRef_CLOSE_SPECIALIZED(sub_st, (destructor)PyObject_Free);
799805
DEAD(sub_st);
800806
PyStackRef_CLOSE(list_st);

Python/executor_cases.c.h

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

Python/generated_cases.c.h

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

0 commit comments

Comments
 (0)