DPC++ Runtime
Runtime libraries for oneAPI DPC++
elem_type_traits.hpp
Go to the documentation of this file.
1 //==------------ - elem_type_traits.hpp - DPC++ Explicit SIMD API ----------==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 // This header provides basic infrastructure to support non-standard C++ types
9 // as simd element types. This non-standard element types are usually structs or
10 // classes (example: sycl::half).
11 // Terms:
12 // - "wrapper type" - a non-standard element type
13 // - "raw type" - the real types used to represent real storage type of the data
14 // bits wrapped by the corresponding wrapper structure/class
15 // By design, user program never uses the raw types, so they are not exposed at
16 // user level.
17 //
18 // The main reasons why the infrastructure is needed are:
19 // - attempt to create a clang vector with wrapper element type
20 // vector_type_t<WrapperT, N> will result in compilation error
21 // - C++ operations on WrapperT are usually supported by the Intel GPU hardware
22 // (which is the main reason of supporting them in ESIMD) and need to be
23 // mapped to efficient hardware code sequences.
24 //
25 // To make a wrapper type appear as first-class element type, the following
26 // major components must be available/implemented for the type:
27 // 1) Storage ("raw") type must be defined. The raw type must be bit-castable to
28 // the wrapper type and thus must have the same bit size and alignment
29 // requirements.
30 // 2) "Nearest enclosing" standard C++ type must be defined. This is a standard
31 // C++ type which can represent values of the wrapper type. The enclosing type
32 // can be used as a fall-back type for default implementations of operations
33 // on the wrapper type
34 // 3) Type conversion intrinsics between the bit representation of a wrapper
35 // type value and the equivalent enclosing C++ type value
36 // 4) The above three are enough to emulate any wrapper type, as all operations
37 // can be performed on the enclosing type values, converting from raw to
38 // enclosing before the operation and converting back from enclosing to raw
39 // after the operation. But this would be inefficient in some cases - when
40 // enclosing C++ type does not match the raw type, as H/W usually supports
41 // many operations directly on the raw type (which is bit representation of
42 // the wrapper type). So mapping to efficient H/W operations must be defined.
43 // For example, for SYCL half type efficient mapping primitive operations to
44 // Intel GPU harware is as easy as "unwrapping" sycl::half value, which yields
45 // "_Float16" natively supported by the device compiler and hardware, then
46 // using standard C++, operations such as '+', on _Float16 values. For other
47 // types like bfloat16 this will require mapping to appropriate intrinsics.
48 // Important note: some of these components might have different definition for
49 // the same wrapper type depending on host vs device compilation. E.g. for SYCL
50 // half the raw type is uint16_t on host and _Float16 on device.
51 //
52 // - The mechanism to define components 1) and 2) for a new wrapper type is to
53 // provide a specialization of the `element_type_traits` structure for this
54 // type.
55 // - Component 3) is provided via implementing specializations of the
56 // conversion traits:
57 // * scalar_conversion_traits: functions to bitcast between the raw and the
58 // wrapper types (should not be necessary with C++ 20 where there is a
59 // standard bitcast operation)
60 // * vector_conversion_traits: functions to type-convert between clang
61 // vectors of the wrapper type (bit-represented with the raw type) and clang
62 // vectors the the enclosing std type values.
63 // - Component 4) is provided via:
64 // * (primitive operations) Specializations of the
65 // - scalar_binary_op_traits
66 // - vector_binary_op_traits
67 // - scalar_unary_op_traits
68 // - vector_unary_op_traits
69 // - scalar_comparison_op_traits
70 // - vector_comparison_op_traits
71 // structs. If the `use_native_cpp_ops` element type trait is true, then
72 // implementing those specializations is not necessary and std C++
73 // operations will be used.
74 // * (math operations) Overloading std math functions for the new wrapper
75 // type.
76 //===----------------------------------------------------------------------===//
77 
78 #pragma once
79 
83 
84 #include <utility>
85 
87 
88 namespace sycl {
89 inline namespace _V1 {
90 namespace ext::intel::esimd::detail {
91 
92 // -----------------------------------------------------------------------------
93 // General declarations
94 // -----------------------------------------------------------------------------
95 
96 // Primitive C++ operations supported by simd objects and templated upon by some
97 // of the functions/classes.
98 
99 enum class BinOp {
100  add,
101  sub,
102  mul,
103  div,
104  rem,
105  shl,
106  shr,
107  bit_or,
108  bit_and,
109  bit_xor,
110  log_or,
111  log_and
112 };
113 
114 enum class CmpOp { lt, lte, gte, gt, eq, ne };
115 
116 enum class UnaryOp { minus, plus, bit_not, log_not };
117 
118 struct invalid_raw_element_type;
119 
120 // -----------------------------------------------------------------------------
121 // Traits to be implemented for wrapper types (interleaving with some useful
122 // meta-functions and declarations).
123 // -----------------------------------------------------------------------------
124 
125 // ------------------- Basic type traits
126 
127 // Default (unusable) definition of the element type traits.
128 template <class T, class SFINAE = void> struct element_type_traits {
129  // The raw element type of the underlying clang vector used as a
130  // storage.
131  using RawT = invalid_raw_element_type;
132  // A starndard C++ type which this one can be converted to/from.
133  // The conversions are usually H/W-supported, and the C++ type can
134  // represent the entire range of values of this type.
135  using EnclosingCppT = void;
136  // Whether a value or clang vector value the raw element type can be used
137  // directly as operand to std C++ operations.
138  static constexpr bool use_native_cpp_ops = true;
139  // W/A for MSVC compiler problems which thinks
140  // std::is_floating_point_v<_Float16> is false; so require new element types
141  // implementations to state "is floating point" trait explicitly
142  static constexpr bool is_floating_point = false;
143 };
144 
145 // Element type traits specialization for C++ standard element type.
146 template <class T>
147 struct element_type_traits<T, std::enable_if_t<is_vectorizable_v<T>>> {
148  using RawT = T;
149  using EnclosingCppT = T;
150  static constexpr bool use_native_cpp_ops = true;
151  static constexpr bool is_floating_point = std::is_floating_point_v<T>;
152 };
153 
154 // ------------------- Useful meta-functions and declarations
155 
156 template <class T> using __raw_t = typename element_type_traits<T>::RawT;
157 template <class T>
158 using __cpp_t = typename element_type_traits<T>::EnclosingCppT;
159 
160 template <class T, int N>
161 using __raw_vec_t = vector_type_t<typename element_type_traits<T>::RawT, N>;
162 
163 // Note: using RawVecT in comparison result type calculation does *not* mean
164 // the comparison is actually performed on the raw types.
165 template <class T, int N>
166 using __cmp_t = decltype(std::declval<__raw_vec_t<T, N>>() <
167  std::declval<__raw_vec_t<T, N>>());
168 
169 // Is given type is a special "wrapper" element type?
170 template <class T>
171 static inline constexpr bool is_wrapper_elem_type_v =
172  !std::is_same_v<__raw_t<T>, invalid_raw_element_type> &&
173  !std::is_same_v<__raw_t<T>, T>;
174 
175 template <class T>
176 static inline constexpr bool is_valid_simd_elem_type_v =
177  (is_vectorizable_v<T> || is_wrapper_elem_type_v<T>);
178 
179 // ------------------- Type conversion traits
180 
181 template <class WrapperT, int N> struct vector_conversion_traits {
182  static_assert(is_wrapper_elem_type_v<WrapperT>, "");
183  using StdT = __cpp_t<WrapperT>;
184  using RawT = __raw_t<WrapperT>;
185 
186  static vector_type_t<RawT, N> convert_to_raw(vector_type_t<StdT, N>);
187  static vector_type_t<StdT, N> convert_to_cpp(vector_type_t<RawT, N>);
188 };
189 
190 template <class WrapperT> struct scalar_conversion_traits {
191  static_assert(is_wrapper_elem_type_v<WrapperT>, "");
192  using RawT = __raw_t<WrapperT>;
193 
194  static RawT bitcast_to_raw(WrapperT);
195  static WrapperT bitcast_to_wrapper(RawT);
196 };
197 
198 // ------------------- Binary operation traits
199 
200 template <BinOp Op, class WrapperT> struct scalar_binary_op_traits {
201  static_assert(is_wrapper_elem_type_v<WrapperT>, "");
202 
203  static WrapperT impl(WrapperT X, WrapperT Y);
204 };
205 
206 template <BinOp Op, class WrapperT, int N> struct vector_binary_op_traits {
207  static_assert(is_wrapper_elem_type_v<WrapperT>, "");
208  using RawVecT = __raw_vec_t<WrapperT, N>;
209 
210  static RawVecT impl(RawVecT X, RawVecT Y);
211 };
212 
213 // ------------------- Comparison operation traits
214 
215 template <CmpOp Op, class WrapperT> struct scalar_comparison_op_traits {
216  static_assert(is_wrapper_elem_type_v<WrapperT>, "");
217 
218  static bool impl(WrapperT X, WrapperT Y);
219 };
220 
221 template <CmpOp Op, class WrapperT, int N> struct vector_comparison_op_traits {
222  static_assert(is_wrapper_elem_type_v<WrapperT>, "");
223  using RawVecT = __raw_vec_t<WrapperT, N>;
224 
225  static __cmp_t<WrapperT, N> impl(RawVecT X, RawVecT Y);
226 };
227 
228 // ------------------- Unary operation traits
229 
230 template <UnaryOp Op, class WrapperT> struct scalar_unary_op_traits {
231  static_assert(is_wrapper_elem_type_v<WrapperT>, "");
232 
233  static WrapperT impl(WrapperT X);
234 };
235 
236 template <UnaryOp Op, class WrapperT, int N> struct vector_unary_op_traits {
237  static_assert(is_wrapper_elem_type_v<WrapperT>, "");
238  using RawVecT = __raw_vec_t<WrapperT, N>;
239 
240  static RawVecT impl(RawVecT X);
241 };
242 
243 // -----------------------------------------------------------------------------
244 // Main type conversion meta-functions used in traits implementations and other
245 // ESIMD components.
246 // -----------------------------------------------------------------------------
247 
248 template <class WrapperT> struct wrapper_type_converter {
249  using StdT = __cpp_t<WrapperT>;
250  using RawT = __raw_t<WrapperT>;
251 
252  template <int N>
253  ESIMD_INLINE static vector_type_t<RawT, N>
254  to_vector(vector_type_t<StdT, N> Val) {
255  if constexpr (element_type_traits<WrapperT>::use_native_cpp_ops) {
256  return __builtin_convertvector(Val, vector_type_t<RawT, N>);
257  } else {
258  return vector_conversion_traits<WrapperT, N>::convert_to_raw(Val);
259  }
260  }
261 
262  template <int N>
263  ESIMD_INLINE static vector_type_t<StdT, N>
264  from_vector(vector_type_t<RawT, N> Val) {
265  if constexpr (element_type_traits<WrapperT>::use_native_cpp_ops) {
266  return __builtin_convertvector(Val, vector_type_t<StdT, N>);
267  } else {
268  return vector_conversion_traits<WrapperT, N>::convert_to_cpp(Val);
269  }
270  }
271 };
272 
273 // Converts a raw representation of a simd vector with element type
274 // SrcWrapperTy to a raw representation of a simd vector with element type
275 // DstWrapperTy.
276 template <class DstWrapperTy, class SrcWrapperTy, int N,
277  class DstRawVecTy = vector_type_t<__raw_t<DstWrapperTy>, N>,
278  class SrcRawVecTy = vector_type_t<__raw_t<SrcWrapperTy>, N>>
279 ESIMD_INLINE DstRawVecTy convert_vector(SrcRawVecTy Val) {
280  if constexpr (std::is_same_v<SrcWrapperTy, DstWrapperTy>) {
281  return Val;
282  } else if constexpr (!is_wrapper_elem_type_v<SrcWrapperTy> &&
283  !is_wrapper_elem_type_v<DstWrapperTy>) {
284  return __builtin_convertvector(Val, DstRawVecTy);
285  } else {
286  // The chain of conversions (some can be no-op if types match):
287  // SrcRawVecTy (of SrcWrapperTy)
288  // | step A [wrapper_type_converter<SrcWrapperTy>]::from_vector
289  // v
290  // SrcStdT
291  // | step B [__builtin_convertvector]
292  // v
293  // DstStdT
294  // | step C [wrapper_type_converter<DstWrapperTy>]::to_vector
295  // v
296  // DstRawVecTy (of DstWrapperTy)
297  //
298  using SrcConv = wrapper_type_converter<SrcWrapperTy>;
299  using DstConv = wrapper_type_converter<DstWrapperTy>;
300  using SrcStdT = typename SrcConv::StdT;
301  using DstStdT = typename DstConv::StdT;
302  using DstStdVecT = vector_type_t<DstStdT, N>;
303  using SrcStdVecT = vector_type_t<SrcStdT, N>;
304  SrcStdVecT TmpSrcVal;
305 
306  if constexpr (std::is_same_v<SrcStdT, SrcWrapperTy>) {
307  TmpSrcVal = std::move(Val);
308  } else {
309  TmpSrcVal = SrcConv::template from_vector<N>(Val); // step A
310  }
311  if constexpr (std::is_same_v<SrcStdT, DstWrapperTy>) {
312  return TmpSrcVal;
313  } else {
314  DstStdVecT TmpDstVal;
315 
316  if constexpr (std::is_same_v<SrcStdT, DstStdVecT>) {
317  TmpDstVal = std::move(TmpSrcVal);
318  } else {
319  TmpDstVal = __builtin_convertvector(TmpSrcVal, DstStdVecT); // step B
320  }
321  if constexpr (std::is_same_v<DstStdT, DstWrapperTy>) {
322  return TmpDstVal;
323  } else {
324  return DstConv::template to_vector<N>(TmpDstVal); // step C
325  }
326  }
327  }
328 }
329 
330 // -----------------------------------------------------------------------------
331 // Implementations of standard C++ operations - (comparison, binary and unary)
332 // for the vectors and scalars of wrapper types based the traits declared above.
333 // -----------------------------------------------------------------------------
334 
335 template <class Ty> ESIMD_INLINE __raw_t<Ty> bitcast_to_raw_type(Ty Val) {
336  if constexpr (!is_wrapper_elem_type_v<Ty>) {
337  return Val;
338  } else {
339  return scalar_conversion_traits<Ty>::bitcast_to_raw(Val);
340  }
341 }
342 
343 template <class Ty> ESIMD_INLINE Ty bitcast_to_wrapper_type(__raw_t<Ty> Val) {
344  if constexpr (!is_wrapper_elem_type_v<Ty>) {
345  return Val;
346  } else {
347  return scalar_conversion_traits<Ty>::bitcast_to_wrapper(Val);
348  }
349 }
350 
351 // Converts a scalar value from given source type to destination type. Both
352 // types can be non-std element types, in which case additional non-C++
353 // conversions happen if the types are different.
354 // NOTE: this is not symmetric with convert_vector, which inputs and outputs
355 // raw (storage) vector types.
356 template <class DstWrapperTy, class SrcWrapperTy,
357  class DstRawTy = __raw_t<DstWrapperTy>,
358  class SrcRawTy = __raw_t<SrcWrapperTy>>
359 ESIMD_INLINE DstWrapperTy convert_scalar(SrcWrapperTy Val) {
360  if constexpr (std::is_same_v<SrcWrapperTy, DstWrapperTy>) {
361  return Val;
362  } else if constexpr (!is_wrapper_elem_type_v<SrcWrapperTy> &&
363  !is_wrapper_elem_type_v<DstWrapperTy>) {
364  return static_cast<DstRawTy>(Val);
365  } else {
366  vector_type_t<SrcRawTy, 1> V0 = bitcast_to_raw_type<SrcWrapperTy>(Val);
367  vector_type_t<DstRawTy, 1> V1 =
368  convert_vector<DstWrapperTy, SrcWrapperTy, 1>(V0);
369  return bitcast_to_wrapper_type<DstWrapperTy>(V1[0]);
370  }
371 }
372 
373 // Default implementation of a binary arithmetic operation. Works for both
374 // scalar and vector types.
375 template <BinOp Op, class T> T binary_op_default_impl(T X, T Y) {
376  T Res{};
377  if constexpr (Op == BinOp::add)
378  Res = X + Y;
379  else if constexpr (Op == BinOp::sub)
380  Res = X - Y;
381  else if constexpr (Op == BinOp::mul)
382  Res = X * Y;
383  else if constexpr (Op == BinOp::div)
384  Res = X / Y;
385  else if constexpr (Op == BinOp::rem)
386  Res = X % Y;
387  else if constexpr (Op == BinOp::shl)
388  Res = X << Y;
389  else if constexpr (Op == BinOp::shr)
390  Res = X >> Y;
391  else if constexpr (Op == BinOp::bit_or)
392  Res = X | Y;
393  else if constexpr (Op == BinOp::bit_and)
394  Res = X & Y;
395  else if constexpr (Op == BinOp::bit_xor)
396  Res = X ^ Y;
397  else if constexpr (Op == BinOp::log_or)
398  Res = X || Y;
399  else if constexpr (Op == BinOp::log_and)
400  Res = X && Y;
401  return Res;
402 }
403 
404 // Default implementation of a comparison operation. Works for both scalar and
405 // vector types.
406 template <CmpOp Op, class T> auto comparison_op_default_impl(T X, T Y) {
407  decltype(X < Y) Res{};
408  if constexpr (Op == CmpOp::lt)
409  Res = X < Y;
410  else if constexpr (Op == CmpOp::lte)
411  Res = X <= Y;
412  else if constexpr (Op == CmpOp::eq)
413  Res = X == Y;
414  else if constexpr (Op == CmpOp::ne)
415  Res = X != Y;
416  else if constexpr (Op == CmpOp::gte)
417  Res = X >= Y;
418  else if constexpr (Op == CmpOp::gt)
419  Res = X > Y;
420  return Res;
421 }
422 
423 // Default implementation of an unary operation. Works for both scalar and
424 // vector types.
425 template <UnaryOp Op, class T> auto unary_op_default_impl(T X) {
426  if constexpr (Op == UnaryOp::minus)
427  return -X;
428  else if constexpr (Op == UnaryOp::plus)
429  return +X;
430  else if constexpr (Op == UnaryOp::bit_not)
431  return ~X;
432  else if constexpr (Op == UnaryOp::log_not)
433  return !X;
434 }
435 
436 // --- Scalar versions of binary operations
437 
438 template <BinOp Op, class T,
439  class = std::enable_if_t<is_valid_simd_elem_type_v<T>>>
440 ESIMD_INLINE T binary_op_default(T X, T Y) {
441  static_assert(element_type_traits<T>::use_native_cpp_ops);
442  using T1 = __raw_t<T>;
443  T1 X1 = bitcast_to_raw_type(X);
444  T1 Y1 = bitcast_to_raw_type(Y);
445  T1 Res = binary_op_default_impl<Op>(X1, Y1);
446  return bitcast_to_wrapper_type<T>(Res);
447 }
448 
449 template <BinOp Op, class T,
450  class = std::enable_if_t<is_valid_simd_elem_type_v<T>>>
451 ESIMD_INLINE T binary_op(T X, T Y) {
452  if constexpr (element_type_traits<T>::use_native_cpp_ops) {
453  return binary_op_default<Op>(X, Y);
454  } else {
455  return scalar_binary_op_traits<Op, T>::impl(X, Y);
456  }
457 }
458 
459 // --- Vector versions of binary operations
460 
461 template <BinOp Op, class ElemT, int N, class RawVecT = __raw_vec_t<ElemT, N>>
462 ESIMD_INLINE RawVecT vector_binary_op_default(RawVecT X, RawVecT Y) {
463  static_assert(element_type_traits<ElemT>::use_native_cpp_ops);
464  return binary_op_default_impl<Op, RawVecT>(X, Y);
465 }
466 
467 template <BinOp Op, class ElemT, int N, class RawVecT = __raw_vec_t<ElemT, N>>
468 ESIMD_INLINE RawVecT vector_binary_op(RawVecT X, RawVecT Y) {
469  if constexpr (element_type_traits<ElemT>::use_native_cpp_ops) {
470  return vector_binary_op_default<Op, ElemT, N>(X, Y);
471  } else {
472  return vector_binary_op_traits<Op, ElemT, N>::impl(X, Y);
473  }
474 }
475 
476 // --- Scalar versions of unary operations
477 
478 template <UnaryOp Op, class T,
479  class = std::enable_if_t<is_valid_simd_elem_type_v<T>>>
480 ESIMD_INLINE T unary_op_default(T X) {
481  static_assert(element_type_traits<T>::use_native_cpp_ops);
482  using T1 = __raw_t<T>;
483  T1 X1 = bitcast_to_raw_type(X);
484  T1 Res = unary_op_default_impl<Op>(X1);
485  return bitcast_to_wrapper_type<T>(Res);
486 }
487 
488 template <UnaryOp Op, class T,
489  class = std::enable_if_t<is_valid_simd_elem_type_v<T>>>
490 ESIMD_INLINE T unary_op(T X) {
491  if constexpr (element_type_traits<T>::use_native_cpp_ops) {
492  return unary_op_default<Op>(X);
493  } else {
494  return scalar_unary_op_traits<Op, T>::impl(X);
495  }
496 }
497 
498 // --- Vector versions of unary operations
499 
500 template <UnaryOp Op, class ElemT, int N, class RawVecT = __raw_vec_t<ElemT, N>>
501 ESIMD_INLINE RawVecT vector_unary_op_default(RawVecT X) {
502  static_assert(element_type_traits<ElemT>::use_native_cpp_ops);
503  return unary_op_default_impl<Op, RawVecT>(X);
504 }
505 
506 template <UnaryOp Op, class ElemT, int N, class RawVecT = __raw_vec_t<ElemT, N>>
507 ESIMD_INLINE RawVecT vector_unary_op(RawVecT X) {
508  if constexpr (element_type_traits<ElemT>::use_native_cpp_ops) {
509  return vector_unary_op_default<Op, ElemT, N>(X);
510  } else {
511  return vector_unary_op_traits<Op, ElemT, N>::impl(X);
512  }
513 }
514 
515 // --- Vector versions of comparison operations
516 
517 template <CmpOp Op, class ElemT, int N, class RetT = __cmp_t<ElemT, N>,
518  class RawVecT = __raw_vec_t<ElemT, N>>
519 ESIMD_INLINE RetT vector_comparison_op_default(RawVecT X, RawVecT Y) {
520  static_assert(element_type_traits<ElemT>::use_native_cpp_ops);
521  return comparison_op_default_impl<Op, RawVecT>(X, Y);
522 }
523 
524 template <CmpOp Op, class ElemT, int N, class RetT = __cmp_t<ElemT, N>,
525  class RawVecT = __raw_vec_t<ElemT, N>>
526 ESIMD_INLINE RetT vector_comparison_op(RawVecT X, RawVecT Y) {
527  if constexpr (element_type_traits<ElemT>::use_native_cpp_ops) {
528  return vector_comparison_op_default<Op, ElemT, N>(X, Y);
529  } else {
530  return vector_comparison_op_traits<Op, ElemT, N>::impl(X, Y);
531  }
532 }
533 
534 // -----------------------------------------------------------------------------
535 // Default implementations of the traits (used in the operations above).
536 // -----------------------------------------------------------------------------
537 
538 // Default (inefficient) implementation of a scalar binary operation, which
539 // involves conversion to an std C++ type, performing the op and converting
540 // back.
541 template <BinOp Op, class WrapperT>
542 ESIMD_INLINE WrapperT scalar_binary_op_traits<Op, WrapperT>::impl(WrapperT X,
543  WrapperT Y) {
544  using T1 = __cpp_t<WrapperT>;
545  T1 X1 = convert_scalar<T1, WrapperT>(X);
546  T1 Y1 = convert_scalar<T1, WrapperT>(Y);
547  return convert_scalar<WrapperT>(binary_op_default<Op, T1>(X1, Y1));
548 }
549 
550 // Default (inefficient) implementation of a vector binary operation, which
551 // involves conversion to an std C++ type, performing the op and converting
552 // back.
553 template <BinOp Op, class WrapperT, int N>
554 ESIMD_INLINE __raw_vec_t<WrapperT, N>
555 vector_binary_op_traits<Op, WrapperT, N>::impl(__raw_vec_t<WrapperT, N> X,
556  __raw_vec_t<WrapperT, N> Y) {
557  using T1 = __cpp_t<WrapperT>;
558  using VecT1 = vector_type_t<T1, N>;
559  VecT1 X1 = convert_vector<T1, WrapperT, N>(X);
560  VecT1 Y1 = convert_vector<T1, WrapperT, N>(Y);
561  return convert_vector<WrapperT, T1, N>(
562  vector_binary_op_default<Op, T1, N>(X1, Y1));
563 }
564 
565 // Default (inefficient) implementation of a scalar unary operation, which
566 // involves conversion to an std C++ type, performing the op and converting
567 // back.
568 template <UnaryOp Op, class WrapperT>
569 ESIMD_INLINE WrapperT scalar_unary_op_traits<Op, WrapperT>::impl(WrapperT X) {
570  using T1 = __cpp_t<WrapperT>;
571  T1 X1 = convert_scalar<T1, WrapperT>(X);
572  return convert_scalar<WrapperT>(unary_op_default<Op, T1>(X1));
573 }
574 
575 // Default (inefficient) implementation of a vector unary operation, which
576 // involves conversion to an std C++ type, performing the op and converting
577 // back.
578 template <UnaryOp Op, class WrapperT, int N>
579 ESIMD_INLINE __raw_vec_t<WrapperT, N>
580 vector_unary_op_traits<Op, WrapperT, N>::impl(__raw_vec_t<WrapperT, N> X) {
581  using T1 = __cpp_t<WrapperT>;
582  using VecT1 = vector_type_t<T1, N>;
583  VecT1 X1 = convert_vector<T1, WrapperT, N>(X);
584  return convert_vector<WrapperT, T1, N>(
585  vector_unary_op_default<Op, T1, N>(X1));
586 }
587 
588 // Default (inefficient) implementation of a vector comparison operation, which
589 // involves conversion to an std C++ type, performing the op and converting
590 // back.
591 template <CmpOp Op, class WrapperT, int N>
592 ESIMD_INLINE __cmp_t<WrapperT, N>
593 vector_comparison_op_traits<Op, WrapperT, N>::impl(__raw_vec_t<WrapperT, N> X,
594  __raw_vec_t<WrapperT, N> Y) {
595  using T1 = __cpp_t<WrapperT>;
596  using VecT1 = vector_type_t<T1, N>;
597  VecT1 X1 = convert_vector<T1, WrapperT, N>(X);
598  VecT1 Y1 = convert_vector<T1, WrapperT, N>(Y);
599  return convert_vector<vector_element_type_t<__cmp_t<WrapperT, N>>, T1, N>(
600  vector_comparison_op_default<Op, T1, N>(X1, Y1));
601 }
602 
603 // "Generic" version of std::is_floating_point_v which returns "true" also for
604 // the wrapper floating-point types such as sycl::half.
605 template <typename T>
606 static inline constexpr bool is_generic_floating_point_v =
607  element_type_traits<T>::is_floating_point;
608 
609 } // namespace ext::intel::esimd::detail
610 } // namespace _V1
611 } // namespace sycl
612 
__ESIMD_API std::enable_if_t< std::is_integral< T0 >::value &&std::is_integral< T1 >::value &&std::is_integral< U >::value, sycl::ext::intel::esimd::simd< T0, SZ > > shl(sycl::ext::intel::esimd::simd< T1, SZ > src0, U src1, Sat sat={})
Shift left operation (vector version)
Definition: math.hpp:43
__ESIMD_API std::enable_if_t< std::is_integral< T0 >::value &&std::is_integral< T1 >::value &&std::is_integral< U >::value, sycl::ext::intel::esimd::simd< T0, SZ > > shr(sycl::ext::intel::esimd::simd< T1, SZ > src0, U src1, Sat sat={})
Shift right operation (vector version)
Definition: math.hpp:239
@ sub
Subtraction: *addr = *addr - src0.
__ESIMD_API std::enable_if_t< std::is_integral< T >::value &&std::is_integral< U >::value, sycl::ext::intel::esimd::simd< T, SZ > > div(sycl::ext::intel::esimd::simd< T, SZ > &remainder, sycl::ext::intel::esimd::simd< T, SZ > src0, U src1)
Integral division with a vector dividend and a scalar divisor.
Definition: math.hpp:642
void add(const void *DeviceGlobalPtr, const char *UniqueId)
std::plus< T > plus
Definition: functional.hpp:20
std::bit_or< T > bit_or
Definition: functional.hpp:22
std::bit_xor< T > bit_xor
Definition: functional.hpp:23
std::bit_and< T > bit_and
Definition: functional.hpp:24
std::plus< T > plus
Definition: functional.hpp:18
Definition: access.hpp:18