Vc 1.4.5
SIMD Vector Classes for C++
 
Loading...
Searching...
No Matches
subscript.h
1/* This file is part of the Vc library. {{{
2Copyright © 2013-2015 Matthias Kretz <kretz@kde.org>
3
4Redistribution and use in source and binary forms, with or without
5modification, are permitted provided that the following conditions are met:
6 * Redistributions of source code must retain the above copyright
7 notice, this list of conditions and the following disclaimer.
8 * Redistributions in binary form must reproduce the above copyright
9 notice, this list of conditions and the following disclaimer in the
10 documentation and/or other materials provided with the distribution.
11 * Neither the names of contributing organizations nor the
12 names of its contributors may be used to endorse or promote products
13 derived from this software without specific prior written permission.
14
15THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
19DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26}}}*/
27
28#ifndef VC_COMMON_SUBSCRIPT_H_
29#define VC_COMMON_SUBSCRIPT_H_
30
31#include <initializer_list>
32#include <type_traits>
33#include <vector>
34#include "types.h"
35#include "macros.h"
36#include <assert.h>
37
38namespace Vc_VERSIONED_NAMESPACE
39{
40namespace Common
41{
42// AdaptSubscriptOperator {{{
43template <typename Base> class AdaptSubscriptOperator : public Base
44{
45public:
46 // perfect forward all Base constructors
47 template <typename... Args>
48 Vc_ALWAYS_INLINE AdaptSubscriptOperator(Args &&... arguments)
49 : Base(std::forward<Args>(arguments)...)
50 {
51 }
52
53 // perfect forward all Base constructors
54 template <typename T>
55 Vc_ALWAYS_INLINE AdaptSubscriptOperator(std::initializer_list<T> l)
56 : Base(l)
57 {
58 }
59
60 // explicitly enable Base::operator[] because the following would hide it
61 using Base::operator[];
62
64 template <typename I,
65 typename = enable_if<!std::is_arithmetic<
66 typename std::decay<I>::type>::value> // arithmetic types
67 // should always use
68 // Base::operator[] and
69 // never match this one
70 >
71 Vc_ALWAYS_INLINE auto operator[](I &&arg_)
72 -> decltype(subscript_operator(*this, std::forward<I>(arg_)))
73 {
74 return subscript_operator(*this, std::forward<I>(arg_));
75 }
76
77 // const overload of the above
78 template <typename I, typename = enable_if<
79 !std::is_arithmetic<typename std::decay<I>::type>::value>>
80 Vc_ALWAYS_INLINE auto operator[](I &&arg_) const
81 -> decltype(subscript_operator(*this, std::forward<I>(arg_)))
82 {
83 return subscript_operator(*this, std::forward<I>(arg_));
84 }
85};
86
87// }}}
88// is_valid_indexvector {{{
89template <class T, class = decltype(convertIndexVector(std::declval<T>()))>
90std::true_type is_valid_indexvector(T &&);
91std::false_type is_valid_indexvector(...);
92
93template <class IndexVector, class Test = decltype(is_valid_indexvector(
94 std::declval<const IndexVector &>()))>
95struct is_valid_indexvector_ : public std::integral_constant<bool, Test::value> {
96};
97static_assert(!is_valid_indexvector_<const int *>::value,
98 "Pointer is incorrectly classified as valid index vector type");
99static_assert(is_valid_indexvector_<const int[4]>::value,
100 "C-Array is incorrectly classified as invalid index vector type");
101
102// }}}
103// apply Scale (std::ratio) functions {{{1
104template <typename Scale, typename T>
105Vc_ALWAYS_INLINE enable_if<Scale::num == Scale::den, Traits::decay<T>> applyScale(T &&x)
106{
107 return std::forward<T>(x);
108}
109
110template <typename Scale, typename T>
111Vc_ALWAYS_INLINE enable_if<
112 Scale::num != Scale::den && Traits::has_multiply_operator<T, int>::value,
113 Traits::decay<T>>
114 applyScale(T &&x)
115{
116 static_assert(Scale::num % Scale::den == 0,
117 "Non-integral index scaling requested. This typically happens only for "
118 "Vc::Scalar on 32-bit for gathers on double. You can work around the "
119 "issue by ensuring that all doubles in the structure are aligned on 8 "
120 "Bytes.");
121 constexpr int value = Scale::num / Scale::den;
122 Vc_ASSERT(Vc::all_of((x * value) / value == x));
123 return std::forward<T>(x) * value;
124}
125
126template <typename Scale, typename T>
127Vc_ALWAYS_INLINE enable_if<
128 Scale::num != Scale::den && !Traits::has_multiply_operator<T, int>::value,
129 T>
130 applyScale(T x)
131{
132 static_assert(Scale::num % Scale::den == 0,
133 "Non-integral index scaling requested. This typically happens only for "
134 "Vc::Scalar on 32-bit for gathers on double. You can work around the "
135 "issue by ensuring that all doubles in the structure are aligned on 8 "
136 "Bytes.");
137 constexpr int value = Scale::num / Scale::den;
138 for (size_t i = 0; i < x.size(); ++i) {
139 Vc_ASSERT((x[i] * value) / value == x[i]);
140 x[i] *= value;
141 }
142 return x;
143}
144
145template <typename Scale, typename T, typename U,
146 typename = enable_if<Traits::has_multiply_operator<T, int>::value &&
147 Traits::has_addition_operator<T, U>::value>>
148Vc_ALWAYS_INLINE typename std::decay<T>::type applyScaleAndAdd(T &&x, U &&y)
149{
150 constexpr int value = Scale::num / Scale::den;
151 if (value == 1) { // static evaluation
152 return std::forward<T>(x) + std::forward<U>(y);
153 }
154 return std::forward<T>(x) * value + std::forward<U>(y);
155}
156
157template <
158 typename Scale, typename T, typename U,
159 typename = enable_if<
160 !(Traits::has_multiply_operator<T &, int>::value &&
161 Traits::has_addition_operator<T &, decltype(std::declval<U>()[0])>::value) &&
162 Traits::has_subscript_operator<U>::value>>
163Vc_ALWAYS_INLINE T applyScaleAndAdd(T x, U &&y)
164{
165 constexpr int value = Scale::num / Scale::den;
166 for (size_t i = 0; i < x.size(); ++i) {
167 if (value == 1) { // static evaluation
168 x[i] = x[i] + y[i];
169 } else {
170 x[i] = x[i] * value + y[i];
171 }
172 }
173 return x;
174}
175
176template <typename Scale, typename T, typename U>
177Vc_ALWAYS_INLINE enable_if<!(Traits::has_multiply_operator<T &, int>::value &&
178 Traits::has_addition_operator<T &, U>::value) &&
179 !Traits::has_subscript_operator<U>::value,
180 T>
181 applyScaleAndAdd(T x, U &&y)
182{
183 constexpr int value = Scale::num / Scale::den;
184 for (size_t i = 0; i < x.size(); ++i) {
185 if (value == 1) { // static evaluation
186 x[i] = x[i] + y;
187 } else {
188 x[i] = x[i] * value + y;
189 }
190 }
191 return x;
192}
193
194// IndexVectorSizeMatches {{{1
195template <std::size_t MinSize,
196 typename IndexT,
197 bool = Traits::is_simd_vector<IndexT>::value>
198struct IndexVectorSizeMatches
199 : public std::true_type // you might expect this should be false_type here, but the point is
200 // that IndexT is a type where the size is not known at compile time.
201 // Thus it may be good but we cannot know from the type. The only check
202 // we could do is a runtime check, but the type is fine.
203{
204};
205
206template <std::size_t MinSize, typename V>
207struct IndexVectorSizeMatches<MinSize,
208 V,
209 true> : public std::integral_constant<bool, (MinSize <= V::Size)>
210{
211};
212
213template <std::size_t MinSize, typename T, std::size_t ArraySize>
214struct IndexVectorSizeMatches<MinSize,
215 T[ArraySize],
216 false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
217{
218};
219
220template <std::size_t MinSize, typename T, std::size_t ArraySize>
221struct IndexVectorSizeMatches<MinSize,
222 std::array<T, ArraySize>,
223 false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
224{
225};
226
227template <std::size_t MinSize, typename T, std::size_t ArraySize>
228struct IndexVectorSizeMatches<MinSize,
229 Vc::array<T, ArraySize>,
230 false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
231{
232};
233
234template <std::size_t MinSize, typename T, std::ptrdiff_t N>
235struct IndexVectorSizeMatches<MinSize, Vc::Common::span<T, N>, false>
236 : public std::integral_constant<bool, (N == -1 || static_cast<std::ptrdiff_t>(MinSize) <= N)> {
237};
238// SubscriptOperation {{{1
239template <
240 typename T, typename IndexVector, typename Scale = std::ratio<1, 1>,
241 bool = is_valid_indexvector_<IndexVector>::value>
242class SubscriptOperation
243{
244 const IndexVector m_indexes;
245 T *const m_address;
246 using ScalarType = typename std::decay<T>::type;
247
248 using IndexVectorScaled = Traits::decay<decltype(convertIndexVector(std::declval<const IndexVector &>()))>;
249
250public:
251 // try to stop the user from forming lvalues of this type
252 SubscriptOperation &operator=(const SubscriptOperation &) = delete;
253 SubscriptOperation(const SubscriptOperation &) = delete;
254#ifndef __cpp_guaranteed_copy_elision
255 constexpr SubscriptOperation(SubscriptOperation &&) = default;
256#endif
257
258 template <typename U,
259 typename = enable_if<((std::is_convertible<const U &, IndexVector>::value ||
260 std::is_same<U, IndexVector>::value) &&
261 std::is_copy_constructible<IndexVector>::value)>>
262 constexpr Vc_ALWAYS_INLINE SubscriptOperation(T *address, const U &indexes)
263 : m_indexes(indexes), m_address(address)
264 {
265 }
266
267 template <std::size_t... Indexes>
268 constexpr Vc_ALWAYS_INLINE SubscriptOperation(T *address, const IndexVector &indexes,
269 index_sequence<Indexes...>)
270 : m_indexes{indexes[Indexes]...}, m_address(address)
271 {}
272
273 template <typename U>
274 constexpr Vc_ALWAYS_INLINE SubscriptOperation(
275 T *address, const U &indexes,
276 enable_if<((std::is_convertible<const U &, IndexVector>::value ||
277 std::is_same<U, IndexVector>::value) &&
278 !std::is_copy_constructible<IndexVector>::value &&
279 std::is_array<IndexVector>::value &&
280 std::extent<IndexVector>::value > 0)> = nullarg)
281 : SubscriptOperation(address, indexes,
282 make_index_sequence<std::extent<IndexVector>::value>())
283 {
284 }
285
286 static constexpr bool need_explicit_scaling =
287 Scale::num % Scale::den != 0 || Scale::num / Scale::den * sizeof(T) > 8;
288
289 Vc_ALWAYS_INLINE
290 GatherArguments<typename std::remove_cv<T>::type, IndexVectorScaled,
291 (need_explicit_scaling ? 1 : Scale::num / Scale::den)>
292 gatherArguments() &&
293 {
294 static_assert(std::is_arithmetic<ScalarType>::value,
295 "Incorrect type for a SIMD vector gather. Must be an arithmetic type.");
296 return {applyScale<typename std::conditional<need_explicit_scaling, Scale,
297 std::ratio<1, 1>>::type>(
298 convertIndexVector(m_indexes)),
299 m_address};
300 }
301
302 Vc_ALWAYS_INLINE ScatterArguments<T, IndexVectorScaled> scatterArguments() &&
303 {
304 static_assert(std::is_arithmetic<ScalarType>::value,
305 "Incorrect type for a SIMD vector scatter. Must be an arithmetic type.");
306 return {applyScale<Scale>(convertIndexVector(m_indexes)), m_address};
307 }
308
309 template <typename V,
310 typename = enable_if<(std::is_arithmetic<ScalarType>::value &&Traits::is_simd_vector<
311 V>::value &&IndexVectorSizeMatches<V::Size, IndexVector>::value)>>
312 Vc_INTRINSIC operator V() &&
313 {
314 return V(static_cast<SubscriptOperation &&>(*this).gatherArguments());
315 }
316
317 template <typename V,
318 typename = enable_if<(std::is_arithmetic<ScalarType>::value &&Traits::is_simd_vector<
319 V>::value &&IndexVectorSizeMatches<V::Size, IndexVector>::value)>>
320 Vc_ALWAYS_INLINE SubscriptOperation &operator=(const V &rhs) &&
321 {
322 static_assert(std::is_arithmetic<ScalarType>::value,
323 "Incorrect type for a SIMD vector scatter. Must be an arithmetic type.");
324 const auto indexes = applyScale<Scale>(convertIndexVector(m_indexes));
325 rhs.scatter(m_address, indexes);
326 return *this;
327 }
328
329 // precondition: m_address points to a struct/class/union
330 template <
331 typename U,
332 typename S, // S must be equal to T. Still we require this template parameter -
333 // otherwise instantiation of SubscriptOperation would only be valid for
334 // structs/unions.
335 typename = enable_if<std::is_same<S, typename std::remove_cv<T>::type>::value &&(
336 std::is_class<T>::value || std::is_union<T>::value)>>
337 Vc_ALWAYS_INLINE auto operator[](U S::*member) &&
338 -> SubscriptOperation<
339 typename std::conditional<std::is_const<T>::value,
340 const typename std::remove_reference<U>::type,
341 typename std::remove_reference<U>::type>::type,
342 IndexVector,
343 // By passing the scale factor as a fraction of integers in the template
344 // arguments the value does not lose information if the division yields a
345 // non-integral value. This could happen e.g. for a struct of struct (S2 {
346 // S1, char }, with sizeof(S1) = 16, sizeof(S2) = 20. Then scale would be
347 // 20/16)
348 std::ratio_multiply<Scale, std::ratio<sizeof(S), sizeof(U)>>>
349 {
350 static_assert(std::is_same<Traits::decay<decltype(m_address->*member)>,
351 Traits::decay<U>>::value,
352 "Type mismatch that should be impossible.");
353 // TODO: check whether scale really works for unions correctly
354 return {&(m_address->*member), m_indexes};
355 }
356
357 /*
358 * The following functions allow subscripting of nested arrays. But
359 * there are two cases of containers and only one that we want to support:
360 * 1. actual arrays (e.g. T[N] or std::array<T, N>)
361 * 2. dynamically allocated vectors (e.g. std::vector<T>)
362 *
363 * For (1.) the offset calculation is straightforward.
364 * For (2.) the m_address pointer points to memory where pointers are
365 * stored to the actual data. Meaning the data can be scattered
366 * freely in memory (and far away from what m_address points to). Supporting this leads to
367 * serious trouble with the pointer (it does not really point to the start of a memory
368 * region anymore) and inefficient code. The user is better off to write a loop that assigns the
369 * scalars to the vector object sequentially.
370 */
371
372private:
373 // The following is a workaround for MSVC 2015 Update 2. Whenever the ratio
374 // in the return type of the following operator[] is encountered with a sizeof
375 // expression that fails, MSVC decides to substitute a 0 for the sizeof instead of
376 // just leaving the ratio instantiation alone via proper SFINAE. The make_ratio helper
377 // ensures that the 0 from the sizeof failure does not reach the denominator of
378 // std::ratio where it would hit a static_assert.
379 template <intmax_t N, intmax_t D> struct make_ratio {
380 using type = std::ratio<N, D == 0 ? 1 : D>;
381 };
382
383public:
384 // precondition: m_address points to a type that implements the subscript operator
385 template <typename U>
386 // U is only required to delay name lookup to the 2nd phase (on use).
387 // This is necessary because m_address[0][index] is only a correct
388 // expression if has_subscript_operator<T>::value is true.
389 Vc_ALWAYS_INLINE auto operator[](U index) && -> typename std::enable_if<
390#ifndef Vc_IMPROVE_ERROR_MESSAGES
391 Traits::has_no_allocated_data<T>::value &&
392#endif
393 std::is_convertible<U, size_t>::value,
394 SubscriptOperation<
395 // the following decltype expression must depend on index and cannot
396 // simply use [0][0] because it would yield an invalid expression in
397 // case m_address[0] returns a struct/union
398 typename std::remove_reference<decltype(m_address[0][index])>::type,
399 IndexVector,
400 std::ratio_multiply<
401 Scale,
402 typename make_ratio<sizeof(T), sizeof(m_address[0][index])>::type>>>::type
403 {
404 static_assert(Traits::has_subscript_operator<T>::value,
405 "The subscript operator was called on a type that does not implement it.\n");
406 static_assert(Traits::has_no_allocated_data<T>::value,
407 "Invalid container type in gather/scatter operation.\nYou may only use "
408 "nested containers that store the data inside the object (such as builtin "
409 "arrays or std::array) but not containers that store data in allocated "
410 "memory (such as std::vector).\nSince this feature cannot be queried "
411 "generically at compile time you need to spezialize the "
412 "Vc::Traits::has_no_allocated_data_impl<T> type-trait for custom types that "
413 "meet the requirements.\n");
414 static_assert(std::is_lvalue_reference<decltype(m_address[0][index])>::value,
415 "The container does not return an lvalue reference to the data at "
416 "the requested offset. This makes it impossible to execute a "
417 "gather operation.\n");
418 return {&(m_address[0][index]), m_indexes};
419 }
420
421 // precondition: m_address points to a type that implements the subscript operator
422 template <typename IT>
423 Vc_ALWAYS_INLINE typename std::enable_if<
424#ifndef Vc_IMPROVE_ERROR_MESSAGES
425 Traits::has_no_allocated_data<T>::value &&
426 Traits::has_subscript_operator<T>::value &&
427#endif
428 Traits::has_subscript_operator<IT>::value,
429 SubscriptOperation<typename std::remove_reference<decltype(
430 m_address[0][std::declval<
431 const IT &>()[0]] // std::declval<IT>()[0] could
432 // be replaced with 0 if it
433 // were not for two-phase lookup. We need to make the
434 // m_address[0][0] expression dependent on IT
435 )>::type,
436 IndexVectorScaled,
437 std::ratio<1, 1> // reset Scale to 1 since it is applied below
438 >>::type
439 operator[](const IT &index) &&
440 {
441 static_assert(Traits::has_subscript_operator<T>::value,
442 "The subscript operator was called on a type that does not implement it.\n");
443 static_assert(Traits::has_no_allocated_data<T>::value,
444 "Invalid container type in gather/scatter operation.\nYou may only use "
445 "nested containers that store the data inside the object (such as builtin "
446 "arrays or std::array) but not containers that store data in allocated "
447 "memory (such as std::vector).\nSince this feature cannot be queried "
448 "generically at compile time you need to spezialize the "
449 "Vc::Traits::has_no_allocated_data_impl<T> type-trait for custom types that "
450 "meet the requirements.\n");
451 return {&(m_address[0][0]),
452 applyScaleAndAdd<std::ratio_multiply<
453 Scale, std::ratio<sizeof(T), sizeof(m_address[0][0])>>>(
454 convertIndexVector(m_indexes), index)};
455 }
456};
457
458// specialization for invalid IndexVector type
459template <typename T, typename IndexVector, typename Scale>
460class SubscriptOperation<T, IndexVector, Scale, false>;
461
462// subscript_operator {{{1
463template <
464 typename Container,
465 typename IndexVector,
466 typename = enable_if<
467 Traits::has_subscript_operator<IndexVector>::value // The index vector must provide [] for
468 // the implementations of gather/scatter
469 &&Traits::has_contiguous_storage<Container>::value // Container must use contiguous
470 // storage, otherwise the index vector
471 // cannot be used as memory offsets, which is required for efficient
472 // gather/scatter implementations
473 &&std::is_lvalue_reference<decltype(*begin(std::declval<
474 Container>()))>::value // dereferencing the begin iterator must yield an lvalue
475 // reference (const or non-const). Otherwise it is not possible
476 // to determine a pointer to the data storage (see above).
477 >>
478Vc_ALWAYS_INLINE SubscriptOperation<
479 typename std::remove_reference<decltype(*begin(std::declval<Container>()))>::
480 type, // the type of the first value in the container is what the internal array pointer
481 // has to point to. But if the subscript operator of the container returns a
482 // reference we need to drop that part because it's useless information for us. But
483 // const and volatile, as well as array rank/extent are interesting and need not be
484 // dropped.
485 typename std::remove_const<typename std::remove_reference<
486 IndexVector>::type>::type // keep volatile and possibly the array extent, but the const and
487 // & parts of the type need to be removed because
488 // SubscriptOperation explicitly adds them for its member type
489 > subscript_operator(Container &&c, IndexVector &&indexes)
490{
491 Vc_ASSERT(std::addressof(*begin(c)) + 1 ==
492 std::addressof(*(begin(c) + 1))); // runtime assertion for contiguous storage, this
493 // requires a RandomAccessIterator - but that
494 // should be given for a container with contiguous
495 // storage
496 return {std::addressof(*begin(c)), std::forward<IndexVector>(indexes)};
497}
498
508template <typename Container, typename I>
509Vc_ALWAYS_INLINE Vc::Common::SubscriptOperation<
510 typename std::remove_reference<decltype(std::declval<Container>()[0])>::type,
511 const std::initializer_list<I> &> subscript_operator(Container &&vec,
512 const std::initializer_list<I> &indexes)
513{
514 return {&vec[0], indexes};
515}
516//}}}1
517
518} // namespace Common
519
520using Common::subscript_operator;
521
522} // namespace Vc
523
524#endif // VC_COMMON_SUBSCRIPT_H_
525
526// vim: foldmethod=marker
constexpr bool all_of(const Mask &m)
Returns whether all entries in the mask m are true.
Definition mask.h:397