mirror of
https://github.com/python/cpython.git
synced 2025-07-24 11:44:31 +00:00
gh-116381: Specialize CONTAINS_OP (GH-116385)
* Specialize CONTAINS_OP * 📜🤖 Added by blurb_it. * Add PyAPI_FUNC for JIT --------- Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
This commit is contained in:
parent
73807eb634
commit
7114cf20c0
21 changed files with 645 additions and 194 deletions
|
@ -2237,13 +2237,75 @@ dummy_func(
|
|||
b = res ? Py_True : Py_False;
|
||||
}
|
||||
|
||||
inst(CONTAINS_OP, (left, right -- b)) {
|
||||
family(CONTAINS_OP, INLINE_CACHE_ENTRIES_CONTAINS_OP) = {
|
||||
CONTAINS_OP_LIST,
|
||||
CONTAINS_OP_SET,
|
||||
CONTAINS_OP_TUPLE,
|
||||
CONTAINS_OP_DICT,
|
||||
CONTAINS_OP_STR,
|
||||
};
|
||||
|
||||
op(_CONTAINS_OP, (left, right -- b)) {
|
||||
int res = PySequence_Contains(right, left);
|
||||
DECREF_INPUTS();
|
||||
ERROR_IF(res < 0, error);
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
}
|
||||
|
||||
specializing op(_SPECIALIZE_CONTAINS_OP, (counter/1, left, right -- left, right)) {
|
||||
#if ENABLE_SPECIALIZATION
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||
next_instr = this_instr;
|
||||
_Py_Specialize_ContainsOp(right, next_instr);
|
||||
DISPATCH_SAME_OPARG();
|
||||
}
|
||||
STAT_INC(CONTAINS_OP, deferred);
|
||||
DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache);
|
||||
#endif /* ENABLE_SPECIALIZATION */
|
||||
}
|
||||
|
||||
macro(CONTAINS_OP) = _SPECIALIZE_CONTAINS_OP + _CONTAINS_OP;
|
||||
|
||||
inst(CONTAINS_OP_LIST, (unused/1, left, right -- b)) {
|
||||
DEOPT_IF(!PyList_CheckExact(right));
|
||||
int res = _PyList_Contains(right, left);
|
||||
DECREF_INPUTS();
|
||||
ERROR_IF(res < 0, error);
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
}
|
||||
|
||||
inst(CONTAINS_OP_SET, (unused/1, left, right -- b)) {
|
||||
DEOPT_IF(!PySet_CheckExact(right));
|
||||
int res = _PySet_Contains((PySetObject *)right, left);
|
||||
DECREF_INPUTS();
|
||||
ERROR_IF(res < 0, error);
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
}
|
||||
|
||||
inst(CONTAINS_OP_TUPLE, (unused/1, left, right -- b)) {
|
||||
DEOPT_IF(!PyTuple_CheckExact(right));
|
||||
int res = _PyTuple_Contains((PyTupleObject *)right, left);
|
||||
DECREF_INPUTS();
|
||||
ERROR_IF(res < 0, error);
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
}
|
||||
|
||||
inst(CONTAINS_OP_DICT, (unused/1, left, right -- b)) {
|
||||
DEOPT_IF(!PyDict_CheckExact(right));
|
||||
int res = PyDict_Contains(right, left);
|
||||
DECREF_INPUTS();
|
||||
ERROR_IF(res < 0, error);
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
}
|
||||
|
||||
inst(CONTAINS_OP_STR, (unused/1, left, right -- b)) {
|
||||
DEOPT_IF(!PyUnicode_CheckExact(right));
|
||||
int res = PyUnicode_Contains(right, left);
|
||||
DECREF_INPUTS();
|
||||
ERROR_IF(res < 0, error);
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
}
|
||||
|
||||
inst(CHECK_EG_MATCH, (exc_value, match_type -- rest, match)) {
|
||||
if (_PyEval_CheckExceptStarTypeValid(tstate, match_type) < 0) {
|
||||
DECREF_INPUTS();
|
||||
|
|
90
Python/executor_cases.c.h
generated
90
Python/executor_cases.c.h
generated
|
@ -2189,6 +2189,96 @@
|
|||
break;
|
||||
}
|
||||
|
||||
case _CONTAINS_OP_LIST: {
|
||||
PyObject *right;
|
||||
PyObject *left;
|
||||
PyObject *b;
|
||||
oparg = CURRENT_OPARG();
|
||||
right = stack_pointer[-1];
|
||||
left = stack_pointer[-2];
|
||||
if (!PyList_CheckExact(right)) goto deoptimize;
|
||||
int res = _PyList_Contains(right, left);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res < 0) goto pop_2_error_tier_two;
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case _CONTAINS_OP_SET: {
|
||||
PyObject *right;
|
||||
PyObject *left;
|
||||
PyObject *b;
|
||||
oparg = CURRENT_OPARG();
|
||||
right = stack_pointer[-1];
|
||||
left = stack_pointer[-2];
|
||||
if (!PySet_CheckExact(right)) goto deoptimize;
|
||||
int res = _PySet_Contains((PySetObject *)right, left);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res < 0) goto pop_2_error_tier_two;
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case _CONTAINS_OP_TUPLE: {
|
||||
PyObject *right;
|
||||
PyObject *left;
|
||||
PyObject *b;
|
||||
oparg = CURRENT_OPARG();
|
||||
right = stack_pointer[-1];
|
||||
left = stack_pointer[-2];
|
||||
if (!PyTuple_CheckExact(right)) goto deoptimize;
|
||||
int res = _PyTuple_Contains((PyTupleObject *)right, left);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res < 0) goto pop_2_error_tier_two;
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case _CONTAINS_OP_DICT: {
|
||||
PyObject *right;
|
||||
PyObject *left;
|
||||
PyObject *b;
|
||||
oparg = CURRENT_OPARG();
|
||||
right = stack_pointer[-1];
|
||||
left = stack_pointer[-2];
|
||||
if (!PyDict_CheckExact(right)) goto deoptimize;
|
||||
int res = PyDict_Contains(right, left);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res < 0) goto pop_2_error_tier_two;
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case _CONTAINS_OP_STR: {
|
||||
PyObject *right;
|
||||
PyObject *left;
|
||||
PyObject *b;
|
||||
oparg = CURRENT_OPARG();
|
||||
right = stack_pointer[-1];
|
||||
left = stack_pointer[-2];
|
||||
if (!PyUnicode_CheckExact(right)) goto deoptimize;
|
||||
int res = PyUnicode_Contains(right, left);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res < 0) goto pop_2_error_tier_two;
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case _CHECK_EG_MATCH: {
|
||||
PyObject *match_type;
|
||||
PyObject *exc_value;
|
||||
|
|
134
Python/generated_cases.c.h
generated
134
Python/generated_cases.c.h
generated
|
@ -2127,14 +2127,144 @@
|
|||
|
||||
TARGET(CONTAINS_OP) {
|
||||
frame->instr_ptr = next_instr;
|
||||
next_instr += 1;
|
||||
next_instr += 2;
|
||||
INSTRUCTION_STATS(CONTAINS_OP);
|
||||
PREDICTED(CONTAINS_OP);
|
||||
_Py_CODEUNIT *this_instr = next_instr - 2;
|
||||
(void)this_instr;
|
||||
PyObject *right;
|
||||
PyObject *left;
|
||||
PyObject *b;
|
||||
// _SPECIALIZE_CONTAINS_OP
|
||||
right = stack_pointer[-1];
|
||||
left = stack_pointer[-2];
|
||||
int res = PySequence_Contains(right, left);
|
||||
{
|
||||
uint16_t counter = read_u16(&this_instr[1].cache);
|
||||
(void)counter;
|
||||
#if ENABLE_SPECIALIZATION
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||
next_instr = this_instr;
|
||||
_Py_Specialize_ContainsOp(right, next_instr);
|
||||
DISPATCH_SAME_OPARG();
|
||||
}
|
||||
STAT_INC(CONTAINS_OP, deferred);
|
||||
DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache);
|
||||
#endif /* ENABLE_SPECIALIZATION */
|
||||
}
|
||||
// _CONTAINS_OP
|
||||
{
|
||||
int res = PySequence_Contains(right, left);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res < 0) goto pop_2_error;
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
}
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(CONTAINS_OP_DICT) {
|
||||
frame->instr_ptr = next_instr;
|
||||
next_instr += 2;
|
||||
INSTRUCTION_STATS(CONTAINS_OP_DICT);
|
||||
static_assert(INLINE_CACHE_ENTRIES_CONTAINS_OP == 1, "incorrect cache size");
|
||||
PyObject *right;
|
||||
PyObject *left;
|
||||
PyObject *b;
|
||||
/* Skip 1 cache entry */
|
||||
right = stack_pointer[-1];
|
||||
left = stack_pointer[-2];
|
||||
DEOPT_IF(!PyDict_CheckExact(right), CONTAINS_OP);
|
||||
int res = PyDict_Contains(right, left);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res < 0) goto pop_2_error;
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(CONTAINS_OP_LIST) {
|
||||
frame->instr_ptr = next_instr;
|
||||
next_instr += 2;
|
||||
INSTRUCTION_STATS(CONTAINS_OP_LIST);
|
||||
static_assert(INLINE_CACHE_ENTRIES_CONTAINS_OP == 1, "incorrect cache size");
|
||||
PyObject *right;
|
||||
PyObject *left;
|
||||
PyObject *b;
|
||||
/* Skip 1 cache entry */
|
||||
right = stack_pointer[-1];
|
||||
left = stack_pointer[-2];
|
||||
DEOPT_IF(!PyList_CheckExact(right), CONTAINS_OP);
|
||||
int res = _PyList_Contains(right, left);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res < 0) goto pop_2_error;
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(CONTAINS_OP_SET) {
|
||||
frame->instr_ptr = next_instr;
|
||||
next_instr += 2;
|
||||
INSTRUCTION_STATS(CONTAINS_OP_SET);
|
||||
static_assert(INLINE_CACHE_ENTRIES_CONTAINS_OP == 1, "incorrect cache size");
|
||||
PyObject *right;
|
||||
PyObject *left;
|
||||
PyObject *b;
|
||||
/* Skip 1 cache entry */
|
||||
right = stack_pointer[-1];
|
||||
left = stack_pointer[-2];
|
||||
DEOPT_IF(!PySet_CheckExact(right), CONTAINS_OP);
|
||||
int res = _PySet_Contains((PySetObject *)right, left);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res < 0) goto pop_2_error;
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(CONTAINS_OP_STR) {
|
||||
frame->instr_ptr = next_instr;
|
||||
next_instr += 2;
|
||||
INSTRUCTION_STATS(CONTAINS_OP_STR);
|
||||
static_assert(INLINE_CACHE_ENTRIES_CONTAINS_OP == 1, "incorrect cache size");
|
||||
PyObject *right;
|
||||
PyObject *left;
|
||||
PyObject *b;
|
||||
/* Skip 1 cache entry */
|
||||
right = stack_pointer[-1];
|
||||
left = stack_pointer[-2];
|
||||
DEOPT_IF(!PyUnicode_CheckExact(right), CONTAINS_OP);
|
||||
int res = PyUnicode_Contains(right, left);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res < 0) goto pop_2_error;
|
||||
b = (res ^ oparg) ? Py_True : Py_False;
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(CONTAINS_OP_TUPLE) {
|
||||
frame->instr_ptr = next_instr;
|
||||
next_instr += 2;
|
||||
INSTRUCTION_STATS(CONTAINS_OP_TUPLE);
|
||||
static_assert(INLINE_CACHE_ENTRIES_CONTAINS_OP == 1, "incorrect cache size");
|
||||
PyObject *right;
|
||||
PyObject *left;
|
||||
PyObject *b;
|
||||
/* Skip 1 cache entry */
|
||||
right = stack_pointer[-1];
|
||||
left = stack_pointer[-2];
|
||||
DEOPT_IF(!PyTuple_CheckExact(right), CONTAINS_OP);
|
||||
int res = _PyTuple_Contains((PyTupleObject *)right, left);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res < 0) goto pop_2_error;
|
||||
|
|
10
Python/opcode_targets.h
generated
10
Python/opcode_targets.h
generated
|
@ -182,6 +182,11 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_COMPARE_OP_FLOAT,
|
||||
&&TARGET_COMPARE_OP_INT,
|
||||
&&TARGET_COMPARE_OP_STR,
|
||||
&&TARGET_CONTAINS_OP_DICT,
|
||||
&&TARGET_CONTAINS_OP_LIST,
|
||||
&&TARGET_CONTAINS_OP_SET,
|
||||
&&TARGET_CONTAINS_OP_STR,
|
||||
&&TARGET_CONTAINS_OP_TUPLE,
|
||||
&&TARGET_FOR_ITER_GEN,
|
||||
&&TARGET_FOR_ITER_LIST,
|
||||
&&TARGET_FOR_ITER_RANGE,
|
||||
|
@ -230,11 +235,6 @@ static void *opcode_targets[256] = {
|
|||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_INSTRUMENTED_RESUME,
|
||||
&&TARGET_INSTRUMENTED_END_FOR,
|
||||
&&TARGET_INSTRUMENTED_END_SEND,
|
||||
|
|
45
Python/optimizer_cases.c.h
generated
45
Python/optimizer_cases.c.h
generated
|
@ -1206,6 +1206,51 @@
|
|||
break;
|
||||
}
|
||||
|
||||
case _CONTAINS_OP_LIST: {
|
||||
_Py_UopsSymbol *b;
|
||||
b = sym_new_unknown(ctx);
|
||||
if (b == NULL) goto out_of_space;
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case _CONTAINS_OP_SET: {
|
||||
_Py_UopsSymbol *b;
|
||||
b = sym_new_unknown(ctx);
|
||||
if (b == NULL) goto out_of_space;
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case _CONTAINS_OP_TUPLE: {
|
||||
_Py_UopsSymbol *b;
|
||||
b = sym_new_unknown(ctx);
|
||||
if (b == NULL) goto out_of_space;
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case _CONTAINS_OP_DICT: {
|
||||
_Py_UopsSymbol *b;
|
||||
b = sym_new_unknown(ctx);
|
||||
if (b == NULL) goto out_of_space;
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case _CONTAINS_OP_STR: {
|
||||
_Py_UopsSymbol *b;
|
||||
b = sym_new_unknown(ctx);
|
||||
if (b == NULL) goto out_of_space;
|
||||
stack_pointer[-2] = b;
|
||||
stack_pointer += -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case _CHECK_EG_MATCH: {
|
||||
_Py_UopsSymbol *rest;
|
||||
_Py_UopsSymbol *match;
|
||||
|
|
|
@ -103,6 +103,7 @@ _Py_GetSpecializationStats(void) {
|
|||
return NULL;
|
||||
}
|
||||
int err = 0;
|
||||
err += add_stat_dict(stats, CONTAINS_OP, "contains_op");
|
||||
err += add_stat_dict(stats, LOAD_SUPER_ATTR, "load_super_attr");
|
||||
err += add_stat_dict(stats, LOAD_ATTR, "load_attr");
|
||||
err += add_stat_dict(stats, LOAD_GLOBAL, "load_global");
|
||||
|
@ -2561,6 +2562,43 @@ success:
|
|||
cache->counter = adaptive_counter_cooldown();
|
||||
}
|
||||
|
||||
void
|
||||
_Py_Specialize_ContainsOp(PyObject *value, _Py_CODEUNIT *instr)
|
||||
{
|
||||
assert(ENABLE_SPECIALIZATION);
|
||||
assert(_PyOpcode_Caches[CONTAINS_OP] == INLINE_CACHE_ENTRIES_COMPARE_OP);
|
||||
_PyContainsOpCache *cache = (_PyContainsOpCache *)(instr + 1);
|
||||
if (PyUnicode_CheckExact(value)) {
|
||||
instr->op.code = CONTAINS_OP_STR;
|
||||
goto success;
|
||||
}
|
||||
if (PyList_CheckExact(value)) {
|
||||
instr->op.code = CONTAINS_OP_LIST;
|
||||
goto success;
|
||||
}
|
||||
if (PyTuple_CheckExact(value)) {
|
||||
instr->op.code = CONTAINS_OP_TUPLE;
|
||||
goto success;
|
||||
}
|
||||
if (PyDict_CheckExact(value)) {
|
||||
instr->op.code = CONTAINS_OP_DICT;
|
||||
goto success;
|
||||
}
|
||||
if (PySet_CheckExact(value)) {
|
||||
instr->op.code = CONTAINS_OP_SET;
|
||||
goto success;
|
||||
}
|
||||
|
||||
|
||||
STAT_INC(CONTAINS_OP, failure);
|
||||
instr->op.code = CONTAINS_OP;
|
||||
cache->counter = adaptive_counter_backoff(cache->counter);
|
||||
return;
|
||||
success:
|
||||
STAT_INC(CONTAINS_OP, success);
|
||||
cache->counter = adaptive_counter_cooldown();
|
||||
}
|
||||
|
||||
/* Code init cleanup.
|
||||
* CALL_ALLOC_AND_ENTER_INIT will set up
|
||||
* the frame to execute the EXIT_INIT_CHECK
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue