Skip to content

Commit 21fa7a3

Browse files
authored
bpo-45829: Specialize BINARY_SUBSCR for __getitem__ implemented in Python. (GH-29592)
1 parent 5275e59 commit 21fa7a3

File tree

7 files changed

+145
-89
lines changed

7 files changed

+145
-89
lines changed

Include/internal/pycore_code.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ typedef struct {
1717
uint8_t original_oparg;
1818
uint8_t counter;
1919
uint16_t index;
20+
uint32_t version;
2021
} _PyAdaptiveEntry;
2122

2223

@@ -266,7 +267,7 @@ int _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name
266267
int _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache);
267268
int _Py_Specialize_LoadGlobal(PyObject *globals, PyObject *builtins, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache);
268269
int _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache);
269-
int _Py_Specialize_BinarySubscr(PyObject *sub, PyObject *container, _Py_CODEUNIT *instr);
270+
int _Py_Specialize_BinarySubscr(PyObject *sub, PyObject *container, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache);
270271
int _Py_Specialize_CallFunction(PyObject *callable, _Py_CODEUNIT *instr, int nargs, SpecializedCacheEntry *cache, PyObject *builtins);
271272
void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
272273
SpecializedCacheEntry *cache);

Include/opcode.h

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

Lib/opcode.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ def jabs_op(name, op):
234234
"BINARY_OP_SUBTRACT_INT",
235235
"BINARY_OP_SUBTRACT_FLOAT",
236236
"BINARY_SUBSCR_ADAPTIVE",
237+
"BINARY_SUBSCR_GETITEM",
237238
"BINARY_SUBSCR_LIST_INT",
238239
"BINARY_SUBSCR_TUPLE_INT",
239240
"BINARY_SUBSCR_DICT",
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Specialize :opcode:`BINARY_SUBSCR` for classes with a ``__getitem__`` method
2+
implemented in Python

Python/ceval.c

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2140,21 +2140,21 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
21402140
}
21412141

21422142
TARGET(BINARY_SUBSCR_ADAPTIVE) {
2143-
if (oparg == 0) {
2143+
SpecializedCacheEntry *cache = GET_CACHE();
2144+
if (cache->adaptive.counter == 0) {
21442145
PyObject *sub = TOP();
21452146
PyObject *container = SECOND();
21462147
next_instr--;
2147-
if (_Py_Specialize_BinarySubscr(container, sub, next_instr) < 0) {
2148+
if (_Py_Specialize_BinarySubscr(container, sub, next_instr, cache) < 0) {
21482149
goto error;
21492150
}
21502151
DISPATCH();
21512152
}
21522153
else {
21532154
STAT_INC(BINARY_SUBSCR, deferred);
2154-
// oparg is the adaptive cache counter
2155-
UPDATE_PREV_INSTR_OPARG(next_instr, oparg - 1);
2156-
assert(_Py_OPCODE(next_instr[-1]) == BINARY_SUBSCR_ADAPTIVE);
2157-
assert(_Py_OPARG(next_instr[-1]) == oparg - 1);
2155+
cache->adaptive.counter--;
2156+
assert(cache->adaptive.original_oparg == 0);
2157+
/* No need to set oparg here; it isn't used by BINARY_SUBSCR */
21582158
STAT_DEC(BINARY_SUBSCR, unquickened);
21592159
JUMP_TO_INSTRUCTION(BINARY_SUBSCR);
21602160
}
@@ -2223,6 +2223,37 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
22232223
DISPATCH();
22242224
}
22252225

2226+
TARGET(BINARY_SUBSCR_GETITEM) {
2227+
PyObject *sub = TOP();
2228+
PyObject *container = SECOND();
2229+
SpecializedCacheEntry *caches = GET_CACHE();
2230+
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
2231+
_PyObjectCache *cache1 = &caches[-1].obj;
2232+
PyFunctionObject *getitem = (PyFunctionObject *)cache1->obj;
2233+
DEOPT_IF(Py_TYPE(container)->tp_version_tag != cache0->version, BINARY_SUBSCR);
2234+
DEOPT_IF(getitem->func_version != cache0->index, BINARY_SUBSCR);
2235+
PyCodeObject *code = (PyCodeObject *)getitem->func_code;
2236+
size_t size = code->co_nlocalsplus + code->co_stacksize + FRAME_SPECIALS_SIZE;
2237+
assert(code->co_argcount == 2);
2238+
InterpreterFrame *new_frame = _PyThreadState_BumpFramePointer(tstate, size);
2239+
if (new_frame == NULL) {
2240+
goto error;
2241+
}
2242+
_PyFrame_InitializeSpecials(new_frame, PyFunction_AS_FRAME_CONSTRUCTOR(getitem),
2243+
NULL, code->co_nlocalsplus);
2244+
STACK_SHRINK(2);
2245+
new_frame->localsplus[0] = container;
2246+
new_frame->localsplus[1] = sub;
2247+
for (int i = 2; i < code->co_nlocalsplus; i++) {
2248+
new_frame->localsplus[i] = NULL;
2249+
}
2250+
_PyFrame_SetStackPointer(frame, stack_pointer);
2251+
new_frame->previous = frame;
2252+
frame = cframe.current_frame = new_frame;
2253+
new_frame->depth = frame->depth + 1;
2254+
goto start_frame;
2255+
}
2256+
22262257
TARGET(LIST_APPEND) {
22272258
PyObject *v = POP();
22282259
PyObject *list = PEEK(oparg);
@@ -4878,29 +4909,13 @@ opname ## _miss: \
48784909
JUMP_TO_INSTRUCTION(opname); \
48794910
}
48804911

4881-
#define MISS_WITH_OPARG_COUNTER(opname) \
4882-
opname ## _miss: \
4883-
{ \
4884-
STAT_INC(opname, miss); \
4885-
uint8_t oparg = _Py_OPARG(next_instr[-1])-1; \
4886-
UPDATE_PREV_INSTR_OPARG(next_instr, oparg); \
4887-
assert(_Py_OPARG(next_instr[-1]) == oparg); \
4888-
if (oparg == 0) /* too many cache misses */ { \
4889-
oparg = ADAPTIVE_CACHE_BACKOFF; \
4890-
next_instr[-1] = _Py_MAKECODEUNIT(opname ## _ADAPTIVE, oparg); \
4891-
STAT_INC(opname, deopt); \
4892-
} \
4893-
STAT_DEC(opname, unquickened); \
4894-
JUMP_TO_INSTRUCTION(opname); \
4895-
}
4896-
48974912
MISS_WITH_CACHE(LOAD_ATTR)
48984913
MISS_WITH_CACHE(STORE_ATTR)
48994914
MISS_WITH_CACHE(LOAD_GLOBAL)
49004915
MISS_WITH_CACHE(LOAD_METHOD)
49014916
MISS_WITH_CACHE(CALL_FUNCTION)
49024917
MISS_WITH_CACHE(BINARY_OP)
4903-
MISS_WITH_OPARG_COUNTER(BINARY_SUBSCR)
4918+
MISS_WITH_CACHE(BINARY_SUBSCR)
49044919

49054920
binary_subscr_dict_error:
49064921
{

Python/opcode_targets.h

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

0 commit comments

Comments
 (0)