Apply clang-format to source tree
[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,2019, 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 = std::remove_const_t<T>;
58     using pointer     = typename SimdTraits<T>::type*;
59
60 public:
61     //! \brief Constructor
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
90 private:
91     pointer const m_; //!< The pointer used to load memory
92 };
93
94 template<typename T>
95 class SimdIterator
96 {
97 public:
98     //! Type for representing size of the container.
99     using size_type = size_t;
100     //! Type for representing difference between two container indices.
101     using difference_type = std::ptrdiff_t;
102     //! Type of values stored in the container.
103     using value_type = T;
104     //! Pointer to a container element.
105     using pointer = typename SimdTraits<T>::type*;
106     //! Reference to a container element.
107     using reference = internal::SimdReference<T>;
108
109     explicit SimdIterator(pointer p = 0) : p_(p)
110     {
111         GMX_ASSERT((reinterpret_cast<size_type>(p) / sizeof(*p)) % simdWidth == 0,
112                    "Trying to create aligned iterator for non aligned address.");
113     }
114     SimdIterator& operator++()
115     {
116         p_ += simdWidth;
117         return *this;
118     }
119     SimdIterator operator++(int)
120     {
121         SimdIterator retval = *this;
122         ++(*this);
123         return retval;
124     }
125     SimdIterator& operator--()
126     {
127         p_ -= simdWidth;
128         return *this;
129     }
130     SimdIterator operator--(int)
131     {
132         SimdIterator retval = *this;
133         --(*this);
134         return retval;
135     }
136     SimdIterator& operator+=(difference_type d)
137     {
138         p_ += simdWidth * d;
139         return *this;
140     }
141     SimdIterator& operator-=(difference_type d)
142     {
143         p_ -= simdWidth * d;
144         return *this;
145     }
146     SimdIterator operator+(difference_type d) { return SimdIterator(p_ + simdWidth * d); }
147     SimdIterator operator-(difference_type d) { return SimdIterator(p_ - simdWidth * d); }
148
149     difference_type operator-(SimdIterator o) { return (p_ - o.p_) / simdWidth; }
150
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     bool operator>=(SimdIterator other) const { return p_ >= other.p_; }
157
158     reference operator*() const { return reference(p_); }
159
160 private:
161     pointer              p_;
162     static constexpr int simdWidth = SimdTraits<T>::width;
163 };
164
165 /*! \internal
166  * \brief STL-like container for aligned SIMD type. Used as ArrayRef<SimdReal>.
167  *
168  * Should provide the same interface as ArrayRef. Any missing functions should be
169  * added as needed. The pointer type (used e.g. for initialization) is a real
170  * pointer. The reference type (used e.g. for operator[] and iterator dereference)
171  * is SimdReference which executes the aligned load/store as is appropriate. For
172  * both iterator and element access, the access happens in blocks of SIMD width.
173  * Meaning that a[1] refers to the 2nd SIMD vector and thus reals 8-15 for 8-wide
174  * SIMD. The starting address has to be aligned and the length has to be multiple
175  * of the SIMD width.
176  *
177  * \tparam T SIMD type (e.g. SimdReal)
178  */
179 template<typename T>
180 class SimdArrayRef
181 {
182 public:
183     //! Type for representing size of the container.
184     using size_type = size_t;
185     //! Type for representing difference between two container indices.
186     using difference_type = std::ptrdiff_t;
187     //! Type of values stored in the container.
188     using value_type = T;
189     //! Pointer to a container element.
190     using pointer = typename SimdTraits<T>::type*;
191     //! Reference to a container element.
192     using reference = internal::SimdReference<T>;
193     //! Iterator type for the container.
194     using iterator = SimdIterator<T>;
195     //! Standard reverse iterator.
196     using reverse_iterator = std::reverse_iterator<iterator>;
197
198     //! \copydoc ArrayRef::ArrayRef(pointer, pointer)
199     SimdArrayRef(pointer begin, pointer end) : begin_(begin), end_(end)
200     {
201         GMX_ASSERT(end >= begin, "Invalid range");
202         GMX_ASSERT((reinterpret_cast<size_type>(begin) / sizeof(*begin)) % simdWidth == 0,
203                    "Aligned ArrayRef requires aligned starting address");
204         GMX_ASSERT((reinterpret_cast<size_type>(end) / sizeof(*end)) % simdWidth == 0,
205                    "Size of ArrayRef needs to be divisible by type size");
206     }
207     //! \copydoc ArrayRef::ArrayRef(U)
208     template<typename U, typename = std::enable_if_t<std::is_convertible<typename std::remove_reference_t<U>::pointer, pointer>::value>>
209     SimdArrayRef(U&& o) :
210         begin_(reinterpret_cast<pointer>(o.data())),
211         end_(reinterpret_cast<pointer>(o.data() + o.size()))
212     {
213     }
214     // reinterpret_cast is only needed for const conversion of SimdArrayRef itself.
215     // All other containers have type(o.data())==U::pointer (the cast does nothing).
216
217     //! Returns the size of the container.
218     size_type size() const { return (end_ - begin_) / simdWidth; }
219     //! Whether the container is empty.
220     bool empty() const { return begin_ == end_; }
221     //! Returns an iterator to the beginning of the container.
222     iterator begin() const { return iterator(begin_); }
223     //! Returns an iterator to the end of the container.
224     iterator end() const { return iterator(end_); }
225
226     //! Access container element.
227     reference operator[](size_type n) { return reference(begin_ + n * simdWidth); }
228
229     //! Returns the first element in the container.
230     reference front() const { return reference(begin_); }
231     //! Returns the first element in the container.
232     reference back() const { return reference(end_ - simdWidth); }
233
234 private:
235     static constexpr int simdWidth = SimdTraits<T>::width;
236     using pack_type                = typename SimdTraits<T>::type[simdWidth];
237     // Private because dereferencing return value is undefined behavior (strict aliasing rule)
238     // Only use is conversion constructor above which immediately casts it back.
239     // Return type is not "pointer" because then data()+size() would be ill defined.
240     // Has to be pack_type and not value_type in case
241     // sizeof(value_type)/sizeof(pointer)!=simdWidth (e.g. int32 for double SSE2).
242     pack_type* data() const { return reinterpret_cast<pack_type*>(begin_); }
243
244     template<typename U>
245     friend class SimdArrayRef;
246
247     pointer const begin_;
248     pointer const end_;
249 };
250
251 } // namespace internal
252
253 /* Specialize ArraryRef<SimdReal>
254  * So far only an aligned version is implemented. The constructor verifies that
255  * a ArrayRef<SimdReal> is constructed only for properly aligned data.
256  */
257 #if GMX_SIMD_HAVE_FLOAT
258 template<>
259 class ArrayRef<SimdFloat> : public internal::SimdArrayRef<SimdFloat>
260 {
261     using Base = internal::SimdArrayRef<SimdFloat>;
262     using Base::Base;
263 };
264 template<>
265 class ArrayRef<const SimdFloat> : public internal::SimdArrayRef<const SimdFloat>
266 {
267     using Base = internal::SimdArrayRef<const SimdFloat>;
268     using Base::Base;
269 };
270 #endif
271 #if GMX_SIMD_HAVE_DOUBLE
272 template<>
273 class ArrayRef<SimdDouble> : public internal::SimdArrayRef<SimdDouble>
274 {
275     using Base = internal::SimdArrayRef<SimdDouble>;
276     using Base::Base;
277 };
278 template<>
279 class ArrayRef<const SimdDouble> : public internal::SimdArrayRef<const SimdDouble>
280 {
281     using Base = internal::SimdArrayRef<const SimdDouble>;
282     using Base::Base;
283 };
284 #endif
285
286 } // namespace gmx
287
288 #endif