[libsycl] add operators to sycl::range and sycl::id (#203572)

This PR was assisted by GH Copilot (tests extension).

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova@intel.com>
This commit is contained in:
Kseniya Tikhomirova 2026-06-25 16:43:05 +02:00 committed by GitHub
parent 84c42fe09e
commit 0bb2b00bee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 481 additions and 35 deletions

View file

@ -126,7 +126,7 @@ TODO for added SYCL classes
* handle sub devices once they are implemented (blocked by liboffload support)
* ``event``: get_wait_list, get_info, get_profiling_info, wait_and_throw & default ctor are not implemented
* ``range``, ``id`` - to add operators
* ``range``, ``id`` - __SYCL_DISABLE_ID_TO_INT_CONV__ and __SYCL_ASSUME_ID_RANGE optimizations are not implemented
* general opens:
* define a way to report errors from object dtors

View file

@ -27,36 +27,40 @@ namespace detail {
class Builder;
template <typename N, typename T>
using IntegralType = std::enable_if_t<std::is_integral_v<N>, T>;
/// Helper class for dimensions data management.
template <int Dimensions = 1> class RawArray {
template <typename Derived, int Dimensions> class IndexSpaceBase {
static_assert(Dimensions >= 1 && Dimensions <= 3,
"RawArray can only be 1, 2, or 3 Dimensional.");
"IndexSpaceBase can only be 1, 2, or 3 Dimensional.");
public:
/// Constructs a one-dimensional instance and assigns the corresponding data
/// to Dim0 value. Available only if Dimensions = 1.
template <int N = Dimensions, std::enable_if_t<N == 1, bool> = true>
RawArray(size_t Dim0 = 0) : MArray{Dim0} {}
IndexSpaceBase(size_t Dim0 = 0) : MArray{Dim0} {}
/// Constructs a two-dimensional instance and assigns the corresponding data.
/// Available only if Dimensions = 2.
template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
RawArray(size_t Dim0, size_t Dim1) : MArray{Dim0, Dim1} {}
IndexSpaceBase(size_t Dim0, size_t Dim1) : MArray{Dim0, Dim1} {}
/// Constructs a two-dimensional instance with the zero-initialized
/// corresponding data. Available only if Dimensions = 2.
template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
RawArray() : RawArray(0, 0) {}
IndexSpaceBase() : IndexSpaceBase(0, 0) {}
/// Constructs a three-dimensional instance and assigns the corresponding
/// data. Available only if Dimensions = 3.
template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
RawArray(size_t Dim0, size_t Dim1, size_t Dim2) : MArray{Dim0, Dim1, Dim2} {}
IndexSpaceBase(size_t Dim0, size_t Dim1, size_t Dim2)
: MArray{Dim0, Dim1, Dim2} {}
/// Constructs a three-dimensional instance with the zero-initialized
/// corresponding data. Available only if Dimensions = 3.
template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
RawArray() : RawArray(0, 0, 0) {}
IndexSpaceBase() : IndexSpaceBase(0, 0, 0) {}
/// Returns the value for the specified dimension.
/// Results in undefined behavior if dimension is not in the range [0,
@ -81,14 +85,16 @@ public:
return MArray[Dimension];
}
RawArray(const RawArray<Dimensions> &rhs) = default;
RawArray(RawArray<Dimensions> &&rhs) = default;
RawArray<Dimensions> &operator=(const RawArray<Dimensions> &rhs) = default;
RawArray<Dimensions> &operator=(RawArray<Dimensions> &&rhs) = default;
~RawArray() = default;
IndexSpaceBase(const IndexSpaceBase<Derived, Dimensions> &rhs) = default;
IndexSpaceBase(IndexSpaceBase<Derived, Dimensions> &&rhs) = default;
IndexSpaceBase<Derived, Dimensions> &
operator=(const IndexSpaceBase<Derived, Dimensions> &rhs) = default;
IndexSpaceBase<Derived, Dimensions> &
operator=(IndexSpaceBase<Derived, Dimensions> &&rhs) = default;
~IndexSpaceBase() = default;
friend bool operator==(const RawArray<Dimensions> &lhs,
const RawArray<Dimensions> &rhs) {
friend bool operator==(const IndexSpaceBase<Derived, Dimensions> &lhs,
const IndexSpaceBase<Derived, Dimensions> &rhs) {
for (int i = 0; i < Dimensions; ++i) {
if (lhs.MArray[i] != rhs.MArray[i]) {
return false;
@ -97,14 +103,127 @@ public:
return true;
}
friend bool operator!=(const RawArray<Dimensions> &lhs,
const RawArray<Dimensions> &rhs) {
friend bool operator!=(const IndexSpaceBase<Derived, Dimensions> &lhs,
const IndexSpaceBase<Derived, Dimensions> &rhs) {
return !(lhs == rhs);
}
#define _LIBSYCL_GEN_OPT(op) \
friend Derived operator op(const Derived &lhs, \
const Derived &rhs) noexcept { \
Derived result; \
for (int i = 0; i < Dimensions; ++i) { \
result.MArray[i] = lhs.MArray[i] op rhs.MArray[i]; \
} \
return result; \
} \
\
template <typename T> \
friend IntegralType<T, Derived> operator op(const Derived &lhs, \
const T &rhs) noexcept { \
Derived result; \
for (int i = 0; i < Dimensions; ++i) { \
result.MArray[i] = lhs.MArray[i] op rhs; \
} \
return result; \
} \
\
template <typename T> \
friend IntegralType<T, Derived> operator op(const T &lhs, \
const Derived &rhs) noexcept { \
Derived result; \
for (int i = 0; i < Dimensions; ++i) { \
result.MArray[i] = lhs op rhs.MArray[i]; \
} \
return result; \
}
_LIBSYCL_GEN_OPT(+)
_LIBSYCL_GEN_OPT(-)
_LIBSYCL_GEN_OPT(*)
_LIBSYCL_GEN_OPT(/)
_LIBSYCL_GEN_OPT(%)
_LIBSYCL_GEN_OPT(<<)
_LIBSYCL_GEN_OPT(>>)
_LIBSYCL_GEN_OPT(&)
_LIBSYCL_GEN_OPT(|)
_LIBSYCL_GEN_OPT(^)
_LIBSYCL_GEN_OPT(&&)
_LIBSYCL_GEN_OPT(||)
_LIBSYCL_GEN_OPT(<)
_LIBSYCL_GEN_OPT(>)
_LIBSYCL_GEN_OPT(<=)
_LIBSYCL_GEN_OPT(>=)
#undef _LIBSYCL_GEN_OPT
#define _LIBSYCL_GEN_OPT(op) \
friend Derived &operator op(Derived &lhs, const Derived &rhs) noexcept { \
for (int i = 0; i < Dimensions; ++i) { \
lhs.MArray[i] op rhs[i]; \
} \
return lhs; \
} \
template <typename T> \
friend IntegralType<T, Derived> &operator op(Derived &lhs, \
const T &rhs) noexcept { \
for (int i = 0; i < Dimensions; ++i) { \
lhs.MArray[i] op rhs; \
} \
return lhs; \
}
_LIBSYCL_GEN_OPT(+=)
_LIBSYCL_GEN_OPT(-=)
_LIBSYCL_GEN_OPT(*=)
_LIBSYCL_GEN_OPT(/=)
_LIBSYCL_GEN_OPT(%=)
_LIBSYCL_GEN_OPT(<<=)
_LIBSYCL_GEN_OPT(>>=)
_LIBSYCL_GEN_OPT(&=)
_LIBSYCL_GEN_OPT(|=)
_LIBSYCL_GEN_OPT(^=)
#undef _LIBSYCL_GEN_OPT
#define _LIBSYCL_GEN_OPT(op) \
friend Derived operator op(const Derived &rhs) noexcept { \
Derived result; \
for (int i = 0; i < Dimensions; ++i) { \
result.MArray[i] = (op rhs.MArray[i]); \
} \
return result; \
}
_LIBSYCL_GEN_OPT(+)
_LIBSYCL_GEN_OPT(-)
#undef _LIBSYCL_GEN_OPT
#define _LIBSYCL_GEN_OPT(op) \
friend Derived &operator op(Derived &rhs) noexcept { \
for (int i = 0; i < Dimensions; ++i) { \
op rhs.MArray[i]; \
} \
return rhs; \
} \
friend Derived operator op(Derived &lhs, int) noexcept { \
Derived oldLhs(lhs); \
for (int i = 0; i < Dimensions; ++i) { \
op lhs.MArray[i]; \
} \
return oldLhs; \
}
_LIBSYCL_GEN_OPT(++)
_LIBSYCL_GEN_OPT(--)
#undef _LIBSYCL_GEN_OPT
protected:
size_t MArray[Dimensions];
};
} // namespace detail
/// SYCL 2020 4.9.1.1. range class.
@ -112,10 +231,10 @@ protected:
/// domain of either a single work-group in a parallel dispatch, or the overall
/// Dimensions of the dispatch.
template <int Dimensions = 1>
class range : public detail::RawArray<Dimensions> {
class range : public detail::IndexSpaceBase<range<Dimensions>, Dimensions> {
static_assert(Dimensions >= 1 && Dimensions <= 3,
"range can only be 1-, 2-, or 3-dimensional.");
using Base = detail::RawArray<Dimensions>;
using Base = detail::IndexSpaceBase<range<Dimensions>, Dimensions>;
public:
static constexpr int dimensions = Dimensions;
@ -142,7 +261,7 @@ public:
: Base(dim0, dim1, dim2) {}
/*
Declared and implemented in detail::RawArray:
Declared and implemented in detail::IndexSpaceBase:
std::size_t get(int dimension) const noexcept;
std::size_t& operator[](int dimension) noexcept;
std::size_t operator[](int dimension) const noexcept;
@ -156,8 +275,6 @@ public:
}
return size;
}
// TODO: operators to be added
};
/// c++ deduction guides.
@ -173,10 +290,11 @@ template <int Dimensions = 1, bool WithOffset = true> class item;
/// id<int Dimensions> is a vector of Dimensions that is used to represent an id
/// into a global or local range. It can be used as an index in an accessor of
/// the same rank.
template <int Dimensions = 1> class id : public detail::RawArray<Dimensions> {
template <int Dimensions = 1>
class id : public detail::IndexSpaceBase<id<Dimensions>, Dimensions> {
static_assert(Dimensions >= 1 && Dimensions <= 3,
"id can only be 1-, 2-, or 3-dimensional.");
using Base = detail::RawArray<Dimensions>;
using Base = detail::IndexSpaceBase<id<Dimensions>, Dimensions>;
// Helper class for conversion operator. Void type is not suitable. User
// cannot even try to get address of the operator PrivateTag(). User
@ -230,39 +348,67 @@ public:
/// Constructs an id from item.get_id().
/// Only valid when the template parameter Dimensions is equal to 1.
template <int N = Dimensions, std::enable_if_t<N == 1, bool> = true>
id(const item<Dimensions> &item) noexcept : Base(item.get_id(0)) {}
template <int N = Dimensions, bool WithOffset = true,
std::enable_if_t<N == 1, bool> = true>
id(const item<Dimensions, WithOffset> &item) noexcept
: Base(item.get_id(0)) {}
/// Constructs an id from item.get_id().
/// Only valid when the template parameter Dimensions is equal to 2.
template <int N = Dimensions, std::enable_if_t<N == 2, bool> = true>
id(const item<Dimensions> &item) noexcept
template <int N = Dimensions, bool WithOffset = true,
std::enable_if_t<N == 2, bool> = true>
id(const item<Dimensions, WithOffset> &item) noexcept
: Base(item.get_id(0), item.get_id(1)) {}
/// Constructs an id from item.get_id().
/// Only valid when the template parameter Dimensions is equal to 3.
template <int N = Dimensions, std::enable_if_t<N == 3, bool> = true>
id(const item<Dimensions> &item) noexcept
template <int N = Dimensions, bool WithOffset = true,
std::enable_if_t<N == 3, bool> = true>
id(const item<Dimensions, WithOffset> &item) noexcept
: Base(item.get_id(0), item.get_id(1), item.get_id(2)) {}
/*
Declared and implemented in detail::RawArray:
Declared and implemented in detail::IndexSpaceBase:
std::size_t get(int dimension) const noexcept;
std::size_t& operator[](int dimension) noexcept;
std::size_t operator[](int dimension) const noexcept;
*/
// Template operator is not allowed because it disables further type
// conversion. For example, the next code will not work in case of template
// conversion:
// int a = id<1>(value);
// conversion. For example, the next code will not work in case of template
// conversion: int a = id<1>(value);
/// Returns the same value as get(0).
/// Available only when: Dimensions == 1.
operator EnableIfT<(Dimensions == 1), std::size_t>() const noexcept {
return Base::get(0);
}
// TODO: operators to be added
// These operators are not a part of SYCL 2020 spec but are needed to avoid
// ambiguity in case of implicit conversion id<1> vs size_t. Template operators
// take precedence over type conversion. In the case of non-template operators,
// ambiguity appears: "id op size_t" may refer "size_t op size_t" or "id op
// size_t". In the case of template operators it will be "id op size_t".
#define _LIBSYCL_GEN_OPT(op) \
template <typename T, int N = Dimensions, \
std::enable_if_t<N == 1, bool> = true> \
detail::IntegralType<T, bool> operator op(const T &rhs) const noexcept { \
if (this->MArray[0] != rhs) \
return false op true; \
return true op true; \
} \
template <typename T, int N = Dimensions, \
std::enable_if_t<N == 1, bool> = true> \
friend detail::IntegralType<T, bool> operator op( \
const T &lhs, const id<dimensions> &rhs) noexcept { \
if (lhs != rhs.MArray[0]) \
return false op true; \
return true op true; \
}
_LIBSYCL_GEN_OPT(==)
_LIBSYCL_GEN_OPT(!=)
#undef _LIBSYCL_GEN_OPT
};
/// c++ deduction guides.

View file

@ -0,0 +1,284 @@
// REQUIRES: any-device
// RUN: %clangxx -fsycl %s -o %t.out
// RUN: %t.out
//
// Unified test for sycl::range and sycl::id covering all operators defined in
// theirs base class, plus class-specific behaviour for each type.
#include <cassert>
#include <sycl/sycl.hpp>
#include <type_traits>
using sycl::detail::Builder;
// Helper to create values for any dimension from three components.
// Only uses the required number of values based on dimension.
template <template <int> class T, int Dim>
T<Dim> makeValue(std::size_t a, std::size_t b = 0, std::size_t c = 0) {
if constexpr (Dim == 1)
return T<1>(a);
else if constexpr (Dim == 2)
return T<2>(a, b);
else
return T<3>(a, b, c);
}
// Tests binary and compound operators for a specific dimension.
template <template <int> class T, int Dim> void testBinaryOpsForDim() {
const T<Dim> P = makeValue<T, Dim>(8, 9, 10);
const T<Dim> Q = makeValue<T, Dim>(2, 3, 5);
// Binary operators: object op object.
assert((P + Q) == (makeValue<T, Dim>(10, 12, 15)));
assert((P - Q) == (makeValue<T, Dim>(6, 6, 5)));
assert((P * Q) == (makeValue<T, Dim>(16, 27, 50)));
assert((P / Q) == (makeValue<T, Dim>(4, 3, 2)));
assert((P % Q) == (makeValue<T, Dim>(0, 0, 0)));
assert((P << Q) == (makeValue<T, Dim>(32, 72, 320)));
assert((P >> Q) == (makeValue<T, Dim>(2, 1, 0)));
assert((P & Q) == (makeValue<T, Dim>(0, 1, 0)));
assert((P | Q) == (makeValue<T, Dim>(10, 11, 15)));
assert((P ^ Q) == (makeValue<T, Dim>(10, 10, 15)));
assert((P && Q) == (makeValue<T, Dim>(1, 1, 1)));
assert((P || Q) == (makeValue<T, Dim>(1, 1, 1)));
assert((P < Q) == (makeValue<T, Dim>(0, 0, 0)));
assert((P > Q) == (makeValue<T, Dim>(1, 1, 1)));
assert((P <= Q) == (makeValue<T, Dim>(0, 0, 0)));
assert((P >= Q) == (makeValue<T, Dim>(1, 1, 1)));
// Binary operators: object op scalar.
assert((P + 1) == (makeValue<T, Dim>(9, 10, 11)));
assert((P - 1) == (makeValue<T, Dim>(7, 8, 9)));
assert((P * 2) == (makeValue<T, Dim>(16, 18, 20)));
assert((P / 2) == (makeValue<T, Dim>(4, 4, 5)));
assert((P % 4) == (makeValue<T, Dim>(0, 1, 2)));
assert((P << 1) == (makeValue<T, Dim>(16, 18, 20)));
assert((P >> 1) == (makeValue<T, Dim>(4, 4, 5)));
assert((P & 6) == (makeValue<T, Dim>(0, 0, 2)));
assert((P | 1) == (makeValue<T, Dim>(9, 9, 11)));
assert((P ^ 3) == (makeValue<T, Dim>(11, 10, 9)));
assert((P && 0) == (makeValue<T, Dim>(0, 0, 0)));
assert((P || 0) == (makeValue<T, Dim>(1, 1, 1)));
assert((P < 9) == (makeValue<T, Dim>(1, 0, 0)));
assert((P > 9) == (makeValue<T, Dim>(0, 0, 1)));
assert((P <= 9) == (makeValue<T, Dim>(1, 1, 0)));
assert((P >= 9) == (makeValue<T, Dim>(0, 1, 1)));
// Binary operators: scalar op object.
assert((1 + P) == (makeValue<T, Dim>(9, 10, 11)));
assert((20 - Q) == (makeValue<T, Dim>(18, 17, 15)));
assert((2 * P) == (makeValue<T, Dim>(16, 18, 20)));
assert((20 / Q) == (makeValue<T, Dim>(10, 6, 4)));
assert((33 % Q) == (makeValue<T, Dim>(1, 0, 3)));
assert((1 << Q) == (makeValue<T, Dim>(4, 8, 32)));
assert((256 >> Q) == (makeValue<T, Dim>(64, 32, 8)));
assert((15 & Q) == (makeValue<T, Dim>(2, 3, 5)));
assert((1 | Q) == (makeValue<T, Dim>(3, 3, 5)));
assert((3 ^ Q) == (makeValue<T, Dim>(1, 0, 6)));
assert((1 && Q) == (makeValue<T, Dim>(1, 1, 1)));
assert((0 || Q) == (makeValue<T, Dim>(1, 1, 1)));
assert((9 < P) == (makeValue<T, Dim>(0, 0, 1)));
assert((9 > P) == (makeValue<T, Dim>(1, 0, 0)));
assert((9 <= P) == (makeValue<T, Dim>(0, 1, 1)));
assert((9 >= P) == (makeValue<T, Dim>(1, 1, 0)));
// Compound assignment: object op= object.
T<Dim> E = makeValue<T, Dim>(8, 9, 10);
E += Q;
assert(E == (makeValue<T, Dim>(10, 12, 15)));
E -= Q;
assert(E == (makeValue<T, Dim>(8, 9, 10)));
E *= Q;
assert(E == (makeValue<T, Dim>(16, 27, 50)));
E /= Q;
assert(E == (makeValue<T, Dim>(8, 9, 10)));
E %= Q;
assert(E == (makeValue<T, Dim>(0, 0, 0)));
// Compound assignment: object op= scalar.
T<Dim> F = makeValue<T, Dim>(8, 9, 10);
F += 2;
assert(F == (makeValue<T, Dim>(10, 11, 12)));
F -= 2;
assert(F == (makeValue<T, Dim>(8, 9, 10)));
F *= 2;
assert(F == (makeValue<T, Dim>(16, 18, 20)));
F /= 2;
assert(F == (makeValue<T, Dim>(8, 9, 10)));
F %= 6;
assert(F == (makeValue<T, Dim>(2, 3, 4)));
F <<= 1;
assert(F == (makeValue<T, Dim>(4, 6, 8)));
F >>= 1;
assert(F == (makeValue<T, Dim>(2, 3, 4)));
F &= 6;
assert(F == (makeValue<T, Dim>(2, 2, 4)));
F |= 1;
assert(F == (makeValue<T, Dim>(3, 3, 5)));
F ^= 2;
assert(F == (makeValue<T, Dim>(1, 1, 7)));
}
template <template <int> class T> void testCommon() {
// Default construction zero-initialises every dimension.
T<1> Def1;
T<2> Def2;
T<3> Def3;
assert(Def1[0] == 0);
assert(Def2[0] == 0 && Def2[1] == 0);
assert(Def3[0] == 0 && Def3[1] == 0 && Def3[2] == 0);
// Dimensional constructors and element access via get() and const [].
T<1> A1(5);
T<2> A2(3, 7);
T<3> A3(8, 9, 10);
assert(A1.get(0) == 5 && A1[0] == 5);
assert(A2.get(0) == 3 && A2.get(1) == 7);
assert(A3.get(0) == 8 && A3.get(1) == 9 && A3.get(2) == 10);
// dimensions static constexpr.
static_assert(T<1>::dimensions == 1);
static_assert(T<2>::dimensions == 2);
static_assert(T<3>::dimensions == 3);
// Non-const operator[] write.
T<2> Mutable(1, 2);
Mutable[0] = 10;
Mutable[1] = 20;
assert(Mutable[0] == 10 && Mutable[1] == 20);
// Copy constructor and copy assignment.
T<3> CopyCtor(A3);
assert(CopyCtor == A3);
T<3> CopyAssign(1, 1, 1);
CopyAssign = A3;
assert(CopyAssign == A3);
// Move constructor and move assignment.
T<3> MoveSrc1(A3);
T<3> MoveCtor(std::move(MoveSrc1));
assert(MoveCtor == A3);
T<3> MoveSrc2(A3);
T<3> MoveAssign(1, 1, 1);
MoveAssign = std::move(MoveSrc2);
assert(MoveAssign == A3);
// Equality and inequality between same-type objects.
assert((makeValue<T, 3>(8, 9, 10)) == (makeValue<T, 3>(8, 9, 10)));
assert((makeValue<T, 3>(8, 9, 10)) != (makeValue<T, 3>(8, 9, 11)));
// Test binary and compound operators for all dimensions.
testBinaryOpsForDim<T, 1>();
testBinaryOpsForDim<T, 2>();
testBinaryOpsForDim<T, 3>();
// Unary + and -.
T<3> U(8, 9, 10);
assert((+U) == (makeValue<T, 3>(8, 9, 10)));
assert((-U) == (makeValue<T, 3>(static_cast<std::size_t>(-8),
static_cast<std::size_t>(-9),
static_cast<std::size_t>(-10))));
// Pre- and post-increment / decrement.
T<3> Inc(8, 9, 10);
assert((++Inc) == (makeValue<T, 3>(9, 10, 11)));
assert((Inc++) == (makeValue<T, 3>(9, 10, 11)));
assert(Inc == (makeValue<T, 3>(10, 11, 12)));
assert((--Inc) == (makeValue<T, 3>(9, 10, 11)));
assert((Inc--) == (makeValue<T, 3>(9, 10, 11)));
assert(Inc == (makeValue<T, 3>(8, 9, 10)));
}
void testRange() {
testCommon<sycl::range>();
assert(sycl::range<1>(7).size() == 7);
assert(sycl::range<2>(4, 5).size() == 20);
assert(sycl::range<3>(2, 3, 4).size() == 24);
assert(sycl::range<3>(0, 5, 3).size() == 0);
#ifdef __cpp_deduction_guides
auto R1 = sycl::range(7);
auto R2 = sycl::range(4, 5);
auto R3 = sycl::range(2, 3, 4);
static_assert(std::is_same_v<decltype(R1), sycl::range<1>>);
static_assert(std::is_same_v<decltype(R2), sycl::range<2>>);
static_assert(std::is_same_v<decltype(R3), sycl::range<3>>);
assert(R1 == sycl::range<1>(7));
assert(R2 == sycl::range<2>(4, 5));
assert(R3 == sycl::range<3>(2, 3, 4));
#endif
}
void testId() {
testCommon<sycl::id>();
// Construction from range.
assert(sycl::id<1>(sycl::range<1>(2)) == sycl::id<1>(2));
assert(sycl::id<2>(sycl::range<2>(4, 8)) == sycl::id<2>(4, 8));
assert(sycl::id<3>(sycl::range<3>(16, 32, 64)) == sycl::id<3>(16, 32, 64));
// Construction from item (WithOffset=true) preserves the computed id
// coordinates.
sycl::item<1, true> Item1 = Builder::createItem<1, true>({4}, {2}, {1});
sycl::item<2, true> Item2 =
Builder::createItem<2, true>({8, 16}, {4, 8}, {1, 1});
sycl::item<3, true> Item3 =
Builder::createItem<3, true>({32, 64, 128}, {16, 32, 64}, {1, 1, 1});
assert(sycl::id<1>(Item1) == sycl::id<1>(2));
assert(sycl::id<2>(Item2) == sycl::id<2>(4, 8));
assert(sycl::id<3>(Item3) == sycl::id<3>(16, 32, 64));
// Construction from item (WithOffset=false).
sycl::item<1, false> Item1NoOffset = Builder::createItem<1, false>({4}, {2});
sycl::item<2, false> Item2NoOffset =
Builder::createItem<2, false>({8, 16}, {4, 8});
sycl::item<3, false> Item3NoOffset =
Builder::createItem<3, false>({32, 64, 128}, {16, 32, 64});
assert(sycl::id<1>(Item1NoOffset) == sycl::id<1>(2));
assert(sycl::id<2>(Item2NoOffset) == sycl::id<2>(4, 8));
assert(sycl::id<3>(Item3NoOffset) == sycl::id<3>(16, 32, 64));
// Copy-initialization from item must also select id(item) constructor.
sycl::id<1> IdFromItem1NoOffset = Item1NoOffset;
sycl::id<2> IdFromItem2NoOffset = Item2NoOffset;
sycl::id<3> IdFromItem3NoOffset = Item3NoOffset;
assert(IdFromItem1NoOffset == sycl::id<1>(2));
assert(IdFromItem2NoOffset == sycl::id<2>(4, 8));
assert(IdFromItem3NoOffset == sycl::id<3>(16, 32, 64));
#ifdef __cpp_deduction_guides
auto I1 = sycl::id(2);
auto I2 = sycl::id(4, 8);
auto I3 = sycl::id(16, 32, 64);
static_assert(std::is_same_v<decltype(I1), sycl::id<1>>);
static_assert(std::is_same_v<decltype(I2), sycl::id<2>>);
static_assert(std::is_same_v<decltype(I3), sycl::id<3>>);
assert(I1 == sycl::id<1>(2));
assert(I2 == sycl::id<2>(4, 8));
assert(I3 == sycl::id<3>(16, 32, 64));
#endif
// Implicit conversion to size_t (id<1> only).
sycl::id<1> OneDim(16);
std::size_t S = OneDim;
int I = OneDim;
assert(S == 16 && I == 16);
// Scalar equality/inequality operators (id<1> only).
const sycl::id<1> ConstId(10);
assert(ConstId == 10);
assert(10 == ConstId);
assert(ConstId != 19);
assert(19 != ConstId);
assert(ConstId == 10u);
assert(10u == ConstId);
assert(ConstId != static_cast<short>(9));
assert(static_cast<short>(9) != ConstId);
}
int main() {
testRange();
testId();
return 0;
}

View file

@ -0,0 +1,16 @@
// RUN: %clangxx -fsycl %s -fsyntax-only -Xclang -verify \
// RUN: -Xclang -verify-ignore-unexpected=note
#include <sycl/sycl.hpp>
void testInvalidDimensionGatedAPI() {
sycl::id<2> Id2(1, 2);
std::size_t ScalarFromId2 = Id2; // expected-error {{no viable conversion}}
(void)ScalarFromId2;
sycl::id<2> BadId2(7); // expected-error {{no matching constructor}}
sycl::range<3> BadRange3(1, 2); // expected-error {{no matching constructor}}
(void)BadId2;
(void)BadRange3;
}