Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ typedef struct {
* backwards-compatibility */
typedef Py_ssize_t printfunc;

/* Specialize a binary by setting the descriptor pointer */
struct _PyBinopSpecializationDescr;
typedef int(*binop_specialize_func)(PyObject *v, PyObject *w, int oparg,
struct _PyBinopSpecializationDescr **descr);

// If this structure is modified, Doc/includes/typestruct.h should be updated
// as well.
struct _typeobject {
Expand Down Expand Up @@ -233,6 +238,9 @@ struct _typeobject {
/* bitset of which type-watchers care about this type */
unsigned char tp_watched;

/* callback that may specialize BINARY_OP */
binop_specialize_func tp_binop_specialize;

/* Number of tp_version_tag values used.
* Set to _Py_ATTR_CACHE_UNUSED if the attribute cache is
* disabled for this type (e.g. due to custom MRO entries).
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ adaptive_counter_backoff(_Py_BackoffCounter counter) {
typedef int (*binaryopguardfunc)(PyObject *lhs, PyObject *rhs);
typedef PyObject *(*binaryopactionfunc)(PyObject *lhs, PyObject *rhs);

typedef struct {
typedef struct _PyBinopSpecializationDescr {
int oparg;
binaryopguardfunc guard;
binaryopactionfunc action;
Expand Down
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.

4 changes: 2 additions & 2 deletions Include/internal/pycore_uop_metadata.h

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

2 changes: 2 additions & 0 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,8 @@ typedef int (*objobjproc)(PyObject *, PyObject *);
typedef int (*visitproc)(PyObject *, void *);
typedef int (*traverseproc)(PyObject *, visitproc, void *);

struct _PyBinaryOpSpecializationDescr;
typedef int (*binopspecfunc)(PyObject *lhs, PyObject *rhs, int oparg, struct _PyBinaryOpSpecializationDescr* descr);

typedef void (*freefunc)(void *);
typedef void (*destructor)(PyObject *);
Expand Down
1 change: 1 addition & 0 deletions Include/typeslots.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,5 @@
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030E0000
/* New in 3.14 */
#define Py_tp_token 83
#define Py_tp_binop_specialize 84
#endif
2 changes: 1 addition & 1 deletion Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -1776,7 +1776,7 @@ def delx(self): del self.__x
check((1,2,3), vsize('') + self.P + 3*self.P)
# type
# static type: PyTypeObject
fmt = 'P2nPI13Pl4Pn9Pn12PIPc'
fmt = 'P2nPI13Pl4Pn9Pn12PI3Pc'
s = vsize(fmt)
check(int, s)
typeid = 'n' if support.Py_GIL_DISABLED else ''
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add option for extension modules to specialised ``BINARY_OP`` instructions.
Applied to ``array`` objects.
64 changes: 63 additions & 1 deletion Modules/arraymodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
#include "pycore_moduleobject.h" // _PyModule_GetState()

#include "opcode.h" // binary op opargs (NB_*)

#include <stddef.h> // offsetof()
#include <stdbool.h>

Expand Down Expand Up @@ -848,6 +850,10 @@ array_richcompare(PyObject *v, PyObject *w, int op)
return res;
}

static int
array_binop_specialize(PyObject *v, PyObject *w, int oparg,
_PyBinaryOpSpecializationDescr **descr);

static Py_ssize_t
array_length(PyObject *op)
{
Expand Down Expand Up @@ -2963,6 +2969,8 @@ static PyType_Slot array_slots[] = {
{Py_tp_alloc, PyType_GenericAlloc},
{Py_tp_new, array_new},
{Py_tp_traverse, array_tp_traverse},
{Py_tp_token, Py_TP_USE_SPEC},
{Py_tp_binop_specialize, array_binop_specialize},

/* as sequence */
{Py_sq_length, array_length},
Expand Down Expand Up @@ -2995,6 +3003,61 @@ static PyType_Spec array_spec = {
.slots = array_slots,
};

static inline int
array_subscr_guard(PyObject *lhs, PyObject *rhs)
{
PyObject *exc = PyErr_GetRaisedException();
int ret = PyType_GetBaseByToken(Py_TYPE(lhs), &array_spec, NULL);
if (ret < 0) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Clear();
ret = 0;
}
}
_PyErr_ChainExceptions1(exc);
return ret;
}

static PyObject *
array_subscr_action(PyObject *lhs, PyObject *rhs)
{
return array_subscr(lhs, rhs);
}

static int
array_binop_specialize(PyObject *v, PyObject *w, int oparg,
_PyBinaryOpSpecializationDescr **descr)
{
array_state *state = find_array_state_by_type(Py_TYPE(v));

if (!array_Check(v, state)) {
return 0;
}

*descr = NULL;
switch(oparg) {
case NB_SUBSCR:
if (array_subscr_guard(v, w)) {
*descr = (_PyBinaryOpSpecializationDescr*)PyMem_Malloc(
sizeof(_PyBinaryOpSpecializationDescr));
if (*descr == NULL) {
PyErr_NoMemory();
return -1;
}
**descr = (_PyBinaryOpSpecializationDescr) {
.oparg = oparg,
.guard = array_subscr_guard,
.action = array_subscr_action,
};
return 1;
}
break;
}

return 0;
}


/*********************** Array Iterator **************************/

/*[clinic input]
Expand Down Expand Up @@ -3252,7 +3315,6 @@ array_modexec(PyObject *m)
if (PyModule_Add(m, "typecodes", typecodes) < 0) {
return -1;
}

return 0;
}

Expand Down
1 change: 1 addition & 0 deletions Objects/typeslots.inc

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

7 changes: 5 additions & 2 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -801,9 +801,11 @@ dummy_func(
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr;
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);
assert(d && d->guard);
assert(d);
assert(d->guard);
int res = d->guard(left_o, right_o);
DEOPT_IF(!res);
ERROR_IF(res < 0);
DEOPT_IF(res == 0);
}

pure op(_BINARY_OP_EXTEND, (descr/4, left, right -- res)) {
Expand All @@ -816,6 +818,7 @@ dummy_func(

PyObject *res_o = d->action(left_o, right_o);
DECREF_INPUTS();
ERROR_IF(res_o == NULL);
res = PyStackRef_FromPyObjectSteal(res_o);
}

Expand Down
11 changes: 9 additions & 2 deletions Python/executor_cases.c.h

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

11 changes: 9 additions & 2 deletions Python/generated_cases.c.h

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

20 changes: 16 additions & 4 deletions Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -2534,7 +2534,7 @@ LONG_FLOAT_ACTION(compactlong_float_multiply, *)
LONG_FLOAT_ACTION(compactlong_float_true_div, /)
#undef LONG_FLOAT_ACTION

static _PyBinaryOpSpecializationDescr binaryop_extend_descrs[] = {
static const _PyBinaryOpSpecializationDescr binaryop_extend_builtins[] = {
/* long-long arithmetic */
{NB_OR, compactlongs_guard, compactlongs_or},
{NB_AND, compactlongs_guard, compactlongs_and},
Expand All @@ -2560,14 +2560,26 @@ static int
binary_op_extended_specialization(PyObject *lhs, PyObject *rhs, int oparg,
_PyBinaryOpSpecializationDescr **descr)
{
size_t n = sizeof(binaryop_extend_descrs)/sizeof(_PyBinaryOpSpecializationDescr);
for (size_t i = 0; i < n; i++) {
_PyBinaryOpSpecializationDescr *d = &binaryop_extend_descrs[i];
typedef _PyBinaryOpSpecializationDescr descr_type;
size_t size = sizeof(binaryop_extend_builtins)/sizeof(descr_type);
for (size_t i = 0; i < size; i++) {
descr_type *d = (descr_type *)&binaryop_extend_builtins[i];
assert(d != NULL);
assert(d->guard != NULL);
if (d->oparg == oparg && d->guard(lhs, rhs)) {
*descr = d;
return 1;
}
}

if (Py_TYPE(lhs)->tp_binop_specialize != NULL) {
int ret = Py_TYPE(lhs)->tp_binop_specialize(lhs, rhs, oparg, descr);
if (ret < 0) {
return -1;
}

return ret;
}
return 0;
}

Expand Down
Loading