Skip to content

Commit 7c9a4ff

Browse files
author
Diptorup Deb
committed
Reimplementation of the DpctlSyclQueue type in numba-dpex.
- The DpctlSyclQueue is no longer a numba OpaqueType. It is reimplemented as a StructType that stores the native queue_ref pointer and the parent PyObject pointer. - Introduce C runtime helper functions for boxing/unboxing a dpctl.SyclQueue
1 parent de2a895 commit 7c9a4ff

File tree

7 files changed

+271
-44
lines changed

7 files changed

+271
-44
lines changed

numba_dpex/core/datamodel/models.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from numba.core import datamodel, types
66
from numba.core.datamodel.models import ArrayModel as DpnpNdArrayModel
7-
from numba.core.datamodel.models import OpaqueModel, PrimitiveModel, StructModel
7+
from numba.core.datamodel.models import PrimitiveModel, StructModel
88
from numba.core.extending import register_model
99

1010
from numba_dpex.utils import address_space
@@ -54,6 +54,27 @@ def __init__(self, dmm, fe_type):
5454
super(ArrayModel, self).__init__(dmm, fe_type, members)
5555

5656

57+
class SyclQueueModel(StructModel):
58+
"""Represents the native data model for a dpctl.SyclQueue PyObject.
59+
60+
Numba-dpex uses a C struct as defined in
61+
numba_dpex/core/runtime._queuestruct.h to store the required attributes for
62+
a ``dpctl.SyclQueue`` Python object.
63+
64+
- ``queue_ref``: An opaque C pointer to an actual SYCL queue C++ object.
65+
- ``parent``: A PyObject* that stores a reference back to the original
66+
``dpctl.SyclQueue`` PyObject if the native struct is
67+
created by unboxing the PyObject.
68+
"""
69+
70+
def __init__(self, dmm, fe_type):
71+
members = [
72+
("parent", types.CPointer(types.voidptr)),
73+
("queue_ref", types.CPointer(types.voidptr)),
74+
]
75+
super(SyclQueueModel, self).__init__(dmm, fe_type, members)
76+
77+
5778
def _init_data_model_manager():
5879
dmm = datamodel.default_manager.copy()
5980
dmm.register(types.CPointer, GenericPointerModel)
@@ -83,6 +104,6 @@ def _init_data_model_manager():
83104
register_model(DpnpNdArray)(DpnpNdArrayModel)
84105
dpex_data_model_manager.register(DpnpNdArray, DpnpNdArrayModel)
85106

86-
# Register the DpctlSyclQueue type with Numba's OpaqueModel
87-
register_model(DpctlSyclQueue)(OpaqueModel)
88-
dpex_data_model_manager.register(DpctlSyclQueue, OpaqueModel)
107+
# Register the DpctlSyclQueue type
108+
register_model(DpctlSyclQueue)(SyclQueueModel)
109+
dpex_data_model_manager.register(DpctlSyclQueue, SyclQueueModel)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-FileCopyrightText: 2023 Intel Corporation
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
//===----------------------------------------------------------------------===//
6+
///
7+
/// \file
8+
/// A helper macro to print debug prints.
9+
///
10+
//===----------------------------------------------------------------------===//
11+
12+
#pragma once
13+
14+
/* Debugging facilities - enabled at compile-time */
15+
/* #undef NDEBUG */
16+
#if 0
17+
#include <stdio.h>
18+
#define DPEXRT_DEBUG(X) \
19+
{ \
20+
X; \
21+
fflush(stdout); \
22+
}
23+
#else
24+
#define DPEXRT_DEBUG(X) \
25+
if (0) { \
26+
X; \
27+
}
28+
#endif

numba_dpex/core/runtime/_dpexrt_python.c

Lines changed: 116 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,10 @@
1919
#include "_nrt_helper.h"
2020
#include "_nrt_python_helper.h"
2121

