Skip to content

Commit b9b8513

Browse files
[SYCL] Fix weak_object and owner_less for device objects (#8740)
This commit fixes an issue where weak_object and in turn owner_less would fail to construct for SYCL object types that are usable on device. This was due to these object types only having an impl on host. As a result the kernel compilation would fail to get the impl of these. Since weak_object isn't intended for use inside kernels, this was fixed by using a dummy weak_ptr during kernel compilation to make it act like it is containing a weak pointer. Previously this was not found in unittests because they are not compiled with the SYCL compiler. To avoid this breaking in the future, these tests are moved to the test-suite. --------- Signed-off-by: Larsen, Steffen <[email protected]>
1 parent 70871a8 commit b9b8513

15 files changed

+530
-436
lines changed

sycl/include/sycl/accessor.hpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ class __SYCL_EXPORT LocalAccessorBaseHost {
535535

536536
protected:
537537
template <class Obj>
538-
friend decltype(Obj::impl) getSyclObjImpl(const Obj &SyclObject);
538+
friend decltype(Obj::impl) detail::getSyclObjImpl(const Obj &SyclObject);
539539

540540
template <class T>
541541
friend T detail::createSyclObjFromImpl(decltype(T::impl) ImplObj);
@@ -1209,6 +1209,9 @@ class __SYCL_EBO __SYCL_SPECIAL_CLASS __SYCL_TYPE(accessor) accessor :
12091209
friend class sycl::stream;
12101210
friend class sycl::ext::intel::esimd::detail::AccessorPrivateProxy;
12111211

1212+
template <class Obj>
1213+
friend decltype(Obj::impl) detail::getSyclObjImpl(const Obj &SyclObject);
1214+
12121215
template <class T>
12131216
friend T detail::createSyclObjFromImpl(decltype(T::impl) ImplObj);
12141217

@@ -2528,6 +2531,9 @@ class __SYCL_SPECIAL_CLASS local_accessor_base :
25282531
return Result;
25292532
}
25302533

2534+
template <class Obj>
2535+
friend decltype(Obj::impl) detail::getSyclObjImpl(const Obj &SyclObject);
2536+
25312537
template <class T>
25322538
friend T detail::createSyclObjFromImpl(decltype(T::impl) ImplObj);
25332539

sycl/include/sycl/detail/owner_less_base.hpp

+7
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ template <class SyclObjT> class OwnerLessBase {
4242
return getSyclObjImpl(*static_cast<const SyclObjT *>(this))
4343
.owner_before(getSyclObjImpl(Other));
4444
}
45+
#else
46+
// On device calls to these functions are disallowed, so declare them but
47+
// don't define them to avoid compilation failures.
48+
bool ext_oneapi_owner_before(
49+
const ext::oneapi::detail::weak_object_base<SyclObjT> &Other)
50+
const noexcept;
51+
bool ext_oneapi_owner_before(const SyclObjT &Other) const noexcept;
4552
#endif
4653
};
4754

sycl/include/sycl/ext/oneapi/weak_object.hpp

+16-2
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,13 @@ class weak_object : public detail::weak_object_base<SYCLObjT> {
5050

5151
weak_object &operator=(const SYCLObjT &SYCLObj) noexcept {
5252
// Create weak_ptr from the shared_ptr to SYCLObj's implementation object.
53-
this->MObjWeakPtr = sycl::detail::getSyclObjImpl(SYCLObj);
53+
this->MObjWeakPtr = GetWeakImpl(SYCLObj);
5454
return *this;
5555
}
5656
weak_object &operator=(const weak_object &Other) noexcept = default;
5757
weak_object &operator=(weak_object &&Other) noexcept = default;
5858

59+
#ifndef __SYCL_DEVICE_ONLY__
5960
std::optional<SYCLObjT> try_lock() const noexcept {
6061
auto MObjImplPtr = this->MObjWeakPtr.lock();
6162
if (!MObjImplPtr)
@@ -69,6 +70,12 @@ class weak_object : public detail::weak_object_base<SYCLObjT> {
6970
"Referenced object has expired.");
7071
return *OptionalObj;
7172
}
73+
#else
74+
// On device calls to these functions are disallowed, so declare them but
75+
// don't define them to avoid compilation failures.
76+
std::optional<SYCLObjT> try_lock() const noexcept;
77+
SYCLObjT lock() const;
78+
#endif // __SYCL_DEVICE_ONLY__
7279
};
7380

