diff --git a/numba_dpex/core/kernel_interface/__init__.py b/numba_dpex/core/kernel_interface/__init__.py deleted file mode 100644 index 31a84744f4..0000000000 --- a/numba_dpex/core/kernel_interface/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-FileCopyrightText: 2020 - 2024 Intel Corporation -# -# SPDX-License-Identifier: Apache-2.0 - -"""Defines the interface for kernel compilation using numba-dpex. -""" diff --git a/numba_dpex/core/kernel_interface/arrayobj.py b/numba_dpex/core/kernel_interface/arrayobj.py deleted file mode 100644 index 8a823a265e..0000000000 --- a/numba_dpex/core/kernel_interface/arrayobj.py +++ /dev/null @@ -1,137 +0,0 @@ -# SPDX-FileCopyrightText: 2012 - 2024 Anaconda Inc. -# SPDX-FileCopyrightText: 2024 Intel Corporation -# -# SPDX-License-Identifier: BSD-2-Clause - -""" -This package contains implementation of some numpy.np.arrayobj functions without -parent and meminfo fields required, because they don't make sense on device. -These functions intended to be used only in kernel targets like local/private or -usm array view. -""" - - -from numba.core import cgutils, types -from numba.np import arrayobj as np_arrayobj - -from numba_dpex.core import types as dpex_types - - -def populate_array(array, data, shape, strides, itemsize): - """ - Helper function for populating array structures. - This avoids forgetting to set fields. - - *shape* and *strides* can be Python tuples or LLVM arrays. - - This is analog of numpy.np.arrayobj.populate_array without parent and - meminfo fields, because they don't make sense on device. This function - intended to be used only in kernel targets. - """ - context = array._context - builder = array._builder - datamodel = array._datamodel - # doesn't matter what this array type instance is, it's just to get the - # fields for the datamodel of the standard array type in this context - standard_array = dpex_types.Array(types.float64, 1, "C") - standard_array_type_datamodel = context.data_model_manager[standard_array] - required_fields = set(standard_array_type_datamodel._fields) - datamodel_fields = set(datamodel._fields) - # Make sure that the presented array object has a data model that is close - # enough to an array for this function to proceed. - if (required_fields & datamodel_fields) != required_fields: - missing = required_fields - datamodel_fields - msg = ( - f"The datamodel for type {array._fe_type} is missing " - f"field{'s' if len(missing) > 1 else ''} {missing}." - ) - raise ValueError(msg) - - intp_t = context.get_value_type(types.intp) - if isinstance(shape, (tuple, list)): - shape = cgutils.pack_array(builder, shape, intp_t) - if isinstance(strides, (tuple, list)): - strides = cgutils.pack_array(builder, strides, intp_t) - if isinstance(itemsize, int): - itemsize = intp_t(itemsize) - - attrs = dict( - shape=shape, - strides=strides, - data=data, - itemsize=itemsize, - ) - - # Calc num of items from shape - nitems = context.get_constant(types.intp, 1) - unpacked_shape = cgutils.unpack_tuple(builder, shape, shape.type.count) - # (note empty shape => 0d array therefore nitems = 1) - for axlen in unpacked_shape: - nitems = builder.mul(nitems, axlen, flags=["nsw"]) - attrs["nitems"] = nitems - - # Make sure that we have all the fields - got_fields = set(attrs.keys()) - if got_fields != required_fields: - raise ValueError("missing {0}".format(required_fields - got_fields)) - - # Set field value - for k, v in attrs.items(): - setattr(array, k, v) - - return array - - -def make_view(context, builder, aryty, ary, return_type, data, shapes, strides): - """ - Build a view over the given array with the given parameters. - - This is analog of numpy.np.arrayobj.make_view without parent and - meminfo fields, because they don't make sense on device. This function - intended to be used only in kernel targets. - """ - retary = np_arrayobj.make_array(return_type)(context, builder) - populate_array( - retary, data=data, shape=shapes, strides=strides, itemsize=ary.itemsize - ) - return retary - - -def _getitem_array_generic( - context, builder, return_type, aryty, ary, index_types, indices -): - """ - Return the result of indexing *ary* with the given *indices*, - returning either a scalar or a view. - - This is analog of numpy.np.arrayobj._getitem_array_generic without parent - and meminfo fields, because they don't make sense on device. This function - intended to be used only in kernel targets. - """ - dataptr, view_shapes, view_strides = np_arrayobj.basic_indexing( - context, - builder, - aryty, - ary, - index_types, - indices, - boundscheck=context.enable_boundscheck, - ) - - if isinstance(return_type, types.Buffer): - # Build array view - retary = make_view( - context, - builder, - aryty, - ary, - return_type, - dataptr, - view_shapes, - view_strides, - ) - return retary._getvalue() - else: - # Load scalar from 0-d result - assert not view_shapes - return np_arrayobj.load_item(context, builder, aryty, dataptr) diff --git a/numba_dpex/dpnp_iface/arrayobj.py b/numba_dpex/dpnp_iface/arrayobj.py index 65ac816f89..784c9f045b 100644 --- a/numba_dpex/dpnp_iface/arrayobj.py +++ b/numba_dpex/dpnp_iface/arrayobj.py @@ -16,10 +16,10 @@ from numba.np.arrayobj import make_array from numba.np.numpy_support import is_nonelike -from numba_dpex.core.kernel_interface.arrayobj import ( +from numba_dpex.core.types import DpnpNdArray +from numba_dpex.kernel_api_impl.spirv.arrayobj import ( _getitem_array_generic as kernel_getitem_array_generic, ) -from numba_dpex.core.types import DpnpNdArray from numba_dpex.kernel_api_impl.spirv.target import SPIRVTargetContext from ._intrinsic import ( diff --git a/numba_dpex/kernel_api_impl/spirv/arrayobj.py b/numba_dpex/kernel_api_impl/spirv/arrayobj.py index 062d526247..00b6100ff7 100644 --- a/numba_dpex/kernel_api_impl/spirv/arrayobj.py +++ b/numba_dpex/kernel_api_impl/spirv/arrayobj.py @@ -1,6 +1,8 @@ +# SPDX-FileCopyrightText: 2012 - 2024 Anaconda Inc. # SPDX-FileCopyrightText: 2024 Intel Corporation # # SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: BSD-2-Clause """Contains SPIR-V specific array functions.""" @@ -10,7 +12,138 @@ from llvmlite.ir.builder import IRBuilder from numba.core import cgutils, errors, types from numba.core.base import BaseContext -from numba.np.arrayobj import get_itemsize +from numba.np.arrayobj import ( + basic_indexing, + get_itemsize, + load_item, + make_array, +) + +from numba_dpex.core.types import USMNdArray + + +def populate_array( + arraystruct, data, shape, strides, itemsize +): # pylint: disable=too-many-arguments,too-many-locals + """ + Helper function for populating array structures. + + The function is copied from upstream Numba and modified to support the + USMNdArray data type that uses a different data model on SYCL devices + than the upstream types.Array data type. USMNdArray data model does not + have the ``parent`` and ``meminfo`` fields. This function intended to be + used only in the SPIRVKernelTarget. + + *shape* and *strides* can be Python tuples or LLVM arrays. + """ + context = arraystruct._context # pylint: disable=protected-access + builder = arraystruct._builder # pylint: disable=protected-access + datamodel = arraystruct._datamodel # pylint: disable=protected-access + # doesn't matter what this array type instance is, it's just to get the + # fields for the data model of the standard array type in this context + standard_array = USMNdArray(ndim=1, layout="C", dtype=types.float64) + standard_array_type_datamodel = context.data_model_manager[standard_array] + required_fields = set(standard_array_type_datamodel._fields) + datamodel_fields = set(datamodel._fields) + # Make sure that the presented array object has a data model that is + # close enough to an array for this function to proceed. + if (required_fields & datamodel_fields) != required_fields: + missing = required_fields - datamodel_fields + msg = ( + f"The datamodel for type {arraystruct} is missing " + f"field{'s' if len(missing) > 1 else ''} {missing}." + ) + raise ValueError(msg) + + intp_t = context.get_value_type(types.intp) + if isinstance(shape, (tuple, list)): + shape = cgutils.pack_array(builder, shape, intp_t) + if isinstance(strides, (tuple, list)): + strides = cgutils.pack_array(builder, strides, intp_t) + if isinstance(itemsize, int): + itemsize = intp_t(itemsize) + + attrs = { + "shape": shape, + "strides": strides, + "data": data, + "itemsize": itemsize, + } + + # Calc num of items from shape + nitems = context.get_constant(types.intp, 1) + unpacked_shape = cgutils.unpack_tuple(builder, shape, shape.type.count) + # (note empty shape => 0d array therefore nitems = 1) + for axlen in unpacked_shape: + nitems = builder.mul(nitems, axlen, flags=["nsw"]) + attrs["nitems"] = nitems + + # Make sure that we have all the fields + got_fields = set(attrs.keys()) + if got_fields != required_fields: + raise ValueError(f"missing {required_fields - got_fields}") + + # Set field value + for k, v in attrs.items(): + setattr(arraystruct, k, v) + + return arraystruct + + +def make_view( + context, builder, ary, return_type, data, shapes, strides +): # pylint: disable=too-many-arguments + """ + Build a view over the given array with the given parameters. + + This is analog of numpy.np.arrayobj.make_view without parent and + meminfo fields, because they don't make sense on device. This function + intended to be used only in kernel targets. + """ + retary = make_array(return_type)(context, builder) + context.populate_array( + retary, data=data, shape=shapes, strides=strides, itemsize=ary.itemsize + ) + return retary + + +def _getitem_array_generic( + context, builder, return_type, aryty, ary, index_types, indices +): # pylint: disable=too-many-arguments + """ + Return the result of indexing *ary* with the given *indices*, + returning either a scalar or a view. + + This is analog of numpy.np.arrayobj._getitem_array_generic without parent + and meminfo fields, because they don't make sense on device. This function + intended to be used only in kernel targets. + """ + dataptr, view_shapes, view_strides = basic_indexing( + context, + builder, + aryty, + ary, + index_types, + indices, + boundscheck=context.enable_boundscheck, + ) + + if isinstance(return_type, types.Buffer): + # Build array view + retary = make_view( + context, + builder, + ary, + return_type, + dataptr, + view_shapes, + view_strides, + ) + return retary._getvalue() # pylint: disable=protected-access + + # Load scalar from 0-d result + assert not view_shapes + return load_item(context, builder, aryty, dataptr) def require_literal(literal_type: types.Type): diff --git a/numba_dpex/kernel_api_impl/spirv/target.py b/numba_dpex/kernel_api_impl/spirv/target.py index 88e1114d6f..103e6f9c72 100644 --- a/numba_dpex/kernel_api_impl/spirv/target.py +++ b/numba_dpex/kernel_api_impl/spirv/target.py @@ -24,6 +24,7 @@ from numba_dpex.core.types import IntEnumLiteral from numba_dpex.core.typing import dpnpdecl from numba_dpex.kernel_api.flag_enum import FlagEnum +from numba_dpex.kernel_api_impl.spirv.arrayobj import populate_array from numba_dpex.ocl.mathimpl import lower_ocl_impl, sig_mapper from numba_dpex.utils import address_space, calling_conv @@ -37,9 +38,7 @@ class CompilationMode(IntEnum): """Flags used to determine how a function should be compiled by the - numba_dpex.experimental.dispatcher.KernelDispatcher. Note the functionality - will be merged into numba_dpex.core.kernel_interface.dispatcher in the - future. + numba_dpex.kernel_api_impl_spirv.dispatcher.KernelDispatcher. KERNEL : Indicates that the function will be compiled into an LLVM function that has ``spir_kernel`` calling @@ -419,10 +418,7 @@ def populate_array(self, arr, **kwargs): """ Populate array structure. """ - # pylint: disable=import-outside-toplevel - from numba_dpex.core.kernel_interface import arrayobj - - return arrayobj.populate_array(arr, **kwargs) + return populate_array(arr, **kwargs) def get_executable(self, func, fndesc, env): """Not implemented for SPIRVTargetContext"""