Skip to content
This repository was archived by the owner on Jan 25, 2023. It is now read-only.

Commit 2dabcd0

Browse files
Initial support for Numpy subclasses
Co-authored-by: Todd A. Anderson <drtodd13@comcast.net>
1 parent 27db7a1 commit 2dabcd0

File tree

12 files changed

+203
-37
lines changed

12 files changed

+203
-37
lines changed

numba/_typeof.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,7 @@ int
768768
typeof_typecode(PyObject *dispatcher, PyObject *val)
769769
{
770770
PyTypeObject *tyobj = Py_TYPE(val);
771+
int no_subtype_attr;
771772
/* This needs to be kept in sync with Dispatcher.typeof_pyval(),
772773
* otherwise funny things may happen.
773774
*/
@@ -794,9 +795,19 @@ typeof_typecode(PyObject *dispatcher, PyObject *val)
794795
return typecode_arrayscalar(dispatcher, val);
795796
}
796797
/* Array handling */
797-
else if (PyType_IsSubtype(tyobj, &PyArray_Type)) {
798+
else if (tyobj == &PyArray_Type) {
798799
return typecode_ndarray(dispatcher, (PyArrayObject*)val);
799800
}
801+
/* Subtypes of Array handling */
802+
else if (PyType_IsSubtype(tyobj, &PyArray_Type)) {
803+
/* If the class has an attribute named __numba_no_subtype_ndarray then
804+
don't treat it as a normal variant of a Numpy ndarray but as it's own
805+
separate type. */
806+
no_subtype_attr = PyObject_HasAttrString(val, "__numba_no_subtype_ndarray__");
807+
if (!no_subtype_attr) {
808+
return typecode_ndarray(dispatcher, (PyArrayObject*)val);
809+
}
810+
}
800811

801812
return typecode_using_fingerprint(dispatcher, val);
802813
}

numba/core/extending.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
lower_setattr, lower_setattr_generic, lower_cast) # noqa: F401
1515
from numba.core.datamodel import models # noqa: F401
1616
from numba.core.datamodel import register_default as register_model # noqa: F401, E501
17-
from numba.core.pythonapi import box, unbox, reflect, NativeValue # noqa: F401
17+
from numba.core.pythonapi import box, unbox, reflect, NativeValue, allocator # noqa: F401
1818
from numba._helperlib import _import_cython_function # noqa: F401
1919
from numba.core.serialize import ReduceMixin
2020

numba/core/ir_utils.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ def mk_alloc(typemap, calltypes, lhs, size_var, dtype, scope, loc):
6464
out = []
6565
ndims = 1
6666
size_typ = types.intp
67+
# Get the type of the array being allocated.
68+
arr_typ = typemap[lhs.name]
6769
if isinstance(size_var, tuple):
6870
if len(size_var) == 1:
6971
size_var = size_var[0]
@@ -108,11 +110,13 @@ def mk_alloc(typemap, calltypes, lhs, size_var, dtype, scope, loc):
108110
typ_var_assign = ir.Assign(np_typ_getattr, typ_var, loc)
109111
alloc_call = ir.Expr.call(attr_var, [size_var, typ_var], (), loc)
110112
if calltypes:
111-
calltypes[alloc_call] = typemap[attr_var.name].get_call_type(
113+
cac = typemap[attr_var.name].get_call_type(
112114
typing.Context(), [size_typ, types.functions.NumberClass(dtype)], {})
113-
# signature(
114-
# types.npytypes.Array(dtype, ndims, 'C'), size_typ,
115-
# types.functions.NumberClass(dtype))
115+
# By default, all calls to "empty" are typed as returning a standard
116+
# Numpy ndarray. If we are allocating a ndarray subclass here then
117+
# just change the return type to be that of the subclass.
118+
cac._return_type = arr_typ
119+
calltypes[alloc_call] = cac
116120
alloc_assign = ir.Assign(alloc_call, lhs, loc)
117121

118122
out.extend([g_np_assign, attr_assign, typ_var_assign, alloc_assign])

numba/core/pythonapi.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,13 @@ def lookup(self, typeclass, default=None):
4545
_boxers = _Registry()
4646
_unboxers = _Registry()
4747
_reflectors = _Registry()
48+
# Registry of special allocators for types.
49+
_allocators = _Registry()
4850

4951
box = _boxers.register
5052
unbox = _unboxers.register
5153
reflect = _reflectors.register
54+
allocator = _allocators.register
5255

5356
class _BoxContext(namedtuple("_BoxContext",
5457
("context", "builder", "pyapi", "env_manager"))):
@@ -1186,8 +1189,11 @@ def nrt_adapt_ndarray_to_python(self, aryty, ary, dtypeptr):
11861189
assert self.context.enable_nrt, "NRT required"
11871190

11881191
intty = ir.IntType(32)
1192+
# Embed the Python type of the array (maybe subclass) in the LLVM.
1193+
serial_aryty_pytype = self.unserialize(self.serialize_object(aryty.py_type))
1194+
11891195
fnty = Type.function(self.pyobj,
1190-
[self.voidptr, intty, intty, self.pyobj])
1196+
[self.voidptr, self.pyobj, intty, intty, self.pyobj])
11911197
fn = self._get_function(fnty, name="NRT_adapt_ndarray_to_python")
11921198
fn.args[0].add_attribute(lc.ATTR_NO_CAPTURE)
11931199

