Skip to content

Commit 5fcfdd8

Browse files
authored
GH-91432: Specialize FOR_ITER (GH-91713)
* Adds FOR_ITER_LIST and FOR_ITER_RANGE specializations. * Adds _PyLong_AssignValue() internal function to avoid temporary boxing of ints.
1 parent c735d54 commit 5fcfdd8

22 files changed

+447
-282
lines changed

Include/internal/pycore_code.h

+7
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ typedef struct {
8585

8686
#define INLINE_CACHE_ENTRIES_STORE_SUBSCR CACHE_ENTRIES(_PyStoreSubscrCache)
8787

88+
typedef struct {
89+
_Py_CODEUNIT counter;
90+
} _PyForIterCache;
91+
92+
#define INLINE_CACHE_ENTRIES_FOR_ITER CACHE_ENTRIES(_PyForIterCache)
93+
8894
#define QUICKENING_WARMUP_DELAY 8
8995

9096
/* We want to compare to zero for efficiency, so we offset values accordingly */
@@ -243,6 +249,7 @@ extern void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs,
243249
_Py_CODEUNIT *instr, int oparg);
244250
extern void _Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr,
245251
int oparg);
252+
extern void _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr);
246253

247254
/* Deallocator function for static codeobjects used in deepfreeze.py */
248255
extern void _PyStaticCode_Dealloc(PyCodeObject *co);

Include/internal/pycore_list.h

+6
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ _PyList_AppendTakeRef(PyListObject *self, PyObject *newitem)
5656
return _PyList_AppendTakeRefListResize(self, newitem);
5757
}
5858

59+
typedef struct {
60+
PyObject_HEAD
61+
Py_ssize_t it_index;
62+
PyListObject *it_seq; /* Set to NULL when iterator is exhausted */
63+
} _PyListIterObject;
64+
5965
#ifdef __cplusplus
6066
}
6167
#endif

Include/internal/pycore_long.h

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ PyObject *_PyLong_Add(PyLongObject *left, PyLongObject *right);
4747
PyObject *_PyLong_Multiply(PyLongObject *left, PyLongObject *right);
4848
PyObject *_PyLong_Subtract(PyLongObject *left, PyLongObject *right);
4949

50+
int _PyLong_AssignValue(PyObject **target, Py_ssize_t value);
51+
5052
/* Used by Python/mystrtoul.c, _PyBytes_FromHex(),
5153
_PyBytes_DecodeEscape(), etc. */
5254
PyAPI_DATA(unsigned char) _PyLong_DigitValue[256];

Include/internal/pycore_opcode.h

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

Include/internal/pycore_range.h

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef Py_INTERNAL_RANGE_H
2+
#define Py_INTERNAL_RANGE_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#ifndef Py_BUILD_CORE
8+
# error "this header requires Py_BUILD_CORE define"
9+
#endif
10+
11+
typedef struct {
12+
PyObject_HEAD
13+
long index;
14+
long start;
15+
long step;
16+
long len;
17+
} _PyRangeIterObject;
18+
19+
#ifdef __cplusplus
20+
}
21+
#endif
22+
#endif /* !Py_INTERNAL_RANGE_H */

Include/opcode.h

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

Lib/dis.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
LOAD_GLOBAL = opmap['LOAD_GLOBAL']
3838
BINARY_OP = opmap['BINARY_OP']
3939
JUMP_BACKWARD = opmap['JUMP_BACKWARD']
40+
FOR_ITER = opmap['FOR_ITER']
4041
LOAD_ATTR = opmap['LOAD_ATTR']
4142

4243
CACHE = opmap["CACHE"]
@@ -476,6 +477,8 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
476477
elif deop in hasjrel:
477478
signed_arg = -arg if _is_backward_jump(deop) else arg
478479
argval = offset + 2 + signed_arg*2
480+
if deop == FOR_ITER:
481+
argval += 2
479482
argrepr = "to " + repr(argval)
480483
elif deop in haslocal or deop in hasfree:
481484
argval, argrepr = _get_name_info(arg, varname_from_oparg)
@@ -629,11 +632,14 @@ def findlabels(code):
629632
labels = []
630633
for offset, op, arg in _unpack_opargs(code):
631634
if arg is not None:
632-
if op in hasjrel:
633-
if _is_backward_jump(op):
635+
deop = _deoptop(op)
636+
if deop in hasjrel:
637+
if _is_backward_jump(deop):
634638
arg = -arg
635639
label = offset + 2 + arg*2
636-
elif op in hasjabs:
640+
if deop == FOR_ITER:
641+
label += 2
642+
elif deop in hasjabs:
637643
label = arg*2
638644
else:
639645
continue

Lib/importlib/_bootstrap_external.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -403,12 +403,12 @@ def _write_atomic(path, data, mode=0o666):
403403
# Python 3.11a7 3492 (make POP_JUMP_IF_NONE/NOT_NONE/TRUE/FALSE relative)
404404
# Python 3.11a7 3493 (Make JUMP_IF_TRUE_OR_POP/JUMP_IF_FALSE_OR_POP relative)
405405
# Python 3.11a7 3494 (New location info table)
406-
407406
# Python 3.12a1 3500 (Remove PRECALL opcode)
408407
# Python 3.12a1 3501 (YIELD_VALUE oparg == stack_depth)
409408
# Python 3.12a1 3502 (LOAD_FAST_CHECK, no NULL-check in LOAD_FAST)
410409
# Python 3.12a1 3503 (Shrink LOAD_METHOD cache)
411410
# Python 3.12a1 3504 (Merge LOAD_METHOD back into LOAD_ATTR)
411+
# Python 3.12a1 3505 (Specialization/Cache for FOR_ITER)
412412

413413
# Python 3.13 will start with 3550
414414

@@ -422,7 +422,7 @@ def _write_atomic(path, data, mode=0o666):
422422
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
423423
# in PC/launcher.c must also be updated.
424424

425-
MAGIC_NUMBER = (3504).to_bytes(2, 'little') + b'\r\n'
425+
MAGIC_NUMBER = (3505).to_bytes(2, 'little') + b'\r\n'
426426

427427
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
428428

Lib/opcode.py

+8
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,11 @@ def jabs_op(name, op):
278278
"EXTENDED_ARG": [
279279
"EXTENDED_ARG_QUICK",
280280
],
281+
"FOR_ITER": [
282+
"FOR_ITER_ADAPTIVE",
283+
"FOR_ITER_LIST",
284+
"FOR_ITER_RANGE",
285+
],
281286
"JUMP_BACKWARD": [
282287
"JUMP_BACKWARD_QUICK",
283288
],
@@ -367,6 +372,9 @@ def jabs_op(name, op):
367372
"type_version": 2,
368373
"func_version": 1,
369374
},
375+
"FOR_ITER": {
376+
"counter": 1,
377+
},
370378
"LOAD_ATTR": {
371379
"counter": 1,
372380
"version": 2,

0 commit comments

Comments
 (0)