7481
// Specialization of weak_object for buffer as it needs additional members
@@ -96,7 +103,7 @@ class weak_object<buffer<T, Dimensions, AllocatorT>>
96103

97104
weak_object &operator=(const buffer_type &SYCLObj) noexcept {
98105
// Create weak_ptr from the shared_ptr to SYCLObj's implementation object.
99-
this->MObjWeakPtr = sycl::detail::getSyclObjImpl(SYCLObj);
106+
this->MObjWeakPtr = GetWeakImpl(SYCLObj);
100107
this->MRange = SYCLObj.Range;
101108
this->MOffsetInBytes = SYCLObj.OffsetInBytes;
102109
this->MIsSubBuffer = SYCLObj.IsSubBuffer;
@@ -105,6 +112,7 @@ class weak_object<buffer<T, Dimensions, AllocatorT>>
105112
weak_object &operator=(const weak_object &Other) noexcept = default;
106113
weak_object &operator=(weak_object &&Other) noexcept = default;
107114

115+
#ifndef __SYCL_DEVICE_ONLY__
108116
std::optional<buffer_type> try_lock() const noexcept {
109117
auto MObjImplPtr = this->MObjWeakPtr.lock();
110118
if (!MObjImplPtr)
@@ -119,6 +127,12 @@ class weak_object<buffer<T, Dimensions, AllocatorT>>
119127
"Referenced object has expired.");
120128
return *OptionalObj;
121129
}
130+
#else
131+
// On device calls to these functions are disallowed, so declare them but
132+
// don't define them to avoid compilation failures.
133+
std::optional<buffer_type> try_lock() const noexcept;
134+
buffer_type lock() const;
135+
#endif // __SYCL_DEVICE_ONLY__
122136

123137
private:
124138
// Additional members required for recreating buffers.

sycl/include/sycl/ext/oneapi/weak_object_base.hpp

