Skip to content

Commit c6127af

Browse files
mdboommarkshannonZeroIntensity
authored
gh-125063: Emit slices as constants in the bytecode compiler (#125064)
* Make slices marshallable * Emit slices as constants * Update Python/marshal.c Co-authored-by: Peter Bierma <[email protected]> * Refactor codegen_slice into two functions so it always has the same net effect * Fix for free-threaded builds * Simplify marshal loading of slices * Only return SUCCESS/ERROR from codegen_slice --------- Co-authored-by: Mark Shannon <[email protected]> Co-authored-by: Peter Bierma <[email protected]>
1 parent 7dca732 commit c6127af

File tree

6 files changed

+122
-23
lines changed

6 files changed

+122
-23
lines changed

Include/internal/pycore_magic_number.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ Known values:
259259
Python 3.14a1 3605 (Move ENTER_EXECUTOR to opcode 255)
260260
Python 3.14a1 3606 (Specialize CALL_KW)
261261
Python 3.14a1 3607 (Add pseudo instructions JUMP_IF_TRUE/FALSE)
262+
Python 3.14a1 3608 (Add support for slices)
262263
263264
Python 3.15 will start with 3650
264265
@@ -271,7 +272,7 @@ PC/launcher.c must also be updated.
271272
272273
*/
273274

274-
#define PYC_MAGIC_NUMBER 3607
275+
#define PYC_MAGIC_NUMBER 3608
275276
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
276277
(little-endian) and then appending b'\r\n'. */
277278
#define PYC_MAGIC_NUMBER_TOKEN \

Lib/test/test_compile.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,14 @@ def check_op_count(func, op, expected):
13861386
actual += 1
13871387
self.assertEqual(actual, expected)
13881388

1389+
def check_consts(func, typ, expected):
1390+
slice_consts = 0
1391+
consts = func.__code__.co_consts
1392+
for instr in dis.Bytecode(func):
1393+
if instr.opname == "LOAD_CONST" and isinstance(consts[instr.oparg], typ):
1394+
slice_consts += 1
1395+
self.assertEqual(slice_consts, expected)
1396+
13891397
def load():
13901398
return x[a:b] + x [a:] + x[:b] + x[:]
13911399

@@ -1401,15 +1409,30 @@ def long_slice():
14011409
def aug():
14021410
x[a:b] += y
14031411

1404-
check_op_count(load, "BINARY_SLICE", 4)
1412+
def aug_const():
1413+
x[1:2] += y
1414+
1415+
def compound_const_slice():
1416+
x[1:2:3, 4:5:6] = y
1417+
1418+
check_op_count(load, "BINARY_SLICE", 3)
14051419
check_op_count(load, "BUILD_SLICE", 0)
1406-
check_op_count(store, "STORE_SLICE", 4)
1420+
check_consts(load, slice, 1)
1421+
check_op_count(store, "STORE_SLICE", 3)
14071422
check_op_count(store, "BUILD_SLICE", 0)
1423+
check_consts(store, slice, 1)
14081424
check_op_count(long_slice, "BUILD_SLICE", 1)
14091425
check_op_count(long_slice, "BINARY_SLICE", 0)
14101426
check_op_count(aug, "BINARY_SLICE", 1)
14111427
check_op_count(aug, "STORE_SLICE", 1)
14121428
check_op_count(aug, "BUILD_SLICE", 0)
1429+
check_op_count(aug_const, "BINARY_SLICE", 0)
1430+
check_op_count(aug_const, "STORE_SLICE", 0)
1431+
check_consts(aug_const, slice, 1)
1432+
check_op_count(compound_const_slice, "BINARY_SLICE", 0)
1433+
check_op_count(compound_const_slice, "BUILD_SLICE", 0)
1434+
check_consts(compound_const_slice, slice, 0)
1435+
check_consts(compound_const_slice, tuple, 1)
14131436

14141437
def test_compare_positions(self):
14151438
for opname_prefix, op in [

Objects/codeobject.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2336,6 +2336,7 @@ _PyCode_ConstantKey(PyObject *op)
23362336
if (op == Py_None || op == Py_Ellipsis
23372337
|| PyLong_CheckExact(op)
23382338
|| PyUnicode_CheckExact(op)
2339+
|| PySlice_Check(op)
23392340
/* code_richcompare() uses _PyCode_ConstantKey() internally */
23402341
|| PyCode_Check(op))
23412342
{

Objects/sliceobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ Create a slice object. This is used for extended slicing (e.g. a[0:10:2]).");
343343
static void
344344
slice_dealloc(PySliceObject *r)
345345
{
346-
_PyObject_GC_UNTRACK(r);
346+
PyObject_GC_UnTrack(r);
347347
Py_DECREF(r->step);
348348
Py_DECREF(r->start);
349349
Py_DECREF(r->stop);

Python/codegen.c

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ static int codegen_visit_expr(compiler *, expr_ty);
194194
static int codegen_augassign(compiler *, stmt_ty);
195195
static int codegen_annassign(compiler *, stmt_ty);
196196
static int codegen_subscript(compiler *, expr_ty);
197+
static int codegen_slice_two_parts(compiler *, expr_ty);
197198
static int codegen_slice(compiler *, expr_ty);
198199

199200
static bool are_all_items_const(asdl_expr_seq *, Py_ssize_t, Py_ssize_t);
@@ -5005,12 +5006,8 @@ codegen_visit_expr(compiler *c, expr_ty e)
50055006
}
50065007
break;
50075008
case Slice_kind:
5008-
{
5009-
int n = codegen_slice(c, e);
5010-
RETURN_IF_ERROR(n);
5011-
ADDOP_I(c, loc, BUILD_SLICE, n);
5009+
RETURN_IF_ERROR(codegen_slice(c, e));
50125010
break;
5013-
}
50145011
case Name_kind:
50155012
return codegen_nameop(c, loc, e->v.Name.id, e->v.Name.ctx);
50165013
/* child nodes of List and Tuple will have expr_context set */
@@ -5023,9 +5020,22 @@ codegen_visit_expr(compiler *c, expr_ty e)
50235020
}
50245021

50255022
static bool
5026-
is_two_element_slice(expr_ty s)
5023+
is_constant_slice(expr_ty s)
50275024
{
50285025
return s->kind == Slice_kind &&
5026+
(s->v.Slice.lower == NULL ||
5027+
s->v.Slice.lower->kind == Constant_kind) &&
5028+
(s->v.Slice.upper == NULL ||
5029+
s->v.Slice.upper->kind == Constant_kind) &&
5030+
(s->v.Slice.step == NULL ||
5031+
s->v.Slice.step->kind == Constant_kind);
5032+
}
5033+
5034+
static bool
5035+
should_apply_two_element_slice_optimization(expr_ty s)
5036+
{
5037+
return !is_constant_slice(s) &&
5038+
s->kind == Slice_kind &&
50295039
s->v.Slice.step == NULL;
50305040
}
50315041

@@ -5046,8 +5056,8 @@ codegen_augassign(compiler *c, stmt_ty s)
50465056
break;
50475057
case Subscript_kind:
50485058
VISIT(c, expr, e->v.Subscript.value);
5049-
if (is_two_element_slice(e->v.Subscript.slice)) {
5050-
RETURN_IF_ERROR(codegen_slice(c, e->v.Subscript.slice));
5059+
if (should_apply_two_element_slice_optimization(e->v.Subscript.slice)) {
5060+
RETURN_IF_ERROR(codegen_slice_two_parts(c, e->v.Subscript.slice));
50515061
ADDOP_I(c, loc, COPY, 3);
50525062
ADDOP_I(c, loc, COPY, 3);
50535063
ADDOP_I(c, loc, COPY, 3);
@@ -5084,7 +5094,7 @@ codegen_augassign(compiler *c, stmt_ty s)
50845094
ADDOP_NAME(c, loc, STORE_ATTR, e->v.Attribute.attr, names);
50855095
break;
50865096
case Subscript_kind:
5087-
if (is_two_element_slice(e->v.Subscript.slice)) {
5097+
if (should_apply_two_element_slice_optimization(e->v.Subscript.slice)) {
50885098
ADDOP_I(c, loc, SWAP, 4);
50895099
ADDOP_I(c, loc, SWAP, 3);
50905100
ADDOP_I(c, loc, SWAP, 2);
@@ -5231,8 +5241,10 @@ codegen_subscript(compiler *c, expr_ty e)
52315241
}
52325242

52335243
VISIT(c, expr, e->v.Subscript.value);
5234-
if (is_two_element_slice(e->v.Subscript.slice) && ctx != Del) {
5235-
RETURN_IF_ERROR(codegen_slice(c, e->v.Subscript.slice));
5244+
if (should_apply_two_element_slice_optimization(e->v.Subscript.slice) &&
5245+
ctx != Del
5246+
) {
5247+
RETURN_IF_ERROR(codegen_slice_two_parts(c, e->v.Subscript.slice));
52365248
if (ctx == Load) {
52375249
ADDOP(c, loc, BINARY_SLICE);
52385250
}
@@ -5254,15 +5266,9 @@ codegen_subscript(compiler *c, expr_ty e)
52545266
return SUCCESS;
52555267
}
52565268

5257-
/* Returns the number of the values emitted,
5258-
* thus are needed to build the slice, or -1 if there is an error. */
52595269
static int
5260-
codegen_slice(compiler *c, expr_ty s)
5270+
codegen_slice_two_parts(compiler *c, expr_ty s)
52615271
{
5262-
int n = 2;
5263-
assert(s->kind == Slice_kind);
5264-
5265-
/* only handles the cases where BUILD_SLICE is emitted */
52665272
if (s->v.Slice.lower) {
52675273
VISIT(c, expr, s->v.Slice.lower);
52685274
}
@@ -5277,11 +5283,45 @@ codegen_slice(compiler *c, expr_ty s)
52775283
ADDOP_LOAD_CONST(c, LOC(s), Py_None);
52785284
}
52795285

5286+
return 0;
5287+
}
5288+
5289+
static int
5290+
codegen_slice(compiler *c, expr_ty s)
5291+
{
5292+
int n = 2;
5293+
assert(s->kind == Slice_kind);
5294+
5295+
if (is_constant_slice(s)) {
5296+
PyObject *start = NULL;
5297+
if (s->v.Slice.lower) {
5298+
start = s->v.Slice.lower->v.Constant.value;
5299+
}
5300+
PyObject *stop = NULL;
5301+
if (s->v.Slice.upper) {
5302+
stop = s->v.Slice.upper->v.Constant.value;
5303+
}
5304+
PyObject *step = NULL;
5305+
if (s->v.Slice.step) {
5306+
step = s->v.Slice.step->v.Constant.value;
5307+
}
5308+
PyObject *slice = PySlice_New(start, stop, step);
5309+
if (slice == NULL) {
5310+
return ERROR;
5311+
}
5312+
ADDOP_LOAD_CONST_NEW(c, LOC(s), slice);
5313+
return SUCCESS;
5314+
}
5315+
5316+
RETURN_IF_ERROR(codegen_slice_two_parts(c, s));
5317+
52805318
if (s->v.Slice.step) {
52815319
n++;
52825320
VISIT(c, expr, s->v.Slice.step);
52835321
}
5284-
return n;
5322+
5323+
ADDOP_I(c, LOC(s), BUILD_SLICE, n);
5324+
return SUCCESS;
52855325
}
52865326

52875327

Python/marshal.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ module marshal
7676
#define TYPE_UNKNOWN '?'
7777
#define TYPE_SET '<'
7878
#define TYPE_FROZENSET '>'
79+
#define TYPE_SLICE ':'
7980
#define FLAG_REF '\x80' /* with a type, add obj to index */
8081

8182
#define TYPE_ASCII 'a'
@@ -613,6 +614,13 @@ w_complex_object(PyObject *v, char flag, WFILE *p)
613614
w_pstring(view.buf, view.len, p);
614615
PyBuffer_Release(&view);
615616
}
617+
else if (PySlice_Check(v)) {
618+
PySliceObject *slice = (PySliceObject *)v;
619+
W_TYPE(TYPE_SLICE, p);
620+
w_object(slice->start, p);
621+
w_object(slice->stop, p);
622+
w_object(slice->step, p);
623+
}
616624
else {
617625
W_TYPE(TYPE_UNKNOWN, p);
618626
p->error = WFERR_UNMARSHALLABLE;
@@ -1534,6 +1542,32 @@ r_object(RFILE *p)
15341542
retval = Py_NewRef(v);
15351543
break;
15361544

1545+
case TYPE_SLICE:
1546+
{
1547+
Py_ssize_t idx = r_ref_reserve(flag, p);
1548+
PyObject *stop = NULL;
1549+
PyObject *step = NULL;
1550+
PyObject *start = r_object(p);
1551+
if (start == NULL) {
1552+
goto cleanup;
1553+
}
1554+
stop = r_object(p);
1555+
if (stop == NULL) {
1556+
goto cleanup;
1557+
}
1558+
step = r_object(p);
1559+
if (step == NULL) {
1560+
goto cleanup;
1561+
}
1562+
retval = PySlice_New(start, stop, step);
1563+
r_ref_insert(retval, idx, flag, p);
1564+
cleanup:
1565+
Py_XDECREF(start);
1566+
Py_XDECREF(stop);
1567+
Py_XDECREF(step);
1568+
break;
1569+
}
1570+
15371571
default:
15381572
/* Bogus data got written, which isn't ideal.
15391573
This will let you keep working and recover. */

0 commit comments

Comments
 (0)