Skip to content

Commit c2ebf84

Browse files
[SYCL] Make vec conversion operator to scalar non-template (#14668)
Template vs. non-template conversion operator behave differently for ``` sycl::vec<int, 1> v; std::ignore = static_cast<bool>(v); ``` where non-template path selects the conversion operator and then uses implicit `int->bool` conversion while the example fails to compile when operator is a template. This is important because the spec requires `vec::x()` and other swizzles to return a swizzle class and not a `const DataT &` that it currently does. However, if we'd fix the swizzle without addressing this issues first the code like ``` sycl::vec<int, 8> v = {...}; if (v.x() == 42) ... ``` would fail to compile. Also, the spec is explicit that this conversion operator must not be `template`: > The SYCL vec instance shall be implicitly convertible to the same data types, > to which DataT is implicitly convertible. Note that conversion operator shall > not be templated to allow standard conversion sequence for implicit conversion.
1 parent 53a8960 commit c2ebf84

File tree

12 files changed

+131
-99
lines changed

12 files changed

+131
-99
lines changed

sycl/include/sycl/aliases.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
namespace sycl {
1717
inline namespace _V1 {
18-
template <typename T, int N> class vec;
18+
template <typename T, int N> class __SYCL_EBO vec;
1919
namespace detail::half_impl {
2020
class half;
2121
} // namespace detail::half_impl

sycl/include/sycl/detail/common.hpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,6 @@ class __SYCL_EXPORT tls_code_loc_t {
167167

168168
#include <sycl/exception.hpp>
169169

170-
// Helper for enabling empty-base optimizations on MSVC.
171-
// TODO: Remove this when MSVC has this optimization enabled by default.
172-
#ifdef _MSC_VER
173-
#define __SYCL_EBO __declspec(empty_bases)
174-
#else
175-
#define __SYCL_EBO
176-
#endif
177-
178170
namespace sycl {
179171
inline namespace _V1 {
180172
namespace detail {

sycl/include/sycl/detail/defines_elementary.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@
3535
#endif
3636
#endif
3737

38+
// Helper for enabling empty-base optimizations on MSVC.
39+
// TODO: Remove this when MSVC has this optimization enabled by default.
40+
#ifdef _MSC_VER
41+
#define __SYCL_EBO __declspec(empty_bases)
42+
#else
43+
#define __SYCL_EBO
44+
#endif
45+
46+
3847
#ifndef __SYCL_ID_QUERIES_FIT_IN_INT__
3948
#define __SYCL_ID_QUERIES_FIT_IN_INT__ 0
4049
#endif

sycl/include/sycl/detail/generic_type_lists.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
// Forward declarations
2121
namespace sycl {
2222
inline namespace _V1 {
23-
template <typename T, int N> class vec;
23+
template <typename T, int N> class __SYCL_EBO vec;
2424
template <typename Type, std::size_t NumElements> class marray;
2525

2626
namespace detail {

sycl/include/sycl/detail/vector_arith.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
namespace sycl {
2222
inline namespace _V1 {
2323

24-
template <typename DataT, int NumElem> class vec;
24+
template <typename DataT, int NumElem> class __SYCL_EBO vec;
2525

2626
namespace detail {
2727

@@ -287,7 +287,7 @@ class vec_arith : public vec_arith_common<DataT, NumElements> {
287287
__SYCL_BINOP(<<, <<=, true, (!detail::is_vgenfloat_v<DataT>))
288288

289289
// friends
290-
template <typename T1, int T2> friend class vec;
290+
template <typename T1, int T2> friend class __SYCL_EBO vec;
291291
}; // class vec_arith<>
292292

293293
#if (!defined(_HAS_STD_BYTE) || _HAS_STD_BYTE != 0)
@@ -340,7 +340,7 @@ class vec_arith<std::byte, NumElements>
340340
__SYCL_BINOP(^, ^=, false, true)
341341

342342
// friends
343-
template <typename T1, int T2> friend class vec;
343+
template <typename T1, int T2> friend class __SYCL_EBO vec;
344344
};
345345
#endif // (!defined(_HAS_STD_BYTE) || _HAS_STD_BYTE != 0)
346346

@@ -383,7 +383,7 @@ template <typename DataT, int NumElements> class vec_arith_common {
383383
#endif
384384

385385
// friends
386-
template <typename T1, int T2> friend class vec;
386+
template <typename T1, int T2> friend class __SYCL_EBO vec;
387387
};
388388

389389
#undef __SYCL_BINOP

sycl/include/sycl/sub_group.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ inline namespace _V1 {
2727
template <typename T, access::address_space Space,
2828
access::decorated DecorateAddress>
2929
class multi_ptr;
30-
template <typename Type, int NumElements> class vec;
30+
template <typename Type, int NumElements> class __SYCL_EBO vec;
3131
namespace detail {
3232

3333
namespace sub_group {

sycl/include/sycl/vector_preview.hpp

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,38 @@ template <typename T> class GetOp {
9797
DataT operator()(DataT, DataT) { return (DataT)0; }
9898
};
9999

100+
// Templated vs. non-templated conversion operator behaves differently when two
101+
// conversions are needed as in the case below:
102+
//
103+
// sycl::vec<int, 1> v;
104+
// std::ignore = static_cast<bool>(v);
105+
//
106+
// Make sure the snippet above compiles. That is important because
107+
//
108+
// sycl::vec<int, 2> v;
109+
// if (v.x() == 42)
110+
// ...
111+
//
112+
// must go throw `v.x()` returning a swizzle, then its `operator==` returning
113+
// vec<int, 1> and we want that code to compile.
114+
template <typename Vec, typename T, int N, typename = void>
115+
struct ScalarConversionOperatorMixIn {};
116+
117+
template <typename Vec, typename T, int N>
118+
struct ScalarConversionOperatorMixIn<Vec, T, N, std::enable_if_t<N == 1>> {
119+
operator T() const { return (*static_cast<const Vec *>(this))[0]; }
120+
};
121+
100122
} // namespace detail
101123

102124
///////////////////////// class sycl::vec /////////////////////////
103125
// Provides a cross-platform vector class template that works efficiently on
104126
// SYCL devices as well as in host C++ code.
105127
template <typename DataT, int NumElements>
106-
class vec : public detail::vec_arith<DataT, NumElements> {
128+
class __SYCL_EBO vec
129+
: public detail::vec_arith<DataT, NumElements>,
130+
public detail::ScalarConversionOperatorMixIn<vec<DataT, NumElements>,
131+
DataT, NumElements> {
107132

108133
static_assert(NumElements == 1 || NumElements == 2 || NumElements == 3 ||
109134
NumElements == 4 || NumElements == 8 || NumElements == 16,
@@ -373,12 +398,6 @@ class vec : public detail::vec_arith<DataT, NumElements> {
373398
operator vector_t() const { return sycl::bit_cast<vector_t>(m_Data); }
374399
#endif // __SYCL_DEVICE_ONLY__
375400

376-
// Available only when: NumElements == 1
377-
template <int N = NumElements>
378-
operator typename std::enable_if_t<N == 1, DataT>() const {
379-
return m_Data[0];
380-
}
381-
382401
__SYCL2020_DEPRECATED("get_count() is deprecated, please use size() instead")
383402
static constexpr size_t get_count() { return size(); }
384403
static constexpr size_t size() noexcept { return NumElements; }
@@ -605,7 +624,7 @@ class vec : public detail::vec_arith<DataT, NumElements> {
605624
template <typename T1, typename T2, typename T3, template <typename> class T4,
606625
int... T5>
607626
friend class detail::SwizzleOp;
608-
template <typename T1, int T2> friend class vec;
627+
template <typename T1, int T2> friend class __SYCL_EBO vec;
609628
// To allow arithmetic operators access private members of vec.
610629
template <typename T1, int T2> friend class detail::vec_arith;
611630
template <typename T1, int T2> friend class detail::vec_arith_common;

sycl/test/abi/layout_vec.cpp

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,20 @@
1010

1111
SYCL_EXTERNAL void foo(sycl::vec<int, 4>) {}
1212

13-
// CHECK: 0 | class sycl::vec<int, 4>
14-
// CHECK: 0 | class sycl::detail::vec_arith<int, 4> (base) (empty)
15-
// CHECK-NEXT: 0 | class sycl::detail::vec_arith_common<int, 4> (base) (empty)
16-
// CHECK-NEXT: 0 | struct std::array<int, 4> m_Data
13+
// CHECK: 0 | class sycl::vec<int, 4>
14+
// ignore empty base classes
15+
// CHECK: 0 | struct std::array<int, 4> m_Data
1716
// CHECK-NEXT: 0 | typename _AT_Type::_Type _M_elems
18-
// CHECK-NEXT: | [sizeof=16, dsize=16, align=16,
19-
// CHECK-NEXT: | nvsize=16, nvalign=16]
17+
// CHECK-NEXT: | [sizeof=16, dsize=16, align=16,
18+
// CHECK-NEXT: | nvsize=16, nvalign=16]
2019

2120
//--------------------------------------
2221

2322
SYCL_EXTERNAL void foo(sycl::vec<bool, 16>) {}
2423

25-
// CHECK: 0 | class sycl::vec<_Bool, 16>
26-
// CHECK: 0 | class sycl::detail::vec_arith<_Bool, 16> (base) (empty)
27-
// CHECK-NEXT: 0 | class sycl::detail::vec_arith_common<_Bool, 16> (base) (empty)
28-
// CHECK-NEXT: 0 | struct std::array<_Bool, 16> m_Data
24+
// CHECK: 0 | class sycl::vec<_Bool, 16>
25+
// ignore empty base classes
26+
// CHECK: 0 | struct std::array<_Bool, 16> m_Data
2927
// CHECK-NEXT: 0 | typename _AT_Type::_Type _M_elems
30-
// CHECK-NEXT: | [sizeof=16, dsize=16, align=16,
31-
// CHECK-NEXT: | nvsize=16, nvalign=16]
28+
// CHECK-NEXT: | [sizeof=16, dsize=16, align=16,
29+
// CHECK-NEXT: | nvsize=16, nvalign=16]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %clangxx -fsycl %s -o %t_default.out
2+
// RUN: %t_default.out
3+
// RUN: %if preview-breaking-changes-supported %{ %clangxx -fsycl -fpreview-breaking-changes %s -o %t_vec.out %}
4+
// RUN: %if preview-breaking-changes-supported %{ %t_vec.out %}
5+
6+
#include <sycl/types.hpp>
7+
8+
int main() {
9+
sycl::vec<int, 1> v1{42};
10+
sycl::vec<int, 1> v2{0};
11+
assert(static_cast<bool>(v1) == true);
12+
assert(static_cast<bool>(v2) == false);
13+
return 0;
14+
}

0 commit comments

Comments
 (0)