+19-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ template <typename SYCLObjT> class weak_object_base {
2929

3030
constexpr weak_object_base() noexcept : MObjWeakPtr() {}
3131
weak_object_base(const SYCLObjT &SYCLObj) noexcept
32-
: MObjWeakPtr(sycl::detail::getSyclObjImpl(SYCLObj)) {}
32+
: MObjWeakPtr(GetWeakImpl(SYCLObj)) {}
3333
weak_object_base(const weak_object_base &Other) noexcept = default;
3434
weak_object_base(weak_object_base &&Other) noexcept = default;
3535

@@ -43,19 +43,36 @@ template <typename SYCLObjT> class weak_object_base {
4343

4444
bool expired() const noexcept { return MObjWeakPtr.expired(); }
4545

46+
#ifndef __SYCL_DEVICE_ONLY__
4647
bool owner_before(const SYCLObjT &Other) const noexcept {
47-
return MObjWeakPtr.owner_before(sycl::detail::getSyclObjImpl(Other));
48+
return MObjWeakPtr.owner_before(GetWeakImpl(Other));
4849
}
4950
bool owner_before(const weak_object_base &Other) const noexcept {
5051
return MObjWeakPtr.owner_before(Other.MObjWeakPtr);
5152
}
53+
#else
54+
// On device calls to these functions are disallowed, so declare them but
55+
// don't define them to avoid compilation failures.
56+
bool owner_before(const SYCLObjT &Other) const noexcept;
57+
bool owner_before(const weak_object_base &Other) const noexcept;
58+
#endif // __SYCL_DEVICE_ONLY__
5259

5360
protected:
61+
#ifndef __SYCL_DEVICE_ONLY__
5462
// Store a weak variant of the impl in the SYCLObjT.
5563
typename std::invoke_result_t<
5664
decltype(sycl::detail::getSyclObjImpl<SYCLObjT>), SYCLObjT>::weak_type
5765
MObjWeakPtr;
5866

67+
static decltype(MObjWeakPtr) GetWeakImpl(const SYCLObjT &SYCLObj) {
68+
return sycl::detail::getSyclObjImpl(SYCLObj);
69+
}
70+
#else
71+
// On device we may not have an impl, so we pad with an unused void pointer.
72+
std::weak_ptr<void> MObjWeakPtr;
73+
static std::weak_ptr<void> GetWeakImpl(const SYCLObjT &) { return {}; }
74+
#endif // __SYCL_DEVICE_ONLY__
75+
5976
template <class Obj>
6077
friend decltype(weak_object_base<Obj>::MObjWeakPtr)
6178
detail::getSyclWeakObjImpl(const weak_object_base<Obj> &WeakObj);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out
2+
// RUN: %BE_RUN_PLACEHOLDER %t.out
3+
4+
// This test checks the behavior of the copy ctor and assignment operator for
5+
// `weak_object`.
6+
7+
#include "weak_object_utils.hpp"
8+
9+
template <typename SyclObjT> struct WeakObjectCheckCopy {
10+
void operator()(SyclObjT Obj) {
11+
sycl::ext::oneapi::weak_object<SyclObjT> WeakObj{Obj};
12+
13+
sycl::ext::oneapi::weak_object<SyclObjT> WeakObjCopyCtor{WeakObj};
14+
sycl::ext::oneapi::weak_object<SyclObjT> WeakObjCopyAssign = WeakObj;
15+
16+
assert(!WeakObjCopyCtor.expired());
17+
assert(!WeakObjCopyAssign.expired());
18+
19+
assert(WeakObjCopyCtor.lock() == Obj);
20+
assert(WeakObjCopyAssign.lock() == Obj);
21+
}
22+
};
23+
24+
int main() {
25+
sycl::queue Q;
26+
runTest<WeakObjectCheckCopy>(Q);
27+
return 0;
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out
2+
// RUN: %BE_RUN_PLACEHOLDER %t.out
3+
4+
// This test checks the behavior of `expired()` for `weak_object`.
5+
6+
#include "weak_object_utils.hpp"
7+
8+
template <typename SyclObjT> struct WeakObjectCheckExpired {
9+
void operator()(SyclObjT Obj) {
10+
sycl::ext::oneapi::weak_object<SyclObjT> WeakObj{Obj};
11+
sycl::ext::oneapi::weak_object<SyclObjT> NullWeakObj;
12+
13+
assert(!WeakObj.expired());
14+
assert(NullWeakObj.expired());
15+
}
16+
};
17+
18+
int main() {
19+
sycl::queue Q;
20+
runTest<WeakObjectCheckExpired>(Q);
21+
return 0;
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out
2+
// RUN: %BE_RUN_PLACEHOLDER %t.out
3+
4+
// This test checks the behavior of `lock()` for `weak_object`.
5+
6+
#include "weak_object_utils.hpp"
7+
8+
template <typename SyclObjT> struct WeakObjectCheckLock {
9+
void operator()(SyclObjT Obj) {
10+
sycl::ext::oneapi::weak_object<SyclObjT> WeakObj{Obj};
11+
sycl::ext::oneapi::weak_object<SyclObjT> NullWeakObj;
12+
13+
SyclObjT LObj = WeakObj.lock();
14+
assert(LObj == Obj);
15+
16+
try {
17+
SyclObjT LNull = NullWeakObj.lock();
18+
assert(false && "Locking empty weak object did not throw.");
19+
} catch (sycl::exception &E) {
20+
assert(E.code() == sycl::make_error_code(sycl::errc::invalid) &&
21+
"Unexpected thrown error code.");
22+
}
23+
}
24+
};
25+
26+
int main() {
27+
sycl::queue Q;
28+
runTest<WeakObjectCheckLock>(Q);
29+
return 0;
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out
2+
// RUN: %BE_RUN_PLACEHOLDER %t.out
3+
4+
// This test checks the behavior of the copy ctor and assignment operator for
5+
// `weak_object`.
6+
7+
#include "weak_object_utils.hpp"
8+
9+
template <typename SyclObjT> struct WeakObjectCheckMove {
10+
void operator()(SyclObjT Obj) {
11+
sycl::ext::oneapi::weak_object<SyclObjT> WeakObj1{Obj};
12+
sycl::ext::oneapi::weak_object<SyclObjT> WeakObj2{Obj};
13+
14+
sycl::ext::oneapi::weak_object<SyclObjT> WeakObjMoveCtor{
15+
std::move(WeakObj1)};
16+
sycl::ext::oneapi::weak_object<SyclObjT> WeakObjMoveAssign =
17+
std::move(WeakObj2);
18+
19+
assert(!WeakObjMoveCtor.expired());
20+
assert(!WeakObjMoveAssign.expired());
21+
22+
assert(WeakObjMoveCtor.lock() == Obj);
23+
assert(WeakObjMoveAssign.lock() == Obj);
24+
}
25+
};
26+
27+
int main() {
28+
sycl::queue Q;
29+
runTest<WeakObjectCheckMove>(Q);
30+
return 0;
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out
2+
// RUN: %BE_RUN_PLACEHOLDER %t.out
3+
4+
// This test checks the behavior of owner_before semantics for `weak_object`.
5+
6+
#include "weak_object_utils.hpp"
7+
8+
template <typename SyclObjT> struct WeakObjectCheckOwnerBefore {
9+
void operator()(SyclObjT Obj) {
10+
sycl::ext::oneapi::weak_object<SyclObjT> WeakObj{Obj};
11+
sycl::ext::oneapi::weak_object<SyclObjT> NullWeakObj;
12+
13+
assert((WeakObj.owner_before(NullWeakObj) &&
14+
!NullWeakObj.owner_before(WeakObj)) ||
15+
(NullWeakObj.owner_before(WeakObj) &&
16+
!WeakObj.owner_before(NullWeakObj)));
17+
18+
assert(!WeakObj.owner_before(Obj));
19+
assert(!Obj.ext_oneapi_owner_before(WeakObj));
20+
21+
assert(!Obj.ext_oneapi_owner_before(Obj));
22+
}
23+
};
24+
25+
template <typename SyclObjT> struct WeakObjectCheckOwnerBeforeMulti {
26+
void operator()(SyclObjT Obj1, SyclObjT Obj2) {
27+
sycl::ext::oneapi::weak_object<SyclObjT> WeakObj1{Obj1};
28+
sycl::ext::oneapi::weak_object<SyclObjT> WeakObj2{Obj2};
29+
30+
assert(
31+
(WeakObj1.owner_before(WeakObj2) && !WeakObj2.owner_before(WeakObj1)) ||
32+
(WeakObj2.owner_before(WeakObj1) && !WeakObj1.owner_before(WeakObj2)));
33+
34+
assert(!WeakObj1.owner_before(Obj1));
35+
assert(!Obj1.ext_oneapi_owner_before(WeakObj1));
36+
37+
assert(!WeakObj2.owner_before(Obj2));
38+
assert(!Obj2.ext_oneapi_owner_before(WeakObj2));
39+
40+
assert((Obj1.ext_oneapi_owner_before(Obj2) &&
41+
!Obj2.ext_oneapi_owner_before(Obj1)) ||
42+
(Obj2.ext_oneapi_owner_before(Obj1) &&
43+
!Obj1.ext_oneapi_owner_before(Obj2)));
44+
}
45+
};
46+
47+
int main() {
48+
sycl::queue Q;
49+
runTest<WeakObjectCheckOwnerBefore>(Q);
50+
runTestMulti<WeakObjectCheckOwnerBeforeMulti>(Q);
51+
return 0;
52+
}

0 commit comments

Comments
 (0)