22+
#include "_dbg_printer.h"
23+
#include "_queuestruct.h"
2224
#include "numba/_arraystruct.h"
2325

24-
/* Debugging facilities - enabled at compile-time */
25-
/* #undef NDEBUG */
26-
#if 0
27-
#include <stdio.h>
28-
#define DPEXRT_DEBUG(X) \
29-
{ \
30-
X; \
31-
fflush(stdout); \
32-
}
33-
#else
34-
#define DPEXRT_DEBUG(X) \
35-
if (0) { \
36-
X; \
37-
}
38-
#endif
39-
4026
// forward declarations
4127
static struct PyUSMArrayObject *PyUSMNdArray_ARRAYOBJ(PyObject *obj);
4228
static npy_intp product_of_shape(npy_intp *shape, npy_intp ndim);
@@ -66,6 +52,9 @@ DPEXRT_sycl_usm_ndarray_to_python_acqref(arystruct_t *arystruct,
6652
int ndim,
6753
int writeable,
6854
PyArray_Descr *descr);
55+
static int DPEXRT_sycl_queue_from_python(PyObject *obj,
56+
queuestruct_t *queue_struct);
57+
static PyObject *DPEXRT_sycl_queue_to_python(queuestruct_t *queuestruct);
6958

7059
/*
7160
* Debugging printf function used internally
@@ -663,7 +652,9 @@ static npy_intp product_of_shape(npy_intp *shape, npy_intp ndim)
663652
return nelems;
664653
}
665654

655+
/*----------------------------------------------------------------------------*/
666656
/*----- Boxing and Unboxing implementations for a dpnp.ndarray PyObject ------*/
657+
/*----------------------------------------------------------------------------*/
667658

