Skip to content

Commit 9a34a11

Browse files
authored
[SYCL] Preserve original message and code of kernel/program build result (#1108)
Signed-off-by: Sergey Kanaev <[email protected]>
1 parent 322d86e commit 9a34a11

File tree

3 files changed

+106
-23
lines changed

3 files changed

+106
-23
lines changed

sycl/include/CL/sycl/detail/kernel_program_cache.hpp

+19-6
Original file line numberDiff line numberDiff line change
@@ -25,29 +25,42 @@ namespace detail {
2525
class context_impl;
2626
class KernelProgramCache {
2727
public:
28-
/// Denotes pointer to some entity with its state.
28+
/// Denotes build error data. The data is filled in from cl::sycl::exception
29+
/// class instance.
30+
struct BuildError {
31+
std::string Msg;
32+
pi_int32 Code;
33+
34+
/// Equals to true if the Msg and Code are initialized. This flag is added
35+
/// due to the possibility of error code being equal to zero even in case
36+
/// if build is failed and cl::sycl::exception is thrown.
37+
bool FilledIn;
38+
};
39+
40+
/// Denotes pointer to some entity with its general state and build error.
2941
/// The pointer is not null if and only if the entity is usable.
3042
/// State of the entity is provided by the user of cache instance.
3143
/// Currently there is only a single user - ProgramManager class.
3244
template<typename T>
33-
struct EntityWithState {
45+
struct BuildResult {
3446
std::atomic<T *> Ptr;
3547
std::atomic<int> State;
48+
BuildError Error;
3649

37-
EntityWithState(T* P, int S)
38-
: Ptr{P}, State{S}
50+
BuildResult(T* P, int S)
51+
: Ptr{P}, State{S}, Error{"", 0, false}
3952
{}
4053
};
4154

4255
using PiProgramT = std::remove_pointer<RT::PiProgram>::type;
4356
using PiProgramPtrT = std::atomic<PiProgramT *>;
44-
using ProgramWithBuildStateT = EntityWithState<PiProgramT>;
57+
using ProgramWithBuildStateT = BuildResult<PiProgramT>;
4558
using ProgramCacheT = std::map<OSModuleHandle, ProgramWithBuildStateT>;
4659
using ContextPtr = context_impl *;
4760

4861
using PiKernelT = std::remove_pointer<RT::PiKernel>::type;
4962
using PiKernelPtrT = std::atomic<PiKernelT *>;
50-
using KernelWithBuildStateT = EntityWithState<PiKernelT>;
63+
using KernelWithBuildStateT = BuildResult<PiKernelT>;
5164
using KernelByNameT = std::map<string_class, KernelWithBuildStateT>;
5265
using KernelCacheT = std::map<RT::PiProgram, KernelByNameT>;
5366

sycl/source/detail/program_manager/program_manager.cpp

+40-17
Original file line numberDiff line numberDiff line change
@@ -110,22 +110,22 @@ DeviceImage &ProgramManager::getDeviceImage(OSModuleHandle M,
110110
}
111111

112112
template <typename ExceptionT, typename RetT>
113-
RetT *
114-
waitUntilBuilt(KernelProgramCache &Cache,
115-
KernelProgramCache::EntityWithState<RetT> *WithBuildState) {
113+
RetT *waitUntilBuilt(KernelProgramCache &Cache,
114+
KernelProgramCache::BuildResult<RetT> *BuildResult) {
116115
// any thread which will find nullptr in cache will wait until the pointer
117116
// is not null anymore
118-
Cache.waitUntilBuilt([WithBuildState]() {
119-
int State = WithBuildState->State.load();
117+
Cache.waitUntilBuilt([BuildResult]() {
118+
int State = BuildResult->State.load();
120119

121120
return State == BS_Done || State == BS_Failed;
122121
});
123122

124-
RetT *Result = WithBuildState->Ptr.load();
123+
if (BuildResult->Error.FilledIn) {
124+
const KernelProgramCache::BuildError &Error = BuildResult->Error;
125+
throw ExceptionT(Error.Msg, Error.Code);
126+
}
125127

126-
if (!Result)
127-
throw ExceptionT("The other thread tried to build the program/kernel but "
128-
"did not succeed.");
128+
RetT *Result = BuildResult->Ptr.load();
129129

130130
return Result;
131131
}
@@ -152,7 +152,7 @@ template <typename RetT, typename ExceptionT, typename KeyT, typename AcquireFT,
152152
RetT *getOrBuild(KernelProgramCache &KPCache, const KeyT &CacheKey,
153153
AcquireFT &&Acquire, GetCacheFT &&GetCache, BuildFT &&Build) {
154154
bool InsertionTookPlace;
155-
KernelProgramCache::EntityWithState<RetT> *WithState;
155+
KernelProgramCache::BuildResult<RetT> *BuildResult;
156156

157157
{
158158
auto LockedCache = Acquire(KPCache);
@@ -162,36 +162,59 @@ RetT *getOrBuild(KernelProgramCache &KPCache, const KeyT &CacheKey,
162162
std::forward_as_tuple(nullptr, BS_InProgress));
163163

164164
InsertionTookPlace = Inserted.second;
165-
WithState = &Inserted.first->second;
165+
BuildResult = &Inserted.first->second;
166166
}
167167

168168
// no insertion took place, thus some other thread has already inserted smth
169169
// in the cache
170170
if (!InsertionTookPlace) {
171-
return waitUntilBuilt<ExceptionT>(KPCache, WithState);
171+
for (;;) {
172+
RetT *Result = waitUntilBuilt<ExceptionT>(KPCache, BuildResult);
173+
174+
if (Result)
175+
return Result;
176+
177+
// Previous build is failed. There was no SYCL exception though.
178+
// We might try to build once more.
179+
int Expected = BS_Failed;
180+
int Desired = BS_InProgress;
181+
182+
if (BuildResult->State.compare_exchange_strong(Expected, Desired))
183+
break; // this thread is the building thread now
184+
}
172185
}
173186

174-
// only the building thread will run this, and only once.
187+
// only the building thread will run this
175188
try {
176189
RetT *Desired = Build();
177190

178191
#ifndef NDEBUG
179192
RetT *Expected = nullptr;
180193

181-
if (!WithState->Ptr.compare_exchange_strong(Expected, Desired))
194+
if (!BuildResult->Ptr.compare_exchange_strong(Expected, Desired))
182195
// We've got a funny story here
183196
assert(false && "We've build an entity that is already have been built.");
184197
#else
185-
WithState->Ptr.store(Desired);
198+
BuildResult->Ptr.store(Desired);
186199
#endif
187200

188-
WithState->State.store(BS_Done);
201+
BuildResult->State.store(BS_Done);
189202

190203
KPCache.notifyAllBuild();
191204

192205
return Desired;
206+
} catch (const exception &Ex) {
207+
BuildResult->Error.Msg = Ex.what();
208+
BuildResult->Error.Code = Ex.get_cl_code();
209+
BuildResult->Error.FilledIn = true;
210+
211+
BuildResult->State.store(BS_Failed);
212+
213+
KPCache.notifyAllBuild();
214+
215+
std::rethrow_exception(std::current_exception());
193216
} catch (...) {
194-
WithState->State.store(BS_Failed);
217+
BuildResult->State.store(BS_Failed);
195218

196219
KPCache.notifyAllBuild();
197220

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %clangxx -fsycl %s -o %t.out
2+
// RUN: %CPU_RUN_PLACEHOLDER %t.out
3+
4+
#include <CL/sycl.hpp>
5+
6+
SYCL_EXTERNAL
7+
void undefined();
8+
9+
void test() {
10+
cl::sycl::queue Queue;
11+
12+
auto Kernel = []() {
13+
#ifdef __SYCL_DEVICE_ONLY__
14+
undefined();
15+
#endif
16+
};
17+
18+
std::string Msg;
19+
int Result;
20+
21+
for (int Idx = 0; Idx < 2; ++Idx) {
22+
try {
23+
Queue.submit([&](cl::sycl::handler &CGH) {
24+
CGH.single_task<class SingleTask>(Kernel);
25+
});
26+
assert(false && "There must be compilation error");
27+
} catch (const cl::sycl::compile_program_error &e) {
28+
fprintf(stderr, "Exception: %s, %d\n", e.what(), e.get_cl_code());
29+
if (Idx == 0) {
30+
Msg = e.what();
31+
Result = e.get_cl_code();
32+
} else {
33+
// Exception constantly adds info on its error code in the message
34+
assert(Msg.find_first_of(e.what()) == 0 && "Exception text differs");
35+
assert(Result == e.get_cl_code() && "Exception code differs");
36+
}
37+
} catch (...) {
38+
assert(false && "There must be cl::sycl::compile_program_error");
39+
}
40+
}
41+
}
42+
43+
int main() {
44+
test();
45+
46+
return 0;
47+
}

0 commit comments

Comments
 (0)