2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2017, 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 = typename std::remove_const<T>::type;
58 using pointer = typename SimdTraits<T>::type*;
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)
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);
90 pointer const m_; //!< The pointer used to load memory
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>;
108 explicit SimdIterator(pointer p = 0) : p_(p)
110 GMX_ASSERT((reinterpret_cast<size_type>(p)/sizeof(*p))%simdWidth == 0,
111 "Trying to create aligned iterator for non aligned address.");
113 SimdIterator &operator++()
118 SimdIterator operator++(int)
120 SimdIterator retval = *this;
124 SimdIterator &operator--()
129 SimdIterator operator--(int)
131 SimdIterator retval = *this;
135 SimdIterator &operator+=(difference_type d)
140 SimdIterator &operator-=(difference_type d)
145 SimdIterator operator+(difference_type d) { return SimdIterator(p_ + simdWidth*d); }
146 SimdIterator operator-(difference_type d) { return SimdIterator(p_ - simdWidth*d); }
148 difference_type operator-(SimdIterator o) { return (p_ - o.p_)/simdWidth; }
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_; }
157 reference operator*() const { return reference(p_); }
160 static constexpr int simdWidth = SimdTraits<T>::width;
164 * \brief STL-like container for aligned SIMD type. Used as ArrayRef<SimdReal>.
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
175 * \tparam T SIMD type (e.g. SimdReal)
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>;
196 //! \copydoc ArrayRef::ArrayRef(pointer, pointer)
197 SimdArrayRef(pointer begin, pointer end)
198 : begin_(begin), end_(end)
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");
206 //! \copydoc ArrayRef::ArrayRef(const EmptyArrayRef&)
207 SimdArrayRef(const EmptyArrayRef &) : begin_(nullptr), end_(nullptr) {}
208 //! \copydoc ArrayRef::ArrayRef(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).
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_); }
227 //! Access container element.
228 reference operator[](size_type n)
230 return reference(begin_+n*simdWidth);
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); }
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_); }
248 friend class SimdArrayRef;
250 pointer const begin_;
254 } //namespace internal
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.
260 #if GMX_SIMD_HAVE_FLOAT
262 class ArrayRef<SimdFloat> : public internal::SimdArrayRef<SimdFloat>
264 using Base = internal::SimdArrayRef<SimdFloat>;
268 class ArrayRef<const SimdFloat> : public internal::SimdArrayRef<const SimdFloat>
270 using Base = internal::SimdArrayRef<const SimdFloat>;
274 #if GMX_SIMD_HAVE_DOUBLE
276 class ArrayRef<SimdDouble> : public internal::SimdArrayRef<SimdDouble>
278 using Base = internal::SimdArrayRef<SimdDouble>;
282 class ArrayRef<const SimdDouble> : public internal::SimdArrayRef<const SimdDouble>
284 using Base = internal::SimdArrayRef<const SimdDouble>;