@@ -1197,6 +1203,7 @@ def nrt_adapt_ndarray_to_python(self, aryty, ary, dtypeptr):
11971203
aryptr = cgutils.alloca_once_value(self.builder, ary)
11981204
return self.builder.call(fn, [self.builder.bitcast(aryptr,
11991205
self.voidptr),
1206+
serial_aryty_pytype,
12001207
ndim, writable, dtypeptr])
12011208

12021209
def nrt_meminfo_new_from_pyobject(self, data, pyobj):

numba/core/runtime/_nrt_python.c

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ int MemInfo_init(MemInfoObject *self, PyObject *args, PyObject *kwds) {
5555
return -1;
5656
}
5757
raw_ptr = PyLong_AsVoidPtr(raw_ptr_obj);
58+
NRT_Debug(nrt_debug_print("MemInfo_init self=%p raw_ptr=%p\n", self, raw_ptr));
59+
5860
if(PyErr_Occurred()) return -1;
5961
self->meminfo = (NRT_MemInfo *)raw_ptr;
6062
assert (NRT_MemInfo_refcount(self->meminfo) > 0 && "0 refcount");
@@ -109,6 +111,27 @@ MemInfo_get_refcount(MemInfoObject *self, void *closure) {
109111
return PyLong_FromSize_t(refct);
110112
}
111113

114+
static
115+
PyObject*
116+
MemInfo_get_external_allocator(MemInfoObject *self, void *closure) {
117+
void *p = NRT_MemInfo_external_allocator(self->meminfo);
118+
printf("MemInfo_get_external_allocator %p\n", p);
119+
return PyLong_FromVoidPtr(p);
120+
}
121+
122+
static
123+
PyObject*
124+
MemInfo_get_parent(MemInfoObject *self, void *closure) {
125+
void *p = NRT_MemInfo_parent(self->meminfo);
126+
if (p) {
127+
Py_INCREF(p);
128+
return (PyObject*)p;
129+
} else {
130+
Py_INCREF(Py_None);
131+
return Py_None;
132+
}
133+
}
134+
112135
static void
113136
MemInfo_dealloc(MemInfoObject *self)
114137
{
@@ -136,6 +159,13 @@ static PyGetSetDef MemInfo_getsets[] = {
136159
(getter)MemInfo_get_refcount, NULL,
137160
"Get the refcount",
138161
NULL},
162+
{"external_allocator",
163+
(getter)MemInfo_get_external_allocator, NULL,
164+
"Get the external allocator",
165+
NULL},
166+
{"parent",
167+
(getter)MemInfo_get_parent, NULL,
168+
NULL},
139169
{NULL} /* Sentinel */
140170
};
141171

@@ -286,7 +316,7 @@ PyObject* try_to_return_parent(arystruct_t *arystruct, int ndim,
286316
}
287317

