2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2017,2018,2019,2020, 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 = SimdTraitsT<T>*;
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
96 public boost::stl_interfaces::iterator_interface<SimdIterator<T>, std::random_access_iterator_tag, T, SimdReference<T>>
99 boost::stl_interfaces::iterator_interface<SimdIterator<T>, std::random_access_iterator_tag, T, SimdReference<T>>;
101 using DataPointer = SimdTraitsT<T>*;
104 explicit SimdIterator(DataPointer p = 0) : p_(p)
106 GMX_ASSERT((reinterpret_cast<size_t>(p) / sizeof(*p)) % simdWidth == 0,
107 "Trying to create aligned iterator for non aligned address.");
109 SimdIterator& operator+=(typename Base::difference_type d)
114 typename Base::difference_type operator-(SimdIterator o) { return (p_ - o.p_) / simdWidth; }
115 typename Base::reference operator*() const { return typename Base::reference(p_); }
119 static constexpr int simdWidth = SimdTraits<T>::width;
123 * \brief STL-like container for aligned SIMD type. Used as ArrayRef<SimdReal>.
125 * Should provide the same interface as ArrayRef. Any missing functions should be
126 * added as needed. The pointer type (used e.g. for initialization) is a real
127 * pointer. The reference type (used e.g. for operator[] and iterator dereference)
128 * is SimdReference which executes the aligned load/store as is appropriate. For
129 * both iterator and element access, the access happens in blocks of SIMD width.
130 * Meaning that a[1] refers to the 2nd SIMD vector and thus reals 8-15 for 8-wide
131 * SIMD. The starting address has to be aligned and the length has to be multiple
134 * \tparam T SIMD type (e.g. SimdReal)
140 //! Type for representing size of the container.
141 using size_type = size_t;
142 //! Type for representing difference between two container indices.
143 using difference_type = std::ptrdiff_t;
144 //! Type of values stored in the container.
145 using value_type = T;
146 //! Pointer to a container element.
147 using pointer = SimdTraitsT<T>*;
148 //! Reference to a container element.
149 using reference = internal::SimdReference<T>;
150 //! Iterator type for the container.
151 using iterator = SimdIterator<T>;
152 //! Standard reverse iterator.
153 using reverse_iterator = std::reverse_iterator<iterator>;
155 //! \copydoc ArrayRef::ArrayRef(pointer, pointer)
156 SimdArrayRef(pointer begin, pointer end) : begin_(begin), end_(end)
158 GMX_ASSERT(end >= begin, "Invalid range");
159 GMX_ASSERT((reinterpret_cast<size_type>(begin) / sizeof(*begin)) % simdWidth == 0,
160 "Aligned ArrayRef requires aligned starting address");
161 GMX_ASSERT((reinterpret_cast<size_type>(end) / sizeof(*end)) % simdWidth == 0,
162 "Size of ArrayRef needs to be divisible by type size");
164 //! \copydoc ArrayRef::ArrayRef(U)
165 template<typename U, typename = std::enable_if_t<std::is_convertible_v<typename std::remove_reference_t<U>::pointer, pointer>>>
166 SimdArrayRef(U&& o) :
167 begin_(reinterpret_cast<pointer>(o.data())),
168 end_(reinterpret_cast<pointer>(o.data() + o.size()))
171 // reinterpret_cast is only needed for const conversion of SimdArrayRef itself.
172 // All other containers have type(o.data())==U::pointer (the cast does nothing).
174 //! Returns the size of the container.
175 size_type size() const { return (end_ - begin_) / simdWidth; }
176 //! Whether the container is empty.
177 bool empty() const { return begin_ == end_; }
178 //! Returns an iterator to the beginning of the container.
179 iterator begin() const { return iterator(begin_); }
180 //! Returns an iterator to the end of the container.
181 iterator end() const { return iterator(end_); }
183 //! Access container element.
184 reference operator[](size_type n) { return reference(begin_ + n * simdWidth); }
186 //! Returns the first element in the container.
187 reference front() const { return reference(begin_); }
188 //! Returns the first element in the container.
189 reference back() const { return reference(end_ - simdWidth); }
192 static constexpr int simdWidth = SimdTraits<T>::width;
193 using pack_type = SimdTraitsT<T>[simdWidth];
194 // Private because dereferencing return value is undefined behavior (strict aliasing rule)
195 // Only use is conversion constructor above which immediately casts it back.
196 // Return type is not "pointer" because then data()+size() would be ill defined.
197 // Has to be pack_type and not value_type in case
198 // sizeof(value_type)/sizeof(pointer)!=simdWidth (e.g. int32 for double SSE2).
199 pack_type* data() const { return reinterpret_cast<pack_type*>(begin_); }
202 friend class SimdArrayRef;
204 pointer const begin_;
208 } // namespace internal
210 /* Specialize ArraryRef<SimdReal>
211 * So far only an aligned version is implemented. The constructor verifies that
212 * a ArrayRef<SimdReal> is constructed only for properly aligned data.
214 #if GMX_SIMD_HAVE_FLOAT
216 class ArrayRef<SimdFloat> : public internal::SimdArrayRef<SimdFloat>
218 using Base = internal::SimdArrayRef<SimdFloat>;
222 class ArrayRef<const SimdFloat> : public internal::SimdArrayRef<const SimdFloat>
224 using Base = internal::SimdArrayRef<const SimdFloat>;
228 #if GMX_SIMD_HAVE_DOUBLE
230 class ArrayRef<SimdDouble> : public internal::SimdArrayRef<SimdDouble>
232 using Base = internal::SimdArrayRef<SimdDouble>;
236 class ArrayRef<const SimdDouble> : public internal::SimdArrayRef<const SimdDouble>
238 using Base = internal::SimdArrayRef<const SimdDouble>;