668659
/*!
669660
* @brief Unboxes a PyObject that may represent a dpnp.ndarray into a Numba
@@ -1049,6 +1040,107 @@ DPEXRT_sycl_usm_ndarray_to_python_acqref(arystruct_t *arystruct,
10491040
return (PyObject *)dpnp_ary;
10501041
}
10511042

1043+
/*----------------------------------------------------------------------------*/
1044+
/*--------------------- Box-unbox helpers for dpctl.SyclQueue ----------*/
1045+
/*----------------------------------------------------------------------------*/
1046+
1047+
/*!
1048+
* @brief Helper to unbox a Python dpctl.SyclQueue object to a Numba-native
1049+
* queuestruct_t instance.
1050+
*
1051+
* @param obj A dpctl.SyclQueue Python object
1052+
* @param queue_struct An instance of the struct numba-dpex uses to
1053+
* represent a dpctl.SyclQueue inside Numba.
1054+
* @return {return} Return code indicating success (0) or failure (-1).
1055+
*/
1056+
static int DPEXRT_sycl_queue_from_python(PyObject *obj,
1057+
queuestruct_t *queue_struct)
1058+
{
1059+
1060+
struct PySyclQueueObject *queue_obj = NULL;
1061+
DPCTLSyclQueueRef queue_ref = NULL;
1062+
PyGILState_STATE gstate;
1063+
1064+
// Increment the ref count on obj to prevent CPython from garbage
1065+
// collecting the array.
1066+
Py_IncRef(obj);
1067+
1068+
// We are unconditionally casting obj to a struct PySyclQueueObject*. If
1069+
// the obj is not a struct PySyclQueueObject* then the SyclQueue_GetQueueRef
1070+
// will error out.
1071+
queue_obj = (struct PySyclQueueObject *)obj;
1072+
1073+
DPEXRT_DEBUG(
1074+
nrt_debug_print("DPEXRT-DEBUG: In DPEXRT_sycl_queue_from_python.\n"));
1075+
1076+
if (!(queue_ref = SyclQueue_GetQueueRef(queue_obj))) {
1077+
DPEXRT_DEBUG(nrt_debug_print(
1078+
"DPEXRT-ERROR: SyclQueue_GetQueueRef returned NULL at "
1079+
"%s, line %d.\n",
1080+
__FILE__, __LINE__));
1081+
goto error;
1082+
}
1083+
1084+
queue_struct->parent = obj;
1085+
queue_struct->queue_ref = queue_ref;
1086+
1087+
return 0;
1088+
1089+
error:
1090+
// If the check failed then decrement the refcount and return an error
1091+
// code of -1.
1092+
// Decref the Pyobject of the array
1093+
// ensure the GIL
1094+
DPEXRT_DEBUG(nrt_debug_print(
1095+
"DPEXRT-ERROR: Failed to unbox dpctl SyclQueue into a Numba "
1096+
"queuestruct at %s, line %d\n",
1097+
__FILE__, __LINE__));
1098+
gstate = PyGILState_Ensure();
1099+
// decref the python object
1100+
Py_DECREF(obj);
1101+
// release the GIL
1102+
PyGILState_Release(gstate);
1103+
1104+
return -1;
1105+
}
1106+
1107+
/*!
1108+
* @brief A helper function that boxes a Numba-dpex queuestruct_t object into a
1109+
* dctl.SyclQueue PyObject using the queuestruct_t's parent attribute.
1110+
*
1111+
* If there is no parent pointer stored in the queuestruct, then an error will
1112+
* be raised.
1113+
*
1114+
* @param queuestruct A Numba-dpex queuestruct object.
1115+
* @return {return} A PyObject created from the queuestruct->parent, if
1116+
* the PyObject could not be created return NULL.
1117+
*/
1118+
static PyObject *DPEXRT_sycl_queue_to_python(queuestruct_t *queuestruct)
1119+
{
1120+
PyObject *orig_queue = NULL;
1121+
PyGILState_STATE gstate;
1122+
1123+
orig_queue = queuestruct->parent;
1124+
// FIXME: Better error checking is needed to enforce the boxing of the queue
1125+
// object. For now, only the minimal is done as the returning of SyclQueue
1126+
// from a dpjit function should not be a used often and the dpctl C API for
1127+
// type checking etc. is not ready.
1128+
if (orig_queue == NULL) {
1129+
PyErr_Format(PyExc_ValueError,
1130+
"In 'box_from_queuestruct_parent', "
1131+
"failed to create a new dpctl.SyclQueue object.");
1132+
return NULL;
1133+
}
1134+
1135+
gstate = PyGILState_Ensure();
1136+
// decref the parent python object as we did an incref when unboxing it
1137+
Py_DECREF(orig_queue);
1138+
// release the GIL
1139+
PyGILState_Release(gstate);
1140+
1141+
return orig_queue;
1142+
}
1143+
10521144
/*----------------------------------------------------------------------------*/
10531145
/*--------------------- The _dpexrt_python Python extension module -- -------*/
10541146
/*----------------------------------------------------------------------------*/
@@ -1082,6 +1174,9 @@ static PyObject *build_c_helpers_dict(void)
10821174
_declpointer("DPEXRT_MemInfo_fill", &DPEXRT_MemInfo_fill);
10831175
_declpointer("NRT_ExternalAllocator_new_for_usm",
10841176
&NRT_ExternalAllocator_new_for_usm);
1177+
_declpointer("DPEXRT_sycl_queue_from_python",
1178+
&DPEXRT_sycl_queue_from_python);
1179+
_declpointer("DPEXRT_sycl_queue_to_python", &DPEXRT_sycl_queue_to_python);
10851180

10861181
#undef _declpointer
10871182
return dct;
@@ -1129,6 +1224,11 @@ MOD_INIT(_dpexrt_python)
11291224
m, "DPEXRT_sycl_usm_ndarray_to_python_acqref",
11301225
PyLong_FromVoidPtr(&DPEXRT_sycl_usm_ndarray_to_python_acqref));
11311226

