Skip to content

Check the number of kernel arguments only for interoperability kernels - #2425

Open
ndgrigorian wants to merge 3 commits into
masterfrom
fix-kernel-arg-count-check
Open

ndgrigorian wants to merge 3 commits into
masterfrom
fix-kernel-arg-count-check

Conversation

@ndgrigorian

@ndgrigorian ndgrigorian commented Nov 8, 2026 •

Copy link
Copy Markdown
Collaborator

This PR fixes the check on the number of kernel arguments added to SyclQueue.submit and SyclQueue.submit_async in gh-2382. It compares the number of arguments given with info::kernel::num_args, which SYCL defines only for kernels in kernel bundles created through backend interoperability. For a kernel compiled from SYCL source, DPC++ reports the backend's count instead, in which a sycl::local_accessor takes 4 arguments, so such a kernel can not be submitted with a dpctl.LocalAccessor:

ValueError: Kernel 'reverse_in_group' takes 6 argument(s), got 3.

dpctl.compiler.SyclKernel now records the kind of kernel bundle it comes from, and the number of arguments is only checked for kernels from kernel bundles created from OpenCL C source or SPIR-V. The arguments of other kernels, including those passed to dpctl through SyclKernel_Make, are not checked. On OpenCL, the backend rejects a wrong number of arguments on submission, raising SyclKernelSubmitError, while on Level Zero too few arguments are not detected

A kernel compiled from SYCL to SPIR-V may also expect a sycl::local_accessor parameter as several arguments: the local memory pointer, followed by the accessor's access range, memory range and offset. A dpctl.LocalAccessor provides only the pointer, so the others have to be passed explicitly after it. For kernels from a SPIR-V bundle, the error now says so when the arguments include a dpctl.LocalAccessor, and the dpctl.LocalAccessor docstring shows how to pass them:

ValueError: Kernel '__sycl_kernel_local_accessor_kernel' takes 5 argument(s), got 2. A kernel compiled from SYCL to SPIR-V may expect a sycl::local_accessor parameter as several arguments, of which a dpctl.LocalAccessor is only the first; see the dpctl.LocalAccessor documentation.

DPCTLKernel_GetNumArgs also no longer aborts the process when the query throws, returning -1, and SyclKernel.get_num_args raises RuntimeError in that case

  • Have you provided a meaningful PR description?
  • Have you added a test, reproducer or referred to an issue with a reproducer?
  • Have you tested your changes locally for CPU and GPU devices?
  • Have you made sure that new changes do not introduce compiler warnings?
  • Have you checked performance impact of proposed changes?
  • Have you added documentation for your changes, if necessary?
  • Have you added your changes to the changelog?
  • If this PR is a work in progress, are you opening the PR as a draft?

The number of arguments is only defined for kernels from OpenCL C source or
SPIR-V bundles, so SyclKernel records the kind of bundle it comes from and
submit checks the count only for those. DPCTLKernel_GetNumArgs no longer
aborts when the query throws.
@ndgrigorian
ndgrigorian force-pushed the fix-kernel-arg-count-check branch from c562604 to b7f435b Compare November 8, 2026 20:51
@github-actions

github-actions Bot commented Nov 8, 2026

Copy link
Copy Markdown

@coveralls

coveralls commented Nov 8, 2026 •

Copy link
Copy Markdown
Collaborator

Coverage Status

coverage: 75.112% (-0.03%) from 75.137% — fix-kernel-arg-count-check into master

A sycl::local_accessor of a kernel compiled from SYCL to SPIR-V can take
several arguments; the error says so when a LocalAccessor is given, and the
LocalAccessor docstring explains it.
A LocalAccessor works for a kernel compiled from SYCL to SPIR-V when the
accessor's ranges are passed after it; say so in the docstring and the error,
and test it.
'''
cdef DPCTLSyclKernelRef _kernel_ref
cdef str _function_name
cdef int _origin

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new _origin field makes PySyclKernelObject 8 bytes larger. Extensions built against an earlier dpctl that cimport dpctl or include dpctl4pybind11.hpp (all of dpnp) now get a RuntimeWarning on import, and fail with warnings as errors:

# dpnp built against dpctl master, run with this branch
import warnings
warnings.simplefilter("error", RuntimeWarning)
import dpnp  # RuntimeWarning: dpctl.compiler._compiler.SyclKernel size changed ... Expected 40 from C header, got 48

Could interop kernels be a subclass of SyclKernel without new fields instead, so the struct and the _create vtable slot stay the same?

Comment thread dpctl/_sycl_queue.pyx
f"argument(s), got {len(args)}."
)
# provided, so check it here for the kernels it defines that number for
if kernel._origin == _INTEROP_ORIGIN:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SYCL source kernels are no longer checked at all. On Level Zero a missing argument now runs the kernel silently, while master raises ValueError, and for a kernel without a local accessor the count is right:

import numpy as np, dpctl, dpctl.compiler as dpc, dpctl.memory as dpm

src = """
#include <sycl/sycl.hpp>
namespace syclexp = sycl::ext::oneapi::experimental;
extern "C" SYCL_EXTERNAL
SYCL_EXT_ONEAPI_FUNCTION_PROPERTY((syclexp::nd_range_kernel<1>))
void fill(int *x, int v) {
    auto it = sycl::ext::oneapi::this_work_item::get_nd_item<1>();
    x[it.get_global_linear_id()] = v;
}
"""
q = dpctl.SyclQueue()
krn = dpc.create_kernel_bundle_from_sycl_source(q, src).get_sycl_kernel("fill")
x = dpm.MemoryUSMDevice(64 * 4, queue=q)
q.submit(krn, [x], [64], [16]).wait()  # `v` is missing
res = np.empty(64, dtype="i4")
q.memcpy(res, x, res.nbytes)
print(res[:4])  # Level Zero GPU: [0 0 0 0]; OpenCL CPU: SyclKernelSubmitError; master: ValueError

Could the check stay for SYCL source kernels when no LocalAccessor is passed, or is the count unreliable there too?

Comment thread dpctl/_sycl_queue.pyx
Comment on lines +178 to +186
A ``LocalAccessor`` is passed as a single kernel argument, the pointer to
the local memory. A kernel compiled from SYCL to SPIR-V may expect a
``sycl::local_accessor`` parameter as several arguments instead: the
pointer, followed by the accessor's access range, memory range and
offset. Pass the ``LocalAccessor`` as the first of them and the others
explicitly, e.g. for a one-dimensional accessor of ``n`` elements::

[dpctl.LocalAccessor("i4", (n,)), ctypes.c_size_t(n),
ctypes.c_size_t(n), ctypes.c_size_t(0), ...]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For an N-D accessor, each range and the offset is one argument of N size_t, not N arguments. A SPIR-V kernel with sycl::local_accessor<int, 2> and int * has 5 arguments, and it gives the right result on GPU and CPU with:

r = lambda a, b: dpctl.RawKernelArg(np.array([a, b], dtype=np.uintp))
args = [dpctl.LocalAccessor("i4", (4, 4)), r(4, 4), r(4, 4), r(0, 0), x_usm]

Could the docstring say this?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants