gh-100239: Specialize more binary operations using BINARY_OP_EXTEND (GH-128956)

This commit is contained in:
Pieter Eendebak 2026-04-16 10:22:41 +02:00 committed by GitHub
parent 9d38143088
commit 1f6a09fb36
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 1850 additions and 1224 deletions

View file

@ -1008,14 +1008,32 @@ dummy_func(
res = PyStackRef_FromPyObjectSteal(temp);
}
tier2 op(_GUARD_BINARY_OP_EXTEND_LHS, (descr/4, left, right -- left, right)) {
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr;
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard == NULL && d->lhs_type != NULL);
EXIT_IF(Py_TYPE(left_o) != d->lhs_type);
}
tier2 op(_GUARD_BINARY_OP_EXTEND_RHS, (descr/4, left, right -- left, right)) {
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr;
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard == NULL && d->rhs_type != NULL);
EXIT_IF(Py_TYPE(right_o) != d->rhs_type);
}
op(_GUARD_BINARY_OP_EXTEND, (descr/4, left, right -- left, right)) {
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr;
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard);
int res = d->guard(left_o, right_o);
EXIT_IF(!res);
assert(d != NULL);
int match = (d->guard != NULL)
? d->guard(left_o, right_o)
: (Py_TYPE(left_o) == d->lhs_type && Py_TYPE(right_o) == d->rhs_type);
EXIT_IF(!match);
}
op(_BINARY_OP_EXTEND, (descr/4, left, right -- res, l, r)) {
@ -1030,11 +1048,14 @@ dummy_func(
if (res_o == NULL) {
ERROR_NO_POP();
}
assert(d->result_type == NULL || Py_TYPE(res_o) == d->result_type);
assert(!d->result_unique || Py_REFCNT(res_o) == 1 || _Py_IsImmortal(res_o));
// The JIT and tier 2 optimizer assume that float results from
// binary operations are always uniquely referenced (refcount == 1).
// If this assertion fails, update the optimizer to stop marking
// float results as unique in optimizer_bytecodes.c.
assert(!PyFloat_CheckExact(res_o) || Py_REFCNT(res_o) == 1);
res = PyStackRef_FromPyObjectSteal(res_o);
l = left;
r = right;

View file

@ -6395,6 +6395,216 @@
break;
}
case _GUARD_BINARY_OP_EXTEND_LHS_r02: {
CHECK_CURRENT_CACHED_VALUES(0);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef left;
left = stack_pointer[-2];
PyObject *descr = (PyObject *)CURRENT_OPERAND0_64();
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr;
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard == NULL && d->lhs_type != NULL);
if (Py_TYPE(left_o) != d->lhs_type) {
UOP_STAT_INC(uopcode, miss);
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_JUMP_TARGET();
}
_tos_cache1 = stack_pointer[-1];
_tos_cache0 = left;
SET_CURRENT_CACHED_VALUES(2);
stack_pointer += -2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _GUARD_BINARY_OP_EXTEND_LHS_r12: {
CHECK_CURRENT_CACHED_VALUES(1);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef left;
_PyStackRef _stack_item_0 = _tos_cache0;
left = stack_pointer[-1];
PyObject *descr = (PyObject *)CURRENT_OPERAND0_64();
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr;
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard == NULL && d->lhs_type != NULL);
if (Py_TYPE(left_o) != d->lhs_type) {
UOP_STAT_INC(uopcode, miss);
_tos_cache0 = _stack_item_0;
SET_CURRENT_CACHED_VALUES(1);
JUMP_TO_JUMP_TARGET();
}
_tos_cache1 = _stack_item_0;
_tos_cache0 = left;
SET_CURRENT_CACHED_VALUES(2);
stack_pointer += -1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _GUARD_BINARY_OP_EXTEND_LHS_r22: {
CHECK_CURRENT_CACHED_VALUES(2);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef left;
_PyStackRef _stack_item_0 = _tos_cache0;
_PyStackRef _stack_item_1 = _tos_cache1;
left = _stack_item_0;
PyObject *descr = (PyObject *)CURRENT_OPERAND0_64();
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr;
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard == NULL && d->lhs_type != NULL);
if (Py_TYPE(left_o) != d->lhs_type) {
UOP_STAT_INC(uopcode, miss);
_tos_cache1 = _stack_item_1;
_tos_cache0 = left;
SET_CURRENT_CACHED_VALUES(2);
JUMP_TO_JUMP_TARGET();
}
_tos_cache1 = _stack_item_1;
_tos_cache0 = left;
SET_CURRENT_CACHED_VALUES(2);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _GUARD_BINARY_OP_EXTEND_LHS_r33: {
CHECK_CURRENT_CACHED_VALUES(3);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef left;
_PyStackRef _stack_item_0 = _tos_cache0;
_PyStackRef _stack_item_1 = _tos_cache1;
_PyStackRef _stack_item_2 = _tos_cache2;
left = _stack_item_1;
PyObject *descr = (PyObject *)CURRENT_OPERAND0_64();
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr;
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard == NULL && d->lhs_type != NULL);
if (Py_TYPE(left_o) != d->lhs_type) {
UOP_STAT_INC(uopcode, miss);
_tos_cache2 = _stack_item_2;
_tos_cache1 = left;
_tos_cache0 = _stack_item_0;
SET_CURRENT_CACHED_VALUES(3);
JUMP_TO_JUMP_TARGET();
}
_tos_cache2 = _stack_item_2;
_tos_cache1 = left;
_tos_cache0 = _stack_item_0;
SET_CURRENT_CACHED_VALUES(3);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _GUARD_BINARY_OP_EXTEND_RHS_r02: {
CHECK_CURRENT_CACHED_VALUES(0);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef right;
right = stack_pointer[-1];
PyObject *descr = (PyObject *)CURRENT_OPERAND0_64();
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr;
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard == NULL && d->rhs_type != NULL);
if (Py_TYPE(right_o) != d->rhs_type) {
UOP_STAT_INC(uopcode, miss);
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_JUMP_TARGET();
}
_tos_cache1 = right;
_tos_cache0 = stack_pointer[-2];
SET_CURRENT_CACHED_VALUES(2);
stack_pointer += -2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _GUARD_BINARY_OP_EXTEND_RHS_r12: {
CHECK_CURRENT_CACHED_VALUES(1);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef right;
_PyStackRef _stack_item_0 = _tos_cache0;
right = _stack_item_0;
PyObject *descr = (PyObject *)CURRENT_OPERAND0_64();
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr;
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard == NULL && d->rhs_type != NULL);
if (Py_TYPE(right_o) != d->rhs_type) {
UOP_STAT_INC(uopcode, miss);
_tos_cache0 = right;
SET_CURRENT_CACHED_VALUES(1);
JUMP_TO_JUMP_TARGET();
}
_tos_cache1 = right;
_tos_cache0 = stack_pointer[-1];
SET_CURRENT_CACHED_VALUES(2);
stack_pointer += -1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _GUARD_BINARY_OP_EXTEND_RHS_r22: {
CHECK_CURRENT_CACHED_VALUES(2);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef right;
_PyStackRef _stack_item_0 = _tos_cache0;
_PyStackRef _stack_item_1 = _tos_cache1;
right = _stack_item_1;
PyObject *descr = (PyObject *)CURRENT_OPERAND0_64();
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr;
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard == NULL && d->rhs_type != NULL);
if (Py_TYPE(right_o) != d->rhs_type) {
UOP_STAT_INC(uopcode, miss);
_tos_cache1 = right;
_tos_cache0 = _stack_item_0;
SET_CURRENT_CACHED_VALUES(2);
JUMP_TO_JUMP_TARGET();
}
_tos_cache1 = right;
_tos_cache0 = _stack_item_0;
SET_CURRENT_CACHED_VALUES(2);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _GUARD_BINARY_OP_EXTEND_RHS_r33: {
CHECK_CURRENT_CACHED_VALUES(3);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
_PyStackRef right;
_PyStackRef _stack_item_0 = _tos_cache0;
_PyStackRef _stack_item_1 = _tos_cache1;
_PyStackRef _stack_item_2 = _tos_cache2;
right = _stack_item_2;
PyObject *descr = (PyObject *)CURRENT_OPERAND0_64();
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr;
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard == NULL && d->rhs_type != NULL);
if (Py_TYPE(right_o) != d->rhs_type) {
UOP_STAT_INC(uopcode, miss);
_tos_cache2 = right;
_tos_cache1 = _stack_item_1;
_tos_cache0 = _stack_item_0;
SET_CURRENT_CACHED_VALUES(3);
JUMP_TO_JUMP_TARGET();
}
_tos_cache2 = right;
_tos_cache1 = _stack_item_1;
_tos_cache0 = _stack_item_0;
SET_CURRENT_CACHED_VALUES(3);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
break;
}
case _GUARD_BINARY_OP_EXTEND_r22: {
CHECK_CURRENT_CACHED_VALUES(2);
assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
@ -6409,15 +6619,17 @@
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr;
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard);
assert(d != NULL);
stack_pointer[0] = left;
stack_pointer[1] = right;
stack_pointer += 2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
int res = d->guard(left_o, right_o);
int match = (d->guard != NULL)
? d->guard(left_o, right_o)
: (Py_TYPE(left_o) == d->lhs_type && Py_TYPE(right_o) == d->rhs_type);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (!res) {
if (!match) {
UOP_STAT_INC(uopcode, miss);
_tos_cache1 = right;
_tos_cache0 = left;
@ -6465,6 +6677,8 @@
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
assert(d->result_type == NULL || Py_TYPE(res_o) == d->result_type);
assert(!d->result_unique || Py_REFCNT(res_o) == 1 || _Py_IsImmortal(res_o));
assert(!PyFloat_CheckExact(res_o) || Py_REFCNT(res_o) == 1);
res = PyStackRef_FromPyObjectSteal(res_o);
l = left;

View file

@ -342,11 +342,13 @@
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr;
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard);
assert(d != NULL);
_PyFrame_SetStackPointer(frame, stack_pointer);
int res = d->guard(left_o, right_o);
int match = (d->guard != NULL)
? d->guard(left_o, right_o)
: (Py_TYPE(left_o) == d->lhs_type && Py_TYPE(right_o) == d->rhs_type);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (!res) {
if (!match) {
UPDATE_MISS_STATS(BINARY_OP);
assert(_PyOpcode_Deopt[opcode] == (BINARY_OP));
JUMP_TO_PREDICTED(BINARY_OP);
@ -367,6 +369,8 @@
if (res_o == NULL) {
JUMP_TO_LABEL(error);
}
assert(d->result_type == NULL || Py_TYPE(res_o) == d->result_type);
assert(!d->result_unique || Py_REFCNT(res_o) == 1 || _Py_IsImmortal(res_o));
assert(!PyFloat_CheckExact(res_o) || Py_REFCNT(res_o) == 1);
res = PyStackRef_FromPyObjectSteal(res_o);
l = left;

View file

@ -485,6 +485,46 @@ dummy_func(void) {
r = right;
}
op(_GUARD_BINARY_OP_EXTEND_LHS, (descr/4, left, right -- left, right)) {
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr;
assert(d != NULL && d->guard == NULL && d->lhs_type != NULL);
if (sym_matches_type(left, d->lhs_type)) {
ADD_OP(_NOP, 0, 0);
}
sym_set_type(left, d->lhs_type);
}
op(_GUARD_BINARY_OP_EXTEND_RHS, (descr/4, left, right -- left, right)) {
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr;
assert(d != NULL && d->guard == NULL && d->rhs_type != NULL);
if (sym_matches_type(right, d->rhs_type)) {
ADD_OP(_NOP, 0, 0);
}
sym_set_type(right, d->rhs_type);
}
op(_GUARD_BINARY_OP_EXTEND, (descr/4, left, right -- left, right)) {
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr;
if (d != NULL && d->guard == NULL) {
/* guard == NULL means the check is purely a type test against
lhs_type/rhs_type, so eliminate it when types are already known. */
assert(d->lhs_type != NULL && d->rhs_type != NULL);
bool lhs_known = sym_matches_type(left, d->lhs_type);
bool rhs_known = sym_matches_type(right, d->rhs_type);
if (lhs_known && rhs_known) {
ADD_OP(_NOP, 0, 0);
}
else if (lhs_known) {
ADD_OP(_GUARD_BINARY_OP_EXTEND_RHS, 0, (uintptr_t)d);
sym_set_type(right, d->rhs_type);
}
else if (rhs_known) {
ADD_OP(_GUARD_BINARY_OP_EXTEND_LHS, 0, (uintptr_t)d);
sym_set_type(left, d->lhs_type);
}
}
}
op(_BINARY_OP_EXTEND, (descr/4, left, right -- res, l, r)) {
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr;
if (d != NULL && d->result_type != NULL) {

View file

@ -1210,7 +1210,55 @@
break;
}
case _GUARD_BINARY_OP_EXTEND_LHS: {
JitOptRef left;
left = stack_pointer[-2];
PyObject *descr = (PyObject *)this_instr->operand0;
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr;
assert(d != NULL && d->guard == NULL && d->lhs_type != NULL);
if (sym_matches_type(left, d->lhs_type)) {
ADD_OP(_NOP, 0, 0);
}
sym_set_type(left, d->lhs_type);
break;
}
case _GUARD_BINARY_OP_EXTEND_RHS: {
JitOptRef right;
right = stack_pointer[-1];
PyObject *descr = (PyObject *)this_instr->operand0;
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr;
assert(d != NULL && d->guard == NULL && d->rhs_type != NULL);
if (sym_matches_type(right, d->rhs_type)) {
ADD_OP(_NOP, 0, 0);
}
sym_set_type(right, d->rhs_type);
break;
}
case _GUARD_BINARY_OP_EXTEND: {
JitOptRef right;
JitOptRef left;
right = stack_pointer[-1];
left = stack_pointer[-2];
PyObject *descr = (PyObject *)this_instr->operand0;
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr;
if (d != NULL && d->guard == NULL) {
assert(d->lhs_type != NULL && d->rhs_type != NULL);
bool lhs_known = sym_matches_type(left, d->lhs_type);
bool rhs_known = sym_matches_type(right, d->rhs_type);
if (lhs_known && rhs_known) {
ADD_OP(_NOP, 0, 0);
}
else if (lhs_known) {
ADD_OP(_GUARD_BINARY_OP_EXTEND_RHS, 0, (uintptr_t)d);
sym_set_type(right, d->rhs_type);
}
else if (rhs_known) {
ADD_OP(_GUARD_BINARY_OP_EXTEND_LHS, 0, (uintptr_t)d);
sym_set_type(left, d->lhs_type);
}
}
break;
}

View file

@ -2,6 +2,7 @@
#include "opcode.h"
#include "pycore_bytesobject.h" // _PyBytes_Concat
#include "pycore_code.h"
#include "pycore_critical_section.h"
#include "pycore_descrobject.h" // _PyMethodWrapper_Type
@ -9,7 +10,8 @@
#include "pycore_function.h" // _PyFunction_GetVersionForCurrentState()
#include "pycore_interpframe.h" // FRAME_SPECIALS_SIZE
#include "pycore_lazyimportobject.h" // PyLazyImport_CheckExact
#include "pycore_list.h" // _PyListIterObject
#include "pycore_list.h" // _PyListIterObject, _PyList_Concat
#include "pycore_tuple.h" // _PyTuple_Concat
#include "pycore_long.h" // _PyLong_IsNonNegativeCompact()
#include "pycore_moduleobject.h"
#include "pycore_object.h"
@ -2120,6 +2122,56 @@ is_compactlong(PyObject *v)
_PyLong_IsCompact((PyLongObject *)v);
}
/* sequence * int helpers: bypass PyNumber_Multiply dispatch overhead
by calling sq_repeat directly with PyLong_AsSsize_t. */
static inline PyObject *
seq_int_multiply(PyObject *seq, PyObject *n,
ssizeargfunc repeat)
{
Py_ssize_t count = PyLong_AsSsize_t(n);
if (count == -1 && PyErr_Occurred()) {
return NULL;
}
return repeat(seq, count);
}
static PyObject *
str_int_multiply(PyObject *lhs, PyObject *rhs)
{
return seq_int_multiply(lhs, rhs, PyUnicode_Type.tp_as_sequence->sq_repeat);
}
static PyObject *
int_str_multiply(PyObject *lhs, PyObject *rhs)
{
return seq_int_multiply(rhs, lhs, PyUnicode_Type.tp_as_sequence->sq_repeat);
}
static PyObject *
bytes_int_multiply(PyObject *lhs, PyObject *rhs)
{
return seq_int_multiply(lhs, rhs, PyBytes_Type.tp_as_sequence->sq_repeat);
}
static PyObject *
int_bytes_multiply(PyObject *lhs, PyObject *rhs)
{
return seq_int_multiply(rhs, lhs, PyBytes_Type.tp_as_sequence->sq_repeat);
}
static PyObject *
tuple_int_multiply(PyObject *lhs, PyObject *rhs)
{
return seq_int_multiply(lhs, rhs, PyTuple_Type.tp_as_sequence->sq_repeat);
}
static PyObject *
int_tuple_multiply(PyObject *lhs, PyObject *rhs)
{
return seq_int_multiply(rhs, lhs, PyTuple_Type.tp_as_sequence->sq_repeat);
}
static int
compactlongs_guard(PyObject *lhs, PyObject *rhs)
{
@ -2210,25 +2262,63 @@ LONG_FLOAT_ACTION(compactlong_float_true_div, /)
#undef LONG_FLOAT_ACTION
static _PyBinaryOpSpecializationDescr binaryop_extend_descrs[] = {
/* long-long arithmetic */
{NB_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1},
{NB_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1},
{NB_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1},
{NB_INPLACE_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1},
{NB_INPLACE_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1},
{NB_INPLACE_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1},
/* long-long arithmetic: guards also check _PyLong_IsCompact, so
type alone is not sufficient to eliminate the guard. */
{NB_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1, NULL, NULL},
{NB_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1, NULL, NULL},
{NB_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1, NULL, NULL},
{NB_INPLACE_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1, NULL, NULL},
{NB_INPLACE_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1, NULL, NULL},
{NB_INPLACE_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1, NULL, NULL},
/* float-long arithemetic */
{NB_ADD, float_compactlong_guard, float_compactlong_add, &PyFloat_Type, 1},
{NB_SUBTRACT, float_compactlong_guard, float_compactlong_subtract, &PyFloat_Type, 1},
{NB_TRUE_DIVIDE, nonzero_float_compactlong_guard, float_compactlong_true_div, &PyFloat_Type, 1},
{NB_MULTIPLY, float_compactlong_guard, float_compactlong_multiply, &PyFloat_Type, 1},
/* float-long arithmetic: guards also check NaN and compactness. */
{NB_ADD, float_compactlong_guard, float_compactlong_add, &PyFloat_Type, 1, NULL, NULL},
{NB_SUBTRACT, float_compactlong_guard, float_compactlong_subtract, &PyFloat_Type, 1, NULL, NULL},
{NB_TRUE_DIVIDE, nonzero_float_compactlong_guard, float_compactlong_true_div, &PyFloat_Type, 1, NULL, NULL},
{NB_MULTIPLY, float_compactlong_guard, float_compactlong_multiply, &PyFloat_Type, 1, NULL, NULL},
/* float-float arithmetic */
{NB_ADD, compactlong_float_guard, compactlong_float_add, &PyFloat_Type, 1},
{NB_SUBTRACT, compactlong_float_guard, compactlong_float_subtract, &PyFloat_Type, 1},
{NB_TRUE_DIVIDE, nonzero_compactlong_float_guard, compactlong_float_true_div, &PyFloat_Type, 1},
{NB_MULTIPLY, compactlong_float_guard, compactlong_float_multiply, &PyFloat_Type, 1},
/* long-float arithmetic: guards also check NaN and compactness. */
{NB_ADD, compactlong_float_guard, compactlong_float_add, &PyFloat_Type, 1, NULL, NULL},
{NB_SUBTRACT, compactlong_float_guard, compactlong_float_subtract, &PyFloat_Type, 1, NULL, NULL},
{NB_TRUE_DIVIDE, nonzero_compactlong_float_guard, compactlong_float_true_div, &PyFloat_Type, 1, NULL, NULL},
{NB_MULTIPLY, compactlong_float_guard, compactlong_float_multiply, &PyFloat_Type, 1, NULL, NULL},
/* list-list concatenation: _PyList_Concat always allocates a new list */
{NB_ADD, NULL, _PyList_Concat, &PyList_Type, 1, &PyList_Type, &PyList_Type},
/* tuple-tuple concatenation: _PyTuple_Concat has a zero-length shortcut
that can return one of the operands, so the result is not guaranteed
to be a freshly allocated object. */
{NB_ADD, NULL, _PyTuple_Concat, &PyTuple_Type, 0, &PyTuple_Type, &PyTuple_Type},
/* str * int / int * str: call unicode_repeat directly.
unicode_repeat returns the original when n == 1. */
{NB_MULTIPLY, NULL, str_int_multiply, &PyUnicode_Type, 0, &PyUnicode_Type, &PyLong_Type},
{NB_MULTIPLY, NULL, int_str_multiply, &PyUnicode_Type, 0, &PyLong_Type, &PyUnicode_Type},
{NB_INPLACE_MULTIPLY, NULL, str_int_multiply, &PyUnicode_Type, 0, &PyUnicode_Type, &PyLong_Type},
{NB_INPLACE_MULTIPLY, NULL, int_str_multiply, &PyUnicode_Type, 0, &PyLong_Type, &PyUnicode_Type},
/* bytes + bytes: bytes_concat may return an operand when one side
is empty, so result is not always unique. */
{NB_ADD, NULL, _PyBytes_Concat, &PyBytes_Type, 0, &PyBytes_Type, &PyBytes_Type},
{NB_INPLACE_ADD, NULL, _PyBytes_Concat, &PyBytes_Type, 0, &PyBytes_Type, &PyBytes_Type},
/* bytes * int / int * bytes: call bytes_repeat directly.
bytes_repeat returns the original when n == 1. */
{NB_MULTIPLY, NULL, bytes_int_multiply, &PyBytes_Type, 0, &PyBytes_Type, &PyLong_Type},
{NB_MULTIPLY, NULL, int_bytes_multiply, &PyBytes_Type, 0, &PyLong_Type, &PyBytes_Type},
{NB_INPLACE_MULTIPLY, NULL, bytes_int_multiply, &PyBytes_Type, 0, &PyBytes_Type, &PyLong_Type},
{NB_INPLACE_MULTIPLY, NULL, int_bytes_multiply, &PyBytes_Type, 0, &PyLong_Type, &PyBytes_Type},
/* tuple * int / int * tuple: call tuple_repeat directly.
tuple_repeat returns the original when n == 1. */
{NB_MULTIPLY, NULL, tuple_int_multiply, &PyTuple_Type, 0, &PyTuple_Type, &PyLong_Type},
{NB_MULTIPLY, NULL, int_tuple_multiply, &PyTuple_Type, 0, &PyLong_Type, &PyTuple_Type},
{NB_INPLACE_MULTIPLY, NULL, tuple_int_multiply, &PyTuple_Type, 0, &PyTuple_Type, &PyLong_Type},
{NB_INPLACE_MULTIPLY, NULL, int_tuple_multiply, &PyTuple_Type, 0, &PyLong_Type, &PyTuple_Type},
/* dict | dict */
{NB_OR, NULL, _PyDict_Or, &PyDict_Type, 1, &PyDict_Type, &PyDict_Type},
{NB_INPLACE_OR, NULL, _PyDict_IOr, &PyDict_Type, 0, &PyDict_Type, &PyDict_Type},
};
static int
@ -2238,7 +2328,13 @@ binary_op_extended_specialization(PyObject *lhs, PyObject *rhs, int oparg,
size_t n = sizeof(binaryop_extend_descrs)/sizeof(_PyBinaryOpSpecializationDescr);
for (size_t i = 0; i < n; i++) {
_PyBinaryOpSpecializationDescr *d = &binaryop_extend_descrs[i];
if (d->oparg == oparg && d->guard(lhs, rhs)) {
if (d->oparg != oparg) {
continue;
}
int match = (d->guard != NULL)
? d->guard(lhs, rhs)
: (Py_TYPE(lhs) == d->lhs_type && Py_TYPE(rhs) == d->rhs_type);
if (match) {
*descr = d;
return 1;
}