Skip to content

Commit 6145876

Browse files
committed
Support compilation from SYCL source code
Enable SYCL source compilation, but only for DPC++ versions that actually support the compilation, based on the __SYCL_COMPILER_VERSION reported. Uses the correct naming for the property based on DPC++ version, detected through C++ type traits to check which property actually refers to a fully defined type. This commit also works around a bug in DPC++ version 2025.1. The constructor with no parameter of class `include_files` was only declared, but never defined. Calling it when creating a SYCL source kernel bundle therefore leads to references to undefined symbols with DPC++ version 2025.1. This change works around this issue by calling an alternative constructor, which is defined in the release. Signed-off-by: Lukas Sommer <lukas.sommer@codeplay.com>
1 parent 80213b4 commit 6145876

12 files changed

+993
-9
lines changed

dpctl/_backend.pxd

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,12 @@ cdef extern from "syclinterface/dpctl_sycl_device_interface.h":
287287
_peer_access PT)
288288
cdef void DPCTLDevice_EnablePeerAccess(const DPCTLSyclDeviceRef DRef,
289289
const DPCTLSyclDeviceRef PDRef)
290-
291290
cdef void DPCTLDevice_DisablePeerAccess(const DPCTLSyclDeviceRef DRef,
292291
const DPCTLSyclDeviceRef PDRef)
292+
cdef bool DPCTLDevice_CanCompileSPIRV(const DPCTLSyclDeviceRef DRef)
293+
cdef bool DPCTLDevice_CanCompileOpenCL(const DPCTLSyclDeviceRef DRef)
294+
cdef bool DPCTLDevice_CanCompileSYCL(const DPCTLSyclDeviceRef DRef)
295+
293296

