mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
bpo-26280: Port BINARY_SUBSCR to PEP 659 adaptive interpreter (GH-27043)
This commit is contained in:
parent
a0551059ba
commit
641345d636
7 changed files with 203 additions and 23 deletions
116
Python/ceval.c
116
Python/ceval.c
|
@ -15,6 +15,7 @@
|
|||
#include "pycore_ceval.h" // _PyEval_SignalAsyncExc()
|
||||
#include "pycore_code.h"
|
||||
#include "pycore_initconfig.h" // _PyStatus_OK()
|
||||
#include "pycore_long.h" // _PyLong_GetZero()
|
||||
#include "pycore_object.h" // _PyObject_GC_TRACK()
|
||||
#include "pycore_moduleobject.h"
|
||||
#include "pycore_pyerrors.h" // _PyErr_Fetch()
|
||||
|
@ -1398,6 +1399,8 @@ eval_frame_handle_pending(PyThreadState *tstate)
|
|||
|
||||
#define DEOPT_IF(cond, instname) if (cond) { goto instname ## _miss; }
|
||||
|
||||
#define UPDATE_PREV_INSTR_OPARG(instr, oparg) ((uint8_t*)(instr))[-1] = (oparg)
|
||||
|
||||
#define GLOBALS() specials[FRAME_SPECIALS_GLOBALS_OFFSET]
|
||||
#define BUILTINS() specials[FRAME_SPECIALS_BUILTINS_OFFSET]
|
||||
#define LOCALS() specials[FRAME_SPECIALS_LOCALS_OFFSET]
|
||||
|
@ -1913,6 +1916,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
|
|||
}
|
||||
|
||||
case TARGET(BINARY_SUBSCR): {
|
||||
PREDICTED(BINARY_SUBSCR);
|
||||
STAT_INC(BINARY_SUBSCR, unquickened);
|
||||
PyObject *sub = POP();
|
||||
PyObject *container = TOP();
|
||||
PyObject *res = PyObject_GetItem(container, sub);
|
||||
|
@ -1924,6 +1929,91 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(BINARY_SUBSCR_ADAPTIVE): {
|
||||
if (oparg == 0) {
|
||||
PyObject *sub = TOP();
|
||||
PyObject *container = SECOND();
|
||||
next_instr--;
|
||||
if (_Py_Specialize_BinarySubscr(container, sub, next_instr) < 0) {
|
||||
goto error;
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
else {
|
||||
STAT_INC(BINARY_SUBSCR, deferred);
|
||||
// oparg is the adaptive cache counter
|
||||
UPDATE_PREV_INSTR_OPARG(next_instr, oparg - 1);
|
||||
assert(_Py_OPCODE(next_instr[-1]) == BINARY_SUBSCR_ADAPTIVE);
|
||||
assert(_Py_OPARG(next_instr[-1]) == oparg - 1);
|
||||
JUMP_TO_INSTRUCTION(BINARY_SUBSCR);
|
||||
}
|
||||
}
|
||||
|
||||
case TARGET(BINARY_SUBSCR_LIST_INT): {
|
||||
PyObject *sub = TOP();
|
||||
PyObject *list = SECOND();
|
||||
DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR);
|
||||
DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR);
|
||||
|
||||
// Deopt unless 0 <= sub < PyList_Size(list)
|
||||
Py_ssize_t signed_magnitude = Py_SIZE(sub);
|
||||
DEOPT_IF(((size_t)signed_magnitude) > 1, BINARY_SUBSCR);
|
||||
assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0);
|
||||
Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0];
|
||||
DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR);
|
||||
|
||||
STAT_INC(BINARY_SUBSCR, hit);
|
||||
PyObject *res = PyList_GET_ITEM(list, index);
|
||||
assert(res != NULL);
|
||||
Py_INCREF(res);
|
||||
STACK_SHRINK(1);
|
||||
Py_DECREF(sub);
|
||||
SET_TOP(res);
|
||||
Py_DECREF(list);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(BINARY_SUBSCR_TUPLE_INT): {
|
||||
PyObject *sub = TOP();
|
||||
PyObject *tuple = SECOND();
|
||||
DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR);
|
||||
DEOPT_IF(!PyTuple_CheckExact(tuple), BINARY_SUBSCR);
|
||||
|
||||
// Deopt unless 0 <= sub < PyTuple_Size(list)
|
||||
Py_ssize_t signed_magnitude = Py_SIZE(sub);
|
||||
DEOPT_IF(((size_t)signed_magnitude) > 1, BINARY_SUBSCR);
|
||||
assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0);
|
||||
Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0];
|
||||
DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR);
|
||||
|
||||
STAT_INC(BINARY_SUBSCR, hit);
|
||||
PyObject *res = PyTuple_GET_ITEM(tuple, index);
|
||||
assert(res != NULL);
|
||||
Py_INCREF(res);
|
||||
STACK_SHRINK(1);
|
||||
Py_DECREF(sub);
|
||||
SET_TOP(res);
|
||||
Py_DECREF(tuple);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(BINARY_SUBSCR_DICT): {
|
||||
PyObject *dict = SECOND();
|
||||
DEOPT_IF(!PyDict_CheckExact(SECOND()), BINARY_SUBSCR);
|
||||
STAT_INC(BINARY_SUBSCR, hit);
|
||||
PyObject *sub = TOP();
|
||||
PyObject *res = PyDict_GetItemWithError(dict, sub);
|
||||
if (res == NULL) {
|
||||
goto binary_subscr_dict_error;
|
||||
}
|
||||
Py_INCREF(res);
|
||||
STACK_SHRINK(1);
|
||||
Py_DECREF(sub);
|
||||
SET_TOP(res);
|
||||
Py_DECREF(dict);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(BINARY_LSHIFT): {
|
||||
PyObject *right = POP();
|
||||
PyObject *left = TOP();
|
||||
|
@ -4327,8 +4417,34 @@ opname ## _miss: \
|
|||
JUMP_TO_INSTRUCTION(opname); \
|
||||
}
|
||||
|
||||
#define MISS_WITH_OPARG_COUNTER(opname) \
|
||||
opname ## _miss: \
|
||||
{ \
|
||||
STAT_INC(opname, miss); \
|
||||
uint8_t oparg = saturating_decrement(_Py_OPARG(next_instr[-1])); \
|
||||
UPDATE_PREV_INSTR_OPARG(next_instr, oparg); \
|
||||
assert(_Py_OPARG(next_instr[-1]) == oparg); \
|
||||
if (oparg == saturating_zero()) /* too many cache misses */ { \
|
||||
oparg = ADAPTIVE_CACHE_BACKOFF; \
|
||||
next_instr[-1] = _Py_MAKECODEUNIT(opname ## _ADAPTIVE, oparg); \
|
||||
STAT_INC(opname, deopt); \
|
||||
} \
|
||||
JUMP_TO_INSTRUCTION(opname); \
|
||||
}
|
||||
|
||||
MISS_WITH_CACHE(LOAD_ATTR)
|
||||
MISS_WITH_CACHE(LOAD_GLOBAL)
|
||||
MISS_WITH_OPARG_COUNTER(BINARY_SUBSCR)
|
||||
|
||||
binary_subscr_dict_error:
|
||||
{
|
||||
PyObject *sub = POP();
|
||||
if (!_PyErr_Occurred(tstate)) {
|
||||
_PyErr_SetKeyError(sub);
|
||||
}
|
||||
Py_DECREF(sub);
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
/* Double-check exception status. */
|
||||
|
|
22
Python/opcode_targets.h
generated
22
Python/opcode_targets.h
generated
|
@ -6,21 +6,21 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_DUP_TOP,
|
||||
&&TARGET_DUP_TOP_TWO,
|
||||
&&TARGET_ROT_FOUR,
|
||||
&&TARGET_JUMP_ABSOLUTE_QUICK,
|
||||
&&TARGET_LOAD_ATTR_ADAPTIVE,
|
||||
&&TARGET_BINARY_SUBSCR_ADAPTIVE,
|
||||
&&TARGET_BINARY_SUBSCR_LIST_INT,
|
||||
&&TARGET_NOP,
|
||||
&&TARGET_UNARY_POSITIVE,
|
||||
&&TARGET_UNARY_NEGATIVE,
|
||||
&&TARGET_UNARY_NOT,
|
||||
&&TARGET_LOAD_ATTR_SPLIT_KEYS,
|
||||
&&TARGET_LOAD_ATTR_WITH_HINT,
|
||||
&&TARGET_BINARY_SUBSCR_TUPLE_INT,
|
||||
&&TARGET_BINARY_SUBSCR_DICT,
|
||||
&&TARGET_UNARY_INVERT,
|
||||
&&TARGET_BINARY_MATRIX_MULTIPLY,
|
||||
&&TARGET_INPLACE_MATRIX_MULTIPLY,
|
||||
&&TARGET_LOAD_ATTR_SLOT,
|
||||
&&TARGET_JUMP_ABSOLUTE_QUICK,
|
||||
&&TARGET_BINARY_POWER,
|
||||
&&TARGET_BINARY_MULTIPLY,
|
||||
&&TARGET_LOAD_ATTR_MODULE,
|
||||
&&TARGET_LOAD_ATTR_ADAPTIVE,
|
||||
&&TARGET_BINARY_MODULO,
|
||||
&&TARGET_BINARY_ADD,
|
||||
&&TARGET_BINARY_SUBTRACT,
|
||||
|
@ -35,8 +35,12 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_MATCH_KEYS,
|
||||
&&TARGET_COPY_DICT_WITHOUT_KEYS,
|
||||
&&TARGET_PUSH_EXC_INFO,
|
||||
&&TARGET_LOAD_GLOBAL_ADAPTIVE,
|
||||
&&TARGET_LOAD_ATTR_SPLIT_KEYS,
|
||||
&&TARGET_POP_EXCEPT_AND_RERAISE,
|
||||
&&TARGET_LOAD_ATTR_WITH_HINT,
|
||||
&&TARGET_LOAD_ATTR_SLOT,
|
||||
&&TARGET_LOAD_ATTR_MODULE,
|
||||
&&TARGET_LOAD_GLOBAL_ADAPTIVE,
|
||||
&&TARGET_LOAD_GLOBAL_MODULE,
|
||||
&&TARGET_LOAD_GLOBAL_BUILTIN,
|
||||
&&_unknown_opcode,
|
||||
|
@ -44,10 +48,6 @@ static void *opcode_targets[256] = {
|
|||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_WITH_EXCEPT_START,
|
||||
&&TARGET_GET_AITER,
|
||||
&&TARGET_GET_ANEXT,
|
||||
|
|
|
@ -78,6 +78,7 @@ _Py_PrintSpecializationStats(void)
|
|||
printf("Specialization stats:\n");
|
||||
print_stats(&_specialization_stats[LOAD_ATTR], "load_attr");
|
||||
print_stats(&_specialization_stats[LOAD_GLOBAL], "load_global");
|
||||
print_stats(&_specialization_stats[BINARY_SUBSCR], "binary_subscr");
|
||||
}
|
||||
|
||||
#if SPECIALIZATION_STATS_DETAILED
|
||||
|
@ -162,12 +163,14 @@ get_cache_count(SpecializedCacheOrInstruction *quickened) {
|
|||
static uint8_t adaptive_opcodes[256] = {
|
||||
[LOAD_ATTR] = LOAD_ATTR_ADAPTIVE,
|
||||
[LOAD_GLOBAL] = LOAD_GLOBAL_ADAPTIVE,
|
||||
[BINARY_SUBSCR] = BINARY_SUBSCR_ADAPTIVE,
|
||||
};
|
||||
|
||||
/* The number of cache entries required for a "family" of instructions. */
|
||||
static uint8_t cache_requirements[256] = {
|
||||
[LOAD_ATTR] = 2, /* _PyAdaptiveEntry and _PyLoadAttrCache */
|
||||
[LOAD_GLOBAL] = 2, /* _PyAdaptiveEntry and _PyLoadGlobalCache */
|
||||
[BINARY_SUBSCR] = 0,
|
||||
};
|
||||
|
||||
/* Return the oparg for the cache_offset and instruction index.
|
||||
|
@ -251,7 +254,6 @@ optimize(SpecializedCacheOrInstruction *quickened, int len)
|
|||
previous_opcode = opcode;
|
||||
continue;
|
||||
}
|
||||
instructions[i] = _Py_MAKECODEUNIT(adaptive_opcode, new_oparg);
|
||||
previous_opcode = adaptive_opcode;
|
||||
int entries_needed = cache_requirements[opcode];
|
||||
if (entries_needed) {
|
||||
|
@ -261,7 +263,11 @@ optimize(SpecializedCacheOrInstruction *quickened, int len)
|
|||
_GetSpecializedCacheEntry(instructions, cache0_offset);
|
||||
cache->adaptive.original_oparg = oparg;
|
||||
cache->adaptive.counter = 0;
|
||||
} else {
|
||||
// oparg is the adaptive cache counter
|
||||
new_oparg = 0;
|
||||
}
|
||||
instructions[i] = _Py_MAKECODEUNIT(adaptive_opcode, new_oparg);
|
||||
}
|
||||
else {
|
||||
/* Super instructions don't use the cache,
|
||||
|
@ -637,3 +643,43 @@ success:
|
|||
cache0->counter = saturating_start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_Py_Specialize_BinarySubscr(
|
||||
PyObject *container, PyObject *sub, _Py_CODEUNIT *instr)
|
||||
{
|
||||
PyTypeObject *container_type = Py_TYPE(container);
|
||||
if (container_type == &PyList_Type) {
|
||||
if (PyLong_CheckExact(sub)) {
|
||||
*instr = _Py_MAKECODEUNIT(BINARY_SUBSCR_LIST_INT, saturating_start());
|
||||
goto success;
|
||||
} else {
|
||||
SPECIALIZATION_FAIL(BINARY_SUBSCR, Py_TYPE(container), sub, "list; non-integer subscr");
|
||||
}
|
||||
}
|
||||
if (container_type == &PyTuple_Type) {
|
||||
if (PyLong_CheckExact(sub)) {
|
||||
*instr = _Py_MAKECODEUNIT(BINARY_SUBSCR_TUPLE_INT, saturating_start());
|
||||
goto success;
|
||||
} else {
|
||||
SPECIALIZATION_FAIL(BINARY_SUBSCR, Py_TYPE(container), sub, "tuple; non-integer subscr");
|
||||
}
|
||||
}
|
||||
if (container_type == &PyDict_Type) {
|
||||
*instr = _Py_MAKECODEUNIT(BINARY_SUBSCR_DICT, saturating_start());
|
||||
goto success;
|
||||
}
|
||||
|
||||
SPECIALIZATION_FAIL(BINARY_SUBSCR, Py_TYPE(container), sub, "not list|tuple|dict");
|
||||
goto fail;
|
||||
fail:
|
||||
STAT_INC(BINARY_SUBSCR, specialization_failure);
|
||||
assert(!PyErr_Occurred());
|
||||
*instr = _Py_MAKECODEUNIT(_Py_OPCODE(*instr), ADAPTIVE_CACHE_BACKOFF);
|
||||
return 0;
|
||||
success:
|
||||
STAT_INC(BINARY_SUBSCR, specialization_success);
|
||||
assert(!PyErr_Occurred());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue