2 * This file is part of the GROMACS molecular simulation package.
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.
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.
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.
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.
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.
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.
35 /*! \libinternal \file
36 * \brief Declares SimdArrayRef
38 * \author Roland Schulz <roland.schulz@intel.com>
40 * \ingroup module_simd
42 #ifndef GMX_SIMD_SIMD_MEMORY_H
43 #define GMX_SIMD_SIMD_MEMORY_H
45 #include "gromacs/utility/arrayref.h"
46 #include "gromacs/utility/gmxassert.h"
57 using non_const_T = std::remove_const_t<T>;
58 using pointer = typename SimdTraits<T>::type*;
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)
71 //! \brief Addition assignment operator that will execute load+store
72 SimdReference operator+=(T o)
74 store(m_, load<non_const_T>(m_) + o);
77 //! \brief Subtraction assignment operator that will execute load+store
78 SimdReference operator-=(T o)
80 store(m_, load<non_const_T>(m_) - o);
83 //! \brief Multiplication assignment operator that will execute load+store
84 SimdReference operator*=(T o)
86 store(m_, load<non_const_T>(m_) * o);
91 pointer const m_; //!< The pointer used to load memory
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>;
109 explicit SimdIterator(pointer p = 0) : p_(p)
111 GMX_ASSERT((reinterpret_cast<size_type>(p) / sizeof(*p)) % simdWidth == 0,
112 "Trying to create aligned iterator for non aligned address.");
114 SimdIterator& operator++()
119 SimdIterator operator++(int)
121 SimdIterator retval = *this;
125 SimdIterator& operator--()
130 SimdIterator operator--(int)
132 SimdIterator retval = *this;
136 SimdIterator& operator+=(difference_type d)
141 SimdIterator& operator-=(difference_type d)
146 SimdIterator operator+(difference_type d) { return SimdIterator(p_ + simdWidth * d); }
147 SimdIterator operator-(difference_type d) { return SimdIterator(p_ - simdWidth * d); }
149 difference_type operator-(SimdIterator o) { return (p_ - o.p_) / simdWidth; }
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_; }
158 reference operator*() const { return reference(p_); }
162 static constexpr int simdWidth = SimdTraits<T>::width;
166 * \brief STL-like container for aligned SIMD type. Used as ArrayRef<SimdReal>.
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
177 * \tparam T SIMD type (e.g. SimdReal)
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>;
198 //! \copydoc ArrayRef::ArrayRef(pointer, pointer)
199 SimdArrayRef(pointer begin, pointer end) : begin_(begin), end_(end)
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");
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()))
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).
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_); }
226 //! Access container element.
227 reference operator[](size_type n) { return reference(begin_ + n * simdWidth); }
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); }
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_); }
245 friend class SimdArrayRef;
247 pointer const begin_;
251 } // namespace internal
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.
257 #if GMX_SIMD_HAVE_FLOAT
259 class ArrayRef<SimdFloat> : public internal::SimdArrayRef<SimdFloat>
261 using Base = internal::SimdArrayRef<SimdFloat>;
265 class ArrayRef<const SimdFloat> : public internal::SimdArrayRef<const SimdFloat>
267 using Base = internal::SimdArrayRef<const SimdFloat>;
271 #if GMX_SIMD_HAVE_DOUBLE
273 class ArrayRef<SimdDouble> : public internal::SimdArrayRef<SimdDouble>
275 using Base = internal::SimdArrayRef<SimdDouble>;
279 class ArrayRef<const SimdDouble> : public internal::SimdArrayRef<const SimdDouble>
281 using Base = internal::SimdArrayRef<const SimdDouble>;