From 3e44f7f3558d32f124adcbe3ccc5e2adfdca750c Mon Sep 17 00:00:00 2001 From: Graham Markall Date: Mon, 12 Oct 2020 09:19:03 +0100 Subject: [PATCH 01/12] Make _dispatcher.c a C++ source Associated changes to types of functions to avoid casts are required. Some casts of malloc returns also added. --- numba/{_dispatcher.c => _dispatcher.cpp} | 16 ++++++++++---- numba/{_dispatcher.h => _dispatcher.hpp} | 6 ++++-- numba/_dispatcherimpl.cpp | 27 ++++++++++++++++-------- numba/_typeof.h | 6 ++++++ setup.py | 4 ++-- 5 files changed, 42 insertions(+), 17 deletions(-) rename numba/{_dispatcher.c => _dispatcher.cpp} (98%) rename numba/{_dispatcher.h => _dispatcher.hpp} (83%) diff --git a/numba/_dispatcher.c b/numba/_dispatcher.cpp similarity index 98% rename from numba/_dispatcher.c rename to numba/_dispatcher.cpp index b59e9b741c6..5982fd7ab7f 100644 --- a/numba/_dispatcher.c +++ b/numba/_dispatcher.cpp @@ -4,10 +4,14 @@ #include #include -#include "_dispatcher.h" +#include "_dispatcher.hpp" #include "_typeof.h" #include "frameobject.h" +#ifdef __cplusplus + extern "C" { +#endif + /* * The following call_trace and call_trace_protected functions * as well as the C_TRACE macro are taken from ceval.c @@ -194,7 +198,7 @@ Dispatcher_Insert(DispatcherObject *self, PyObject *args) } sigsz = PySequence_Fast_GET_SIZE(sigtup); - sig = malloc(sigsz * sizeof(int)); + sig = (int*)malloc(sigsz * sizeof(int)); for (i = 0; i < sigsz; ++i) { sig[i] = PyLong_AsLong(PySequence_Fast_GET_ITEM(sigtup, i)); @@ -202,7 +206,7 @@ Dispatcher_Insert(DispatcherObject *self, PyObject *args) /* The reference to cfunc is borrowed; this only works because the derived Python class also stores an (owned) reference to cfunc. */ - dispatcher_add_defn(self->dispatcher, sig, (void*) cfunc); + dispatcher_add_defn(self->dispatcher, sig, cfunc); /* Add pure python fallback */ if (!self->fallbackdef && objectmode){ @@ -515,7 +519,7 @@ Dispatcher_call(DispatcherObject *self, PyObject *args, PyObject *kws) if (argct < (Py_ssize_t) (sizeof(prealloc) / sizeof(int))) tys = prealloc; else - tys = malloc(argct * sizeof(int)); + tys = (int*)malloc(argct * sizeof(int)); for (i = 0; i < argct; ++i) { tmptype = PySequence_Fast_GET_ITEM(args, i); @@ -691,3 +695,7 @@ MOD_INIT(_dispatcher) { return MOD_SUCCESS_VAL(m); } + +#ifdef __cplusplus + } +#endif \ No newline at end of file diff --git a/numba/_dispatcher.h b/numba/_dispatcher.hpp similarity index 83% rename from numba/_dispatcher.h rename to numba/_dispatcher.hpp index 3517775e1af..fcf182d9b3b 100644 --- a/numba/_dispatcher.h +++ b/numba/_dispatcher.hpp @@ -1,6 +1,8 @@ #ifndef NUMBA_DISPATCHER_H_ #define NUMBA_DISPATCHER_H_ +#include + #ifdef __cplusplus extern "C" { #endif @@ -17,9 +19,9 @@ void dispatcher_del(dispatcher_t *obj); void -dispatcher_add_defn(dispatcher_t *obj, int tys[], void* callable); +dispatcher_add_defn(dispatcher_t *obj, int tys[], PyObject* callable); -void* +PyObject* dispatcher_resolve(dispatcher_t *obj, int sig[], int *matches, int allow_unsafe, int exact_match_required); diff --git a/numba/_dispatcherimpl.cpp b/numba/_dispatcherimpl.cpp index 67ea1351116..af58eff3fb1 100644 --- a/numba/_dispatcherimpl.cpp +++ b/numba/_dispatcherimpl.cpp @@ -1,9 +1,10 @@ #include "core/typeconv/typeconv.hpp" #include #include +#include typedef std::vector TypeTable; -typedef std::vector Functions; +typedef std::vector Functions; struct _opaque_dispatcher {}; @@ -11,7 +12,7 @@ class Dispatcher: public _opaque_dispatcher { public: Dispatcher(TypeManager *tm, int argct): argct(argct), tm(tm) { } - void addDefinition(Type args[], void *callable) { + void addDefinition(Type args[], PyObject *callable) { overloads.reserve(argct + overloads.size()); for (int i=0; iaddDefinition(args, callable); } -void* +PyObject* dispatcher_resolve(dispatcher_t *obj, int sig[], int *count, int allow_unsafe, int exact_match_required) { Dispatcher *disp = static_cast(obj); Type *args = reinterpret_cast(sig); - void *callable = disp->resolve(args, *count, (bool) allow_unsafe, - (bool) exact_match_required); + PyObject* callable = disp->resolve(args, *count, (bool) allow_unsafe, + (bool) exact_match_required); return callable; } @@ -106,3 +111,7 @@ dispatcher_count(dispatcher_t *obj) { Dispatcher *disp = static_cast(obj); return disp->count(); } + +#ifdef __cplusplus + } +#endif \ No newline at end of file diff --git a/numba/_typeof.h b/numba/_typeof.h index 66e29c53ff5..6e0039b5f38 100644 --- a/numba/_typeof.h +++ b/numba/_typeof.h @@ -1,10 +1,16 @@ #ifndef NUMBA_TYPEOF_H_ #define NUMBA_TYPEOF_H_ +#ifdef __cplusplus + extern "C" { +#endif extern PyObject *typeof_init(PyObject *self, PyObject *args); extern int typeof_typecode(PyObject *dispatcher, PyObject *val); extern PyObject *typeof_compute_fingerprint(PyObject *val); +#ifdef __cplusplus + } +#endif #endif /* NUMBA_TYPEOF_H_ */ diff --git a/setup.py b/setup.py index bebdf0f8d76..5141cb1eea4 100644 --- a/setup.py +++ b/setup.py @@ -128,13 +128,13 @@ def get_ext_modules(): 'numba/_dynfunc.c']) ext_dispatcher = Extension(name="numba._dispatcher", - sources=['numba/_dispatcher.c', + sources=['numba/_dispatcher.cpp', 'numba/_typeof.c', 'numba/_hashtable.c', 'numba/_dispatcherimpl.cpp', 'numba/core/typeconv/typeconv.cpp'], depends=["numba/_pymodule.h", - "numba/_dispatcher.h", + "numba/_dispatcher.hpp", "numba/_typeof.h", "numba/_hashtable.h"], **np_compile_args) From eda4dc7262bba9bd621797b831af06cccc2a0719 Mon Sep 17 00:00:00 2001 From: Graham Markall Date: Mon, 12 Oct 2020 10:40:09 +0100 Subject: [PATCH 02/12] Move _dispatcherimpl into dispatcher --- numba/_dispatcher.cpp | 112 ++++++++++++++++++++++++++++++++++-- numba/_dispatcher.hpp | 35 ------------ numba/_dispatcherimpl.cpp | 117 -------------------------------------- setup.py | 2 - 4 files changed, 108 insertions(+), 158 deletions(-) delete mode 100644 numba/_dispatcher.hpp delete mode 100644 numba/_dispatcherimpl.cpp diff --git a/numba/_dispatcher.cpp b/numba/_dispatcher.cpp index 5982fd7ab7f..1d5afdd6108 100644 --- a/numba/_dispatcher.cpp +++ b/numba/_dispatcher.cpp @@ -1,13 +1,117 @@ #include "_pymodule.h" +#include "core/typeconv/typeconv.hpp" -#include -#include -#include +#include +#include +#include +#include -#include "_dispatcher.hpp" #include "_typeof.h" #include "frameobject.h" +typedef std::vector TypeTable; +typedef std::vector Functions; + +struct _opaque_dispatcher {}; +typedef struct _opaque_dispatcher dispatcher_t; + +class Dispatcher: public _opaque_dispatcher { +public: + Dispatcher(TypeManager *tm, int argct): argct(argct), tm(tm) { } + + void addDefinition(Type args[], PyObject *callable) { + overloads.reserve(argct + overloads.size()); + for (int i=0; iselectOverload(sig, &overloads[0], selected, argct, + ovct, allow_unsafe, + exact_match_required); + } + if (matches == 1) { + return functions[selected]; + } + return NULL; + } + + int count() const { return functions.size(); } + + void clear() { + functions.clear(); + overloads.clear(); + } + +private: + const int argct; + TypeManager *tm; + // An array of overloads + Functions functions; + // A flattened array of argument types to all overloads + // (invariant: sizeof(overloads) == argct * sizeof(functions)) + TypeTable overloads; +}; + +static dispatcher_t * +dispatcher_new(void *tm, int argct){ + return new Dispatcher(static_cast(tm), argct); +} + +static void +dispatcher_clear(dispatcher_t *obj) { + Dispatcher *disp = static_cast(obj); + disp->clear(); +} + +static void +dispatcher_del(dispatcher_t *obj) { + Dispatcher *disp = static_cast(obj); + delete disp; +} + +static void +dispatcher_add_defn(dispatcher_t *obj, int tys[], PyObject* callable) { + assert(sizeof(int) == sizeof(Type) && + "Type should be representable by an int"); + + Dispatcher *disp = static_cast(obj); + Type *args = reinterpret_cast(tys); + disp->addDefinition(args, callable); +} + +static PyObject* +dispatcher_resolve(dispatcher_t *obj, int sig[], int *count, int allow_unsafe, + int exact_match_required) { + Dispatcher *disp = static_cast(obj); + Type *args = reinterpret_cast(sig); + PyObject* callable = disp->resolve(args, *count, (bool) allow_unsafe, + (bool) exact_match_required); + return callable; +} + +static int +dispatcher_count(dispatcher_t *obj) { + Dispatcher *disp = static_cast(obj); + return disp->count(); +} + #ifdef __cplusplus extern "C" { #endif diff --git a/numba/_dispatcher.hpp b/numba/_dispatcher.hpp deleted file mode 100644 index fcf182d9b3b..00000000000 --- a/numba/_dispatcher.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef NUMBA_DISPATCHER_H_ -#define NUMBA_DISPATCHER_H_ - -#include - -#ifdef __cplusplus - extern "C" { -#endif - -typedef struct _opaque_dispatcher dispatcher_t; - -dispatcher_t * -dispatcher_new(void *tm, int argct); - -void -dispatcher_clear(dispatcher_t *obj); - -void -dispatcher_del(dispatcher_t *obj); - -void -dispatcher_add_defn(dispatcher_t *obj, int tys[], PyObject* callable); - -PyObject* -dispatcher_resolve(dispatcher_t *obj, int sig[], int *matches, - int allow_unsafe, int exact_match_required); - -int -dispatcher_count(dispatcher_t *obj); - -#ifdef __cplusplus - } -#endif - -#endif /* NUMBA_DISPATCHER_H_ */ diff --git a/numba/_dispatcherimpl.cpp b/numba/_dispatcherimpl.cpp deleted file mode 100644 index af58eff3fb1..00000000000 --- a/numba/_dispatcherimpl.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "core/typeconv/typeconv.hpp" -#include -#include -#include - -typedef std::vector TypeTable; -typedef std::vector Functions; - -struct _opaque_dispatcher {}; - -class Dispatcher: public _opaque_dispatcher { -public: - Dispatcher(TypeManager *tm, int argct): argct(argct), tm(tm) { } - - void addDefinition(Type args[], PyObject *callable) { - overloads.reserve(argct + overloads.size()); - for (int i=0; iselectOverload(sig, &overloads[0], selected, argct, - ovct, allow_unsafe, - exact_match_required); - } - if (matches == 1) { - return functions[selected]; - } - return NULL; - } - - int count() const { return functions.size(); } - - void clear() { - functions.clear(); - overloads.clear(); - } - -private: - const int argct; - TypeManager *tm; - // An array of overloads - Functions functions; - // A flattened array of argument types to all overloads - // (invariant: sizeof(overloads) == argct * sizeof(functions)) - TypeTable overloads; -}; - - -#include "_dispatcher.hpp" - -#ifdef __cplusplus - extern "C" { -#endif - -dispatcher_t * -dispatcher_new(void *tm, int argct){ - return new Dispatcher(static_cast(tm), argct); -} - -void -dispatcher_clear(dispatcher_t *obj) { - Dispatcher *disp = static_cast(obj); - disp->clear(); -} - -void -dispatcher_del(dispatcher_t *obj) { - Dispatcher *disp = static_cast(obj); - delete disp; -} - -void -dispatcher_add_defn(dispatcher_t *obj, int tys[], PyObject* callable) { - assert(sizeof(int) == sizeof(Type) && - "Type should be representable by an int"); - - Dispatcher *disp = static_cast(obj); - Type *args = reinterpret_cast(tys); - disp->addDefinition(args, callable); -} - -PyObject* -dispatcher_resolve(dispatcher_t *obj, int sig[], int *count, int allow_unsafe, - int exact_match_required) { - Dispatcher *disp = static_cast(obj); - Type *args = reinterpret_cast(sig); - PyObject* callable = disp->resolve(args, *count, (bool) allow_unsafe, - (bool) exact_match_required); - return callable; -} - -int -dispatcher_count(dispatcher_t *obj) { - Dispatcher *disp = static_cast(obj); - return disp->count(); -} - -#ifdef __cplusplus - } -#endif \ No newline at end of file diff --git a/setup.py b/setup.py index 5141cb1eea4..3ccbc1fb138 100644 --- a/setup.py +++ b/setup.py @@ -131,10 +131,8 @@ def get_ext_modules(): sources=['numba/_dispatcher.cpp', 'numba/_typeof.c', 'numba/_hashtable.c', - 'numba/_dispatcherimpl.cpp', 'numba/core/typeconv/typeconv.cpp'], depends=["numba/_pymodule.h", - "numba/_dispatcher.hpp", "numba/_typeof.h", "numba/_hashtable.h"], **np_compile_args) From 2ed6560554e6a28313f9fca181d4508151d7bd71 Mon Sep 17 00:00:00 2001 From: Graham Markall Date: Mon, 12 Oct 2020 11:18:08 +0100 Subject: [PATCH 03/12] Replace proxy methods with direct calls to C++ dispatcher methods --- numba/_dispatcher.cpp | 66 +++++++------------------------------------ 1 file changed, 10 insertions(+), 56 deletions(-) diff --git a/numba/_dispatcher.cpp b/numba/_dispatcher.cpp index 1d5afdd6108..bde233024a7 100644 --- a/numba/_dispatcher.cpp +++ b/numba/_dispatcher.cpp @@ -12,10 +12,7 @@ typedef std::vector TypeTable; typedef std::vector Functions; -struct _opaque_dispatcher {}; -typedef struct _opaque_dispatcher dispatcher_t; - -class Dispatcher: public _opaque_dispatcher { +class Dispatcher { public: Dispatcher(TypeManager *tm, int argct): argct(argct), tm(tm) { } @@ -69,48 +66,6 @@ class Dispatcher: public _opaque_dispatcher { TypeTable overloads; }; -static dispatcher_t * -dispatcher_new(void *tm, int argct){ - return new Dispatcher(static_cast(tm), argct); -} - -static void -dispatcher_clear(dispatcher_t *obj) { - Dispatcher *disp = static_cast(obj); - disp->clear(); -} - -static void -dispatcher_del(dispatcher_t *obj) { - Dispatcher *disp = static_cast(obj); - delete disp; -} - -static void -dispatcher_add_defn(dispatcher_t *obj, int tys[], PyObject* callable) { - assert(sizeof(int) == sizeof(Type) && - "Type should be representable by an int"); - - Dispatcher *disp = static_cast(obj); - Type *args = reinterpret_cast(tys); - disp->addDefinition(args, callable); -} - -static PyObject* -dispatcher_resolve(dispatcher_t *obj, int sig[], int *count, int allow_unsafe, - int exact_match_required) { - Dispatcher *disp = static_cast(obj); - Type *args = reinterpret_cast(sig); - PyObject* callable = disp->resolve(args, *count, (bool) allow_unsafe, - (bool) exact_match_required); - return callable; -} - -static int -dispatcher_count(dispatcher_t *obj) { - Dispatcher *disp = static_cast(obj); - return disp->count(); -} #ifdef __cplusplus extern "C" { @@ -209,7 +164,7 @@ else \ typedef struct DispatcherObject{ PyObject_HEAD /* Holds borrowed references to PyCFunction objects */ - dispatcher_t *dispatcher; + Dispatcher *dispatcher; char can_compile; /* Can auto compile */ char can_fallback; /* Can fallback */ char exact_match_required; @@ -238,7 +193,7 @@ Dispatcher_dealloc(DispatcherObject *self) { Py_XDECREF(self->argnames); Py_XDECREF(self->defargs); - dispatcher_del(self->dispatcher); + delete self->dispatcher; Py_TYPE(self)->tp_free((PyObject*)self); } @@ -266,7 +221,7 @@ Dispatcher_init(DispatcherObject *self, PyObject *args, PyObject *kwds) Py_INCREF(self->argnames); Py_INCREF(self->defargs); tmaddr = PyLong_AsVoidPtr(tmaddrobj); - self->dispatcher = dispatcher_new(tmaddr, argct); + self->dispatcher = new Dispatcher(static_cast(tmaddr), argct); self->can_compile = 1; self->can_fallback = can_fallback; self->fallbackdef = NULL; @@ -278,7 +233,7 @@ Dispatcher_init(DispatcherObject *self, PyObject *args, PyObject *kwds) static PyObject * Dispatcher_clear(DispatcherObject *self, PyObject *args) { - dispatcher_clear(self->dispatcher); + self->dispatcher->clear(); Py_RETURN_NONE; } @@ -310,7 +265,7 @@ Dispatcher_Insert(DispatcherObject *self, PyObject *args) /* The reference to cfunc is borrowed; this only works because the derived Python class also stores an (owned) reference to cfunc. */ - dispatcher_add_defn(self->dispatcher, sig, cfunc); + self->dispatcher->addDefinition(sig, cfunc); /* Add pure python fallback */ if (!self->fallbackdef && objectmode){ @@ -644,8 +599,8 @@ Dispatcher_call(DispatcherObject *self, PyObject *args, PyObject *kws) /* We only allow unsafe conversions if compilation of new specializations has been disabled. */ - cfunc = dispatcher_resolve(self->dispatcher, tys, &matches, - !self->can_compile, self->exact_match_required); + cfunc = self->dispatcher->resolve(tys, matches, !self->can_compile, + self->exact_match_required); if (matches == 0 && !self->can_compile) { /* @@ -660,9 +615,8 @@ Dispatcher_call(DispatcherObject *self, PyObject *args, PyObject *kws) } if (res > 0) { /* Retry with the newly registered conversions */ - cfunc = dispatcher_resolve(self->dispatcher, tys, &matches, - !self->can_compile, - self->exact_match_required); + cfunc = self->dispatcher->resolve(tys, matches, !self->can_compile, + self->exact_match_required); } } From 3b47e111b17ab0f6e89456d4011f838a1baa920a Mon Sep 17 00:00:00 2001 From: Graham Markall Date: Mon, 12 Oct 2020 11:25:48 +0100 Subject: [PATCH 04/12] Remove unnecessary extern C block from _dispatcher --- numba/_dispatcher.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/numba/_dispatcher.cpp b/numba/_dispatcher.cpp index bde233024a7..6bbdb6b5758 100644 --- a/numba/_dispatcher.cpp +++ b/numba/_dispatcher.cpp @@ -66,11 +66,6 @@ class Dispatcher { TypeTable overloads; }; - -#ifdef __cplusplus - extern "C" { -#endif - /* * The following call_trace and call_trace_protected functions * as well as the C_TRACE macro are taken from ceval.c @@ -752,8 +747,4 @@ MOD_INIT(_dispatcher) { PyModule_AddObject(m, "Dispatcher", (PyObject*)(&DispatcherType)); return MOD_SUCCESS_VAL(m); -} - -#ifdef __cplusplus - } -#endif \ No newline at end of file +} \ No newline at end of file From c17f5741f62742814da690c6f54d3c4cfd41ab61 Mon Sep 17 00:00:00 2001 From: Graham Markall Date: Mon, 12 Oct 2020 11:38:18 +0100 Subject: [PATCH 05/12] Replace malloc / free with new / delete --- numba/_dispatcher.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/numba/_dispatcher.cpp b/numba/_dispatcher.cpp index 6bbdb6b5758..18e8fcd5ff1 100644 --- a/numba/_dispatcher.cpp +++ b/numba/_dispatcher.cpp @@ -252,7 +252,7 @@ Dispatcher_Insert(DispatcherObject *self, PyObject *args) } sigsz = PySequence_Fast_GET_SIZE(sigtup); - sig = (int*)malloc(sigsz * sizeof(int)); + sig = new int[sigsz]; for (i = 0; i < sigsz; ++i) { sig[i] = PyLong_AsLong(PySequence_Fast_GET_ITEM(sigtup, i)); @@ -267,7 +267,7 @@ Dispatcher_Insert(DispatcherObject *self, PyObject *args) self->fallbackdef = cfunc; } - free(sig); + delete[] sig; Py_RETURN_NONE; } @@ -573,7 +573,7 @@ Dispatcher_call(DispatcherObject *self, PyObject *args, PyObject *kws) if (argct < (Py_ssize_t) (sizeof(prealloc) / sizeof(int))) tys = prealloc; else - tys = (int*)malloc(argct * sizeof(int)); + tys = new int[argct]; for (i = 0; i < argct; ++i) { tmptype = PySequence_Fast_GET_ITEM(args, i); @@ -641,7 +641,7 @@ Dispatcher_call(DispatcherObject *self, PyObject *args, PyObject *kws) CLEANUP: if (tys != prealloc) - free(tys); + delete[] tys; Py_DECREF(args); return retval; From 9dbc603cade4a7d0e6acd3fa684b0c1a65f6dd92 Mon Sep 17 00:00:00 2001 From: Graham Markall Date: Mon, 12 Oct 2020 11:59:57 +0100 Subject: [PATCH 06/12] Fold Dispatcher into DispatcherObject --- numba/_dispatcher.cpp | 155 +++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 79 deletions(-) diff --git a/numba/_dispatcher.cpp b/numba/_dispatcher.cpp index 18e8fcd5ff1..576ed98b69b 100644 --- a/numba/_dispatcher.cpp +++ b/numba/_dispatcher.cpp @@ -9,62 +9,6 @@ #include "_typeof.h" #include "frameobject.h" -typedef std::vector TypeTable; -typedef std::vector Functions; - -class Dispatcher { -public: - Dispatcher(TypeManager *tm, int argct): argct(argct), tm(tm) { } - - void addDefinition(Type args[], PyObject *callable) { - overloads.reserve(argct + overloads.size()); - for (int i=0; iselectOverload(sig, &overloads[0], selected, argct, - ovct, allow_unsafe, - exact_match_required); - } - if (matches == 1) { - return functions[selected]; - } - return NULL; - } - - int count() const { return functions.size(); } - - void clear() { - functions.clear(); - overloads.clear(); - } - -private: - const int argct; - TypeManager *tm; - // An array of overloads - Functions functions; - // A flattened array of argument types to all overloads - // (invariant: sizeof(overloads) == argct * sizeof(functions)) - TypeTable overloads; -}; /* * The following call_trace and call_trace_protected functions @@ -155,11 +99,12 @@ else \ } \ } +typedef std::vector TypeTable; +typedef std::vector Functions; -typedef struct DispatcherObject{ +class Dispatcher { +public: PyObject_HEAD - /* Holds borrowed references to PyCFunction objects */ - Dispatcher *dispatcher; char can_compile; /* Can auto compile */ char can_fallback; /* Can fallback */ char exact_match_required; @@ -173,28 +118,79 @@ typedef struct DispatcherObject{ PyObject *argnames; /* Tuple of default values */ PyObject *defargs; -} DispatcherObject; + /* Number of arguments to function */ + int argct; + /* Used for selecting overloaded function implementations */ + TypeManager *tm; + + void addDefinition(Type args[], PyObject *callable) { + overloads.reserve(argct + overloads.size()); + for (int i=0; iselectOverload(sig, &overloads[0], selected, argct, + ovct, allow_unsafe, + exact_match_required); + } + if (matches == 1) { + return functions[selected]; + } + return NULL; + } + + int count() const { return functions.size(); } + + void clear() { + functions.clear(); + overloads.clear(); + } + +private: + /* An array of overloads */ + Functions functions; + /* A flattened array of argument types to all overloads + * (invariant: sizeof(overloads) == argct * sizeof(functions)) */ + TypeTable overloads; +}; static int -Dispatcher_traverse(DispatcherObject *self, visitproc visit, void *arg) +Dispatcher_traverse(Dispatcher *self, visitproc visit, void *arg) { Py_VISIT(self->defargs); return 0; } static void -Dispatcher_dealloc(DispatcherObject *self) +Dispatcher_dealloc(Dispatcher *self) { Py_XDECREF(self->argnames); Py_XDECREF(self->defargs); - delete self->dispatcher; + self->clear(); Py_TYPE(self)->tp_free((PyObject*)self); } static int -Dispatcher_init(DispatcherObject *self, PyObject *args, PyObject *kwds) +Dispatcher_init(Dispatcher *self, PyObject *args, PyObject *kwds) { PyObject *tmaddrobj; void *tmaddr; @@ -216,7 +212,8 @@ Dispatcher_init(DispatcherObject *self, PyObject *args, PyObject *kwds) Py_INCREF(self->argnames); Py_INCREF(self->defargs); tmaddr = PyLong_AsVoidPtr(tmaddrobj); - self->dispatcher = new Dispatcher(static_cast(tmaddr), argct); + self->tm = static_cast(tmaddr); + self->argct = argct; self->can_compile = 1; self->can_fallback = can_fallback; self->fallbackdef = NULL; @@ -226,15 +223,15 @@ Dispatcher_init(DispatcherObject *self, PyObject *args, PyObject *kwds) } static PyObject * -Dispatcher_clear(DispatcherObject *self, PyObject *args) +Dispatcher_clear(Dispatcher *self, PyObject *args) { - self->dispatcher->clear(); + self->clear(); Py_RETURN_NONE; } static PyObject* -Dispatcher_Insert(DispatcherObject *self, PyObject *args) +Dispatcher_Insert(Dispatcher *self, PyObject *args) { PyObject *sigtup, *cfunc; int i, sigsz; @@ -260,7 +257,7 @@ Dispatcher_Insert(DispatcherObject *self, PyObject *args) /* The reference to cfunc is borrowed; this only works because the derived Python class also stores an (owned) reference to cfunc. */ - self->dispatcher->addDefinition(sig, cfunc); + self->addDefinition(sig, cfunc); /* Add pure python fallback */ if (!self->fallbackdef && objectmode){ @@ -335,7 +332,7 @@ int search_new_conversions(PyObject *dispatcher, PyObject *args, PyObject *kws) /* A custom, fast, inlinable version of PyCFunction_Call() */ static PyObject * -call_cfunc(DispatcherObject *self, PyObject *cfunc, PyObject *args, PyObject *kws, PyObject *locals) +call_cfunc(Dispatcher *self, PyObject *cfunc, PyObject *args, PyObject *kws, PyObject *locals) { PyCFunctionWithKeywords fn; PyThreadState *tstate; @@ -400,7 +397,7 @@ call_cfunc(DispatcherObject *self, PyObject *cfunc, PyObject *args, PyObject *kw static PyObject* -compile_and_invoke(DispatcherObject *self, PyObject *args, PyObject *kws, PyObject *locals) +compile_and_invoke(Dispatcher *self, PyObject *args, PyObject *kws, PyObject *locals) { /* Compile a new one */ PyObject *cfa, *cfunc, *retval; @@ -429,7 +426,7 @@ compile_and_invoke(DispatcherObject *self, PyObject *args, PyObject *kws, PyObje } static int -find_named_args(DispatcherObject *self, PyObject **pargs, PyObject **pkws) +find_named_args(Dispatcher *self, PyObject **pargs, PyObject **pkws) { PyObject *oldargs = *pargs, *newargs; PyObject *kws = *pkws; @@ -543,7 +540,7 @@ find_named_args(DispatcherObject *self, PyObject **pargs, PyObject **pkws) } static PyObject* -Dispatcher_call(DispatcherObject *self, PyObject *args, PyObject *kws) +Dispatcher_call(Dispatcher *self, PyObject *args, PyObject *kws) { PyObject *tmptype, *retval = NULL; int *tys = NULL; @@ -594,8 +591,8 @@ Dispatcher_call(DispatcherObject *self, PyObject *args, PyObject *kws) /* We only allow unsafe conversions if compilation of new specializations has been disabled. */ - cfunc = self->dispatcher->resolve(tys, matches, !self->can_compile, - self->exact_match_required); + cfunc = self->resolve(tys, matches, !self->can_compile, + self->exact_match_required); if (matches == 0 && !self->can_compile) { /* @@ -610,8 +607,8 @@ Dispatcher_call(DispatcherObject *self, PyObject *args, PyObject *kws) } if (res > 0) { /* Retry with the newly registered conversions */ - cfunc = self->dispatcher->resolve(tys, matches, !self->can_compile, - self->exact_match_required); + cfunc = self->resolve(tys, matches, !self->can_compile, + self->exact_match_required); } } @@ -655,7 +652,7 @@ static PyMethodDef Dispatcher_methods[] = { }; static PyMemberDef Dispatcher_members[] = { - {"_can_compile", T_BOOL, offsetof(DispatcherObject, can_compile), 0, NULL }, + {"_can_compile", T_BOOL, offsetof(Dispatcher, can_compile), 0, NULL }, {NULL} /* Sentinel */ }; @@ -663,7 +660,7 @@ static PyMemberDef Dispatcher_members[] = { static PyTypeObject DispatcherType = { PyVarObject_HEAD_INIT(NULL, 0) "_dispatcher.Dispatcher", /* tp_name */ - sizeof(DispatcherObject), /* tp_basicsize */ + sizeof(Dispatcher), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Dispatcher_dealloc, /* tp_dealloc */ 0, /* tp_print */ From 80968fd509296f167f70d3b55ea87699f846bb26 Mon Sep 17 00:00:00 2001 From: Graham Markall Date: Mon, 12 Oct 2020 12:22:25 +0100 Subject: [PATCH 07/12] Make Dispatcher a POD class again Mixed public / private access controls for members results in a non-standard-layout class. A standard-layout class is required for offsetof, used in the implementation of the `Dispatcher._can_compile` member in Python. --- numba/_dispatcher.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/numba/_dispatcher.cpp b/numba/_dispatcher.cpp index 576ed98b69b..8d6ba619f45 100644 --- a/numba/_dispatcher.cpp +++ b/numba/_dispatcher.cpp @@ -122,6 +122,11 @@ class Dispatcher { int argct; /* Used for selecting overloaded function implementations */ TypeManager *tm; + /* An array of overloads */ + Functions functions; + /* A flattened array of argument types to all overloads + * (invariant: sizeof(overloads) == argct * sizeof(functions)) */ + TypeTable overloads; void addDefinition(Type args[], PyObject *callable) { overloads.reserve(argct + overloads.size()); @@ -163,12 +168,6 @@ class Dispatcher { overloads.clear(); } -private: - /* An array of overloads */ - Functions functions; - /* A flattened array of argument types to all overloads - * (invariant: sizeof(overloads) == argct * sizeof(functions)) */ - TypeTable overloads; }; @@ -744,4 +743,4 @@ MOD_INIT(_dispatcher) { PyModule_AddObject(m, "Dispatcher", (PyObject*)(&DispatcherType)); return MOD_SUCCESS_VAL(m); -} \ No newline at end of file +} From 4c0083d7ad5dece9d751b5304157edab8ab44b78 Mon Sep 17 00:00:00 2001 From: Graham Markall Date: Mon, 12 Oct 2020 12:38:19 +0100 Subject: [PATCH 08/12] Cast string constant to char* This is required in `Dispatcher_members` for the `_can_compile` member. --- numba/_dispatcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/numba/_dispatcher.cpp b/numba/_dispatcher.cpp index 8d6ba619f45..935b584c6f9 100644 --- a/numba/_dispatcher.cpp +++ b/numba/_dispatcher.cpp @@ -651,7 +651,7 @@ static PyMethodDef Dispatcher_methods[] = { }; static PyMemberDef Dispatcher_members[] = { - {"_can_compile", T_BOOL, offsetof(Dispatcher, can_compile), 0, NULL }, + {(char*)"_can_compile", T_BOOL, offsetof(Dispatcher, can_compile), 0, NULL }, {NULL} /* Sentinel */ }; From 2c2d3f4733ebb95f20ef7f0767139b496ae1258e Mon Sep 17 00:00:00 2001 From: Graham Markall Date: Mon, 12 Oct 2020 12:51:49 +0100 Subject: [PATCH 09/12] Update docs for C++-only dispatcher --- docs/source/developer/repomap.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/source/developer/repomap.rst b/docs/source/developer/repomap.rst index 808b9714010..0b09d14a63e 100644 --- a/docs/source/developer/repomap.rst +++ b/docs/source/developer/repomap.rst @@ -101,10 +101,8 @@ Dispatching - :ghfile:`numba/core/dispatcher.py` - Dispatcher objects are compiled functions produced by ``@jit``. A dispatcher has different implementations for different type signatures. -- :ghfile:`numba/_dispatcher.{h,c}` - C interface to C++ dispatcher - implementation -- :ghfile:`numba/_dispatcherimpl.cpp` - C++ dispatcher implementation (for - speed on common data types) +- :ghfile:`numba/_dispatcher.cpp` - C++ dispatcher implementation (for speed on + common data types) Compiler Pipeline From 3d1d91b62bd430927b8e01977fe579f9dc136fee Mon Sep 17 00:00:00 2001 From: Graham Markall Date: Mon, 12 Oct 2020 16:21:42 +0100 Subject: [PATCH 10/12] Some minor tidy-ups --- numba/_dispatcher.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/numba/_dispatcher.cpp b/numba/_dispatcher.cpp index 935b584c6f9..57bab810346 100644 --- a/numba/_dispatcher.cpp +++ b/numba/_dispatcher.cpp @@ -1,5 +1,4 @@ #include "_pymodule.h" -#include "core/typeconv/typeconv.hpp" #include #include @@ -8,7 +7,7 @@ #include "_typeof.h" #include "frameobject.h" - +#include "core/typeconv/typeconv.hpp" /* * The following call_trace and call_trace_protected functions @@ -137,7 +136,7 @@ class Dispatcher { } PyObject* resolve(Type sig[], int &matches, bool allow_unsafe, - bool exact_match_required) { + bool exact_match_required) const { const int ovct = functions.size(); int selected; matches = 0; @@ -161,8 +160,6 @@ class Dispatcher { return NULL; } - int count() const { return functions.size(); } - void clear() { functions.clear(); overloads.clear(); From 9e82c76a4231145375fc5d385e7d1a7c34e413b2 Mon Sep 17 00:00:00 2001 From: Graham Markall Date: Thu, 19 Nov 2020 15:30:57 +0000 Subject: [PATCH 11/12] Add documentation comments to _dispatcher.cpp --- numba/_dispatcher.cpp | 45 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/numba/_dispatcher.cpp b/numba/_dispatcher.cpp index 6ad7f0d01fa..cadd976eef2 100644 --- a/numba/_dispatcher.cpp +++ b/numba/_dispatcher.cpp @@ -101,15 +101,31 @@ else \ typedef std::vector TypeTable; typedef std::vector Functions; +/* The Dispatcher class is the base class of all dispatchers in the CPU and + CUDA targets. Its main responsibilities are: + + - Resolving the best overload to call for a given set of arguments, and + - Calling the resolved overload. + + This logic is implemented within this class for efficiency (lookup of the + appropriate overload needs to be fast) and ease of implementation (calling + directly into a compiled function using a function pointer is easier within + the C++ code where the overload has been resolved). */ class Dispatcher { public: PyObject_HEAD - char can_compile; /* Can auto compile */ - char can_fallback; /* Can fallback */ + /* Whether compilation of new overloads is permitted */ + char can_compile; + /* Whether fallback to object mode is permitted */ + char can_fallback; + /* Whether types must match exactly when resolving overloads. + If not, conversions (e.g. float32 -> float64) are permitted when + searching for a match. */ char exact_match_required; /* Borrowed reference */ PyObject *fallbackdef; - /* Whether to fold named arguments and default values (false for lifted loops)*/ + /* Whether to fold named arguments and default values + (false for lifted loops) */ int fold_args; /* Whether the last positional argument is a stararg */ int has_stararg; @@ -127,6 +143,10 @@ class Dispatcher { * (invariant: sizeof(overloads) == argct * sizeof(functions)) */ TypeTable overloads; + /* Add a new overload. Parameters: + + - args: An array of Type objects, one for each parameter + - callable: The callable implementing this overload. */ void addDefinition(Type args[], PyObject *callable) { overloads.reserve(argct + overloads.size()); for (int i=0; iresolve(tys, matches, !self->can_compile, exact_match_required); From cf96948af5772b0cbf66f8dbfa08df484eb5d587 Mon Sep 17 00:00:00 2001 From: Graham Markall <535640+gmarkall@users.noreply.github.com> Date: Thu, 19 Nov 2020 17:27:43 +0000 Subject: [PATCH 12/12] Update numba/_dispatcher.cpp Co-authored-by: stuartarchibald --- numba/_dispatcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/numba/_dispatcher.cpp b/numba/_dispatcher.cpp index cadd976eef2..dedb0ba1823 100644 --- a/numba/_dispatcher.cpp +++ b/numba/_dispatcher.cpp @@ -165,7 +165,7 @@ class Dispatcher { - allow_unsafe: whether to match overloads that would require an unsafe cast. - exact_match_required: Whether all arguments types must match the - overload's types exactly. When not true, + overload's types exactly. When false, overloads that would require a type conversion can also be matched. */ PyObject* resolve(Type sig[], int &matches, bool allow_unsafe,