1227+
PyModule_AddObject(m, "DPEXRT_sycl_queue_from_python",
1228+
PyLong_FromVoidPtr(&DPEXRT_sycl_queue_from_python));
1229+
PyModule_AddObject(m, "DPEXRT_sycl_queue_to_python",
1230+
PyLong_FromVoidPtr(&DPEXRT_sycl_queue_to_python));
1231+
11321232
PyModule_AddObject(m, "DPEXRTQueue_CreateFromFilterString",
11331233
PyLong_FromVoidPtr(&DPEXRTQueue_CreateFromFilterString));
11341234
PyModule_AddObject(m, "DpexrtQueue_SubmitRange",
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#ifndef NUMBA_DPEX_QUEUESTRUCT_H_
2+
#define NUMBA_DPEX_QUEUESTRUCT_H_
3+
/*
4+
* Fill in the *queuestruct* with information from the Numpy array *obj*.
5+
* *queuestruct*'s layout is defined in numba.targets.arrayobj (look
6+
* for the ArrayTemplate class).
7+
*/
8+
9+
#include <Python.h>
10+
11+
typedef struct
12+
{
13+
PyObject *parent;
14+
void *queue_ref;
15+
} queuestruct_t;
16+
17+
#endif /* NUMBA_DPEX_QUEUESTRUCT_H_ */

numba_dpex/core/runtime/context.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,6 @@ def arraystruct_from_python(self, pyapi, obj, ptr):
109109
"""Generates a call to DPEXRT_sycl_usm_ndarray_from_python C function
110110
defined in the _DPREXRT_python Python extension.
111111
112-
Args:
113-
pyapi (_type_): _description_
114-
obj (_type_): _description_
115-
ptr (_type_): _description_
116-
117-
Returns:
118-
_type_: _description_
119112
"""
120113
fnty = llvmir.FunctionType(
121114
llvmir.IntType(32), [pyapi.pyobj, pyapi.voidptr]
@@ -128,6 +121,34 @@ def arraystruct_from_python(self, pyapi, obj, ptr):
128121

129122
return self.error
130123

124+
def queuestruct_from_python(self, pyapi, obj, ptr):
125+
"""Calls the c function DPEXRT_sycl_queue_from_python"""
126+
127+
fnty = llvmir.FunctionType(
128+
llvmir.IntType(32), [pyapi.pyobj, pyapi.voidptr]
129+
)
130+
131+
fn = pyapi._get_function(fnty, "DPEXRT_sycl_queue_from_python")
132+
fn.args[0].add_attribute("nocapture")
133+
fn.args[1].add_attribute("nocapture")
134+
135+
self.error = pyapi.builder.call(fn, (obj, ptr))
136+
137+
return self.error
138+
139+
def queuestruct_to_python(self, pyapi, val):
140+
"""Calls the c function DPEXRT_sycl_queue_to_python"""
141+
142+
fnty = llvmir.FunctionType(pyapi.pyobj, [pyapi.voidptr])
143+
144+
fn = pyapi._get_function(fnty, "DPEXRT_sycl_queue_to_python")
145+
fn.args[0].add_attribute("nocapture")
146+
aryptr = cgutils.alloca_once_value(pyapi.builder, val)
147+
ptr = pyapi.builder.bitcast(aryptr, pyapi.voidptr)
148+
self.error = pyapi.builder.call(fn, [ptr])
149+
150+
return self.error
151+
131152
def usm_ndarray_to_python_acqref(self, pyapi, aryty, ary, dtypeptr):
132153
"""Boxes a DpnpNdArray native object into a Python dpnp.ndarray.
133154
@@ -230,7 +251,6 @@ def submit_range(
230251
fn = cgutils.get_or_insert_function(
231252
mod, fnty, "DpexrtQueue_SubmitRange"
232253
)
233-
# fn.return_value.add_attribute("noalias")
234254

235255
ret = builder.call(
236256
fn,

0 commit comments

Comments
 (0)