Vc  1.4.1
SIMD Vector Classes for C++
interleavedmemory.h
1 /* This file is part of the Vc library. {{{
2 Copyright © 2012-2015 Matthias Kretz <kretz@kde.org>
3 
4 Redistribution and use in source and binary forms, with or without
5 modification, 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 
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
19 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 ON 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
24 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 }}}*/
27 
28 #ifndef VC_COMMON_INTERLEAVEDMEMORY_H_
29 #define VC_COMMON_INTERLEAVEDMEMORY_H_
30 
31 #include "macros.h"
32 
33 namespace Vc_VERSIONED_NAMESPACE
34 {
35 namespace Common
36 {
40 template<typename V, typename I, bool Readonly> struct InterleavedMemoryAccessBase
41 {
42  // Partial specialization doesn't work for functions without partial specialization of the whole
43  // class. Therefore we capture the contents of InterleavedMemoryAccessBase in a macro to easily
44  // copy it into its specializations.
45  typedef typename std::conditional<
46  Readonly, typename std::add_const<typename V::EntryType>::type,
47  typename V::EntryType>::type T;
48  typedef typename V::AsArg VArg;
49  typedef T Ta Vc_MAY_ALIAS;
50  const I m_indexes;
51  Ta *const m_data;
52 
53  Vc_ALWAYS_INLINE InterleavedMemoryAccessBase(typename I::AsArg indexes, Ta *data)
54  : m_indexes(indexes), m_data(data)
55  {
56  }
57 
58  // implementations of the following are in {scalar,sse,avx}/detail.h
59  template <typename... Vs> Vc_INTRINSIC void deinterleave(Vs &&... vs) const
60  {
61  Impl::deinterleave(m_data, m_indexes, std::forward<Vs>(vs)...);
62  }
63 
64 protected:
65  using Impl = Vc::Detail::InterleaveImpl<V, V::Size, sizeof(V)>;
66 
67  template <typename T, std::size_t... Indexes>
68  Vc_INTRINSIC void callInterleave(T &&a, index_sequence<Indexes...>)
69  {
70  Impl::interleave(m_data, m_indexes, a[Indexes]...);
71  }
72 };
73 
77 // delay execution of the deinterleaving gather until operator=
78 template <size_t StructSize, typename V, typename I = typename V::IndexType,
79  bool Readonly>
80 struct InterleavedMemoryReadAccess : public InterleavedMemoryAccessBase<V, I, Readonly>
81 {
82  typedef InterleavedMemoryAccessBase<V, I, Readonly> Base;
83  typedef typename Base::Ta Ta;
84 
85  Vc_ALWAYS_INLINE InterleavedMemoryReadAccess(Ta *data, typename I::AsArg indexes)
86  : Base(StructSize == 1u
87  ? indexes
88  : StructSize == 2u
89  ? indexes << 1
90  : StructSize == 4u
91  ? indexes << 2
92  : StructSize == 8u
93  ? indexes << 3
94  : StructSize == 16u ? indexes << 4
95  : indexes * I(int(StructSize)),
96  data)
97  {
98  }
99 
100  template <typename T, std::size_t... Indexes>
101  Vc_ALWAYS_INLINE T deinterleave_unpack(index_sequence<Indexes...>) const
102  {
103  T r;
104  Base::Impl::deinterleave(this->m_data, this->m_indexes, std::get<Indexes>(r)...);
105  return r;
106  }
107 
108  template <typename T,
109  typename = enable_if<(std::is_default_constructible<T>::value &&
110  std::is_same<V, Traits::decay<decltype(std::get<0>(
111  std::declval<T &>()))>>::value)>>
112  Vc_ALWAYS_INLINE operator T() const
113  {
114  return deinterleave_unpack<T>(make_index_sequence<std::tuple_size<T>::value>());
115  }
116 };
117 
119 template<typename I> struct CheckIndexesUnique
120 {
121 #ifdef NDEBUG
122  static Vc_INTRINSIC void test(const I &) {}
123 #else
124  static void test(const I &indexes)
125  {
126  const I test = indexes.sorted();
127  Vc_ASSERT(I::Size == 1 || (test == test.rotated(1)).isEmpty())
128  }
129 #endif
130 };
132 template<size_t S> struct CheckIndexesUnique<SuccessiveEntries<S> >
133 {
134  static Vc_INTRINSIC void test(const SuccessiveEntries<S> &) {}
135 };
136 
140 template <size_t StructSize, typename V, typename I = typename V::IndexType>
141 struct InterleavedMemoryAccess : public InterleavedMemoryReadAccess<StructSize, V, I, false>
142 {
143  typedef InterleavedMemoryAccessBase<V, I, false> Base;
144  typedef typename Base::Ta Ta;
145 
146  Vc_ALWAYS_INLINE InterleavedMemoryAccess(Ta *data, typename I::AsArg indexes)
147  : InterleavedMemoryReadAccess<StructSize, V, I, false>(data, indexes)
148  {
149  CheckIndexesUnique<I>::test(indexes);
150  }
151 
152  template <int N> Vc_ALWAYS_INLINE void operator=(VectorReferenceArray<N, V> &&rhs)
153  {
154  static_assert(N <= StructSize,
155  "You_are_trying_to_scatter_more_data_into_the_struct_than_it_has");
156  this->callInterleave(std::move(rhs), make_index_sequence<N>());
157  }
158  template <int N> Vc_ALWAYS_INLINE void operator=(VectorReferenceArray<N, const V> &&rhs)
159  {
160  static_assert(N <= StructSize,
161  "You_are_trying_to_scatter_more_data_into_the_struct_than_it_has");
162  this->callInterleave(std::move(rhs), make_index_sequence<N>());
163  }
164 };
165 
177 template<typename S, typename V> class InterleavedMemoryWrapper
178 {
179  typedef typename std::conditional<std::is_const<S>::value,
180  const typename V::EntryType,
181  typename V::EntryType>::type T;
182  typedef typename V::IndexType I;
183  typedef typename V::AsArg VArg;
184  typedef const I &IndexType;
185  static constexpr std::size_t StructSize = sizeof(S) / sizeof(T);
186  using ReadAccess = InterleavedMemoryReadAccess<StructSize, V>;
187  using Access =
188  typename std::conditional<std::is_const<T>::value, ReadAccess,
189  InterleavedMemoryAccess<StructSize, V>>::type;
190  using ReadSuccessiveEntries =
191  InterleavedMemoryReadAccess<StructSize, V, SuccessiveEntries<StructSize>>;
192  using AccessSuccessiveEntries = typename std::conditional<
193  std::is_const<T>::value, ReadSuccessiveEntries,
194  InterleavedMemoryAccess<StructSize, V, SuccessiveEntries<StructSize>>>::type;
195  typedef T Ta Vc_MAY_ALIAS;
196  Ta *const m_data;
197 
198  static_assert(StructSize * sizeof(T) == sizeof(S),
199  "InterleavedMemoryAccess_does_not_support_packed_structs");
200 
201 public:
207  Vc_ALWAYS_INLINE InterleavedMemoryWrapper(S *s)
208  : m_data(reinterpret_cast<Ta *>(s))
209  {
210  }
211 
264  template <typename IT>
265  Vc_ALWAYS_INLINE enable_if<!std::is_convertible<IT, size_t>::value &&
266  std::is_convertible<IT, IndexType>::value &&
267  !std::is_const<S>::value,
268  Access>
269  operator[](IT indexes)
270  {
271  return Access(m_data, indexes);
272  }
273 
275  Vc_ALWAYS_INLINE ReadAccess operator[](IndexType indexes) const
276  {
277  return ReadAccess(m_data, indexes);
278  }
279 
281  Vc_ALWAYS_INLINE ReadAccess gather(IndexType indexes) const { return operator[](indexes); }
282 
316  Vc_ALWAYS_INLINE ReadSuccessiveEntries operator[](size_t first) const
317  {
318  return ReadSuccessiveEntries(m_data, first);
319  }
320 
321  Vc_ALWAYS_INLINE AccessSuccessiveEntries operator[](size_t first)
322  {
323  return AccessSuccessiveEntries(m_data, first);
324  }
325 
326  //Vc_ALWAYS_INLINE Access scatter(I indexes, VArg v0, VArg v1);
327 };
328 } // namespace Common
329 
331 
344 template <typename V, typename S>
346 {
348 }
349 } // namespace Vc
350 
351 #endif // VC_COMMON_INTERLEAVEDMEMORY_H_
Common::InterleavedMemoryWrapper< S, V > make_interleave_wrapper(S *s)
Creates an adapter around a given array of structure (AoS) that enables optimized loads...
enable_if<!std::is_convertible< IT, size_t >::value &&std::is_convertible< IT, IndexType >::value &&!std::is_const< S >::value, Access > operator[](IT indexes)
Interleaved scatter/gather access.
Wraps a pointer to memory with convenience functions to access it via vectors.
ReadSuccessiveEntries operator[](size_t first) const
Interleaved access.
ReadAccess operator[](IndexType indexes) const
const overload (gathers only) of the above function
InterleavedMemoryWrapper(S *s)
Constructs the wrapper object.
void deinterleave(V *a, V *b, const M *memory, A align)
Definition: deinterleave.h:76
std::pair< V, V > interleave(const V &a, const V &b)
Interleaves the entries from a and b into two vectors of the same type.
Definition: interleave.h:55
ReadAccess gather(IndexType indexes) const
alias of the above function