Skip to content

Commit a38e82b

Browse files
authored
gh-126298: Don't deduplicate slice constants based on equality (#126398)
* gh-126298: Don't deduplicated slice constants based on equality * NULL check for PySlice_New * Fix refcounting * Fix refcounting some more * Fix refcounting * Make tests more complete * Fix tests
1 parent 9357fdc commit a38e82b

File tree

2 files changed

+93
-20
lines changed

2 files changed

+93
-20
lines changed

Lib/test/test_compile.py

+59-19
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import dis
33
import io
44
import itertools
5+
import marshal
56
import math
67
import opcode
78
import os
@@ -1385,52 +1386,91 @@ def check_op_count(func, op, expected):
13851386
self.assertEqual(actual, expected)
13861387

13871388
def check_consts(func, typ, expected):
1388-
slice_consts = 0
1389+
expected = set([repr(x) for x in expected])
1390+
all_consts = set()
13891391
consts = func.__code__.co_consts
13901392
for instr in dis.Bytecode(func):
13911393
if instr.opname == "LOAD_CONST" and isinstance(consts[instr.oparg], typ):
1392-
slice_consts += 1
1393-
self.assertEqual(slice_consts, expected)
1394+
all_consts.add(repr(consts[instr.oparg]))
1395+
self.assertEqual(all_consts, expected)
13941396

13951397
def load():
13961398
return x[a:b] + x [a:] + x[:b] + x[:]
13971399

1400+
check_op_count(load, "BINARY_SLICE", 3)
1401+
check_op_count(load, "BUILD_SLICE", 0)
1402+
check_consts(load, slice, [slice(None, None, None)])
1403+
check_op_count(load, "BINARY_SUBSCR", 1)
1404+
13981405
def store():
13991406
x[a:b] = y
14001407
x [a:] = y
14011408
x[:b] = y
14021409
x[:] = y
14031410

1411+
check_op_count(store, "STORE_SLICE", 3)
1412+
check_op_count(store, "BUILD_SLICE", 0)
1413+
check_op_count(store, "STORE_SUBSCR", 1)
1414+
check_consts(store, slice, [slice(None, None, None)])
1415+
14041416
def long_slice():
14051417
return x[a:b:c]
14061418

1419+
check_op_count(long_slice, "BUILD_SLICE", 1)
1420+
check_op_count(long_slice, "BINARY_SLICE", 0)
1421+
check_consts(long_slice, slice, [])
1422+
check_op_count(long_slice, "BINARY_SUBSCR", 1)
1423+
14071424
def aug():
14081425
x[a:b] += y
14091426

1427+
check_op_count(aug, "BINARY_SLICE", 1)
1428+
check_op_count(aug, "STORE_SLICE", 1)
1429+
check_op_count(aug, "BUILD_SLICE", 0)
1430+
check_op_count(aug, "BINARY_SUBSCR", 0)
1431+
check_op_count(aug, "STORE_SUBSCR", 0)
1432+
check_consts(aug, slice, [])
1433+
14101434
def aug_const():
14111435
x[1:2] += y
14121436

1437+
check_op_count(aug_const, "BINARY_SLICE", 0)
1438+
check_op_count(aug_const, "STORE_SLICE", 0)
1439+
check_op_count(aug_const, "BINARY_SUBSCR", 1)
1440+
check_op_count(aug_const, "STORE_SUBSCR", 1)
1441+
check_consts(aug_const, slice, [slice(1, 2)])
1442+
14131443
def compound_const_slice():
14141444
x[1:2:3, 4:5:6] = y
14151445

