Skip to content

Commit b7654b9

Browse files
[ESIMD] Add writeable subsript operator for simd_view (#4384)
The primary goal for this patch is to allow writing through simd_view subscript operator, e.g.: ``` simd<int, 4> v = 1; auto vv = v.select<2, 1>(0); vv[0] = 42; ``` All the other changes are required to make that happen: 1. One more specialization for nested simd_view class with len=1. 2. Added explicit definition of BINOP for simd_view_impl with a scalar value because of the following code: ``` simd<ushort, 64> s = 0; auto g = s.bit_cast_view<ushort, 4, 16>(); auto x = g.row(1) - (g.row(1))[0]; ``` Here `g.row(1)` return simd_view and `(g.row(1))[0]` returns simd_view of size 1. To use any binops defined in simd_view_impl before this patch we would need to implicitly convert RHS to scalar and then convert scalar to vector type. Two implicit conversions are not allowed according to C++ language rules, hence new BINOP operator definition for simd_view_impl. 3. I also added additional template parameter for simd_view specialization for lenght=1 to support the following code: ``` simd<int, 1> s = 0; float f = s.bit_cast_view<float>(); ``` This code is OK. The result of bit_cast_view should be mapped to specialization of simd_view with length 1, so conversion to scalar is allowed. To enable such mapping, I fixed the definition of region_base_1. Previously we had zeros for StrideY and StrideX but in fact, we don't care about their values, they can take any values.
1 parent b4670ce commit b7654b9

File tree

5 files changed

+166
-11
lines changed

5 files changed

+166
-11
lines changed

sycl/include/sycl/ext/intel/experimental/esimd/detail/region.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ template <typename T, int SizeY, int StrideY, int SizeX, int StrideX>
5757
using region2d_t = region_base<true, T, SizeY, StrideY, SizeX, StrideX>;
5858

5959
// A region with a single element.
60-
template <typename T> using region_base_1 = region_base<false, T, 1, 0, 1, 0>;
60+
template <typename T, int StrideY, int StrideX>
61+
using region1d_scalar_t = region_base<false, T, 1, StrideY, 1, StrideX>;
6162

6263
// simd_view forward declaration.
6364
template <typename BaseTy, typename RegionTy> class simd_view;

sycl/include/sycl/ext/intel/experimental/esimd/detail/simd_view_impl.hpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,11 @@ class simd_view_impl {
213213
return ComputeTy(V2); \
214214
} \
215215
template <class T1 = Derived, class = std::enable_if_t<T1::length != 1>> \
216+
ESIMD_INLINE friend auto operator BINOP(const Derived &X, \
217+
const element_type &Y) { \
218+
return X BINOP(value_type) Y; \
219+
} \
220+
template <class T1 = Derived, class = std::enable_if_t<T1::length != 1>> \
216221
ESIMD_INLINE friend auto operator BINOP(const value_type &X, \
217222
const Derived &Y) { \
218223
using ComputeTy = detail::compute_type_t<value_type>; \
@@ -256,6 +261,11 @@ class simd_view_impl {
256261
return simd<element_type, length>(V2); \
257262
} \
258263
template <class T1 = Derived, class = std::enable_if_t<T1::length != 1>> \
264+
ESIMD_INLINE friend auto operator BITWISE_OP(const Derived &X, \
265+
const element_type &Y) { \
266+
return X BITWISE_OP(value_type) Y; \
267+
} \
268+
template <class T1 = Derived, class = std::enable_if_t<T1::length != 1>> \
259269
ESIMD_INLINE friend auto operator BITWISE_OP(const value_type &X, \
260270
const Derived &Y) { \
261271
static_assert(std::is_integral<element_type>(), "not integral type"); \
@@ -352,6 +362,21 @@ class simd_view_impl {
352362
return v[i];
353363
}
354364

365+
/// Return a writeable view of a single element.
366+
template <typename T = Derived,
367+
typename = sycl::detail::enable_if_t<T::is1D()>>
368+
auto operator[](int i) {
369+
return select<1, 0>(i);
370+
}
371+
372+
/// Return a writeable view of a single element.
373+
template <typename T = Derived,
374+
typename = sycl::detail::enable_if_t<T::is1D()>>
375+
__SYCL_DEPRECATED("use operator[] form.")
376+
auto operator()(int i) {
377+
return select<1, 0>(i);
378+
}
379+
355380
/// \name Replicate
356381
/// Replicate simd instance given a simd_view
357382
/// @{

sycl/include/sycl/ext/intel/experimental/esimd/simd_view.hpp

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@ class simd_view : public detail::simd_view_impl<BaseTy, RegionTy,
3636
using ShapeTy = typename shape_type<RegionTy>::type;
3737
static constexpr int length = ShapeTy::Size_x * ShapeTy::Size_y;
3838

39+
using element_type = typename ShapeTy::element_type;
40+
3941
/// The simd type if reading this simd_view object.
40-
using value_type = simd<typename ShapeTy::element_type, length>;
42+
using value_type = simd<element_type, length>;
4143

4244
private:
4345
simd_view(BaseTy &Base, RegionTy Region) : BaseClass(Base, Region) {}
@@ -79,6 +81,10 @@ class simd_view : public detail::simd_view_impl<BaseTy, RegionTy,
7981
mask_type_t<length> M(1); \
8082
return M & detail::convert<mask_type_t<length>>(R); \
8183
} \
84+
ESIMD_INLINE friend simd<uint16_t, length> operator RELOP( \
85+
const simd_view &X, const element_type &Y) { \
86+
return X RELOP(value_type) Y; \
87+
} \
8288
ESIMD_INLINE friend simd<uint16_t, length> operator RELOP( \
8389
const simd_view &X, const simd_view &Y) { \
8490
return (X RELOP Y.read()); \
@@ -95,6 +101,9 @@ class simd_view : public detail::simd_view_impl<BaseTy, RegionTy,
95101
};
96102

97103
/// This is a specialization of simd_view class with a single element.
104+
/// Objects of such a class are created in the following situation:
105+
/// simd<int, 4> v = 1;
106+
/// auto v1 = v[0];
98107
/// We allow implicit conversion to underlying type, e.g.:
99108
/// simd<int, 4> v = 1;
100109
/// int i = v[0];
@@ -103,38 +112,113 @@ class simd_view : public detail::simd_view_impl<BaseTy, RegionTy,
103112
/// bool b = v[0] > v[1] && v[2] < 42;
104113
///
105114
/// \ingroup sycl_esimd
106-
template <typename BaseTy>
107-
class simd_view<BaseTy, region_base_1<typename BaseTy::element_type>>
115+
template <typename BaseTy, typename T, int StrideY, int StrideX>
116+
class simd_view<BaseTy, region1d_scalar_t<T, StrideY, StrideX>>
108117
: public detail::simd_view_impl<
109-
BaseTy, region_base_1<typename BaseTy::element_type>,
110-
simd_view<BaseTy, region_base_1<typename BaseTy::element_type>>> {
118+
BaseTy, region1d_scalar_t<T, StrideY, StrideX>,
119+
simd_view<BaseTy, region1d_scalar_t<T, StrideY, StrideX>>> {
111120
template <typename, int> friend class simd;
112121
template <typename, typename, typename> friend class detail::simd_view_impl;
113122

114123
public:
115-
using RegionTy = region_base_1<typename BaseTy::element_type>;
124+
using RegionTy = region1d_scalar_t<T, StrideY, StrideX>;
116125
using BaseClass =
117126
detail::simd_view_impl<BaseTy, RegionTy, simd_view<BaseTy, RegionTy>>;
118127
using ShapeTy = typename shape_type<RegionTy>::type;
119128
static constexpr int length = ShapeTy::Size_x * ShapeTy::Size_y;
120129
static_assert(1 == length, "length of this view is not equal to 1");
121130
/// The element type of this class, which could be different from the element
122131
/// type of the base object type.
123-
using element_type = typename ShapeTy::element_type;
132+
using element_type = T;
124133

125134
private:
126135
simd_view(BaseTy &Base, RegionTy Region) : BaseClass(Base, Region) {}
127136
simd_view(BaseTy &&Base, RegionTy Region) : BaseClass(Base, Region) {}
128137

129138
public:
130-
operator element_type() const { return (*this)[0]; }
139+
operator element_type() const {
140+
const auto v = BaseClass::read();
141+
return v[0];
142+
}
131143

132144
using BaseClass::operator=;
133145

134146
#define DEF_RELOP(RELOP) \
135147
ESIMD_INLINE friend bool operator RELOP(const simd_view &X, \
136148
const simd_view &Y) { \
137149
return (element_type)X RELOP(element_type) Y; \
150+
} \
151+
template <typename T1, typename = sycl::detail::enable_if_t< \
152+
detail::is_esimd_scalar<T1>::value && \
153+
detail::is_vectorizable_v<T1>::value>> \
154+
ESIMD_INLINE friend bool operator RELOP(const simd_view &X, T1 Y) { \
155+
return (element_type)X RELOP Y; \
156+
}
157+
158+
DEF_RELOP(>)
159+
DEF_RELOP(>=)
160+
DEF_RELOP(<)
161+
DEF_RELOP(<=)
162+
DEF_RELOP(==)
163+
DEF_RELOP(!=)
164+
165+
#undef DEF_RELOP
166+
};
167+
168+
// TODO: remove code duplication in two class specializations for a simd_view
169+
// with a single element
170+
171+
/// This is a specialization of nested simd_view class with a single element.
172+
/// Objects of such a class are created in the following situation:
173+
/// simd<int, 4> v = 1;
174+
/// auto v1 = v.select<2, 1>(0);
175+
/// auto v2 = v1[0]; // simd_view of a nested region for a single element
176+
template <typename BaseTy, typename T, int StrideY, int StrideX,
177+
typename NestedRegion>
178+
class simd_view<BaseTy,
179+
std::pair<region1d_scalar_t<T, StrideY, StrideX>, NestedRegion>>
180+
: public detail::simd_view_impl<
181+
BaseTy,
182+
std::pair<region1d_scalar_t<T, StrideY, StrideX>, NestedRegion>,
183+
simd_view<BaseTy, std::pair<region1d_scalar_t<T, StrideY, StrideX>,
184+
NestedRegion>>> {
185+
template <typename, int> friend class simd;
186+
template <typename, typename, typename> friend class detail::simd_view_impl;
187+
188+
public:
189+
using RegionTy =
190+
std::pair<region1d_scalar_t<T, StrideY, StrideX>, NestedRegion>;
191+
using BaseClass =
192+
detail::simd_view_impl<BaseTy, RegionTy, simd_view<BaseTy, RegionTy>>;
193+
using ShapeTy = typename shape_type<RegionTy>::type;
194+
static constexpr int length = ShapeTy::Size_x * ShapeTy::Size_y;
195+
static_assert(1 == length, "length of this view is not equal to 1");
196+
/// The element type of this class, which could be different from the element
197+
/// type of the base object type.
198+
using element_type = T;
199+
200+
private:
201+
simd_view(BaseTy &Base, RegionTy Region) : BaseClass(Base, Region) {}
202+
simd_view(BaseTy &&Base, RegionTy Region) : BaseClass(Base, Region) {}
203+
204+
public:
205+
operator element_type() const {
206+
const auto v = BaseClass::read();
207+
return v[0];
208+
}
209+
210+
using BaseClass::operator=;
211+
212+
#define DEF_RELOP(RELOP) \
213+
ESIMD_INLINE friend bool operator RELOP(const simd_view &X, \
214+
const simd_view &Y) { \
215+
return (element_type)X RELOP(element_type) Y; \
216+
} \
217+
template <typename T1, typename = sycl::detail::enable_if_t< \
218+
detail::is_esimd_scalar<T1>::value && \
219+
detail::is_vectorizable_v<T1>::value>> \
220+
ESIMD_INLINE friend bool operator RELOP(const simd_view &X, T1 Y) { \
221+
return (element_type)X RELOP Y; \
138222
}
139223

140224
DEF_RELOP(>)

sycl/test/esimd/esimd-util-compiler-eval.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ static_assert(log2<1024 * 1024>() == 20, "");
1919

2020
using BaseTy = simd<float, 4>;
2121
using RegionTy = region1d_t<float, 2, 1>;
22-
using RegionTy1 = region_base_1<float>;
22+
using RegionTy1 = region1d_scalar_t<float, 0, 0>;
2323
static_assert(
2424
!is_simd_view_v<
2525
simd_view_impl<BaseTy, RegionTy, simd_view<BaseTy, RegionTy>>>::value,

sycl/test/esimd/simd_view.cpp

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,55 @@ void test_simd_view_impl_api_ret_types() SYCL_ESIMD_FUNCTION {
142142

143143
void test_simd_view_subscript() SYCL_ESIMD_FUNCTION {
144144
simd<int, 4> v = 1;
145-
auto vv = v.select<2, 1>(0);
145+
const auto vv = v.select<2, 1>(0);
146146

147147
int x = vv[1];
148148
// expected-warning@+2 2 {{deprecated}}
149149
// expected-note@sycl/ext/intel/experimental/esimd/detail/simd_view_impl.hpp:* 2 {{has been explicitly marked deprecated here}}
150150
int y = vv(1);
151151
}
152+
153+
void test_simd_view_writeable_subscript() SYCL_ESIMD_FUNCTION {
154+
simd<int, 4> v = 1;
155+
auto vv1 = v.select<2, 1>(0);
156+
auto vv2 = v.select<2, 1>(0);
157+
auto x = vv1 == vv2; // test relational operations
158+
vv1[1] = 0; // returns writeable simd_view
159+
int y = vv1[1]; // nested simd_view -> int
160+
161+
// expected-warning@+2 2 {{deprecated}}
162+
// expected-note@sycl/ext/intel/experimental/esimd/detail/simd_view_impl.hpp:* 2 {{has been explicitly marked deprecated here}}
163+
vv1(1) = 1;
164+
}
165+
166+
// In this test `g.row(1)` return simd_view and `(g.row(1))[0]` returns
167+
// simd_view of size 1. To avoid two implicit conversions (which is not
168+
// allowed), simd_view_impl needs explicit definition of BINOP with a scalar.
169+
void test_simd_view_binop_with_conv_to_scalar() SYCL_ESIMD_FUNCTION {
170+
simd<ushort, 64> s = 0;
171+
auto g = s.bit_cast_view<ushort, 4, 16>();
172+
auto x = g.row(1) - (g.row(1))[0]; // binary op
173+
auto y = g.row(1) & (g.row(1))[0]; // bitwise op
174+
auto z = g.row(1) < (g.row(1))[0]; // relational op
175+
}
176+
177+
// This code is OK. The result of bit_cast_view should be mapped
178+
// to specialization of simd_view with length 1, so conversion
179+
// to scalar is allowed.
180+
void test_simd_view_len1_bitcast() SYCL_ESIMD_FUNCTION {
181+
simd<int, 1> s = 0;
182+
float f = s.bit_cast_view<float>();
183+
}
184+
185+
// This test checks the same thing as previous one but for the
186+
// nested simd_view specialization with length 1.
187+
void test_nested_simd_view_len1_bitcast() SYCL_ESIMD_FUNCTION {
188+
simd<int, 2> s = 0;
189+
auto v = s.bit_cast_view<float>(); // generic simd_view
190+
float f = v[0]; // result of v[0] is a specialized nested simd_view
191+
// with length 1, which then converts to a scalar.
192+
193+
// checking nested views with several bitcasts.
194+
simd<double, 4> s2;
195+
((s2[0].bit_cast_view<float>())[1].bit_cast_view<int>())[0] = 1;
196+
}

0 commit comments

Comments
 (0)