diff --git a/sycl/include/CL/sycl/detail/kernel_program_cache.hpp b/sycl/include/CL/sycl/detail/kernel_program_cache.hpp index 470650d84f824..b0de93e6cf616 100644 --- a/sycl/include/CL/sycl/detail/kernel_program_cache.hpp +++ b/sycl/include/CL/sycl/detail/kernel_program_cache.hpp @@ -25,29 +25,42 @@ namespace detail { class context_impl; class KernelProgramCache { public: - /// Denotes pointer to some entity with its state. + /// Denotes build error data. The data is filled in from cl::sycl::exception + /// class instance. + struct BuildError { + std::string Msg; + pi_int32 Code; + + /// Equals to true if the Msg and Code are initialized. This flag is added + /// due to the possibility of error code being equal to zero even in case + /// if build is failed and cl::sycl::exception is thrown. + bool FilledIn; + }; + + /// Denotes pointer to some entity with its general state and build error. /// The pointer is not null if and only if the entity is usable. /// State of the entity is provided by the user of cache instance. /// Currently there is only a single user - ProgramManager class. template - struct EntityWithState { + struct BuildResult { std::atomic Ptr; std::atomic State; + BuildError Error; - EntityWithState(T* P, int S) - : Ptr{P}, State{S} + BuildResult(T* P, int S) + : Ptr{P}, State{S}, Error{"", 0, false} {} }; using PiProgramT = std::remove_pointer::type; using PiProgramPtrT = std::atomic; - using ProgramWithBuildStateT = EntityWithState; + using ProgramWithBuildStateT = BuildResult; using ProgramCacheT = std::map; using ContextPtr = context_impl *; using PiKernelT = std::remove_pointer::type; using PiKernelPtrT = std::atomic; - using KernelWithBuildStateT = EntityWithState; + using KernelWithBuildStateT = BuildResult; using KernelByNameT = std::map; using KernelCacheT = std::map; diff --git a/sycl/source/detail/program_manager/program_manager.cpp b/sycl/source/detail/program_manager/program_manager.cpp index 72971d16a70ad..5a4ef59b40051 100644 --- a/sycl/source/detail/program_manager/program_manager.cpp +++ b/sycl/source/detail/program_manager/program_manager.cpp @@ -110,22 +110,22 @@ DeviceImage &ProgramManager::getDeviceImage(OSModuleHandle M, } template -RetT * -waitUntilBuilt(KernelProgramCache &Cache, - KernelProgramCache::EntityWithState *WithBuildState) { +RetT *waitUntilBuilt(KernelProgramCache &Cache, + KernelProgramCache::BuildResult *BuildResult) { // any thread which will find nullptr in cache will wait until the pointer // is not null anymore - Cache.waitUntilBuilt([WithBuildState]() { - int State = WithBuildState->State.load(); + Cache.waitUntilBuilt([BuildResult]() { + int State = BuildResult->State.load(); return State == BS_Done || State == BS_Failed; }); - RetT *Result = WithBuildState->Ptr.load(); + if (BuildResult->Error.FilledIn) { + const KernelProgramCache::BuildError &Error = BuildResult->Error; + throw ExceptionT(Error.Msg, Error.Code); + } - if (!Result) - throw ExceptionT("The other thread tried to build the program/kernel but " - "did not succeed."); + RetT *Result = BuildResult->Ptr.load(); return Result; } @@ -152,7 +152,7 @@ template *WithState; + KernelProgramCache::BuildResult *BuildResult; { auto LockedCache = Acquire(KPCache); @@ -162,36 +162,59 @@ RetT *getOrBuild(KernelProgramCache &KPCache, const KeyT &CacheKey, std::forward_as_tuple(nullptr, BS_InProgress)); InsertionTookPlace = Inserted.second; - WithState = &Inserted.first->second; + BuildResult = &Inserted.first->second; } // no insertion took place, thus some other thread has already inserted smth // in the cache if (!InsertionTookPlace) { - return waitUntilBuilt(KPCache, WithState); + for (;;) { + RetT *Result = waitUntilBuilt(KPCache, BuildResult); + + if (Result) + return Result; + + // Previous build is failed. There was no SYCL exception though. + // We might try to build once more. + int Expected = BS_Failed; + int Desired = BS_InProgress; + + if (BuildResult->State.compare_exchange_strong(Expected, Desired)) + break; // this thread is the building thread now + } } - // only the building thread will run this, and only once. + // only the building thread will run this try { RetT *Desired = Build(); #ifndef NDEBUG RetT *Expected = nullptr; - if (!WithState->Ptr.compare_exchange_strong(Expected, Desired)) + if (!BuildResult->Ptr.compare_exchange_strong(Expected, Desired)) // We've got a funny story here assert(false && "We've build an entity that is already have been built."); #else - WithState->Ptr.store(Desired); + BuildResult->Ptr.store(Desired); #endif - WithState->State.store(BS_Done); + BuildResult->State.store(BS_Done); KPCache.notifyAllBuild(); return Desired; + } catch (const exception &Ex) { + BuildResult->Error.Msg = Ex.what(); + BuildResult->Error.Code = Ex.get_cl_code(); + BuildResult->Error.FilledIn = true; + + BuildResult->State.store(BS_Failed); + + KPCache.notifyAllBuild(); + + std::rethrow_exception(std::current_exception()); } catch (...) { - WithState->State.store(BS_Failed); + BuildResult->State.store(BS_Failed); KPCache.notifyAllBuild(); diff --git a/sycl/test/kernel-and-program/cache-build-result.cpp b/sycl/test/kernel-and-program/cache-build-result.cpp new file mode 100644 index 0000000000000..adf2bf2706d61 --- /dev/null +++ b/sycl/test/kernel-and-program/cache-build-result.cpp @@ -0,0 +1,47 @@ +// RUN: %clangxx -fsycl %s -o %t.out +// RUN: %CPU_RUN_PLACEHOLDER %t.out + +#include + +SYCL_EXTERNAL +void undefined(); + +void test() { + cl::sycl::queue Queue; + + auto Kernel = []() { +#ifdef __SYCL_DEVICE_ONLY__ + undefined(); +#endif + }; + + std::string Msg; + int Result; + + for (int Idx = 0; Idx < 2; ++Idx) { + try { + Queue.submit([&](cl::sycl::handler &CGH) { + CGH.single_task(Kernel); + }); + assert(false && "There must be compilation error"); + } catch (const cl::sycl::compile_program_error &e) { + fprintf(stderr, "Exception: %s, %d\n", e.what(), e.get_cl_code()); + if (Idx == 0) { + Msg = e.what(); + Result = e.get_cl_code(); + } else { + // Exception constantly adds info on its error code in the message + assert(Msg.find_first_of(e.what()) == 0 && "Exception text differs"); + assert(Result == e.get_cl_code() && "Exception code differs"); + } + } catch (...) { + assert(false && "There must be cl::sycl::compile_program_error"); + } + } +} + +int main() { + test(); + + return 0; +}