1416-
check_op_count(load, "BINARY_SLICE", 3)
1417-
check_op_count(load, "BUILD_SLICE", 0)
1418-
check_consts(load, slice, 1)
1419-
check_op_count(store, "STORE_SLICE", 3)
1420-
check_op_count(store, "BUILD_SLICE", 0)
1421-
check_consts(store, slice, 1)
1422-
check_op_count(long_slice, "BUILD_SLICE", 1)
1423-
check_op_count(long_slice, "BINARY_SLICE", 0)
1424-
check_op_count(aug, "BINARY_SLICE", 1)
1425-
check_op_count(aug, "STORE_SLICE", 1)
1426-
check_op_count(aug, "BUILD_SLICE", 0)
1427-
check_op_count(aug_const, "BINARY_SLICE", 0)
1428-
check_op_count(aug_const, "STORE_SLICE", 0)
1429-
check_consts(aug_const, slice, 1)
14301446
check_op_count(compound_const_slice, "BINARY_SLICE", 0)
14311447
check_op_count(compound_const_slice, "BUILD_SLICE", 0)
1432-
check_consts(compound_const_slice, slice, 0)
1433-
check_consts(compound_const_slice, tuple, 1)
1448+
check_op_count(compound_const_slice, "STORE_SLICE", 0)
1449+
check_op_count(compound_const_slice, "STORE_SUBSCR", 1)
1450+
check_consts(compound_const_slice, slice, [])
1451+
check_consts(compound_const_slice, tuple, [(slice(1, 2, 3), slice(4, 5, 6))])
1452+
1453+
def mutable_slice():
1454+
x[[]:] = y
1455+
1456+
check_consts(mutable_slice, slice, {})
1457+
1458+
def different_but_equal():
1459+
x[:0] = y
1460+
x[:0.0] = y
1461+
x[:False] = y
1462+
x[:None] = y
1463+
1464+
check_consts(
1465+
different_but_equal,
1466+
slice,
1467+
[
1468+
slice(None, 0, None),
1469+
slice(None, 0.0, None),
1470+
slice(None, False, None),
1471+
slice(None, None, None)
1472+
]
1473+
)
14341474

14351475
def test_compare_positions(self):
14361476
for opname_prefix, op in [

Objects/codeobject.c

+34-1
Original file line numberDiff line numberDiff line change
@@ -2388,7 +2388,6 @@ _PyCode_ConstantKey(PyObject *op)
23882388
if (op == Py_None || op == Py_Ellipsis
23892389
|| PyLong_CheckExact(op)
23902390
|| PyUnicode_CheckExact(op)
2391-
|| PySlice_Check(op)
23922391
/* code_richcompare() uses _PyCode_ConstantKey() internally */
23932392
|| PyCode_Check(op))
23942393
{
@@ -2496,6 +2495,40 @@ _PyCode_ConstantKey(PyObject *op)
24962495
Py_DECREF(set);
24972496
return key;
24982497
}
2498+
else if (PySlice_Check(op)) {
2499+
PySliceObject *slice = (PySliceObject *)op;
2500+
PyObject *start_key = NULL;
2501+
PyObject *stop_key = NULL;
2502+
PyObject *step_key = NULL;
2503+
key = NULL;
2504+
2505+
start_key = _PyCode_ConstantKey(slice->start);
2506+
if (start_key == NULL) {
2507+
goto slice_exit;
2508+
}
2509+
2510+
stop_key = _PyCode_ConstantKey(slice->stop);
2511+
if (stop_key == NULL) {
2512+
goto slice_exit;
2513+
}
2514+
2515+
step_key = _PyCode_ConstantKey(slice->step);
2516+
if (step_key == NULL) {
2517+
goto slice_exit;
2518+
}
2519+
2520+
PyObject *slice_key = PySlice_New(start_key, stop_key, step_key);
2521+
if (slice_key == NULL) {
2522+
goto slice_exit;
2523+
}
2524+
2525+
key = PyTuple_Pack(2, slice_key, op);
2526+
Py_DECREF(slice_key);
2527+
slice_exit:
2528+
Py_XDECREF(start_key);
2529+
Py_XDECREF(stop_key);
2530+
Py_XDECREF(step_key);
2531+
}
24992532
else {
25002533
/* for other types, use the object identifier as a unique identifier
25012534
* to ensure that they are seen as unequal. */

0 commit comments

Comments
 (0)