Enable clang-tidy for headers
[alexxy/gromacs.git] / src / gromacs / simd / simd_memory.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2017,2018, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \libinternal \file
36  * \brief Declares SimdArrayRef
37  *
38  * \author Roland Schulz <roland.schulz@intel.com>
39  * \inlibraryapi
40  * \ingroup module_simd
41  */
42 #ifndef GMX_SIMD_SIMD_MEMORY_H
43 #define GMX_SIMD_SIMD_MEMORY_H
44
45 #include "gromacs/utility/arrayref.h"
46 #include "gromacs/utility/gmxassert.h"
47
48 namespace gmx
49 {
50 namespace internal
51 {
52
53 template<typename T>
54 class SimdReference
55 {
56     private:
57         using non_const_T = typename std::remove_const<T>::type;
58         using pointer     = typename SimdTraits<T>::type*;
59     public:
60         //! \brief Constructor
61         // cppcheck-suppress uninitMemberVar
62         explicit SimdReference(pointer m) : m_(m) {}
63         //! \brief Conversion method that will execute load
64         operator non_const_T() const { return load<non_const_T>(m_); }
65         //! \brief Assignment operator that will execute store
66         SimdReference operator=(T o) // NOLINT(misc-unconventional-assign-operator,cppcoreguidelines-c-copy-assignment-signature)
67         {
68             store(m_, o);
69             return *this;
70         }
71         //! \brief Addition assignment operator that will execute load+store
72         SimdReference operator+=(T o)
73         {
74             store(m_, load<non_const_T>(m_) + o);
75             return *this;
76         }
77         //! \brief Subtraction assignment operator that will execute load+store
78         SimdReference operator-=(T o)
79         {
80             store(m_, load<non_const_T>(m_) - o);
81             return *this;
82         }
83         //! \brief Multiplication assignment operator that will execute load+store
84         SimdReference operator*=(T o)
85         {
86             store(m_, load<non_const_T>(m_) * o);
87             return *this;
88         }
89     private:
90         pointer const m_; //!< The pointer used to load memory
91 };
92
93 template<typename T>
94 class SimdIterator
95 {
96     public:
97         //! Type for representing size of the container.
98         using size_type         = size_t;
99         //! Type for representing difference between two container indices.
100         using difference_type   = std::ptrdiff_t;
101         //! Type of values stored in the container.
102         using value_type        = T;
103         //! Pointer to a container element.
104         using pointer           = typename SimdTraits<T>::type*;
105         //! Reference to a container element.
106         using reference         = internal::SimdReference<T>;
107
108         explicit SimdIterator(pointer p = 0) : p_(p)
109         {
110             GMX_ASSERT((reinterpret_cast<size_type>(p)/sizeof(*p))%simdWidth == 0,
111                        "Trying to create aligned iterator for non aligned address.");
112         }
113         SimdIterator &operator++()
114         {
115             p_ += simdWidth;
116             return *this;
117         }
118         SimdIterator operator++(int)
119         {
120             SimdIterator retval = *this;
121             ++(*this);
122             return retval;
123         }
124         SimdIterator &operator--()
125         {
126             p_ -= simdWidth;
127             return *this;
128         }
129         SimdIterator operator--(int)
130         {
131             SimdIterator retval = *this;
132             --(*this);
133             return retval;
134         }
135         SimdIterator &operator+=(difference_type d)
136         {
137             p_ += simdWidth * d;
138             return *this;
139         }
140         SimdIterator &operator-=(difference_type d)
141         {
142             p_ -= simdWidth * d;
143             return *this;
144         }
145         SimdIterator operator+(difference_type d) { return SimdIterator(p_ + simdWidth*d); }
146         SimdIterator operator-(difference_type d) { return SimdIterator(p_ - simdWidth*d); }
147
148         difference_type operator-(SimdIterator o) { return (p_ - o.p_)/simdWidth; }
149
150         bool operator==(SimdIterator other) const { return p_ == other.p_; }
151         bool operator!=(SimdIterator other) const { return p_ != other.p_; }
152         bool operator< (SimdIterator other) const { return p_ <  other.p_; }
153         bool operator> (SimdIterator other) const { return p_ >  other.p_; }
154         bool operator<=(SimdIterator other) const { return p_ <= other.p_; }
155         bool operator>=(SimdIterator other) const { return p_ >= other.p_; }
156
157         reference operator*() const { return reference(p_); }
158     private:
159         pointer              p_;
160         static constexpr int simdWidth = SimdTraits<T>::width;
161 };
162
163 /*! \internal
164  * \brief STL-like container for aligned SIMD type. Used as ArrayRef<SimdReal>.
165  *
166  * Should provide the same interface as ArrayRef. Any missing functions should be
167  * added as needed. The pointer type (used e.g. for initialization) is a real
168  * pointer. The reference type (used e.g. for operator[] and iterator dereference)
169  * is SimdReference which executes the aligned load/store as is appropriate. For
170  * both iterator and element access, the access happens in blocks of SIMD width.
171  * Meaning that a[1] refers to the 2nd SIMD vector and thus reals 8-15 for 8-wide
172  * SIMD. The starting address has to be aligned and the length has to be multiple
173  * of the SIMD width.
174  *
175  * \tparam T SIMD type (e.g. SimdReal)
176  */
177 template<typename T>
178 class SimdArrayRef
179 {
180     public:
181         //! Type for representing size of the container.
182         using size_type         = size_t;
183         //! Type for representing difference between two container indices.
184         using difference_type   = std::ptrdiff_t;
185         //! Type of values stored in the container.
186         using value_type        = T;
187         //! Pointer to a container element.
188         using pointer           = typename SimdTraits<T>::type*;
189         //! Reference to a container element.
190         using reference         = internal::SimdReference<T>;
191         //! Iterator type for the container.
192         using iterator          = SimdIterator<T>;
193         //! Standard reverse iterator.
194         using reverse_iterator  = std::reverse_iterator<iterator>;
195
196         //! \copydoc ArrayRef::ArrayRef(pointer, pointer)
197         SimdArrayRef(pointer begin, pointer end)
198             : begin_(begin), end_(end)
199         {
200             GMX_ASSERT(end >= begin, "Invalid range");
201             GMX_ASSERT((reinterpret_cast<size_type>(begin)/sizeof(*begin))%simdWidth == 0,
202                        "Aligned ArrayRef requires aligned starting address");
203             GMX_ASSERT((reinterpret_cast<size_type>(end)/sizeof(*end))%simdWidth == 0,
204                        "Size of ArrayRef needs to be divisible by type size");
205         }
206         //! \copydoc ArrayRef::ArrayRef(const EmptyArrayRef&)
207         SimdArrayRef(const EmptyArrayRef & /*unused*/) : begin_(nullptr), end_(nullptr) {}
208         //! \copydoc ArrayRef::ArrayRef(U)
209         template<typename U,
210                  typename = typename std::enable_if<
211                          std::is_convertible<typename std::remove_reference<U>::type::pointer,
212                                              pointer>::value>::type>
213         SimdArrayRef(U &&o) : begin_(reinterpret_cast<pointer>(o.data())),
214                               end_(reinterpret_cast<pointer>(o.data()+o.size())) {}
215         //reinterpret_cast is only needed for const conversion of SimdArrayRef itself.
216         //All other containers have type(o.data())==U::pointer (the cast does nothing).
217
218         //! Returns the size of the container.
219         size_type size() const { return (end_-begin_)/simdWidth; }
220         //! Whether the container is empty.
221         bool empty() const { return begin_ == end_; }
222         //! Returns an iterator to the beginning of the container.
223         iterator begin() const { return iterator(begin_); }
224         //! Returns an iterator to the end of the container.
225         iterator end() const { return iterator(end_); }
226
227         //! Access container element.
228         reference operator[](size_type n)
229         {
230             return reference(begin_+n*simdWidth);
231         }
232
233         //! Returns the first element in the container.
234         reference front() const { return reference(begin_); }
235         //! Returns the first element in the container.
236         reference back() const { return reference(end_ - simdWidth); }
237     private:
238         static constexpr int simdWidth = SimdTraits<T>::width;
239         using pack_type = typename SimdTraits<T>::type[simdWidth];
240         // Private because dereferencing return value is undefined behavior (strict aliasing rule)
241         // Only use is conversion constructor above which immediately casts it back.
242         // Return type is not "pointer" because then data()+size() would be ill defined.
243         // Has to be pack_type and not value_type in case
244         // sizeof(value_type)/sizeof(pointer)!=simdWidth (e.g. int32 for double SSE2).
245         pack_type* data() const { return reinterpret_cast<pack_type*>(begin_); }
246
247         template<typename U>
248         friend class SimdArrayRef;
249
250         pointer const        begin_;
251         pointer const        end_;
252 };
253
254 }   //namespace internal
255
256 /* Specialize ArraryRef<SimdReal>
257  * So far only an aligned version is implemented. The constructor verifies that
258  * a ArrayRef<SimdReal> is constructed only for properly aligned data.
259  */
260 #if GMX_SIMD_HAVE_FLOAT
261 template<>
262 class ArrayRef<SimdFloat> : public internal::SimdArrayRef<SimdFloat>
263 {
264     using Base = internal::SimdArrayRef<SimdFloat>;
265     using Base::Base;
266 };
267 template<>
268 class ArrayRef<const SimdFloat> : public internal::SimdArrayRef<const SimdFloat>
269 {
270     using Base = internal::SimdArrayRef<const SimdFloat>;
271     using Base::Base;
272 };
273 #endif
274 #if GMX_SIMD_HAVE_DOUBLE
275 template<>
276 class ArrayRef<SimdDouble> : public internal::SimdArrayRef<SimdDouble>
277 {
278     using Base = internal::SimdArrayRef<SimdDouble>;
279     using Base::Base;
280 };
281 template<>
282 class ArrayRef<const SimdDouble> : public internal::SimdArrayRef<const SimdDouble>
283 {
284     using Base = internal::SimdArrayRef<const SimdDouble>;
285     using Base::Base;
286 };
287 #endif
288
289 } // namespace gmx
290
291 #endif