Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-100239: specialize bitwise logical binary ops on ints #128927

Merged
merged 13 commits into from
Jan 29, 2025
Merged
2 changes: 1 addition & 1 deletion Include/internal/pycore_opcode_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Include/internal/pycore_uop_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions Lib/test/test_opcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -1410,6 +1410,29 @@ def compactlong_rhs(arg):

binary_op_nan()

def binary_op_bitwise_extend():
for _ in range(100):
a, b = 2, 7
x = a | b
self.assertEqual(x, 7)
y = a & b
self.assertEqual(y, 2)
z = a ^ b
self.assertEqual(z, 5)
a, b = 3, 9
a |= b
self.assertEqual(a, 11)
a, b = 11, 9
a &= b
self.assertEqual(a, 9)
a, b = 3, 9
a ^= b
self.assertEqual(a, 10)

binary_op_bitwise_extend()
self.assert_specialized(binary_op_bitwise_extend, "BINARY_OP_EXTEND")
self.assert_no_opcode(binary_op_bitwise_extend, "BINARY_OP")

@cpython_only
@requires_specialization_ft
def test_load_super_attr(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Specialize ``BINARY_OP`` for bitwise logical operations on compact ints.
2 changes: 1 addition & 1 deletion Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ dummy_func(
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard);
int res = d->guard(left_o, right_o);
EXIT_IF(!res);
DEOPT_IF(!res);
}

pure op(_BINARY_OP_EXTEND, (descr/4, left, right -- res)) {
Expand Down
54 changes: 54 additions & 0 deletions Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,10 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, PyObject *consts,
#define SPEC_FAIL_BINARY_OP_TRUE_DIVIDE_FLOAT 26
#define SPEC_FAIL_BINARY_OP_TRUE_DIVIDE_OTHER 27
#define SPEC_FAIL_BINARY_OP_XOR 28
#define SPEC_FAIL_BINARY_OP_OR_INT 29
#define SPEC_FAIL_BINARY_OP_OR_DIFFERENT_TYPES 30
#define SPEC_FAIL_BINARY_OP_XOR_INT 31
#define SPEC_FAIL_BINARY_OP_XOR_DIFFERENT_TYPES 32

/* Calls */

Expand Down Expand Up @@ -2379,6 +2383,12 @@ binary_op_fail_kind(int oparg, PyObject *lhs, PyObject *rhs)
return SPEC_FAIL_BINARY_OP_MULTIPLY_OTHER;
case NB_OR:
case NB_INPLACE_OR:
if (!Py_IS_TYPE(lhs, Py_TYPE(rhs))) {
return SPEC_FAIL_BINARY_OP_OR_DIFFERENT_TYPES;
}
if (PyLong_CheckExact(lhs)) {
return SPEC_FAIL_BINARY_OP_OR_INT;
}
return SPEC_FAIL_BINARY_OP_OR;
case NB_POWER:
case NB_INPLACE_POWER:
Expand Down Expand Up @@ -2406,6 +2416,12 @@ binary_op_fail_kind(int oparg, PyObject *lhs, PyObject *rhs)
return SPEC_FAIL_BINARY_OP_TRUE_DIVIDE_OTHER;
case NB_XOR:
case NB_INPLACE_XOR:
if (!Py_IS_TYPE(lhs, Py_TYPE(rhs))) {
return SPEC_FAIL_BINARY_OP_XOR_DIFFERENT_TYPES;
}
if (PyLong_CheckExact(lhs)) {
return SPEC_FAIL_BINARY_OP_XOR_INT;
}
return SPEC_FAIL_BINARY_OP_XOR;
}
Py_UNREACHABLE();
Expand All @@ -2414,6 +2430,34 @@ binary_op_fail_kind(int oparg, PyObject *lhs, PyObject *rhs)

/** Binary Op Specialization Extensions */

/* long-long */

static inline int
is_compactlong(PyObject *v)
{
return PyLong_CheckExact(v) &&
_PyLong_IsCompact((PyLongObject *)v);
}

static int
compactlongs_guard(PyObject *lhs, PyObject *rhs)
{
return (is_compactlong(lhs) && is_compactlong(rhs));
}

#define BITWISE_LONGS_ACTION(NAME, OP) \
static PyObject * \
(NAME)(PyObject *lhs, PyObject *rhs) \
{ \
Py_ssize_t rhs_val = _PyLong_CompactValue((PyLongObject *)rhs); \
Py_ssize_t lhs_val = _PyLong_CompactValue((PyLongObject *)lhs); \
return PyLong_FromSsize_t(lhs_val OP rhs_val); \
}
BITWISE_LONGS_ACTION(compactlongs_or, |)
BITWISE_LONGS_ACTION(compactlongs_and, &)
BITWISE_LONGS_ACTION(compactlongs_xor, ^)
#undef BITWISE_LONGS_ACTION

/* float-long */

static inline int
Expand Down Expand Up @@ -2484,6 +2528,15 @@ LONG_FLOAT_ACTION(compactlong_float_multiply, *)
LONG_FLOAT_ACTION(compactlong_float_true_div, /)
#undef LONG_FLOAT_ACTION

static _PyBinaryOpSpecializationDescr compactlongs_specs[NB_OPARG_LAST+1] = {
iritkatriel marked this conversation as resolved.
Show resolved Hide resolved
[NB_OR] = {compactlongs_guard, compactlongs_or},
[NB_AND] = {compactlongs_guard, compactlongs_and},
[NB_XOR] = {compactlongs_guard, compactlongs_xor},
[NB_INPLACE_OR] = {compactlongs_guard, compactlongs_or},
[NB_INPLACE_AND] = {compactlongs_guard, compactlongs_and},
[NB_INPLACE_XOR] = {compactlongs_guard, compactlongs_xor},
};

static _PyBinaryOpSpecializationDescr float_compactlong_specs[NB_OPARG_LAST+1] = {
[NB_ADD] = {float_compactlong_guard, float_compactlong_add},
[NB_SUBTRACT] = {float_compactlong_guard, float_compactlong_subtract},
Expand Down Expand Up @@ -2512,6 +2565,7 @@ binary_op_extended_specialization(PyObject *lhs, PyObject *rhs, int oparg,

LOOKUP_SPEC(compactlong_float_specs, oparg);
LOOKUP_SPEC(float_compactlong_specs, oparg);
LOOKUP_SPEC(compactlongs_specs, oparg);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why three tables, rather than one?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A single table would need to have a list of (guard, action) pairs for each OP. So it's a table of tables. Same thing basically.

#undef LOOKUP_SPEC
return 0;
}
Expand Down
1 change: 1 addition & 0 deletions Tools/c-analyzer/cpython/ignored.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ Python/pylifecycle.c - INTERPRETER_TRAMPOLINE_CODEDEF -
Python/pystate.c - initial -
Python/specialize.c - adaptive_opcodes -
Python/specialize.c - cache_requirements -
Python/specialize.c - compactlongs_specs -
Python/specialize.c - float_compactlong_specs -
Python/specialize.c - compactlong_float_specs -
Python/stdlib_module_names.h - _Py_stdlib_module_names -
Expand Down
Loading