294297
cdef extern from "syclinterface/dpctl_sycl_device_manager.h":
295298
cdef DPCTLDeviceVectorRef DPCTLDeviceVector_CreateFromArray(
@@ -452,6 +455,51 @@ cdef extern from "syclinterface/dpctl_sycl_kernel_bundle_interface.h":
452455
cdef DPCTLSyclKernelBundleRef DPCTLKernelBundle_Copy(
453456
const DPCTLSyclKernelBundleRef KBRef)
454457

458+
cdef struct DPCTLBuildOptionList
459+
cdef struct DPCTLKernelNameList
460+
cdef struct DPCTLVirtualHeaderList
461+
cdef struct DPCTLKernelBuildLog
462+
ctypedef DPCTLBuildOptionList* DPCTLBuildOptionListRef
463+
ctypedef DPCTLKernelNameList* DPCTLKernelNameListRef
464+
ctypedef DPCTLVirtualHeaderList* DPCTLVirtualHeaderListRef
465+
ctypedef DPCTLKernelBuildLog* DPCTLKernelBuildLogRef
466+
467+
cdef DPCTLBuildOptionListRef DPCTLBuildOptionList_Create()
468+
cdef void DPCTLBuildOptionList_Delete(DPCTLBuildOptionListRef Ref)
469+
cdef void DPCTLBuildOptionList_Append(DPCTLBuildOptionListRef Ref,
470+
const char *Option)
471+
472+
cdef DPCTLKernelNameListRef DPCTLKernelNameList_Create()
473+
cdef void DPCTLKernelNameList_Delete(DPCTLKernelNameListRef Ref)
474+
cdef void DPCTLKernelNameList_Append(DPCTLKernelNameListRef Ref,
475+
const char *Option)
476+
477+
cdef DPCTLVirtualHeaderListRef DPCTLVirtualHeaderList_Create()
478+
cdef void DPCTLVirtualHeaderList_Delete(DPCTLVirtualHeaderListRef Ref)
479+
cdef void DPCTLVirtualHeaderList_Append(DPCTLVirtualHeaderListRef Ref,
480+
const char *Name,
481+
const char *Content)
482+
483+
cdef DPCTLKernelBuildLogRef DPCTLKernelBuildLog_Create()
484+
cdef void DPCTLKernelBuildLog_Delete(DPCTLKernelBuildLogRef Ref)
485+
cdef const char *DPCTLKernelBuildLog_Get(DPCTLKernelBuildLogRef)
486+
487+
cdef DPCTLSyclKernelBundleRef DPCTLKernelBundle_CreateFromSYCLSource(
488+
const DPCTLSyclContextRef Ctx,
489+
const DPCTLSyclDeviceRef Dev,
490+
const char *Source,
491+
DPCTLVirtualHeaderListRef Headers,
492+
DPCTLKernelNameListRef Names,
493+
DPCTLBuildOptionListRef BuildOptions,
494+
DPCTLKernelBuildLogRef BuildLog)
495+
496+
cdef DPCTLSyclKernelRef DPCTLKernelBundle_GetSyclKernel(
497+
DPCTLSyclKernelBundleRef KBRef,
498+
const char *KernelName)
499+
500+
cdef bool DPCTLKernelBundle_HasSyclKernel(DPCTLSyclKernelBundleRef KBRef,
501+
const char *KernelName)
502+
455503

456504
cdef extern from "syclinterface/dpctl_sycl_queue_interface.h":
457505
ctypedef struct _md_local_accessor "MDLocalAccessor":

dpctl/_sycl_device.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,4 @@ cdef public api class SyclDevice(_SyclDevice) [
6161
cdef int get_overall_ordinal(self)
6262
cdef int get_backend_ordinal(self)
6363
cdef int get_backend_and_device_type_ordinal(self)
64+
cpdef bint can_compile(self, str language)

dpctl/_sycl_device.pyx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ from ._backend cimport ( # noqa: E211
2626
DPCTLDefaultSelector_Create,
2727
DPCTLDevice_AreEq,
2828
DPCTLDevice_CanAccessPeer,
29+
DPCTLDevice_CanCompileOpenCL,
30+
DPCTLDevice_CanCompileSPIRV,
31+
DPCTLDevice_CanCompileSYCL,
2932
DPCTLDevice_Copy,
3033
DPCTLDevice_CreateFromSelector,
3134
DPCTLDevice_CreateSubDevicesByAffinity,
@@ -2367,6 +2370,34 @@ cdef class SyclDevice(_SyclDevice):
23672370
raise ValueError("device could not be found")
23682371
return dev_id
23692372

2373+
cpdef bint can_compile(self, str language):
2374+
"""
2375+
Check whether it is possible to create an executable kernel_bundle
2376+
for this device from the given source language.
2377+
2378+
Parameters:
2379+
language
2380+
Input language. Possible values are "spirv" for SPIR-V binary
2381+
files, "opencl" for OpenCL C device code and "sycl" for SYCL
2382+
device code.
2383+
2384+
Returns:
2385+
bool:
2386+
True if compilation is supported, False otherwise.
2387+
2388+
Raises:
2389+
ValueError:
2390+
If an unknown source language is used.
2391+
"""
2392+
if language == "spirv" or language == "spv":
2393+
return DPCTLDevice_CanCompileSPIRV(self._device_ref)
2394+
if language == "opencl" or language == "ocl":
2395+
return DPCTLDevice_CanCompileOpenCL(self._device_ref)
2396+
if language == "sycl":
2397+
return DPCTLDevice_CanCompileSYCL(self._device_ref)
2398+
2399+
raise ValueError(f"Unknown source language {language}")
2400+
23702401

23712402
cdef api DPCTLSyclDeviceRef SyclDevice_GetDeviceRef(SyclDevice dev):
23722403
"""

dpctl/program/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@
2626
SyclProgramCompilationError,
2727
create_program_from_source,
2828
create_program_from_spirv,
29+
create_program_from_sycl_source,
2930
)
3031

3132
__all__ = [
3233
"create_program_from_source",
3334
"create_program_from_spirv",
35+
"create_program_from_sycl_source",
3436
"SyclKernel",
3537
"SyclProgram",
3638
"SyclProgramCompilationError",

dpctl/program/_program.pxd

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,18 @@ cdef api class SyclProgram [object PySyclProgramObject, type PySyclProgramType]:
4949
binary file.
5050
"""
5151
cdef DPCTLSyclKernelBundleRef _program_ref
52+
cdef bint _is_sycl_source
5253

5354
@staticmethod
54-
cdef SyclProgram _create (DPCTLSyclKernelBundleRef pref)
55+
cdef SyclProgram _create (DPCTLSyclKernelBundleRef pref,
56+
bint _is_sycl_source)
5557
cdef DPCTLSyclKernelBundleRef get_program_ref (self)
5658
cpdef SyclKernel get_sycl_kernel(self, str kernel_name)
5759

5860

5961
cpdef create_program_from_source (SyclQueue q, unicode source, unicode copts=*)
6062
cpdef create_program_from_spirv (SyclQueue q, const unsigned char[:] IL,
6163
unicode copts=*)
64+
cpdef create_program_from_sycl_source(SyclQueue q, unicode source,
65+
list headers=*, list registered_names=*,
66+
list copts=*)

dpctl/program/_program.pyx

Lines changed: 144 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ a OpenCL source string or a SPIR-V binary file.
2828
from libc.stdint cimport uint32_t
2929

3030
from dpctl._backend cimport ( # noqa: E211, E402;
31+
DPCTLBuildOptionList_Append,
32+
DPCTLBuildOptionList_Create,
33+
DPCTLBuildOptionList_Delete,
34+
DPCTLBuildOptionListRef,
3135
DPCTLKernel_Copy,
3236
DPCTLKernel_Delete,
3337
DPCTLKernel_GetCompileNumSubGroups,
@@ -38,16 +42,31 @@ from dpctl._backend cimport ( # noqa: E211, E402;
3842
DPCTLKernel_GetPreferredWorkGroupSizeMultiple,
3943
DPCTLKernel_GetPrivateMemSize,
4044
DPCTLKernel_GetWorkGroupSize,
45+
DPCTLKernelBuildLog_Create,
46+
DPCTLKernelBuildLog_Delete,
47+
DPCTLKernelBuildLog_Get,
48+
DPCTLKernelBuildLogRef,
4149
DPCTLKernelBundle_Copy,
4250
DPCTLKernelBundle_CreateFromOCLSource,
4351
DPCTLKernelBundle_CreateFromSpirv,
52+
DPCTLKernelBundle_CreateFromSYCLSource,
4453
DPCTLKernelBundle_Delete,
4554
DPCTLKernelBundle_GetKernel,
55+
DPCTLKernelBundle_GetSyclKernel,
4656
DPCTLKernelBundle_HasKernel,
57+
DPCTLKernelBundle_HasSyclKernel,
58+
DPCTLKernelNameList_Append,
59+
DPCTLKernelNameList_Create,
60+
DPCTLKernelNameList_Delete,
61+
DPCTLKernelNameListRef,
4762
DPCTLSyclContextRef,
4863
DPCTLSyclDeviceRef,
4964
DPCTLSyclKernelBundleRef,
5065
DPCTLSyclKernelRef,
66+
DPCTLVirtualHeaderList_Append,
67+
DPCTLVirtualHeaderList_Create,
68+
DPCTLVirtualHeaderList_Delete,
69+
DPCTLVirtualHeaderListRef,
5170
)
5271

5372
__all__ = [
@@ -196,9 +215,11 @@ cdef class SyclProgram:
196215
"""
197216

198217
@staticmethod
199-
cdef SyclProgram _create(DPCTLSyclKernelBundleRef KBRef):
218+
cdef SyclProgram _create(DPCTLSyclKernelBundleRef KBRef,
219+
bint is_sycl_source):
200220
cdef SyclProgram ret = SyclProgram.__new__(SyclProgram)
201221
ret._program_ref = KBRef
222+
ret._is_sycl_source = is_sycl_source
202223
return ret
203224

204225
def __dealloc__(self):
@@ -209,13 +230,19 @@ cdef class SyclProgram:
209230

210231
cpdef SyclKernel get_sycl_kernel(self, str kernel_name):
211232
name = kernel_name.encode("utf8")
233+
if self._is_sycl_source:
234+
return SyclKernel._create(
235+
DPCTLKernelBundle_GetSyclKernel(self._program_ref, name),
236+
kernel_name)
212237
return SyclKernel._create(
213238
DPCTLKernelBundle_GetKernel(self._program_ref, name),
214239
kernel_name
215240
)
216241

217242
def has_sycl_kernel(self, str kernel_name):
218243
name = kernel_name.encode("utf8")
244+
if self._is_sycl_source:
245+
return DPCTLKernelBundle_HasSyclKernel(self._program_ref, name)
219246
return DPCTLKernelBundle_HasKernel(self._program_ref, name)
220247

221248
def addressof_ref(self):
@@ -271,7 +298,7 @@ cpdef create_program_from_source(SyclQueue q, str src, str copts=""):
271298
if KBref is NULL:
272299
raise SyclProgramCompilationError()
273300

274-
return SyclProgram._create(KBref)
301+
return SyclProgram._create(KBref, False)
275302

276303

277304
cpdef create_program_from_spirv(SyclQueue q, const unsigned char[:] IL,
@@ -317,7 +344,120 @@ cpdef create_program_from_spirv(SyclQueue q, const unsigned char[:] IL,
317344
if KBref is NULL:
318345
raise SyclProgramCompilationError()
319346

320-
return SyclProgram._create(KBref)
347+
return SyclProgram._create(KBref, False)
348+
349+
350+
cpdef create_program_from_sycl_source(SyclQueue q, unicode source,
351+
list headers=None,
352+
list registered_names=None,
353+
list copts=None):
354+
"""
355+
Creates an executable SYCL kernel_bundle from SYCL source code.
356+
357+
This uses the DPC++ ``kernel_compiler`` extension to create a
358+
``sycl::kernel_bundle<sycl::bundle_state::executable>`` object from
359+
SYCL source code.
360+
361+
Parameters:
362+
q (:class:`dpctl.SyclQueue`)
363+
The :class:`dpctl.SyclQueue` for which the
364+
:class:`.SyclProgram` is going to be built.
365+
source (unicode)
366+
SYCL source code string.
367+
headers (list)
368+
Optional list of virtual headers, where each entry in the list
369+
needs to be a tuple of header name and header content. See the
370+
documentation of the ``include_files`` property in the DPC++
371+
``kernel_compiler`` extension for more information.
372+
Default: []
373+
registered_names (list, optional)
374+
Optional list of kernel names to register. See the
375+
documentation of the ``registered_names`` property in the DPC++
376+
``kernel_compiler`` extension for more information.
377+
Default: []
378+
copts (list)
379+
Optional list of compilation flags that will be used
380+
when compiling the program. Default: ``""``.
381+
382+
Returns:
383+
program (:class:`.SyclProgram`)
384+
A :class:`.SyclProgram` object wrapping the
385+
``sycl::kernel_bundle<sycl::bundle_state::executable>``
386+
returned by the C API.
387+
388+
Raises:
389+
SyclProgramCompilationError
390+
If a SYCL kernel bundle could not be created. The exception
391+
message contains the build log for more details.
392+
"""
393+
cdef DPCTLSyclKernelBundleRef KBref
394+
cdef DPCTLSyclContextRef CRef = q.get_sycl_context().get_context_ref()
395+
cdef DPCTLSyclDeviceRef DRef = q.get_sycl_device().get_device_ref()
396+
cdef bytes bSrc = source.encode("utf8")
397+
cdef const char *Src = <const char*>bSrc
398+
cdef DPCTLBuildOptionListRef BuildOpts = DPCTLBuildOptionList_Create()
399+
cdef bytes bOpt
400+
cdef const char* sOpt
401+
cdef bytes bName
402+
cdef const char* sName
403+
cdef bytes bContent
404+
cdef const char* sContent
405+
cdef const char* buildLogContent
406+
for opt in copts:
407+
if not isinstance(opt, unicode):
408+
DPCTLBuildOptionList_Delete(BuildOpts)
409+
raise SyclProgramCompilationError()
410+
bOpt = opt.encode("utf8")
411+
sOpt = <const char*>bOpt
412+
DPCTLBuildOptionList_Append(BuildOpts, sOpt)
413+
414+
cdef DPCTLKernelNameListRef KernelNames = DPCTLKernelNameList_Create()
415+
for name in registered_names:
416+
if not isinstance(name, unicode):
417+
DPCTLBuildOptionList_Delete(BuildOpts)
418+
DPCTLKernelNameList_Delete(KernelNames)
419+
raise SyclProgramCompilationError()
420+
bName = name.encode("utf8")
421+
sName = <const char*>bName
422+
DPCTLKernelNameList_Append(KernelNames, sName)
423+
424+
cdef DPCTLVirtualHeaderListRef VirtualHeaders
425+
VirtualHeaders = DPCTLVirtualHeaderList_Create()
426+
427+
for name, content in headers:
428+
if not isinstance(name, unicode) or not isinstance(content, unicode):
429+
DPCTLBuildOptionList_Delete(BuildOpts)
430+
DPCTLKernelNameList_Delete(KernelNames)
431+
DPCTLVirtualHeaderList_Delete(VirtualHeaders)
432+
raise SyclProgramCompilationError()
433+
bName = name.encode("utf8")
434+
sName = <const char*>bName
435+
bContent = content.encode("utf8")
436+
sContent = <const char*>bContent
437+
DPCTLVirtualHeaderList_Append(VirtualHeaders, sName, sContent)
438+
439+
cdef DPCTLKernelBuildLogRef BuildLog
440+
BuildLog = DPCTLKernelBuildLog_Create()
441+
442+
KBref = DPCTLKernelBundle_CreateFromSYCLSource(CRef, DRef, Src,
443+
VirtualHeaders, KernelNames,
444+
BuildOpts, BuildLog)
445+
446+
if KBref is NULL:
447+
buildLogContent = DPCTLKernelBuildLog_Get(BuildLog)
448+
buildLogStr = str(buildLogContent, "utf-8")
449+
DPCTLBuildOptionList_Delete(BuildOpts)
450+
DPCTLKernelNameList_Delete(KernelNames)
451+
DPCTLVirtualHeaderList_Delete(VirtualHeaders)
452+
DPCTLKernelBuildLog_Delete(BuildLog)
453+
raise SyclProgramCompilationError(buildLogStr)
454+
455+
DPCTLBuildOptionList_Delete(BuildOpts)
456+
DPCTLKernelNameList_Delete(KernelNames)
457+
DPCTLVirtualHeaderList_Delete(VirtualHeaders)
458+
DPCTLKernelBuildLog_Delete(BuildLog)
459+
460+
return SyclProgram._create(KBref, True)
321461

322462

323463
cdef api DPCTLSyclKernelBundleRef SyclProgram_GetKernelBundleRef(
@@ -336,4 +476,4 @@ cdef api SyclProgram SyclProgram_Make(DPCTLSyclKernelBundleRef KBRef):
336476
reference.
337477
"""
338478
cdef DPCTLSyclKernelBundleRef copied_KBRef = DPCTLKernelBundle_Copy(KBRef)
339-
return SyclProgram._create(copied_KBRef)
479+
return SyclProgram._create(copied_KBRef, False)

0 commit comments

Comments
 (0)