288318
NUMBA_EXPORT_FUNC(PyObject *)
289-
NRT_adapt_ndarray_to_python(arystruct_t* arystruct, int ndim,
319+
NRT_adapt_ndarray_to_python(arystruct_t* arystruct, PyTypeObject *retty, int ndim,
290320
int writeable, PyArray_Descr *descr)
291321
{
292322
PyArrayObject *array;
@@ -324,10 +354,13 @@ NRT_adapt_ndarray_to_python(arystruct_t* arystruct, int ndim,
324354
args = PyTuple_New(1);
325355
/* SETITEM steals reference */
326356
PyTuple_SET_ITEM(args, 0, PyLong_FromVoidPtr(arystruct->meminfo));
357+
NRT_Debug(nrt_debug_print("NRT_adapt_ndarray_to_python arystruct->meminfo=%p\n", arystruct->meminfo));
327358
/* Note: MemInfo_init() does not incref. This function steals the
328359
* NRT reference.
329360
*/
361+
NRT_Debug(nrt_debug_print("NRT_adapt_ndarray_to_python created MemInfo=%p\n", miobj));
330362
if (MemInfo_init(miobj, args, NULL)) {
363+
NRT_Debug(nrt_debug_print("MemInfo_init returned 0.\n"));
331364
return NULL;
332365
}
333366
Py_DECREF(args);
@@ -336,7 +369,7 @@ NRT_adapt_ndarray_to_python(arystruct_t* arystruct, int ndim,
336369
shape = arystruct->shape_and_strides;
337370
strides = shape + ndim;
338371
Py_INCREF((PyObject *) descr);
339-
array = (PyArrayObject *) PyArray_NewFromDescr(&PyArray_Type, descr, ndim,
372+
array = (PyArrayObject *) PyArray_NewFromDescr(retty, descr, ndim,
340373
shape, strides, arystruct->data,
341374
flags, (PyObject *) miobj);
342375

numba/core/runtime/_nrt_pythonmod.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ declmethod(MemInfo_alloc);
163163
declmethod(MemInfo_alloc_safe);
164164
declmethod(MemInfo_alloc_aligned);
165165
declmethod(MemInfo_alloc_safe_aligned);
166+
declmethod(MemInfo_alloc_safe_aligned_external);
166167
declmethod(MemInfo_alloc_dtor_safe);
167168
declmethod(MemInfo_call_dtor);
168169
declmethod(MemInfo_new_varsize);

numba/core/runtime/nrt.c

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct MemInfo {
1919
void *dtor_info;
2020
void *data;
2121
size_t size; /* only used for NRT allocated memory */
22+
NRT_ExternalAllocator *external_allocator;
2223
};
2324

2425

@@ -170,13 +171,16 @@ void NRT_MemSys_set_atomic_cas_stub(void) {
170171
*/
171172

172173
void NRT_MemInfo_init(NRT_MemInfo *mi,void *data, size_t size,
173-
NRT_dtor_function dtor, void *dtor_info)
174+
NRT_dtor_function dtor, void *dtor_info,
175+
NRT_ExternalAllocator *external_allocator)
174176
{
175177
mi->refct = 1; /* starts with 1 refct */
176178
mi->dtor = dtor;
177179
mi->dtor_info = dtor_info;
178180
mi->data = data;
179181
mi->size = size;
182+
mi->external_allocator = external_allocator;
183+
NRT_Debug(nrt_debug_print("NRT_MemInfo_init mi=%p external_allocator=%p\n", mi, external_allocator));
180184
/* Update stats */
181185
TheMSys.atomic_inc(&TheMSys.stats_mi_alloc);
182186
}
@@ -185,7 +189,8 @@ NRT_MemInfo *NRT_MemInfo_new(void *data, size_t size,
185189
NRT_dtor_function dtor, void *dtor_info)
186190
{
187191
NRT_MemInfo *mi = NRT_Allocate(sizeof(NRT_MemInfo));
188-
NRT_MemInfo_init(mi, data, size, dtor, dtor_info);
192+
NRT_Debug(nrt_debug_print("NRT_MemInfo_new mi=%p\n", mi));
193+
NRT_MemInfo_init(mi, data, size, dtor, dtor_info, NULL);
189194
return mi;
190195
}
191196

@@ -206,9 +211,10 @@ void nrt_internal_dtor_safe(void *ptr, size_t size, void *info) {
206211
}
207212

208213
static
209-
void *nrt_allocate_meminfo_and_data(size_t size, NRT_MemInfo **mi_out) {
214+
void *nrt_allocate_meminfo_and_data(size_t size, NRT_MemInfo **mi_out, NRT_ExternalAllocator *allocator) {
210215
NRT_MemInfo *mi;
211-
char *base = NRT_Allocate(sizeof(NRT_MemInfo) + size);
216+
NRT_Debug(nrt_debug_print("nrt_allocate_meminfo_and_data %p\n", allocator));
217+
char *base = NRT_Allocate_External(sizeof(NRT_MemInfo) + size, allocator);
212218
mi = (NRT_MemInfo *) base;
213219
*mi_out = mi;
214220
return base + sizeof(NRT_MemInfo);
@@ -230,9 +236,17 @@ void nrt_internal_custom_dtor_safe(void *ptr, size_t size, void *info) {
230236

231237
NRT_MemInfo *NRT_MemInfo_alloc(size_t size) {
232238
NRT_MemInfo *mi;
233-
void *data = nrt_allocate_meminfo_and_data(size, &mi);
239+
void *data = nrt_allocate_meminfo_and_data(size, &mi, NULL);
234240
NRT_Debug(nrt_debug_print("NRT_MemInfo_alloc %p\n", data));
235-
NRT_MemInfo_init(mi, data, size, NULL, NULL);
241+
NRT_MemInfo_init(mi, data, size, NULL, NULL, NULL);
242+
return mi;
243+
}
244+
245+
NRT_MemInfo *NRT_MemInfo_alloc_external(size_t size, NRT_ExternalAllocator *allocator) {
246+
NRT_MemInfo *mi;
247+
void *data = nrt_allocate_meminfo_and_data(size, &mi, allocator);
248+
NRT_Debug(nrt_debug_print("NRT_MemInfo_alloc %p\n", data));
249+
NRT_MemInfo_init(mi, data, size, NULL, NULL, allocator);
236250
return mi;
237251
}
238252

@@ -242,22 +256,23 @@ NRT_MemInfo *NRT_MemInfo_alloc_safe(size_t size) {
242256

243257
NRT_MemInfo* NRT_MemInfo_alloc_dtor_safe(size_t size, NRT_dtor_function dtor) {
244258
NRT_MemInfo *mi;
245-
void *data = nrt_allocate_meminfo_and_data(size, &mi);
259+
void *data = nrt_allocate_meminfo_and_data(size, &mi, NULL);
246260
/* Only fill up a couple cachelines with debug markers, to minimize
247261
overhead. */
248262
memset(data, 0xCB, MIN(size, 256));
249263
NRT_Debug(nrt_debug_print("NRT_MemInfo_alloc_dtor_safe %p %zu\n", data, size));
250-
NRT_MemInfo_init(mi, data, size, nrt_internal_custom_dtor_safe, dtor);
264+
NRT_MemInfo_init(mi, data, size, nrt_internal_custom_dtor_safe, dtor, NULL);
251265
return mi;
252266
}
253267

254268

255269
static
256270
void *nrt_allocate_meminfo_and_data_align(size_t size, unsigned align,
257-
NRT_MemInfo **mi)
271+
NRT_MemInfo **mi, NRT_ExternalAllocator *allocator)
258272
{
259273
size_t offset, intptr, remainder;
260-
char *base = nrt_allocate_meminfo_and_data(size + 2 * align, mi);
274+
NRT_Debug(nrt_debug_print("nrt_allocate_meminfo_and_data_align %p\n", allocator));
275+
char *base = nrt_allocate_meminfo_and_data(size + 2 * align, mi, allocator);
261276
intptr = (size_t) base;
262277
/* See if we are aligned */
263278
remainder = intptr % align;
@@ -271,26 +286,48 @@ void *nrt_allocate_meminfo_and_data_align(size_t size, unsigned align,
271286

272287
NRT_MemInfo *NRT_MemInfo_alloc_aligned(size_t size, unsigned align) {
273288
NRT_MemInfo *mi;
274-
void *data = nrt_allocate_meminfo_and_data_align(size, align, &mi);
289+
void *data = nrt_allocate_meminfo_and_data_align(size, align, &mi, NULL);
275290
NRT_Debug(nrt_debug_print("NRT_MemInfo_alloc_aligned %p\n", data));
276-
NRT_MemInfo_init(mi, data, size, NULL, NULL);
291+
NRT_MemInfo_init(mi, data, size, NULL, NULL, NULL);
277292
return mi;
278293
}
279294

280295
NRT_MemInfo *NRT_MemInfo_alloc_safe_aligned(size_t size, unsigned align) {
281296
NRT_MemInfo *mi;
282-
void *data = nrt_allocate_meminfo_and_data_align(size, align, &mi);
297+
void *data = nrt_allocate_meminfo_and_data_align(size, align, &mi, NULL);
283298
/* Only fill up a couple cachelines with debug markers, to minimize
284299
overhead. */
285300
memset(data, 0xCB, MIN(size, 256));
286301
NRT_Debug(nrt_debug_print("NRT_MemInfo_alloc_safe_aligned %p %zu\n",
287302
data, size));
288-
NRT_MemInfo_init(mi, data, size, nrt_internal_dtor_safe, (void*)size);
303+
NRT_MemInfo_init(mi, data, size, nrt_internal_dtor_safe, (void*)size, NULL);
289304
return mi;
290305
}
291306

307+
NRT_MemInfo *NRT_MemInfo_alloc_safe_aligned_external(size_t size, unsigned align, NRT_ExternalAllocator *allocator) {
308+
NRT_MemInfo *mi;
309+
NRT_Debug(nrt_debug_print("NRT_MemInfo_alloc_safe_aligned_external %p\n", allocator));
310+
void *data = nrt_allocate_meminfo_and_data_align(size, align, &mi, allocator);
311+
/* Only fill up a couple cachelines with debug markers, to minimize
312+
overhead. */
313+
memset(data, 0xCB, MIN(size, 256));
314+
NRT_Debug(nrt_debug_print("NRT_MemInfo_alloc_safe_aligned %p %zu\n",
315+
data, size));
316+
NRT_MemInfo_init(mi, data, size, nrt_internal_dtor_safe, (void*)size, allocator);
317+
return mi;
318+
}
319+
320+
void NRT_dealloc(NRT_MemInfo *mi) {
321+
NRT_Debug(nrt_debug_print("NRT_dealloc meminfo: %p external_allocator: %p\n", mi, mi->external_allocator));
322+
if (mi->external_allocator) {
323+
mi->external_allocator->free(mi, mi->external_allocator->opaque_data);
324+
} else {
325+
NRT_Free(mi);
326+
}
327+
}
328+
292329
void NRT_MemInfo_destroy(NRT_MemInfo *mi) {
293-
NRT_Free(mi);
330+
NRT_dealloc(mi);
294331
TheMSys.atomic_inc(&TheMSys.stats_mi_free);
295332
}
296333

@@ -328,6 +365,14 @@ size_t NRT_MemInfo_size(NRT_MemInfo* mi) {
328365
return mi->size;
329366
}
330367

368+
void * NRT_MemInfo_external_allocator(NRT_MemInfo *mi) {
369+
NRT_Debug(nrt_debug_print("NRT_MemInfo_external_allocator meminfo: %p external_allocator: %p\n", mi, mi->external_allocator));
370+
return mi->external_allocator;
371+
}
372+
373+
void *NRT_MemInfo_parent(NRT_MemInfo *mi) {
374+
return mi->dtor_info;
375+
}
331376

332377
void NRT_MemInfo_dump(NRT_MemInfo *mi, FILE *out) {
333378
fprintf(out, "MemInfo %p refcount %zu\n", mi, mi->refct);
@@ -414,8 +459,18 @@ void NRT_MemInfo_varsize_free(NRT_MemInfo *mi, void *ptr)
414459
*/
415460

416461
void* NRT_Allocate(size_t size) {
417-
void *ptr = TheMSys.allocator.malloc(size);
418-
NRT_Debug(nrt_debug_print("NRT_Allocate bytes=%zu ptr=%p\n", size, ptr));
462+
return NRT_Allocate_External(size, NULL);
463+
}
464+
465+
void* NRT_Allocate_External(size_t size, NRT_ExternalAllocator *allocator) {
466+
void *ptr;
467+
if (allocator) {
468+
ptr = allocator->malloc(size, allocator->opaque_data);
469+
NRT_Debug(nrt_debug_print("NRT_Allocate custom bytes=%zu ptr=%p\n", size, ptr));
470+
} else {
471+
ptr = TheMSys.allocator.malloc(size);
472+
NRT_Debug(nrt_debug_print("NRT_Allocate bytes=%zu ptr=%p\n", size, ptr));
473+
}
419474
TheMSys.atomic_inc(&TheMSys.stats_alloc);
420475
return ptr;
421476
}
@@ -460,6 +515,7 @@ NRT_MemInfo* nrt_manage_memory(void *data, NRT_managed_dtor dtor) {
460515
static const
461516
NRT_api_functions nrt_functions_table = {
462517
NRT_MemInfo_alloc,
518+
NRT_MemInfo_alloc_external,
463519
nrt_manage_memory,
464520
NRT_MemInfo_acquire,
465521
NRT_MemInfo_release,

0 commit comments

Comments
 (0)