File indexing completed on 2025-01-30 10:25:51
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028 #ifndef VC_COMMON_SUBSCRIPT_H_
0029 #define VC_COMMON_SUBSCRIPT_H_
0030
0031 #include <initializer_list>
0032 #include <type_traits>
0033 #include <vector>
0034 #include "types.h"
0035 #include "macros.h"
0036 #include <assert.h>
0037
0038 namespace Vc_VERSIONED_NAMESPACE
0039 {
0040 namespace Common
0041 {
0042
0043 template <typename Base> class AdaptSubscriptOperator : public Base
0044 {
0045 public:
0046
0047 template <typename... Args>
0048 Vc_ALWAYS_INLINE AdaptSubscriptOperator(Args &&... arguments)
0049 : Base(std::forward<Args>(arguments)...)
0050 {
0051 }
0052
0053
0054 template <typename T>
0055 Vc_ALWAYS_INLINE AdaptSubscriptOperator(std::initializer_list<T> l)
0056 : Base(l)
0057 {
0058 }
0059
0060
0061 using Base::operator[];
0062
0063
0064 template <typename I,
0065 typename = enable_if<!std::is_arithmetic<
0066 typename std::decay<I>::type>::value>
0067
0068
0069
0070 >
0071 Vc_ALWAYS_INLINE auto operator[](I &&arg_)
0072 -> decltype(subscript_operator(*this, std::forward<I>(arg_)))
0073 {
0074 return subscript_operator(*this, std::forward<I>(arg_));
0075 }
0076
0077
0078 template <typename I, typename = enable_if<
0079 !std::is_arithmetic<typename std::decay<I>::type>::value>>
0080 Vc_ALWAYS_INLINE auto operator[](I &&arg_) const
0081 -> decltype(subscript_operator(*this, std::forward<I>(arg_)))
0082 {
0083 return subscript_operator(*this, std::forward<I>(arg_));
0084 }
0085 };
0086
0087
0088
0089 template <class T, class = decltype(convertIndexVector(std::declval<T>()))>
0090 std::true_type is_valid_indexvector(T &&);
0091 std::false_type is_valid_indexvector(...);
0092
0093 template <class IndexVector, class Test = decltype(is_valid_indexvector(
0094 std::declval<const IndexVector &>()))>
0095 struct is_valid_indexvector_ : public std::integral_constant<bool, Test::value> {
0096 };
0097 static_assert(!is_valid_indexvector_<const int *>::value,
0098 "Pointer is incorrectly classified as valid index vector type");
0099 static_assert(is_valid_indexvector_<const int[4]>::value,
0100 "C-Array is incorrectly classified as invalid index vector type");
0101
0102
0103
0104 template <typename Scale, typename T>
0105 Vc_ALWAYS_INLINE enable_if<Scale::num == Scale::den, Traits::decay<T>> applyScale(T &&x)
0106 {
0107 return std::forward<T>(x);
0108 }
0109
0110 template <typename Scale, typename T>
0111 Vc_ALWAYS_INLINE enable_if<
0112 Scale::num != Scale::den && Traits::has_multiply_operator<T, int>::value,
0113 Traits::decay<T>>
0114 applyScale(T &&x)
0115 {
0116 static_assert(Scale::num % Scale::den == 0,
0117 "Non-integral index scaling requested. This typically happens only for "
0118 "Vc::Scalar on 32-bit for gathers on double. You can work around the "
0119 "issue by ensuring that all doubles in the structure are aligned on 8 "
0120 "Bytes.");
0121 constexpr int value = Scale::num / Scale::den;
0122 Vc_ASSERT(Vc::all_of((x * value) / value == x));
0123 return std::forward<T>(x) * value;
0124 }
0125
0126 template <typename Scale, typename T>
0127 Vc_ALWAYS_INLINE enable_if<
0128 Scale::num != Scale::den && !Traits::has_multiply_operator<T, int>::value,
0129 T>
0130 applyScale(T x)
0131 {
0132 static_assert(Scale::num % Scale::den == 0,
0133 "Non-integral index scaling requested. This typically happens only for "
0134 "Vc::Scalar on 32-bit for gathers on double. You can work around the "
0135 "issue by ensuring that all doubles in the structure are aligned on 8 "
0136 "Bytes.");
0137 constexpr int value = Scale::num / Scale::den;
0138 for (size_t i = 0; i < x.size(); ++i) {
0139 Vc_ASSERT((x[i] * value) / value == x[i]);
0140 x[i] *= value;
0141 }
0142 return x;
0143 }
0144
0145 template <typename Scale, typename T, typename U,
0146 typename = enable_if<Traits::has_multiply_operator<T, int>::value &&
0147 Traits::has_addition_operator<T, U>::value>>
0148 Vc_ALWAYS_INLINE typename std::decay<T>::type applyScaleAndAdd(T &&x, U &&y)
0149 {
0150 constexpr int value = Scale::num / Scale::den;
0151 if (value == 1) {
0152 return std::forward<T>(x) + std::forward<U>(y);
0153 }
0154 return std::forward<T>(x) * value + std::forward<U>(y);
0155 }
0156
0157 template <
0158 typename Scale, typename T, typename U,
0159 typename = enable_if<
0160 !(Traits::has_multiply_operator<T &, int>::value &&
0161 Traits::has_addition_operator<T &, decltype(std::declval<U>()[0])>::value) &&
0162 Traits::has_subscript_operator<U>::value>>
0163 Vc_ALWAYS_INLINE T applyScaleAndAdd(T x, U &&y)
0164 {
0165 constexpr int value = Scale::num / Scale::den;
0166 for (size_t i = 0; i < x.size(); ++i) {
0167 if (value == 1) {
0168 x[i] = x[i] + y[i];
0169 } else {
0170 x[i] = x[i] * value + y[i];
0171 }
0172 }
0173 return x;
0174 }
0175
0176 template <typename Scale, typename T, typename U>
0177 Vc_ALWAYS_INLINE enable_if<!(Traits::has_multiply_operator<T &, int>::value &&
0178 Traits::has_addition_operator<T &, U>::value) &&
0179 !Traits::has_subscript_operator<U>::value,
0180 T>
0181 applyScaleAndAdd(T x, U &&y)
0182 {
0183 constexpr int value = Scale::num / Scale::den;
0184 for (size_t i = 0; i < x.size(); ++i) {
0185 if (value == 1) {
0186 x[i] = x[i] + y;
0187 } else {
0188 x[i] = x[i] * value + y;
0189 }
0190 }
0191 return x;
0192 }
0193
0194
0195 template <std::size_t MinSize,
0196 typename IndexT,
0197 bool = Traits::is_simd_vector<IndexT>::value>
0198 struct IndexVectorSizeMatches
0199 : public std::true_type
0200
0201
0202
0203 {
0204 };
0205
0206 template <std::size_t MinSize, typename V>
0207 struct IndexVectorSizeMatches<MinSize,
0208 V,
0209 true> : public std::integral_constant<bool, (MinSize <= V::Size)>
0210 {
0211 };
0212
0213 template <std::size_t MinSize, typename T, std::size_t ArraySize>
0214 struct IndexVectorSizeMatches<MinSize,
0215 T[ArraySize],
0216 false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
0217 {
0218 };
0219
0220 template <std::size_t MinSize, typename T, std::size_t ArraySize>
0221 struct IndexVectorSizeMatches<MinSize,
0222 std::array<T, ArraySize>,
0223 false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
0224 {
0225 };
0226
0227 template <std::size_t MinSize, typename T, std::size_t ArraySize>
0228 struct IndexVectorSizeMatches<MinSize,
0229 Vc::array<T, ArraySize>,
0230 false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
0231 {
0232 };
0233
0234 template <std::size_t MinSize, typename T, std::ptrdiff_t N>
0235 struct IndexVectorSizeMatches<MinSize, Vc::Common::span<T, N>, false>
0236 : public std::integral_constant<bool, (N == -1 || static_cast<std::ptrdiff_t>(MinSize) <= N)> {
0237 };
0238
0239 template <
0240 typename T, typename IndexVector, typename Scale = std::ratio<1, 1>,
0241 bool = is_valid_indexvector_<IndexVector>::value>
0242 class SubscriptOperation
0243 {
0244 const IndexVector m_indexes;
0245 T *const m_address;
0246 using ScalarType = typename std::decay<T>::type;
0247
0248 using IndexVectorScaled = Traits::decay<decltype(convertIndexVector(std::declval<const IndexVector &>()))>;
0249
0250 public:
0251
0252 SubscriptOperation &operator=(const SubscriptOperation &) = delete;
0253 SubscriptOperation(const SubscriptOperation &) = delete;
0254 #ifndef __cpp_guaranteed_copy_elision
0255 constexpr SubscriptOperation(SubscriptOperation &&) = default;
0256 #endif
0257
0258 template <typename U,
0259 typename = enable_if<((std::is_convertible<const U &, IndexVector>::value ||
0260 std::is_same<U, IndexVector>::value) &&
0261 std::is_copy_constructible<IndexVector>::value)>>
0262 constexpr Vc_ALWAYS_INLINE SubscriptOperation(T *address, const U &indexes)
0263 : m_indexes(indexes), m_address(address)
0264 {
0265 }
0266
0267 template <std::size_t... Indexes>
0268 constexpr Vc_ALWAYS_INLINE SubscriptOperation(T *address, const IndexVector &indexes,
0269 index_sequence<Indexes...>)
0270 : m_indexes{indexes[Indexes]...}, m_address(address)
0271 {}
0272
0273 template <typename U>
0274 constexpr Vc_ALWAYS_INLINE SubscriptOperation(
0275 T *address, const U &indexes,
0276 enable_if<((std::is_convertible<const U &, IndexVector>::value ||
0277 std::is_same<U, IndexVector>::value) &&
0278 !std::is_copy_constructible<IndexVector>::value &&
0279 std::is_array<IndexVector>::value &&
0280 std::extent<IndexVector>::value > 0)> = nullarg)
0281 : SubscriptOperation(address, indexes,
0282 make_index_sequence<std::extent<IndexVector>::value>())
0283 {
0284 }
0285
0286 static constexpr bool need_explicit_scaling =
0287 Scale::num % Scale::den != 0 || Scale::num / Scale::den * sizeof(T) > 8;
0288
0289 Vc_ALWAYS_INLINE
0290 GatherArguments<typename std::remove_cv<T>::type, IndexVectorScaled,
0291 (need_explicit_scaling ? 1 : Scale::num / Scale::den)>
0292 gatherArguments() &&
0293 {
0294 static_assert(std::is_arithmetic<ScalarType>::value,
0295 "Incorrect type for a SIMD vector gather. Must be an arithmetic type.");
0296 return {applyScale<typename std::conditional<need_explicit_scaling, Scale,
0297 std::ratio<1, 1>>::type>(
0298 convertIndexVector(m_indexes)),
0299 m_address};
0300 }
0301
0302 Vc_ALWAYS_INLINE ScatterArguments<T, IndexVectorScaled> scatterArguments() &&
0303 {
0304 static_assert(std::is_arithmetic<ScalarType>::value,
0305 "Incorrect type for a SIMD vector scatter. Must be an arithmetic type.");
0306 return {applyScale<Scale>(convertIndexVector(m_indexes)), m_address};
0307 }
0308
0309 template <typename V,
0310 typename = enable_if<(std::is_arithmetic<ScalarType>::value &&Traits::is_simd_vector<
0311 V>::value &&IndexVectorSizeMatches<V::Size, IndexVector>::value)>>
0312 Vc_INTRINSIC operator V() &&
0313 {
0314 return V(static_cast<SubscriptOperation &&>(*this).gatherArguments());
0315 }
0316
0317 template <typename V,
0318 typename = enable_if<(std::is_arithmetic<ScalarType>::value &&Traits::is_simd_vector<
0319 V>::value &&IndexVectorSizeMatches<V::Size, IndexVector>::value)>>
0320 Vc_ALWAYS_INLINE SubscriptOperation &operator=(const V &rhs) &&
0321 {
0322 static_assert(std::is_arithmetic<ScalarType>::value,
0323 "Incorrect type for a SIMD vector scatter. Must be an arithmetic type.");
0324 const auto indexes = applyScale<Scale>(convertIndexVector(m_indexes));
0325 rhs.scatter(m_address, indexes);
0326 return *this;
0327 }
0328
0329
0330 template <
0331 typename U,
0332 typename S,
0333
0334
0335 typename = enable_if<std::is_same<S, typename std::remove_cv<T>::type>::value &&(
0336 std::is_class<T>::value || std::is_union<T>::value)>>
0337 Vc_ALWAYS_INLINE auto operator[](U S::*member) &&
0338 -> SubscriptOperation<
0339 typename std::conditional<std::is_const<T>::value,
0340 const typename std::remove_reference<U>::type,
0341 typename std::remove_reference<U>::type>::type,
0342 IndexVector,
0343
0344
0345
0346
0347
0348 std::ratio_multiply<Scale, std::ratio<sizeof(S), sizeof(U)>>>
0349 {
0350 static_assert(std::is_same<Traits::decay<decltype(m_address->*member)>,
0351 Traits::decay<U>>::value,
0352 "Type mismatch that should be impossible.");
0353
0354 return {&(m_address->*member), m_indexes};
0355 }
0356
0357
0358
0359
0360
0361
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372 private:
0373
0374
0375
0376
0377
0378
0379 template <intmax_t N, intmax_t D> struct make_ratio {
0380 using type = std::ratio<N, D == 0 ? 1 : D>;
0381 };
0382
0383 public:
0384
0385 template <typename U>
0386
0387
0388
0389 Vc_ALWAYS_INLINE auto operator[](U index) && -> typename std::enable_if<
0390 #ifndef Vc_IMPROVE_ERROR_MESSAGES
0391 Traits::has_no_allocated_data<T>::value &&
0392 #endif
0393 std::is_convertible<U, size_t>::value,
0394 SubscriptOperation<
0395
0396
0397
0398 typename std::remove_reference<decltype(m_address[0][index])>::type,
0399 IndexVector,
0400 std::ratio_multiply<
0401 Scale,
0402 typename make_ratio<sizeof(T), sizeof(m_address[0][index])>::type>>>::type
0403 {
0404 static_assert(Traits::has_subscript_operator<T>::value,
0405 "The subscript operator was called on a type that does not implement it.\n");
0406 static_assert(Traits::has_no_allocated_data<T>::value,
0407 "Invalid container type in gather/scatter operation.\nYou may only use "
0408 "nested containers that store the data inside the object (such as builtin "
0409 "arrays or std::array) but not containers that store data in allocated "
0410 "memory (such as std::vector).\nSince this feature cannot be queried "
0411 "generically at compile time you need to spezialize the "
0412 "Vc::Traits::has_no_allocated_data_impl<T> type-trait for custom types that "
0413 "meet the requirements.\n");
0414 static_assert(std::is_lvalue_reference<decltype(m_address[0][index])>::value,
0415 "The container does not return an lvalue reference to the data at "
0416 "the requested offset. This makes it impossible to execute a "
0417 "gather operation.\n");
0418 return {&(m_address[0][index]), m_indexes};
0419 }
0420
0421
0422 template <typename IT>
0423 Vc_ALWAYS_INLINE typename std::enable_if<
0424 #ifndef Vc_IMPROVE_ERROR_MESSAGES
0425 Traits::has_no_allocated_data<T>::value &&
0426 Traits::has_subscript_operator<T>::value &&
0427 #endif
0428 Traits::has_subscript_operator<IT>::value,
0429 SubscriptOperation<typename std::remove_reference<decltype(
0430 m_address[0][std::declval<
0431 const IT &>()[0]]
0432
0433
0434
0435 )>::type,
0436 IndexVectorScaled,
0437 std::ratio<1, 1>
0438 >>::type
0439 operator[](const IT &index) &&
0440 {
0441 static_assert(Traits::has_subscript_operator<T>::value,
0442 "The subscript operator was called on a type that does not implement it.\n");
0443 static_assert(Traits::has_no_allocated_data<T>::value,
0444 "Invalid container type in gather/scatter operation.\nYou may only use "
0445 "nested containers that store the data inside the object (such as builtin "
0446 "arrays or std::array) but not containers that store data in allocated "
0447 "memory (such as std::vector).\nSince this feature cannot be queried "
0448 "generically at compile time you need to spezialize the "
0449 "Vc::Traits::has_no_allocated_data_impl<T> type-trait for custom types that "
0450 "meet the requirements.\n");
0451 return {&(m_address[0][0]),
0452 applyScaleAndAdd<std::ratio_multiply<
0453 Scale, std::ratio<sizeof(T), sizeof(m_address[0][0])>>>(
0454 convertIndexVector(m_indexes), index)};
0455 }
0456 };
0457
0458
0459 template <typename T, typename IndexVector, typename Scale>
0460 class SubscriptOperation<T, IndexVector, Scale, false>;
0461
0462
0463 template <
0464 typename Container,
0465 typename IndexVector,
0466 typename = enable_if<
0467 Traits::has_subscript_operator<IndexVector>::value
0468
0469 &&Traits::has_contiguous_storage<Container>::value
0470
0471
0472
0473 &&std::is_lvalue_reference<decltype(*begin(std::declval<
0474 Container>()))>::value
0475
0476
0477 >>
0478 Vc_ALWAYS_INLINE SubscriptOperation<
0479 typename std::remove_reference<decltype(*begin(std::declval<Container>()))>::
0480 type,
0481
0482
0483
0484
0485 typename std::remove_const<typename std::remove_reference<
0486 IndexVector>::type>::type
0487
0488
0489 > subscript_operator(Container &&c, IndexVector &&indexes)
0490 {
0491 Vc_ASSERT(std::addressof(*begin(c)) + 1 ==
0492 std::addressof(*(begin(c) + 1)));
0493
0494
0495
0496 return {std::addressof(*begin(c)), std::forward<IndexVector>(indexes)};
0497 }
0498
0499
0500
0501
0502
0503
0504
0505
0506
0507
0508 template <typename Container, typename I>
0509 Vc_ALWAYS_INLINE Vc::Common::SubscriptOperation<
0510 typename std::remove_reference<decltype(std::declval<Container>()[0])>::type,
0511 const std::initializer_list<I> &> subscript_operator(Container &&vec,
0512 const std::initializer_list<I> &indexes)
0513 {
0514 return {&vec[0], indexes};
0515 }
0516
0517
0518 }
0519
0520 using Common::subscript_operator;
0521
0522 }
0523
0524 #endif
0525
0526