Skip to content

[SYCL] Implement device libraries: C assert #922

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 33 commits into from
Dec 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
9c58843
[SYCL] Implement device libraries: C assert
Aug 29, 2019
6a8b3e7
Do not use "user" options for device library compilation
Dec 11, 2019
bc335d7
Pass unique_ptr with a program to ProgramManager::build()
Dec 11, 2019
b03b3e2
Refactoring
Dec 11, 2019
08a37e3
Clang format the whole patch
Dec 11, 2019
7b57b6b
Code style change
Dec 11, 2019
462af28
Release library programs
Dec 11, 2019
52e077c
Cleanup invalid library programs if an exception is thrown
Dec 11, 2019
75c05b5
Tabs vs spaces
Dec 11, 2019
269d6dc
Reword documentation in context_impl.hpp
Dec 11, 2019
3d80fe0
Fix error handling of clLinkProgram
Dec 12, 2019
7497a23
Code style changes
Dec 12, 2019
82e0e84
Include path to SYCL headers
Dec 12, 2019
265aaf6
Re-format PI_CALL
Dec 13, 2019
4ce3a57
Misc code review comments
Dec 13, 2019
278206f
Misc code review comments
Dec 13, 2019
a5d65db
Add a dependency for llvm-spirv
Dec 13, 2019
6b67601
Disable the test for assert on Windows
Dec 16, 2019
d585921
Reword documentation
Dec 17, 2019
e882460
Use 'if WIN32' instead of 'if MSVC' in CMake
Dec 17, 2019
f1ae75f
Code review comments
Dec 17, 2019
a1aeda7
Get devices from context instead of going through OpenCL API
Dec 18, 2019
f3a4e07
Refactor getDeviceLibPrograms
Dec 18, 2019
27bcf97
Use an enum instead of std::string for extension id
Dec 18, 2019
45b9be8
Fix typo
Dec 18, 2019
73fdcda
Merge branch 'intel/sycl' into private/asavonic/assert
Dec 18, 2019
9f1aecc
Use detail::get_device_info to get device extensions
Dec 18, 2019
8e826ef
Fix typo
Dec 18, 2019
cf819f1
Use uint64_t instead of size_t for __devicelib_assert_fail
Dec 18, 2019
8b41e4a
Code review comments
Dec 18, 2019
423a054
Workaround bug in IGC
Dec 18, 2019
42d52b8
Re-order local and global id in the assert format string
Dec 19, 2019
2f61342
Code review comments
Dec 19, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sycl/doc/SYCLEnvironmentVariables.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ subject to change. Do not rely on these variables in production code.
| SYCL_DUMP_IMAGES | Any(*) | Dump device image binaries to file. Control has no effect if SYCL_USE_KERNEL_SPV is set. |
| SYCL_PRINT_EXECUTION_GRAPH | Described [below](#sycl_print_execution_graph-options) | Print execution graph to DOT text file. |
| SYCL_THROW_ON_BLOCK | Any(*) | Throw an exception on attempt to wait for a blocked command. |
| SYCL_DEVICELIB_INHIBIT_NATIVE | String of device library extensions (separated by a whitespace) | Do not rely on device native support for devicelib extensions listed in this option. |

`(*) Note: Any means this environment variable is effective when set to any non-null value.`

Expand Down
162 changes: 162 additions & 0 deletions sycl/doc/extensions/C-CXX-StandardLibrary/C-CXX-StandardLibrary.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
C and C++ Standard libraries support
===================================

This extension enables a set of functions from the C and C++ standard
libraries, and allows to use them in SYCL device code.

Function declarations are taken from the standard headers (e.g. from
<assert.h> or <cassert>), and the corresponding header has to be
explicitly included in user code.

Implementation requires a special device library to be linked with a
SYCL program. The library should match the C or C++ standard library
used to compile the program:
Copy link
Contributor

Choose a reason for hiding this comment

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

The library should match the C or C++ standard library used to compile the program:

Can we have a single device library?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, see the line 77 in this document.
Basically, different libraries may use the same function name (e.g. __assert_fail), but with different arguments.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think it's the case for existing C/C++ standard library implementations.
Assuming that we want avoid potential issues with conflicting names, why do you propose "a special device library" instead of "a special header" as it's done for CUDA/OpenMP?

Copy link
Contributor Author

@asavonic asavonic Dec 24, 2019

Choose a reason for hiding this comment

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

I don't think it's the case for existing C/C++ standard library implementations.

These little inconsistencies definitely exist.
For example, in musl C library, __assert_fail has an line argument of type int, and Glibc has this argument as unsigned int.

Even if we consider only one library (e.g. Glibc), I'm not sure that ABI between headers and the library will always remain stable, so future/past versions may bring similar problems.

Assuming that we want avoid potential issues with conflicting names, why do you propose "a special device library" instead of "a special header" as it's done for CUDA/OpenMP?

Can you describe this "special header" approach using the assert function as an example?

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is hard to tell how this approach is going to co-exist with the rest of the standard library, and scale beyond a handful of functions. The code you refer to doesn't seem to support anything other than glibc and libc++/libstdc++.

In general, I think the most difficult part here is (1) to establish an interface with a device compiler (DevicelibExtensions.rst), and (2) wire standard library functions through that interface, instead of using library specific routines.

(2) can probably be done in a header file, but I don't see a huge benefit in comparison with the library approach. If you (or anyone reading this) see a benefit and know to to implement this with portability in mind - feel free to send a patch.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this at least should be discussed with the clang community.
It should be safe to develop solution aligned with the existing approaches in the community code w/o constantly checking with the community, but in this case we went in slightly different direction.


For example, on Linux with GNU glibc:
.. code:
clang++ -fsycl -c main.cpp -o main.o
clang++ -fsycl main.o $(SYCL_INSTALL)/lib/libsycl-glibc.o -o a.out
Copy link
Contributor

Choose a reason for hiding this comment

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

I think device library should be linked by default i.e. the same way OpenMP/CUDA compiler links device side library + driver option to disable linking device code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right. This requires changes in clang driver.


or, in case of Windows:
.. code:
clang++ -fsycl -c main.cpp -o main.obj
clang++ -fsycl main.obj %SYCL_INSTALL%/lib/libsycl-msvc.o -o a.exe

List of supported functions from C standard library:
- assert macro (from <assert.h> or <cassert>)

NOTE: only the GNU glibc and Microsoft C libraries are currently
supported.

Example of usage
================

.. code: c++
#include <assert.h>
#include <CL/sycl.hpp>

template <typename T, size_t N>
void simple_vadd(const std::array<T, N>& VA, const std::array<T, N>& VB,
std::array<T, N>& VC) {
// ...
cl::sycl::range<1> numOfItems{N};
cl::sycl::buffer<T, 1> bufferA(VA.data(), numOfItems);
cl::sycl::buffer<T, 1> bufferB(VB.data(), numOfItems);
cl::sycl::buffer<T, 1> bufferC(VC.data(), numOfItems);

deviceQueue.submit([&](cl::sycl::handler& cgh) {
auto accessorA = bufferA.template get_access<sycl_read>(cgh);
auto accessorB = bufferB.template get_access<sycl_read>(cgh);
auto accessorC = bufferC.template get_access<sycl_write>(cgh);

cgh.parallel_for<class SimpleVadd<T>>(numOfItems,
[=](cl::sycl::id<1> wiID) {
accessorC[wiID] = accessorA[wiID] + accessorB[wiID];
assert(accessorC[wiID] > 0 && "Invalid value");
Copy link
Contributor

Choose a reason for hiding this comment

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

Is assert a good example? It relies on some kernel features not available in OpenCL... Are there real use cases?
What about this implementation:

#ifdef __SYCL_DEVICE_ONLY__
#define NDEBUG 1
#endif 

? :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is assert a good example? It relies on some kernel features not available in OpenCL...

Right, assert is an edge case and it is probably a bad example.
However, it is the only standard C and C++ function supported at this moment.

Are there real use cases?

For assert? It is widely used as a sanity check in host code, so I don't see a reason why it cannot be used for device code as well.

What about this implementation:

#ifdef __SYCL_DEVICE_ONLY__
#define NDEBUG 1
#endif 

That should work if you define the macro before #include <assert.h>.
Beware any significant changes under #if NDEBUG:

struct shared {
  int i;
#ifndef NDEBUG
  int debug;
#endif
};

Copy link
Contributor

Choose a reason for hiding this comment

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

I am unsure that assert() is really "supported", in the sense that the behaviour of what is proposed here is undefined.

The macro __SYCL_DEVICE_ONLY__ is set by the SYCL device compiler.
Having the device compiler driver to also set NDEBUG would be abusive but would somehow solve a little bit of the "problem".
But you could always #define assert() in your sycl.hpp even after including "cassert".
Anyway all this looks messy because, well, this is outside of the scope of SYCL and kernels...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Having the device compiler driver to also set NDEBUG would be abusive but would somehow solve a little bit of the "problem".
But you could always #define assert() in your sycl.hpp even after including "cassert".

There are two problems:

  1. This solution depends on order of #include directives: if you include assert.h after sycl.hpp, the macro will be redefined again.
#ifdef  _ASSERT_H

# undef _ASSERT_H
# undef assert
[...]
#endif /* assert.h      */
  1. Even if you manage to define the macro properly, there is still a problem of not having a proper abort function.

So we avoid this dance around __assert_fail and _wassert, but we still have to manage the devicelib assert extension.

Anyway all this looks messy because, well, this is outside of the scope of SYCL and kernels...

Agree.

});
});
deviceQueue.wait_and_throw();
}

Frontend
========

Once the system header is included, the corresponding functions can be
used in SYCL device code. This results in a handful of unresolved
functions in LLVM IR after clang:

.. code:
; Function Attrs: noreturn nounwind
declare dso_local spir_func void @__assert_fail(i8 addrspace(4)*, i8 addrspace(4)*, i32, i8 addrspace(4)*)

[...]
cond.false:
call spir_func void @__assert_fail([...])
unreachable

The C and C++ specifications do not define names and signatures of the
functions from libc implementation that are used for a particular
function. For example, the `assert` macro:

- in Glibc and musl libraries it expands to `__assert_fail`
- in MSVC library it expands to `_wassert`
- in newlib library it expands to `__assert_func`

This makes it difficult to handle all possible cases in device
compilers. In order to facilitate porting to new platforms, and to
avoid imposing a lot of boilerplate code in *every* device compiler,
wrapper libraries are provided with the SYCL compiler that "lower"
libc implementation-specific functions into a stable set of functions,
that can be later handled by a device compiler.

.. code:
clang++ -fsycl -c main.cpp -o main.o
clang++ -fsycl main.o $(SYCL_INSTALL)/lib/libsycl-glibc.o -o a.out
Copy link
Contributor

Choose a reason for hiding this comment

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

Two points:

  1. Do we really need a separate wrapper for each standard library implementation? I don't think I understand the problem with a boilerplate code in *every* device compiler. I think linker is able to import only necessary functions, so scattering "boilerplate" code into separate wrapper libraries seems unnecessary.
  2. If we use single device library, it can be linked by the driver implicitly, but even if we stick to existing proposal, driver is able to infer right library from the host triple.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(1) should be explained on line 77. Let me know if I should elaborate.
(2) can be done as a separate change in driver.

Copy link
Contributor

Choose a reason for hiding this comment

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

(1) I understand that we should have definitions for __assert_fail, _wassert and __assert_func to support Glibc, MSVC and newlib, but can we keep them all in the same library? What is the strategy for the symbols shared between different implementation (if any): e.g. Glibc and newlib?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(1) I understand that we should have definitions for __assert_fail, _wassert and __assert_func to support Glibc, MSVC and newlib, but can we keep them all in the same library?

This doesn't work for functions that have the same name, but different arguments (e.g. __assert_fail in glibc vs musl).

What is the strategy for the symbols shared between different implementation (if any): e.g. Glibc and newlib?

There are no shared symbols. If there is a function with identical name and signature, it will be duplicated in every wrapper library.

In any case, I anticipate the wrapper libraries to go away soon. @s-kanaev is working on a header-based approach to provide the wrappers.


This `libsycl-glibc.o` is one of these wrapper libraries: it provides
definitions for glibc specific library function, and these definitions
call the corresponding functions from `__devicelib_*` set of
functions.

For example, `__assert_fail` from IR above gets transformed into:
.. code:
; Function Attrs: noreturn nounwind
declare dso_local spir_func void @__devicelib_assert_fail(i8 addrspace(4)*, i8 addrspace(4)*, i32, i8 addrspace(4)*)

; Function Attrs: noreturn nounwind
define dso_local spir_func void @__assert_fail(i8 addrspace(4)*, i8 addrspace(4)*, i32, i8 addrspace(4)*) {
call spir_func void @__devicelib_assert_fail([...])
}

[...]
cond.false:
call spir_func void @__assert_fail([...])
unreachable

A single wrapper object provides function wrappers for *all* supported
library functions. Every supported C library implementation (MSVC or
glibc) has its own wrapper library object:

- libsycl-glibc.o
- libsycl-msvc.o

SPIR-V
======

Standard library functions are represented as external (import)
functions in SPIR-V:

.. code:
8 Decorate 67 LinkageAttributes "__devicelib_assert_fail" Import
...
2 Label 846
8 FunctionCall 63 864 67 855 857 863 859
1 Unreachable

Device compiler
===============

Device compiler is free to implement these `__devicelib_*` functions.
In order to indicate support for a particular set of functions,
underlying runtime have to support the corresponding OpenCL (PI)
extension. See ``DeviceLibExtensions.rst`` for a list of supported
functions and corresponding extensions.

Fallback implementation
=======================

If a device compiler does not indicate "native" support for a
particular function, a fallback library is linked at JIT time by the
SYCL Runtime. This library is distributed with the SYCL Runtime and
resides in the same directory as the `libsycl.so` or `sycl.dll`.

A fallback library is implemented as a device-agnostic SPIR-V program,
and it is supposed to work for any device that supports SPIR-V.

Every set of functions is implemented in a separate fallback
library. For example, a fallback for `cl_intel_devicelib_cassert`
extension is provided as `libsycl-fallback-cassert.spv`

NOTE that AOT compilation is not yet supported. Driver will have to
check for extension support and link the corresponding SPIR-V fallback
implementation, but this is not implemented yet.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Device library extensions
===================================

Device compiler that indicates support for a particular extension is
supposed to support *all* the corresponding functions.

cl_intel_devicelib_cassert
==========================

.. code:
void __devicelib_assert_fail(__generic const char *expr,
__generic const char *file,
int32_t line,
__generic const char *func,
size_t gid0, size_t gid1, size_t gid2,
size_t lid0, size_t lid1, size_t lid2);
Semantic:
the function is called when an assertion expression `expr` is false,
and it indicates that a program does not execute as expected.
The function should print a message containing the information
provided in the arguments. In addition to that, the function is free
to terminate the current kernel invocation.
Copy link
Contributor

Choose a reason for hiding this comment

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

And if not, what happens next?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It returns to the caller, and the kernel continues.

Copy link
Contributor

Choose a reason for hiding this comment

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

Even if it launch a nuclear missile that should have been avoided?

I would prefer the program not to compile at the first place!!!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd advise against using SYCL for anything that involves radioactive materials or weapons of mass destruction.

Copy link
Contributor

Choose a reason for hiding this comment

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

Even if it launch a nuclear missile that should have been avoided?

Wait, have you guarded this just by an assertion? Not the best idea, IMO


Arguments:

- `expr` is a string representation of the assertion condition
- `file` and `line` are the source code location of the assertion
- `func` (optional, may be NULL) name of the function containing the assertion
- `gidX` current work-item global id
- `lidX` current work-item local id
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if there is no such things (single_task and specific FPGA mode where the concepts are not even implemented in hardware)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Global and local IDs should always be available, if we talk about standard OpenCL or SPIR-V.
For a single work-item kernel, get_global_id and get_local_id should return 0. If you don't have these concepts in hardware, I guess you should treat these builtins as compile time constant.


Example of a message:
.. code:
foo.cpp:42: void foo(int): global id: [0,0,0], local id: [0,0,0] Assertion `buf[wiID] == 0 && "Invalid value"` failed.
25 changes: 25 additions & 0 deletions sycl/include/CL/sycl/detail/context_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <CL/sycl/detail/os_util.hpp>
#include <CL/sycl/detail/pi.hpp>
#include <CL/sycl/detail/platform_impl.hpp>
#include <CL/sycl/detail/program_manager/program_manager.hpp>
#include <CL/sycl/detail/usm_dispatch.hpp>
#include <CL/sycl/exception_list.hpp>
#include <CL/sycl/info/info_desc.hpp>
Expand Down Expand Up @@ -103,6 +104,12 @@ class context_impl {
/// @return an instance of raw plug-in context handle.
const RT::PiContext &getHandleRef() const;

/// Unlike `get_info<info::context::devices>', this function returns a
/// reference.
const vector_class<device> &getDevices() const {
return MDevices;
}

/// Gets cached programs.
///
/// @return a map of cached programs.
Expand All @@ -122,6 +129,23 @@ class context_impl {
///
/// @return a pointer to USM dispatcher.
std::shared_ptr<usm::USMDispatcher> getUSMDispatch() const;

/// In contrast to user programs, which are compiled from user code, library
/// programs come from the SYCL runtime. They are identified by the
/// corresponding extension:
///
/// cl_intel_devicelib_assert -> #<pi_program with assert functions>
/// cl_intel_devicelib_complex -> #<pi_program with complex functions>
/// etc.
///
/// See `doc/extensions/C-CXX-StandardLibrary/DeviceLibExtensions.rst' for
/// more details.
///
/// @returns a map with device library programs.
std::map<DeviceLibExt, RT::PiProgram> &getCachedLibPrograms() {
return MCachedLibPrograms;
}

private:
async_handler MAsyncHandler;
vector_class<device> MDevices;
Expand All @@ -132,6 +156,7 @@ class context_impl {
std::map<KernelSetId, RT::PiProgram> MCachedPrograms;
std::map<RT::PiProgram, std::map<string_class, RT::PiKernel>> MCachedKernels;
std::shared_ptr<usm::USMDispatcher> MUSMDispatch;
std::map<DeviceLibExt, RT::PiProgram> MCachedLibPrograms;
};

} // namespace detail
Expand Down
3 changes: 2 additions & 1 deletion sycl/include/CL/sycl/detail/pi.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ typedef enum {
PI_DEVICE_INFO_PARTITION_TYPE = CL_DEVICE_PARTITION_TYPE,
PI_DEVICE_INFO_NAME = CL_DEVICE_NAME,
PI_DEVICE_VERSION = CL_DEVICE_VERSION,
PI_DEVICE_MAX_WORK_GROUP_SIZE = CL_DEVICE_MAX_WORK_GROUP_SIZE
PI_DEVICE_MAX_WORK_GROUP_SIZE = CL_DEVICE_MAX_WORK_GROUP_SIZE,
PI_DEVICE_INFO_EXTENSIONS = CL_DEVICE_EXTENSIONS
} _pi_device_info;

// TODO: populate
Expand Down
13 changes: 11 additions & 2 deletions sycl/include/CL/sycl/detail/program_manager/program_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ using DeviceImage = pi_device_binary_struct;
// be attempted to de-allocate.
struct ImageDeleter;

enum DeviceLibExt {
cl_intel_devicelib_assert = 0
};

// Provides single loading and building OpenCL programs with unique contexts
// that is necessary for no interoperability cases with lambda.
class ProgramManager {
Expand Down Expand Up @@ -70,8 +74,13 @@ class ProgramManager {

DeviceImage &getDeviceImage(OSModuleHandle M, KernelSetId KSId,
const context &Context);
void build(RT::PiProgram Program, const string_class &Options,
std::vector<RT::PiDevice> Devices);
using ProgramPtr = unique_ptr_class<remove_pointer_t<RT::PiProgram>,
decltype(&::piProgramRelease)>;
ProgramPtr build(ProgramPtr Program, RT::PiContext Context,
const string_class &Options,
const std::vector<RT::PiDevice> &Devices,
std::map<DeviceLibExt, RT::PiProgram> &CachedLibPrograms,
bool LinkDeviceLibs = false);
/// Provides a new kernel set id for grouping kernel names together
KernelSetId getNextKernelSetId() const;
/// Returns the kernel set associated with the kernel, handles some special
Expand Down
6 changes: 6 additions & 0 deletions sycl/include/CL/sycl/device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ class device {

template <class Obj>
friend decltype(Obj::impl) detail::getSyclObjImpl(const Obj &SyclObject);

template <class T>
friend
typename std::add_pointer<typename decltype(T::impl)::element_type>::type
detail::getRawSyclObjImpl(const T &SyclObject);

template <class T>
friend T detail::createSyclObjFromImpl(decltype(T::impl) ImplObj);
};
Expand Down
1 change: 1 addition & 0 deletions sycl/source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,4 @@ install(TARGETS ${SYCL_RT_LIBS}
ARCHIVE DESTINATION "lib" COMPONENT sycl
LIBRARY DESTINATION "lib" COMPONENT sycl
RUNTIME DESTINATION "bin" COMPONENT sycl)
add_subdirectory(detail/devicelib)
4 changes: 4 additions & 0 deletions sycl/source/detail/context_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ context_impl::~context_impl() {
PI_CALL(piKernelRelease)(KernIt.second);
PI_CALL(piProgramRelease)(ToBeDeleted);
}
for (auto LibProg : MCachedLibPrograms) {
assert(LibProg.second && "Null program must not be kept in the cache");
PI_CALL(piProgramRelease)(LibProg.second);
}
}

const async_handler &context_impl::get_async_handler() const {
Expand Down
66 changes: 66 additions & 0 deletions sycl/source/detail/devicelib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Place device libraries near the libsycl.so library in a build
# directory
if (WIN32)
set(binary_dir "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
else()
set(binary_dir "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
endif()

set(clang $<TARGET_FILE:clang>)

set(compile_opts
# suppress an error about SYCL_EXTERNAL
-Wno-sycl-strict
# for CL/__spirv/spirv_vars.hpp
-I${sycl_inc_dir})

if (WIN32)
set(devicelib-obj-file ${binary_dir}/libsycl-msvc.o)
add_custom_command(OUTPUT ${devicelib-obj-file}
COMMAND ${clang} -fsycl -c
${compile_opts}
${CMAKE_CURRENT_SOURCE_DIR}/msvc_wrapper.cpp
-o ${devicelib-obj-file}
MAIN_DEPENDENCY msvc_wrapper.cpp
DEPENDS wrapper.h clang
VERBATIM)
else()
set(devicelib-obj-file ${binary_dir}/libsycl-glibc.o)
add_custom_command(OUTPUT ${devicelib-obj-file}
COMMAND ${clang} -fsycl -c
${compile_opts}
${CMAKE_CURRENT_SOURCE_DIR}/glibc_wrapper.cpp
-o ${devicelib-obj-file}
MAIN_DEPENDENCY glibc_wrapper.cpp
DEPENDS wrapper.h clang
VERBATIM)
endif()

add_custom_command(OUTPUT ${binary_dir}/libsycl-fallback-cassert.spv
COMMAND ${clang} -S -fsycl-device-only -fno-sycl-use-bitcode
${compile_opts}
${CMAKE_CURRENT_SOURCE_DIR}/fallback-cassert.cpp
-o ${binary_dir}/libsycl-fallback-cassert.spv
MAIN_DEPENDENCY fallback-cassert.cpp
DEPENDS wrapper.h clang llvm-spirv
VERBATIM)

add_custom_target(devicelib-obj DEPENDS ${devicelib-obj-file})
add_custom_target(devicelib-spv DEPENDS ${binary_dir}/libsycl-fallback-cassert.spv)
add_dependencies(sycl devicelib-obj devicelib-spv)
if (MSVC)
add_dependencies(sycld devicelib-obj devicelib-spv)
endif()

# Place device libraries near the libsycl.so library in an install
# directory as well
if (WIN32)
set(install_dest bin)
else()
set(install_dest lib)
endif()

install(FILES ${devicelib-obj-file}
${binary_dir}/libsycl-fallback-cassert.spv
DESTINATION ${install_dest}
COMPONENT sycl)
Loading