Skip to content

Commit 620a54e

Browse files
committed
Pull request #14: More work on np.array
Merge in ~STEPAN.SINDELAR_ORACLE.COM/numpy-hpy from ss/array_array2 to labs-hpy-port * commit 'bebf094010c275b747e1115881d67fa85a9f6751': Remove dtypemeta_dealloc, visit the new HPyFields in DTypeMeta_traverse_impl Fix more C API usages in HPyArray_AssignArray Last C API usage removed from HPyArray_CheckCastSafety NPY_DType_Slots.within_dtype_castingimpl is HPyField NPY_DType_Slots.castingimpls is HPyField HPyArray_CheckCastSafety (partial port)
2 parents 9642f47 + bebf094 commit 620a54e

11 files changed

+172
-64
lines changed

numpy/core/src/common/array_assign.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ broadcast_strides(int ndim, npy_intp const *shape,
6464
return 0;
6565

6666
broadcast_error: {
67+
CAPI_WARN("broadcast_error");
6768
PyObject *shape1 = convert_shape_to_string(strides_ndim,
6869
strides_shape, "");
6970
if (shape1 == NULL) {
@@ -178,3 +179,18 @@ arrays_overlap(PyArrayObject *arr1, PyArrayObject *arr2)
178179
return 1;
179180
}
180181
}
182+
183+
/* Returns 1 if the arrays have overlapping data, 0 otherwise */
184+
NPY_NO_EXPORT int
185+
hpy_arrays_overlap(HPyContext *ctx, HPy arr1, HPy arr2)
186+
{
187+
mem_overlap_t result;
188+
189+
result = hpy_solve_may_share_memory(ctx, arr1, arr2, NPY_MAY_SHARE_BOUNDS);
190+
if (result == MEM_OVERLAP_NO) {
191+
return 0;
192+
}
193+
else {
194+
return 1;
195+
}
196+
}

numpy/core/src/common/array_assign.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,5 +123,8 @@ HIsUintAligned(HPyContext *ctx, HPy arr, PyArrayObject *arr_data);
123123
NPY_NO_EXPORT int
124124
arrays_overlap(PyArrayObject *arr1, PyArrayObject *arr2);
125125

126+
NPY_NO_EXPORT int
127+
hpy_arrays_overlap(HPyContext *ctx, HPy arr1, HPy arr2);
128+
126129

127130
#endif /* NUMPY_CORE_SRC_COMMON_ARRAY_ASSIGN_H_ */

numpy/core/src/multiarray/_multiarray_tests.c.src

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1057,7 +1057,7 @@ get_all_cast_information(PyObject *NPY_UNUSED(mod), PyObject *NPY_UNUSED(args))
10571057
PyObject *to_dtype, *cast_obj;
10581058
Py_ssize_t pos = 0;
10591059

1060-
while (PyDict_Next(NPY_DT_SLOTS(from_dtype)->castingimpls,
1060+
while (PyDict_Next(DTYPE_SLOTS_CASTINGIMPL(from_dtype),
10611061
&pos, &to_dtype, &cast_obj)) {
10621062
if (cast_obj == Py_None) {
10631063
continue;

numpy/core/src/multiarray/array_assign_array.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -511,12 +511,12 @@ HPyArray_AssignArray(HPyContext *ctx, HPy h_dst, HPy h_src,
511511
* of overlapping data. For bigger ndim and opposite-strided 1D
512512
* data, we make a temporary copy of 'src' if 'src' and 'dst' overlap.'
513513
*/
514-
capi_warn("HPyArray_AssignArray: arrays_overlap and reminder of this function...");
515514
if (((PyArray_NDIM(dst) == 1 && PyArray_NDIM(src) >= 1 &&
516515
PyArray_STRIDES(dst)[0] *
517516
PyArray_STRIDES(src)[PyArray_NDIM(src) - 1] < 0) ||
518517
PyArray_NDIM(dst) > 1 || PyArray_HASFIELDS(dst)) &&
519-
arrays_overlap(src, dst)) {
518+
hpy_arrays_overlap(ctx, h_src, h_dst)) {
519+
CAPI_WARN("HPyArray_AssignArray: overlapping arrays");
520520
PyArrayObject *tmp;
521521

522522
/*
@@ -570,21 +570,21 @@ HPyArray_AssignArray(HPyContext *ctx, HPy h_dst, HPy h_src,
570570

571571
PyArrayObject *wheremask = PyArrayObject_AsStruct(ctx, h_wheremask);
572572
/* optimization: scalar boolean mask */
573-
if (wheremask != NULL &&
573+
if (!HPy_IsNull(h_wheremask) &&
574574
PyArray_NDIM(wheremask) == 0 &&
575-
PyArray_DESCR(wheremask)->type_num == NPY_BOOL) {
575+
PyArray_Descr_AsStruct(ctx, HPyArray_DESCR(ctx, h_wheremask, wheremask))->type_num == NPY_BOOL) {
576576
npy_bool value = *(npy_bool *)PyArray_DATA(wheremask);
577577
if (value) {
578578
/* where=True is the same as no where at all */
579-
wheremask = NULL;
579+
h_wheremask = HPy_NULL;
580580
}
581581
else {
582582
/* where=False copies nothing */
583583
return 0;
584584
}
585585
}
586586

587-
if (wheremask == NULL) {
587+
if (HPy_IsNull(h_wheremask)) {
588588
/* A straightforward value assignment */
589589
/* Do the assignment with raw array iteration */
590590
if (raw_array_assign_array(PyArray_NDIM(dst), PyArray_DIMS(dst),
@@ -606,6 +606,7 @@ HPyArray_AssignArray(HPyContext *ctx, HPy h_dst, HPy h_src,
606606

607607
/* A straightforward where-masked assignment */
608608
/* Do the masked assignment with raw array iteration */
609+
CAPI_WARN("non-straightforward value assignment that needs raw_array_wheremasked_assign_array");
609610
if (raw_array_wheremasked_assign_array(
610611
PyArray_NDIM(dst), PyArray_DIMS(dst),
611612
PyArray_DESCR(dst), PyArray_DATA(dst), PyArray_STRIDES(dst),
@@ -617,12 +618,16 @@ HPyArray_AssignArray(HPyContext *ctx, HPy h_dst, HPy h_src,
617618
}
618619

619620
if (copied_src) {
621+
CAPI_WARN("HPyArray_AssignArray: overlapping arrays");
622+
// See the branch for overlapping arrays above that sets copied_src = true
620623
Py_DECREF(src);
621624
}
622625
return 0;
623626

624627
fail:
625628
if (copied_src) {
629+
CAPI_WARN("HPyArray_AssignArray: overlapping arrays");
630+
// See the branch for overlapping arrays above that sets copied_src = true
626631
Py_DECREF(src);
627632
}
628633
return -1;

numpy/core/src/multiarray/convert_datatype.c

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ PyArray_GetCastingImpl(PyArray_DTypeMeta *from, PyArray_DTypeMeta *to)
7171
{
7272
PyObject *res;
7373
if (from == to) {
74-
res = NPY_DT_SLOTS(from)->within_dtype_castingimpl;
74+
res = DTYPE_SLOTS_WITHIN_DTYPE_CASTINGIMPL(from);
7575
}
7676
else {
77-
res = PyDict_GetItemWithError(NPY_DT_SLOTS(from)->castingimpls, (PyObject *)to);
77+
res = PyDict_GetItemWithError(DTYPE_SLOTS_CASTINGIMPL(from), (PyObject *)to);
7878
}
7979
if (res != NULL || PyErr_Occurred()) {
8080
Py_XINCREF(res);
@@ -120,7 +120,7 @@ PyArray_GetCastingImpl(PyArray_DTypeMeta *from, PyArray_DTypeMeta *to)
120120
if (castfunc == NULL) {
121121
PyErr_Clear();
122122
/* Remember that this cast is not possible */
123-
if (PyDict_SetItem(NPY_DT_SLOTS(from)->castingimpls,
123+
if (PyDict_SetItem(DTYPE_SLOTS_CASTINGIMPL(from),
124124
(PyObject *) to, Py_None) < 0) {
125125
return NULL;
126126
}
@@ -149,7 +149,7 @@ PyArray_GetCastingImpl(PyArray_DTypeMeta *from, PyArray_DTypeMeta *to)
149149
Py_DECREF(res);
150150
return NULL;
151151
}
152-
if (PyDict_SetItem(NPY_DT_SLOTS(from)->castingimpls,
152+
if (PyDict_SetItem(DTYPE_SLOTS_CASTINGIMPL(from),
153153
(PyObject *)to, res) < 0) {
154154
Py_DECREF(res);
155155
return NULL;
@@ -161,15 +161,12 @@ NPY_NO_EXPORT HPy
161161
HPyArray_GetCastingImpl(HPyContext *ctx, HPy from, HPy to)
162162
{
163163
PyArray_DTypeMeta *from_data = PyArray_DTypeMeta_AsStruct(ctx, from);
164-
PyArray_DTypeMeta *to_data = PyArray_DTypeMeta_AsStruct(ctx, to);
165164
HPy res;
166165
if (HPy_Is(ctx, from, to)) {
167-
CAPI_WARN("HPyArray_GetCastingImpl: using PyArray_DTypeMeta slots");
168-
res = HPy_FromPyObject(ctx, NPY_DT_SLOTS(from_data)->within_dtype_castingimpl);
166+
res = HPY_DTYPE_SLOTS_WITHIN_DTYPE_CASTINGIMPL(ctx, from, from_data);
169167
}
170168
else {
171-
CAPI_WARN("HPyArray_GetCastingImpl: using PyArray_DTypeMeta slots");
172-
HPy tmp = HPy_FromPyObject(ctx, NPY_DT_SLOTS(from_data)->castingimpls);
169+
HPy tmp = HPY_DTYPE_SLOTS_CASTINGIMPL(ctx, from, from_data);
173170
res = HPy_GetItem(ctx, tmp, to);
174171
HPy_Close(ctx, tmp);
175172
}
@@ -728,6 +725,48 @@ PyArray_CheckCastSafety(NPY_CASTING casting,
728725
}
729726

730727

728+
NPY_NO_EXPORT int
729+
HPyArray_CheckCastSafety(HPyContext *ctx, NPY_CASTING casting,
730+
HPy h_from, HPy h_to, HPy h_to_dtype_in)
731+
{
732+
HPy h_to_dtype;
733+
if (HPy_IsNull(h_to)) {
734+
h_to_dtype = HPy_Type(ctx, h_to);
735+
} else {
736+
h_to_dtype = HPy_Dup(ctx, h_to_dtype_in);
737+
}
738+
HPy h_from_dtype = HPy_Type(ctx, h_from);
739+
HPy meth = HPyArray_GetCastingImpl(ctx, h_from_dtype, h_to_dtype);
740+
HPy_Close(ctx, h_from_dtype);
741+
if (HPy_IsNull(meth)) {
742+
return -1;
743+
}
744+
if (HPy_Is(ctx, meth, ctx->h_None)) {
745+
HPy_Close(ctx, meth);
746+
return -1;
747+
}
748+
749+
PyArrayMethodObject *castingimpl = PyArrayMethodObject_AsStruct(ctx, meth);
750+
if (PyArray_MinCastSafety(castingimpl->casting, casting) == casting) {
751+
/* No need to check using `castingimpl.resolve_descriptors()` */
752+
HPy_Close(ctx, meth);
753+
return 1;
754+
}
755+
756+
hpy_abort_not_implemented("HPyArray_CheckCastSafety: generic code path");
757+
// PyArray_DTypeMeta *dtypes[2] = {PyArray_DTypeMeta_AsStruct(ctx, h_from_dtype), PyArray_DTypeMeta_AsStruct(ctx, h_to_dtype)};
758+
// npy_intp view_offset;
759+
// NPY_CASTING safety = _get_cast_safety_from_castingimpl(castingimpl,
760+
// dtypes, PyArray_Descr_AsStruct(ctx, h_from), PyArray_Descr_AsStruct(ctx, h_to), &view_offset);
761+
// Py_DECREF(meth);
762+
// /* If casting is the smaller (or equal) safety we match */
763+
// if (safety < 0) {
764+
// return -1;
765+
// }
766+
// return PyArray_MinCastSafety(safety, casting) == casting;
767+
}
768+
769+
731770
/*NUMPY_API
732771
*Check the type coercion rules.
733772
*/
@@ -921,12 +960,8 @@ HPyArray_CanCastTypeTo(HPyContext *ctx, HPy h_from, HPy h_to,
921960
to = NULL; /* consider mainly S0 and U0 as S and U */
922961
}
923962

924-
capi_warn("HPyArray_CanCastTypeTo -> PyArray_CheckCastSafety");
925963
HPy to_meta = HPy_Type(ctx, h_to);
926-
int is_valid = PyArray_CheckCastSafety(casting,
927-
PyArray_Descr_AsStruct(ctx, h_from),
928-
PyArray_Descr_AsStruct(ctx, h_to),
929-
PyArray_DTypeMeta_AsStruct(ctx, to_meta));
964+
int is_valid = HPyArray_CheckCastSafety(ctx, casting, h_from, h_to, to_meta);
930965
HPy_Close(ctx, to_meta);
931966
/* Clear any errors and consider this unsafe (should likely be changed) */
932967
if (is_valid < 0) {
@@ -2513,26 +2548,31 @@ PyArray_AddCastingImplementation(PyBoundArrayMethodObject *meth)
25132548
meth->method->name);
25142549
return -1;
25152550
}
2516-
if (NPY_DT_SLOTS(meth->dtypes[0])->within_dtype_castingimpl != NULL) {
2551+
if (DTYPE_SLOTS_WITHIN_DTYPE_CASTINGIMPL(meth->dtypes[0]) != NULL) {
25172552
PyErr_Format(PyExc_RuntimeError,
25182553
"A cast was already added for %S -> %S. (method: %s)",
25192554
meth->dtypes[0], meth->dtypes[1], meth->method->name);
25202555
return -1;
25212556
}
25222557
Py_INCREF(meth->method);
2523-
NPY_DT_SLOTS(meth->dtypes[0])->within_dtype_castingimpl = (
2524-
(PyObject *)meth->method);
2558+
2559+
HPyContext *ctx = npy_get_context();
2560+
HPy owner = HPy_FromPyObject(ctx, meth->dtypes[0]);
2561+
HPy value = HPy_FromPyObject(ctx, meth->method);
2562+
HPyField_Store(ctx, owner, &NPY_DT_SLOTS(meth->dtypes[0])->within_dtype_castingimpl, value);
2563+
HPy_Close(ctx, owner);
2564+
HPy_Close(ctx, value);
25252565

25262566
return 0;
25272567
}
2528-
if (PyDict_Contains(NPY_DT_SLOTS(meth->dtypes[0])->castingimpls,
2568+
if (PyDict_Contains(DTYPE_SLOTS_CASTINGIMPL(meth->dtypes[0]),
25292569
(PyObject *)meth->dtypes[1])) {
25302570
PyErr_Format(PyExc_RuntimeError,
25312571
"A cast was already added for %S -> %S. (method: %s)",
25322572
meth->dtypes[0], meth->dtypes[1], meth->method->name);
25332573
return -1;
25342574
}
2535-
if (PyDict_SetItem(NPY_DT_SLOTS(meth->dtypes[0])->castingimpls,
2575+
if (PyDict_SetItem(DTYPE_SLOTS_CASTINGIMPL(meth->dtypes[0]),
25362576
(PyObject *)meth->dtypes[1], (PyObject *)meth->method) < 0) {
25372577
return -1;
25382578
}

numpy/core/src/multiarray/dtypemeta.c

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,6 @@
2020

2121
#include <assert.h>
2222

23-
static void
24-
dtypemeta_dealloc(PyArray_DTypeMeta *self) {
25-
/* Do not accidentally delete a statically defined DType: */
26-
assert(((PyTypeObject *)self)->tp_flags & Py_TPFLAGS_HEAPTYPE);
27-
28-
Py_XDECREF(self->scalar_type);
29-
Py_XDECREF(self->singleton);
30-
Py_XDECREF(NPY_DT_SLOTS(self)->castingimpls);
31-
NPY_DT_SLOTS(self)->castingimpls = NULL;
32-
PyMem_Free(self->dt_slots);
33-
PyType_Type.tp_dealloc((PyObject *) self);
34-
}
35-
3623
static PyObject *
3724
dtypemeta_new(PyTypeObject *NPY_UNUSED(type),
3825
PyObject *NPY_UNUSED(args), PyObject *NPY_UNUSED(kwds))
@@ -72,7 +59,7 @@ dtypemeta_is_gc(PyObject *dtype_class)
7259
}
7360

7461
HPyDef_SLOT(DTypeMeta_traverse, DTypeMeta_traverse_impl, HPy_tp_traverse)
75-
static int DTypeMeta_traverse_impl(void *self, HPyFunc_visitproc visit, void *arg) {
62+
static int DTypeMeta_traverse_impl(void *self_p, HPyFunc_visitproc visit, void *arg) {
7663
// HPY TODO: implement
7764
/*
7865
* We have to traverse the base class (if it is a HeapType).
@@ -87,6 +74,11 @@ static int DTypeMeta_traverse_impl(void *self, HPyFunc_visitproc visit, void *ar
8774
// Py_VISIT(type->singleton);
8875
// Py_VISIT(type->scalar_type);
8976
// return PyType_Type.tp_traverse((PyObject *)type, visit, arg);
77+
PyArray_DTypeMeta *self = (PyArray_DTypeMeta*) self_p;
78+
if (NPY_DT_SLOTS(self)) {
79+
HPy_VISIT(&NPY_DT_SLOTS(self)->castingimpls);
80+
HPy_VISIT(&NPY_DT_SLOTS(self)->within_dtype_castingimpl);
81+
}
9082
return 0;
9183
}
9284

@@ -589,11 +581,14 @@ dtypemeta_wrap_legacy_descriptor(HPyContext *ctx, PyArray_Descr *descr)
589581
}
590582

591583
PyArray_DTypeMeta *new_dtype_data = PyArray_DTypeMeta_AsStruct(ctx, h_new_dtype_type);
584+
new_dtype_data->dt_slots = dt_slots;
592585

593-
dt_slots->castingimpls = PyDict_New();
594-
if (dt_slots->castingimpls == NULL) {
586+
HPy h_castingimpls = HPyDict_New(ctx);
587+
if (HPy_IsNull(h_castingimpls)) {
595588
goto cleanup;
596589
}
590+
HPyField_Store(ctx, h_new_dtype_type, &dt_slots->castingimpls, h_castingimpls);
591+
HPy_Close(ctx, h_castingimpls);
597592

598593
/*
599594
* Fill DTypeMeta information that varies between DTypes, any variable
@@ -603,7 +598,6 @@ dtypemeta_wrap_legacy_descriptor(HPyContext *ctx, PyArray_Descr *descr)
603598
Py_INCREF(descr->typeobj);
604599
new_dtype_data->scalar_type = descr->typeobj;
605600
new_dtype_data->type_num = descr->type_num;
606-
new_dtype_data->dt_slots = dt_slots;
607601
new_dtype_data->flags = NPY_DT_LEGACY;
608602
dt_slots->f = *(descr->f);
609603

numpy/core/src/multiarray/dtypemeta.h

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,13 @@ typedef struct {
5454
* The casting implementation (ArrayMethod) to convert between two
5555
* instances of this DType, stored explicitly for fast access:
5656
*/
57-
PyObject *within_dtype_castingimpl;
57+
HPyField within_dtype_castingimpl;
5858
/*
5959
* Dictionary of ArrayMethods representing most possible casts
6060
* (structured and object are exceptions).
6161
* This should potentially become a weak mapping in the future.
6262
*/
63-
PyObject *castingimpls;
63+
HPyField castingimpls;
6464

6565
/*
6666
* Storage for `descr->f`, since we may need to allow some customizatoin
@@ -70,7 +70,6 @@ typedef struct {
7070
PyArray_ArrFuncs f;
7171
} NPY_DType_Slots;
7272

73-
7473
#define NPY_DTYPE(descr) ((PyArray_DTypeMeta *)Py_TYPE(descr))
7574
#define HNPY_DTYPE(ctx, descr) (HPy_Type(ctx, descr))
7675
#define NPY_DT_SLOTS(dtype) ((NPY_DType_Slots *)(dtype)->dt_slots)
@@ -79,6 +78,48 @@ typedef struct {
7978
#define NPY_DT_is_abstract(dtype) (((dtype)->flags & NPY_DT_ABSTRACT) != 0)
8079
#define NPY_DT_is_parametric(dtype) (((dtype)->flags & NPY_DT_PARAMETRIC) != 0)
8180

81+
static inline PyObject* DTYPE_SLOTS_CASTINGIMPL(PyArray_DTypeMeta *meta) {
82+
if (HPyField_IsNull(NPY_DT_SLOTS(meta)->castingimpls)) {
83+
return NULL;
84+
}
85+
HPyContext *ctx = npy_get_context();
86+
HPy owner = HPy_FromPyObject(ctx, (PyObject*) meta);
87+
HPy res = HPyField_Load(ctx, owner, NPY_DT_SLOTS(meta)->castingimpls);
88+
PyObject *py_res = HPy_AsPyObject(ctx, res);
89+
HPy_Close(ctx, owner);
90+
HPy_Close(ctx, res);
91+
Py_DECREF(py_res); // to simulate the borrowed reference...
92+
return py_res;
93+
}
94+
95+
static inline PyObject* DTYPE_SLOTS_WITHIN_DTYPE_CASTINGIMPL(PyArray_DTypeMeta *meta) {
96+
if (HPyField_IsNull(NPY_DT_SLOTS(meta)->within_dtype_castingimpl)) {
97+
return NULL;
98+
}
99+
HPyContext *ctx = npy_get_context();
100+
HPy owner = HPy_FromPyObject(ctx, (PyObject*) meta);
101+
HPy res = HPyField_Load(ctx, owner, NPY_DT_SLOTS(meta)->within_dtype_castingimpl);
102+
PyObject *py_res = HPy_AsPyObject(ctx, res);
103+
HPy_Close(ctx, owner);
104+
HPy_Close(ctx, res);
105+
Py_DECREF(py_res); // to simulate the borrowed reference...
106+
return py_res;
107+
}
108+
109+
static inline HPy HPY_DTYPE_SLOTS_CASTINGIMPL(HPyContext *ctx, HPy h_meta, PyArray_DTypeMeta *meta) {
110+
if (HPyField_IsNull(NPY_DT_SLOTS(meta)->castingimpls)) {
111+
return HPy_NULL;
112+
}
113+
return HPyField_Load(ctx, h_meta, NPY_DT_SLOTS(meta)->castingimpls);
114+
}
115+
116+
static inline HPy HPY_DTYPE_SLOTS_WITHIN_DTYPE_CASTINGIMPL(HPyContext *ctx, HPy h_meta, PyArray_DTypeMeta *meta) {
117+
if (HPyField_IsNull(NPY_DT_SLOTS(meta)->within_dtype_castingimpl)) {
118+
return HPy_NULL;
119+
}
120+
return HPyField_Load(ctx, h_meta, NPY_DT_SLOTS(meta)->within_dtype_castingimpl);
121+
}
122+
82123
/*
83124
* Macros for convenient classmethod calls, since these require
84125
* the DType both for the slot lookup and as first arguments.

0 commit comments

Comments
 (0)