Apply clang-format to source tree
[alexxy/gromacs.git] / src / gromacs / simd / impl_x86_avx_256 / impl_x86_avx_256_simd_double.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2014,2015,2016,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
36 #ifndef GMX_SIMD_IMPL_X86_AVX_256_SIMD_DOUBLE_H
37 #define GMX_SIMD_IMPL_X86_AVX_256_SIMD_DOUBLE_H
38
39 #include "config.h"
40
41 #include <cassert>
42 #include <cstddef>
43 #include <cstdint>
44
45 #include <immintrin.h>
46
47 #include "gromacs/math/utilities.h"
48
49 #include "impl_x86_avx_256_simd_float.h"
50
51
52 namespace gmx
53 {
54
55 class SimdDouble
56 {
57 public:
58     SimdDouble() {}
59
60     SimdDouble(double d) : simdInternal_(_mm256_set1_pd(d)) {}
61
62     // Internal utility constructor to simplify return statements
63     SimdDouble(__m256d simd) : simdInternal_(simd) {}
64
65     __m256d simdInternal_;
66 };
67
68 class SimdDInt32
69 {
70 public:
71     SimdDInt32() {}
72
73     SimdDInt32(std::int32_t i) : simdInternal_(_mm_set1_epi32(i)) {}
74
75     // Internal utility constructor to simplify return statements
76     SimdDInt32(__m128i simd) : simdInternal_(simd) {}
77
78     __m128i simdInternal_;
79 };
80
81 class SimdDBool
82 {
83 public:
84     SimdDBool() {}
85
86     SimdDBool(bool b) : simdInternal_(_mm256_castsi256_pd(_mm256_set1_epi32(b ? 0xFFFFFFFF : 0))) {}
87
88     // Internal utility constructor to simplify return statements
89     SimdDBool(__m256d simd) : simdInternal_(simd) {}
90
91     __m256d simdInternal_;
92 };
93
94 class SimdDIBool
95 {
96 public:
97     SimdDIBool() {}
98
99     SimdDIBool(bool b) : simdInternal_(_mm_set1_epi32(b ? 0xFFFFFFFF : 0)) {}
100
101     // Internal utility constructor to simplify return statements
102     SimdDIBool(__m128i simd) : simdInternal_(simd) {}
103
104     __m128i simdInternal_;
105 };
106
107
108 static inline SimdDouble gmx_simdcall simdLoad(const double* m, SimdDoubleTag /*unused*/ = {})
109 {
110     assert(std::size_t(m) % 32 == 0);
111     return { _mm256_load_pd(m) };
112 }
113
114 static inline void gmx_simdcall store(double* m, SimdDouble a)
115 {
116     assert(std::size_t(m) % 32 == 0);
117     _mm256_store_pd(m, a.simdInternal_);
118 }
119
120 static inline SimdDouble gmx_simdcall simdLoadU(const double* m, SimdDoubleTag /*unused*/ = {})
121 {
122     return { _mm256_loadu_pd(m) };
123 }
124
125 static inline void gmx_simdcall storeU(double* m, SimdDouble a)
126 {
127     _mm256_storeu_pd(m, a.simdInternal_);
128 }
129
130 static inline SimdDouble gmx_simdcall setZeroD()
131 {
132     return { _mm256_setzero_pd() };
133 }
134
135 static inline SimdDInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdDInt32Tag /*unused*/)
136 {
137     assert(std::size_t(m) % 16 == 0);
138     return { _mm_load_si128(reinterpret_cast<const __m128i*>(m)) };
139 }
140
141 static inline void gmx_simdcall store(std::int32_t* m, SimdDInt32 a)
142 {
143     assert(std::size_t(m) % 16 == 0);
144     _mm_store_si128(reinterpret_cast<__m128i*>(m), a.simdInternal_);
145 }
146
147 static inline SimdDInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdDInt32Tag /*unused*/)
148 {
149     return { _mm_loadu_si128(reinterpret_cast<const __m128i*>(m)) };
150 }
151
152 static inline void gmx_simdcall storeU(std::int32_t* m, SimdDInt32 a)
153 {
154     _mm_storeu_si128(reinterpret_cast<__m128i*>(m), a.simdInternal_);
155 }
156
157 static inline SimdDInt32 gmx_simdcall setZeroDI()
158 {
159     return { _mm_setzero_si128() };
160 }
161
162 template<int index>
163 static inline std::int32_t gmx_simdcall extract(SimdDInt32 a)
164 {
165     return _mm_extract_epi32(a.simdInternal_, index);
166 }
167
168 static inline SimdDouble gmx_simdcall operator&(SimdDouble a, SimdDouble b)
169 {
170     return { _mm256_and_pd(a.simdInternal_, b.simdInternal_) };
171 }
172
173 static inline SimdDouble gmx_simdcall andNot(SimdDouble a, SimdDouble b)
174 {
175     return { _mm256_andnot_pd(a.simdInternal_, b.simdInternal_) };
176 }
177
178 static inline SimdDouble gmx_simdcall operator|(SimdDouble a, SimdDouble b)
179 {
180     return { _mm256_or_pd(a.simdInternal_, b.simdInternal_) };
181 }
182
183 static inline SimdDouble gmx_simdcall operator^(SimdDouble a, SimdDouble b)
184 {
185     return { _mm256_xor_pd(a.simdInternal_, b.simdInternal_) };
186 }
187
188 static inline SimdDouble gmx_simdcall operator+(SimdDouble a, SimdDouble b)
189 {
190     return { _mm256_add_pd(a.simdInternal_, b.simdInternal_) };
191 }
192
193 static inline SimdDouble gmx_simdcall operator-(SimdDouble a, SimdDouble b)
194 {
195     return { _mm256_sub_pd(a.simdInternal_, b.simdInternal_) };
196 }
197
198 static inline SimdDouble gmx_simdcall operator-(SimdDouble x)
199 {
200     return { _mm256_xor_pd(x.simdInternal_, _mm256_set1_pd(GMX_DOUBLE_NEGZERO)) };
201 }
202
203 static inline SimdDouble gmx_simdcall operator*(SimdDouble a, SimdDouble b)
204 {
205     return { _mm256_mul_pd(a.simdInternal_, b.simdInternal_) };
206 }
207
208 // Override for AVX2 and higher
209 #if GMX_SIMD_X86_AVX_256
210 static inline SimdDouble gmx_simdcall fma(SimdDouble a, SimdDouble b, SimdDouble c)
211 {
212     return { _mm256_add_pd(_mm256_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
213 }
214
215 static inline SimdDouble gmx_simdcall fms(SimdDouble a, SimdDouble b, SimdDouble c)
216 {
217     return { _mm256_sub_pd(_mm256_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
218 }
219
220 static inline SimdDouble gmx_simdcall fnma(SimdDouble a, SimdDouble b, SimdDouble c)
221 {
222     return { _mm256_sub_pd(c.simdInternal_, _mm256_mul_pd(a.simdInternal_, b.simdInternal_)) };
223 }
224
225 static inline SimdDouble gmx_simdcall fnms(SimdDouble a, SimdDouble b, SimdDouble c)
226 {
227     return { _mm256_sub_pd(_mm256_setzero_pd(),
228                            _mm256_add_pd(_mm256_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_)) };
229 }
230 #endif
231
232 static inline SimdDouble gmx_simdcall rsqrt(SimdDouble x)
233 {
234     return { _mm256_cvtps_pd(_mm_rsqrt_ps(_mm256_cvtpd_ps(x.simdInternal_))) };
235 }
236
237 static inline SimdDouble gmx_simdcall rcp(SimdDouble x)
238 {
239     return { _mm256_cvtps_pd(_mm_rcp_ps(_mm256_cvtpd_ps(x.simdInternal_))) };
240 }
241
242 static inline SimdDouble gmx_simdcall maskAdd(SimdDouble a, SimdDouble b, SimdDBool m)
243 {
244     return { _mm256_add_pd(a.simdInternal_, _mm256_and_pd(b.simdInternal_, m.simdInternal_)) };
245 }
246
247 static inline SimdDouble gmx_simdcall maskzMul(SimdDouble a, SimdDouble b, SimdDBool m)
248 {
249     return { _mm256_and_pd(_mm256_mul_pd(a.simdInternal_, b.simdInternal_), m.simdInternal_) };
250 }
251
252 static inline SimdDouble maskzFma(SimdDouble a, SimdDouble b, SimdDouble c, SimdDBool m)
253 {
254     return { _mm256_and_pd(_mm256_add_pd(_mm256_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_),
255                            m.simdInternal_) };
256 }
257
258 static inline SimdDouble maskzRsqrt(SimdDouble x, SimdDBool m)
259 {
260 #ifndef NDEBUG
261     x.simdInternal_ = _mm256_blendv_pd(_mm256_set1_pd(1.0), x.simdInternal_, m.simdInternal_);
262 #endif
263     return { _mm256_and_pd(_mm256_cvtps_pd(_mm_rsqrt_ps(_mm256_cvtpd_ps(x.simdInternal_))), m.simdInternal_) };
264 }
265
266 static inline SimdDouble maskzRcp(SimdDouble x, SimdDBool m)
267 {
268 #ifndef NDEBUG
269     x.simdInternal_ = _mm256_blendv_pd(_mm256_set1_pd(1.0), x.simdInternal_, m.simdInternal_);
270 #endif
271     return { _mm256_and_pd(_mm256_cvtps_pd(_mm_rcp_ps(_mm256_cvtpd_ps(x.simdInternal_))), m.simdInternal_) };
272 }
273
274 static inline SimdDouble gmx_simdcall abs(SimdDouble x)
275 {
276     return { _mm256_andnot_pd(_mm256_set1_pd(GMX_DOUBLE_NEGZERO), x.simdInternal_) };
277 }
278
279 static inline SimdDouble gmx_simdcall max(SimdDouble a, SimdDouble b)
280 {
281     return { _mm256_max_pd(a.simdInternal_, b.simdInternal_) };
282 }
283
284 static inline SimdDouble gmx_simdcall min(SimdDouble a, SimdDouble b)
285 {
286     return { _mm256_min_pd(a.simdInternal_, b.simdInternal_) };
287 }
288
289 static inline SimdDouble gmx_simdcall round(SimdDouble x)
290 {
291     return { _mm256_round_pd(x.simdInternal_, _MM_FROUND_NINT) };
292 }
293
294 static inline SimdDouble gmx_simdcall trunc(SimdDouble x)
295 {
296     return { _mm256_round_pd(x.simdInternal_, _MM_FROUND_TRUNC) };
297 }
298
299 // Override for AVX2 and higher
300 #if GMX_SIMD_X86_AVX_256
301 static inline SimdDouble frexp(SimdDouble value, SimdDInt32* exponent)
302 {
303     const __m256d exponentMask = _mm256_castsi256_pd(_mm256_set1_epi64x(0x7FF0000000000000LL));
304     const __m256d mantissaMask = _mm256_castsi256_pd(_mm256_set1_epi64x(0x800FFFFFFFFFFFFFLL));
305     const __m256d half         = _mm256_set1_pd(0.5);
306     const __m128i exponentBias = _mm_set1_epi32(1022); // add 1 to make our definition identical to frexp()
307     __m256i iExponent;
308     __m128i iExponentLow, iExponentHigh;
309
310     iExponent               = _mm256_castpd_si256(_mm256_and_pd(value.simdInternal_, exponentMask));
311     iExponentHigh           = _mm256_extractf128_si256(iExponent, 0x1);
312     iExponentLow            = _mm256_castsi256_si128(iExponent);
313     iExponentLow            = _mm_srli_epi64(iExponentLow, 52);
314     iExponentHigh           = _mm_srli_epi64(iExponentHigh, 52);
315     iExponentLow            = _mm_shuffle_epi32(iExponentLow, _MM_SHUFFLE(1, 1, 2, 0));
316     iExponentHigh           = _mm_shuffle_epi32(iExponentHigh, _MM_SHUFFLE(2, 0, 1, 1));
317     iExponentLow            = _mm_or_si128(iExponentLow, iExponentHigh);
318     exponent->simdInternal_ = _mm_sub_epi32(iExponentLow, exponentBias);
319
320     return { _mm256_or_pd(_mm256_and_pd(value.simdInternal_, mantissaMask), half) };
321 }
322
323 template<MathOptimization opt = MathOptimization::Safe>
324 static inline SimdDouble ldexp(SimdDouble value, SimdDInt32 exponent)
325 {
326     const __m128i exponentBias = _mm_set1_epi32(1023);
327     __m128i       iExponentLow, iExponentHigh;
328     __m256d       fExponent;
329
330     iExponentLow = _mm_add_epi32(exponent.simdInternal_, exponentBias);
331
332     if (opt == MathOptimization::Safe)
333     {
334         // Make sure biased argument is not negative
335         iExponentLow = _mm_max_epi32(iExponentLow, _mm_setzero_si128());
336     }
337
338     iExponentHigh = _mm_shuffle_epi32(iExponentLow, _MM_SHUFFLE(3, 3, 2, 2));
339     iExponentLow  = _mm_shuffle_epi32(iExponentLow, _MM_SHUFFLE(1, 1, 0, 0));
340     iExponentHigh = _mm_slli_epi64(iExponentHigh, 52);
341     iExponentLow  = _mm_slli_epi64(iExponentLow, 52);
342     fExponent     = _mm256_castsi256_pd(
343             _mm256_insertf128_si256(_mm256_castsi128_si256(iExponentLow), iExponentHigh, 0x1));
344     return { _mm256_mul_pd(value.simdInternal_, fExponent) };
345 }
346 #endif
347
348 static inline double gmx_simdcall reduce(SimdDouble a)
349 {
350     __m128d a0, a1;
351     a.simdInternal_ = _mm256_add_pd(a.simdInternal_, _mm256_permute_pd(a.simdInternal_, 0b0101));
352     a0              = _mm256_castpd256_pd128(a.simdInternal_);
353     a1              = _mm256_extractf128_pd(a.simdInternal_, 0x1);
354     a0              = _mm_add_sd(a0, a1);
355
356     return *reinterpret_cast<double*>(&a0);
357 }
358
359 static inline SimdDBool gmx_simdcall operator==(SimdDouble a, SimdDouble b)
360 {
361     return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_EQ_OQ) };
362 }
363
364 static inline SimdDBool gmx_simdcall operator!=(SimdDouble a, SimdDouble b)
365 {
366     return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_NEQ_OQ) };
367 }
368
369 static inline SimdDBool gmx_simdcall operator<(SimdDouble a, SimdDouble b)
370 {
371     return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_LT_OQ) };
372 }
373
374 static inline SimdDBool gmx_simdcall operator<=(SimdDouble a, SimdDouble b)
375 {
376     return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_LE_OQ) };
377 }
378
379 // Override for AVX2 and higher
380 #if GMX_SIMD_X86_AVX_256
381 static inline SimdDBool gmx_simdcall testBits(SimdDouble a)
382 {
383     // Do an or of the low/high 32 bits of each double (so the data is replicated),
384     // and then use the same algorithm as we use for single precision.
385     __m256 tst = _mm256_castpd_ps(a.simdInternal_);
386
387     tst = _mm256_or_ps(tst, _mm256_permute_ps(tst, _MM_SHUFFLE(2, 3, 0, 1)));
388     tst = _mm256_cvtepi32_ps(_mm256_castps_si256(tst));
389
390     return { _mm256_castps_pd(_mm256_cmp_ps(tst, _mm256_setzero_ps(), _CMP_NEQ_OQ)) };
391 }
392 #endif
393
394 static inline SimdDBool gmx_simdcall operator&&(SimdDBool a, SimdDBool b)
395 {
396     return { _mm256_and_pd(a.simdInternal_, b.simdInternal_) };
397 }
398
399 static inline SimdDBool gmx_simdcall operator||(SimdDBool a, SimdDBool b)
400 {
401     return { _mm256_or_pd(a.simdInternal_, b.simdInternal_) };
402 }
403
404 static inline bool gmx_simdcall anyTrue(SimdDBool a)
405 {
406     return _mm256_movemask_pd(a.simdInternal_) != 0;
407 }
408
409 static inline SimdDouble gmx_simdcall selectByMask(SimdDouble a, SimdDBool mask)
410 {
411     return { _mm256_and_pd(a.simdInternal_, mask.simdInternal_) };
412 }
413
414 static inline SimdDouble gmx_simdcall selectByNotMask(SimdDouble a, SimdDBool mask)
415 {
416     return { _mm256_andnot_pd(mask.simdInternal_, a.simdInternal_) };
417 }
418
419 static inline SimdDouble gmx_simdcall blend(SimdDouble a, SimdDouble b, SimdDBool sel)
420 {
421     return { _mm256_blendv_pd(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
422 }
423
424 static inline SimdDInt32 gmx_simdcall operator&(SimdDInt32 a, SimdDInt32 b)
425 {
426     return { _mm_and_si128(a.simdInternal_, b.simdInternal_) };
427 }
428
429 static inline SimdDInt32 gmx_simdcall andNot(SimdDInt32 a, SimdDInt32 b)
430 {
431     return { _mm_andnot_si128(a.simdInternal_, b.simdInternal_) };
432 }
433
434 static inline SimdDInt32 gmx_simdcall operator|(SimdDInt32 a, SimdDInt32 b)
435 {
436     return { _mm_or_si128(a.simdInternal_, b.simdInternal_) };
437 }
438
439 static inline SimdDInt32 gmx_simdcall operator^(SimdDInt32 a, SimdDInt32 b)
440 {
441     return { _mm_xor_si128(a.simdInternal_, b.simdInternal_) };
442 }
443
444 static inline SimdDInt32 gmx_simdcall operator+(SimdDInt32 a, SimdDInt32 b)
445 {
446     return { _mm_add_epi32(a.simdInternal_, b.simdInternal_) };
447 }
448
449 static inline SimdDInt32 gmx_simdcall operator-(SimdDInt32 a, SimdDInt32 b)
450 {
451     return { _mm_sub_epi32(a.simdInternal_, b.simdInternal_) };
452 }
453
454 static inline SimdDInt32 gmx_simdcall operator*(SimdDInt32 a, SimdDInt32 b)
455 {
456     return { _mm_mullo_epi32(a.simdInternal_, b.simdInternal_) };
457 }
458
459 static inline SimdDIBool gmx_simdcall operator==(SimdDInt32 a, SimdDInt32 b)
460 {
461     return { _mm_cmpeq_epi32(a.simdInternal_, b.simdInternal_) };
462 }
463
464 static inline SimdDIBool gmx_simdcall operator<(SimdDInt32 a, SimdDInt32 b)
465 {
466     return { _mm_cmplt_epi32(a.simdInternal_, b.simdInternal_) };
467 }
468
469 static inline SimdDIBool gmx_simdcall testBits(SimdDInt32 a)
470 {
471     __m128i x   = a.simdInternal_;
472     __m128i res = _mm_andnot_si128(_mm_cmpeq_epi32(x, _mm_setzero_si128()), _mm_cmpeq_epi32(x, x));
473
474     return { res };
475 }
476
477 static inline SimdDIBool gmx_simdcall operator&&(SimdDIBool a, SimdDIBool b)
478 {
479     return { _mm_and_si128(a.simdInternal_, b.simdInternal_) };
480 }
481
482 static inline SimdDIBool gmx_simdcall operator||(SimdDIBool a, SimdDIBool b)
483 {
484     return { _mm_or_si128(a.simdInternal_, b.simdInternal_) };
485 }
486
487 static inline bool gmx_simdcall anyTrue(SimdDIBool a)
488 {
489     return _mm_movemask_epi8(a.simdInternal_) != 0;
490 }
491
492 static inline SimdDInt32 gmx_simdcall selectByMask(SimdDInt32 a, SimdDIBool mask)
493 {
494     return { _mm_and_si128(a.simdInternal_, mask.simdInternal_) };
495 }
496
497 static inline SimdDInt32 gmx_simdcall selectByNotMask(SimdDInt32 a, SimdDIBool mask)
498 {
499     return { _mm_andnot_si128(mask.simdInternal_, a.simdInternal_) };
500 }
501
502 static inline SimdDInt32 gmx_simdcall blend(SimdDInt32 a, SimdDInt32 b, SimdDIBool sel)
503 {
504     return { _mm_blendv_epi8(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
505 }
506
507 static inline SimdDInt32 gmx_simdcall cvtR2I(SimdDouble a)
508 {
509     return { _mm256_cvtpd_epi32(a.simdInternal_) };
510 }
511
512 static inline SimdDInt32 gmx_simdcall cvttR2I(SimdDouble a)
513 {
514     return { _mm256_cvttpd_epi32(a.simdInternal_) };
515 }
516
517 static inline SimdDouble gmx_simdcall cvtI2R(SimdDInt32 a)
518 {
519     return { _mm256_cvtepi32_pd(a.simdInternal_) };
520 }
521
522 static inline SimdDIBool gmx_simdcall cvtB2IB(SimdDBool a)
523 {
524     __m128i a1 = _mm256_extractf128_si256(_mm256_castpd_si256(a.simdInternal_), 0x1);
525     __m128i a0 = _mm256_castsi256_si128(_mm256_castpd_si256(a.simdInternal_));
526     a0         = _mm_shuffle_epi32(a0, _MM_SHUFFLE(2, 0, 2, 0));
527     a1         = _mm_shuffle_epi32(a1, _MM_SHUFFLE(2, 0, 2, 0));
528
529     return { _mm_blend_epi16(a0, a1, 0xF0) };
530 }
531
532 static inline SimdDBool gmx_simdcall cvtIB2B(SimdDIBool a)
533 {
534     __m128d lo = _mm_castsi128_pd(_mm_unpacklo_epi32(a.simdInternal_, a.simdInternal_));
535     __m128d hi = _mm_castsi128_pd(_mm_unpackhi_epi32(a.simdInternal_, a.simdInternal_));
536
537     return { _mm256_insertf128_pd(_mm256_castpd128_pd256(lo), hi, 0x1) };
538 }
539
540 static inline void gmx_simdcall cvtF2DD(SimdFloat f, SimdDouble* d0, SimdDouble* d1)
541 {
542     d0->simdInternal_ = _mm256_cvtps_pd(_mm256_castps256_ps128(f.simdInternal_));
543     d1->simdInternal_ = _mm256_cvtps_pd(_mm256_extractf128_ps(f.simdInternal_, 0x1));
544 }
545
546 static inline SimdFloat gmx_simdcall cvtDD2F(SimdDouble d0, SimdDouble d1)
547 {
548     __m128 f0 = _mm256_cvtpd_ps(d0.simdInternal_);
549     __m128 f1 = _mm256_cvtpd_ps(d1.simdInternal_);
550     return { _mm256_insertf128_ps(_mm256_castps128_ps256(f0), f1, 0x1) };
551 }
552
553 } // namespace gmx
554
555 #endif // GMX_SIMD_IMPL_X86_AVX_256_SIMD_DOUBLE_H