GDAL
gdalsse_priv.h
1/******************************************************************************
2 * $Id$
3 *
4 * Project: GDAL
5 * Purpose: SSE2 helper
6 * Author: Even Rouault <even dot rouault at spatialys dot com>
7 *
8 ******************************************************************************
9 * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys dot com>
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 ****************************************************************************/
29
30#ifndef GDALSSE_PRIV_H_INCLUDED
31#define GDALSSE_PRIV_H_INCLUDED
32
33#ifndef DOXYGEN_SKIP
34
35#include "cpl_port.h"
36
37/* We restrict to 64bit processors because they are guaranteed to have SSE2 */
38/* Could possibly be used too on 32bit, but we would need to check at runtime */
39#if (defined(__x86_64) || defined(_M_X64)) && !defined(USE_SSE2_EMULATION)
40
41/* Requires SSE2 */
42#include <emmintrin.h>
43#include <string.h>
44
45#ifdef __SSE4_1__
46#include <smmintrin.h>
47#endif
48
49#include "gdal_priv_templates.hpp"
50
51static inline __m128i GDALCopyInt16ToXMM(const void *ptr)
52{
53#ifdef CPL_CPU_REQUIRES_ALIGNED_ACCESS
54 unsigned short s;
55 memcpy(&s, ptr, 2);
56 return _mm_cvtsi32_si128(s);
57#else
58 return _mm_cvtsi32_si128(*static_cast<const unsigned short *>(ptr));
59#endif
60}
61
62static inline __m128i GDALCopyInt32ToXMM(const void *ptr)
63{
64#ifdef CPL_CPU_REQUIRES_ALIGNED_ACCESS
65 GInt32 i;
66 memcpy(&i, ptr, 4);
67 return _mm_cvtsi32_si128(i);
68#else
69 return _mm_cvtsi32_si128(*static_cast<const GInt32 *>(ptr));
70#endif
71}
72
73static inline __m128i GDALCopyInt64ToXMM(const void *ptr)
74{
75#ifdef CPL_CPU_REQUIRES_ALIGNED_ACCESS
76 GInt64 i;
77 memcpy(&i, ptr, 8);
78 return _mm_cvtsi64_si128(i);
79#else
80 return _mm_cvtsi64_si128(*static_cast<const GInt64 *>(ptr));
81#endif
82}
83
84static inline void GDALCopyXMMToInt16(const __m128i xmm, void *pDest)
85{
86#ifdef CPL_CPU_REQUIRES_ALIGNED_ACCESS
87 GInt16 i = static_cast<GInt16>(_mm_extract_epi16(xmm, 0));
88 memcpy(pDest, &i, 2);
89#else
90 *static_cast<GInt16 *>(pDest) =
91 static_cast<GInt16>(_mm_extract_epi16(xmm, 0));
92#endif
93}
94
95class XMMReg2Double
96{
97 public:
98 __m128d xmm;
99
100#if defined(__GNUC__)
101#pragma GCC diagnostic push
102#pragma GCC diagnostic ignored "-Weffc++"
103#endif
104 /* coverity[uninit_member] */
105 XMMReg2Double() = default;
106#if defined(__GNUC__)
107#pragma GCC diagnostic pop
108#endif
109
110 XMMReg2Double(double val) : xmm(_mm_load_sd(&val))
111 {
112 }
113 XMMReg2Double(const XMMReg2Double &other) : xmm(other.xmm)
114 {
115 }
116
117 static inline XMMReg2Double Zero()
118 {
119 XMMReg2Double reg;
120 reg.Zeroize();
121 return reg;
122 }
123
124 static inline XMMReg2Double Load1ValHighAndLow(const double *ptr)
125 {
126 XMMReg2Double reg;
127 reg.nsLoad1ValHighAndLow(ptr);
128 return reg;
129 }
130
131 static inline XMMReg2Double Load2Val(const double *ptr)
132 {
133 XMMReg2Double reg;
134 reg.nsLoad2Val(ptr);
135 return reg;
136 }
137
138 static inline XMMReg2Double Load2Val(const float *ptr)
139 {
140 XMMReg2Double reg;
141 reg.nsLoad2Val(ptr);
142 return reg;
143 }
144
145 static inline XMMReg2Double Load2ValAligned(const double *ptr)
146 {
147 XMMReg2Double reg;
148 reg.nsLoad2ValAligned(ptr);
149 return reg;
150 }
151
152 static inline XMMReg2Double Load2Val(const unsigned char *ptr)
153 {
154 XMMReg2Double reg;
155 reg.nsLoad2Val(ptr);
156 return reg;
157 }
158
159 static inline XMMReg2Double Load2Val(const short *ptr)
160 {
161 XMMReg2Double reg;
162 reg.nsLoad2Val(ptr);
163 return reg;
164 }
165
166 static inline XMMReg2Double Load2Val(const unsigned short *ptr)
167 {
168 XMMReg2Double reg;
169 reg.nsLoad2Val(ptr);
170 return reg;
171 }
172
173 static inline XMMReg2Double Equals(const XMMReg2Double &expr1,
174 const XMMReg2Double &expr2)
175 {
176 XMMReg2Double reg;
177 reg.xmm = _mm_cmpeq_pd(expr1.xmm, expr2.xmm);
178 return reg;
179 }
180
181 static inline XMMReg2Double NotEquals(const XMMReg2Double &expr1,
182 const XMMReg2Double &expr2)
183 {
184 XMMReg2Double reg;
185 reg.xmm = _mm_cmpneq_pd(expr1.xmm, expr2.xmm);
186 return reg;
187 }
188
189 static inline XMMReg2Double Greater(const XMMReg2Double &expr1,
190 const XMMReg2Double &expr2)
191 {
192 XMMReg2Double reg;
193 reg.xmm = _mm_cmpgt_pd(expr1.xmm, expr2.xmm);
194 return reg;
195 }
196
197 static inline XMMReg2Double And(const XMMReg2Double &expr1,
198 const XMMReg2Double &expr2)
199 {
200 XMMReg2Double reg;
201 reg.xmm = _mm_and_pd(expr1.xmm, expr2.xmm);
202 return reg;
203 }
204
205 static inline XMMReg2Double Ternary(const XMMReg2Double &cond,
206 const XMMReg2Double &true_expr,
207 const XMMReg2Double &false_expr)
208 {
209 XMMReg2Double reg;
210 reg.xmm = _mm_or_pd(_mm_and_pd(cond.xmm, true_expr.xmm),
211 _mm_andnot_pd(cond.xmm, false_expr.xmm));
212 return reg;
213 }
214
215 static inline XMMReg2Double Min(const XMMReg2Double &expr1,
216 const XMMReg2Double &expr2)
217 {
218 XMMReg2Double reg;
219 reg.xmm = _mm_min_pd(expr1.xmm, expr2.xmm);
220 return reg;
221 }
222
223 inline void nsLoad1ValHighAndLow(const double *ptr)
224 {
225 xmm = _mm_load1_pd(ptr);
226 }
227
228 inline void nsLoad2Val(const double *ptr)
229 {
230 xmm = _mm_loadu_pd(ptr);
231 }
232
233 inline void nsLoad2ValAligned(const double *ptr)
234 {
235 xmm = _mm_load_pd(ptr);
236 }
237
238 inline void nsLoad2Val(const float *ptr)
239 {
240 xmm = _mm_cvtps_pd(_mm_castsi128_ps(GDALCopyInt64ToXMM(ptr)));
241 }
242
243 inline void nsLoad2Val(const unsigned char *ptr)
244 {
245 __m128i xmm_i = GDALCopyInt16ToXMM(ptr);
246#ifdef __SSE4_1__
247 xmm_i = _mm_cvtepu8_epi32(xmm_i);
248#else
249 xmm_i = _mm_unpacklo_epi8(xmm_i, _mm_setzero_si128());
250 xmm_i = _mm_unpacklo_epi16(xmm_i, _mm_setzero_si128());
251#endif
252 xmm = _mm_cvtepi32_pd(xmm_i);
253 }
254
255 inline void nsLoad2Val(const short *ptr)
256 {
257 __m128i xmm_i = GDALCopyInt32ToXMM(ptr);
258#ifdef __SSE4_1__
259 xmm_i = _mm_cvtepi16_epi32(xmm_i);
260#else
261 xmm_i = _mm_unpacklo_epi16(
262 xmm_i, xmm_i); /* 0|0|0|0|0|0|b|a --> 0|0|0|0|b|b|a|a */
263 xmm_i = _mm_srai_epi32(
264 xmm_i, 16); /* 0|0|0|0|b|b|a|a --> 0|0|0|0|sign(b)|b|sign(a)|a */
265#endif
266 xmm = _mm_cvtepi32_pd(xmm_i);
267 }
268
269 inline void nsLoad2Val(const unsigned short *ptr)
270 {
271 __m128i xmm_i = GDALCopyInt32ToXMM(ptr);
272#ifdef __SSE4_1__
273 xmm_i = _mm_cvtepu16_epi32(xmm_i);
274#else
275 xmm_i = _mm_unpacklo_epi16(
276 xmm_i,
277 _mm_setzero_si128()); /* 0|0|0|0|0|0|b|a --> 0|0|0|0|0|b|0|a */
278#endif
279 xmm = _mm_cvtepi32_pd(xmm_i);
280 }
281
282 static inline void Load4Val(const unsigned char *ptr, XMMReg2Double &low,
283 XMMReg2Double &high)
284 {
285 __m128i xmm_i = GDALCopyInt32ToXMM(ptr);
286#ifdef __SSE4_1__
287 xmm_i = _mm_cvtepu8_epi32(xmm_i);
288#else
289 xmm_i = _mm_unpacklo_epi8(xmm_i, _mm_setzero_si128());
290 xmm_i = _mm_unpacklo_epi16(xmm_i, _mm_setzero_si128());
291#endif
292 low.xmm = _mm_cvtepi32_pd(xmm_i);
293 high.xmm =
294 _mm_cvtepi32_pd(_mm_shuffle_epi32(xmm_i, _MM_SHUFFLE(3, 2, 3, 2)));
295 }
296
297 static inline void Load4Val(const short *ptr, XMMReg2Double &low,
298 XMMReg2Double &high)
299 {
300 low.nsLoad2Val(ptr);
301 high.nsLoad2Val(ptr + 2);
302 }
303
304 static inline void Load4Val(const unsigned short *ptr, XMMReg2Double &low,
305 XMMReg2Double &high)
306 {
307 low.nsLoad2Val(ptr);
308 high.nsLoad2Val(ptr + 2);
309 }
310
311 static inline void Load4Val(const double *ptr, XMMReg2Double &low,
312 XMMReg2Double &high)
313 {
314 low.nsLoad2Val(ptr);
315 high.nsLoad2Val(ptr + 2);
316 }
317
318 static inline void Load4Val(const float *ptr, XMMReg2Double &low,
319 XMMReg2Double &high)
320 {
321 __m128 temp1 = _mm_loadu_ps(ptr);
322 __m128 temp2 = _mm_shuffle_ps(temp1, temp1, _MM_SHUFFLE(3, 2, 3, 2));
323 low.xmm = _mm_cvtps_pd(temp1);
324 high.xmm = _mm_cvtps_pd(temp2);
325 }
326
327 inline void Zeroize()
328 {
329 xmm = _mm_setzero_pd();
330 }
331
332 inline XMMReg2Double &operator=(const XMMReg2Double &other)
333 {
334 xmm = other.xmm;
335 return *this;
336 }
337
338 inline XMMReg2Double &operator+=(const XMMReg2Double &other)
339 {
340 xmm = _mm_add_pd(xmm, other.xmm);
341 return *this;
342 }
343
344 inline XMMReg2Double &operator*=(const XMMReg2Double &other)
345 {
346 xmm = _mm_mul_pd(xmm, other.xmm);
347 return *this;
348 }
349
350 inline XMMReg2Double operator+(const XMMReg2Double &other) const
351 {
352 XMMReg2Double ret;
353 ret.xmm = _mm_add_pd(xmm, other.xmm);
354 return ret;
355 }
356
357 inline XMMReg2Double operator-(const XMMReg2Double &other) const
358 {
359 XMMReg2Double ret;
360 ret.xmm = _mm_sub_pd(xmm, other.xmm);
361 return ret;
362 }
363
364 inline XMMReg2Double operator*(const XMMReg2Double &other) const
365 {
366 XMMReg2Double ret;
367 ret.xmm = _mm_mul_pd(xmm, other.xmm);
368 return ret;
369 }
370
371 inline XMMReg2Double operator/(const XMMReg2Double &other) const
372 {
373 XMMReg2Double ret;
374 ret.xmm = _mm_div_pd(xmm, other.xmm);
375 return ret;
376 }
377
378 inline double GetHorizSum() const
379 {
380 __m128d xmm2;
381 xmm2 = _mm_shuffle_pd(
382 xmm, xmm,
383 _MM_SHUFFLE2(0, 1)); /* transfer high word into low word of xmm2 */
384 return _mm_cvtsd_f64(_mm_add_sd(xmm, xmm2));
385 }
386
387 inline void Store2Val(double *ptr) const
388 {
389 _mm_storeu_pd(ptr, xmm);
390 }
391
392 inline void Store2ValAligned(double *ptr) const
393 {
394 _mm_store_pd(ptr, xmm);
395 }
396
397 inline void Store2Val(float *ptr) const
398 {
399 __m128i xmm_i = _mm_castps_si128(_mm_cvtpd_ps(xmm));
400 GDALCopyXMMToInt64(xmm_i, reinterpret_cast<GInt64 *>(ptr));
401 }
402
403 inline void Store2Val(unsigned char *ptr) const
404 {
405 __m128i tmp = _mm_cvttpd_epi32(_mm_add_pd(
406 xmm,
407 _mm_set1_pd(0.5))); /* Convert the 2 double values to 2 integers */
408 tmp = _mm_packs_epi32(tmp, tmp);
409 tmp = _mm_packus_epi16(tmp, tmp);
410 GDALCopyXMMToInt16(tmp, reinterpret_cast<GInt16 *>(ptr));
411 }
412
413 inline void Store2Val(unsigned short *ptr) const
414 {
415 __m128i tmp = _mm_cvttpd_epi32(_mm_add_pd(
416 xmm,
417 _mm_set1_pd(0.5))); /* Convert the 2 double values to 2 integers */
418 // X X X X 0 B 0 A --> X X X X A A B A
419 tmp = _mm_shufflelo_epi16(tmp, 0 | (2 << 2));
420 GDALCopyXMMToInt32(tmp, reinterpret_cast<GInt32 *>(ptr));
421 }
422
423 inline void StoreMask(unsigned char *ptr) const
424 {
425 _mm_storeu_si128(reinterpret_cast<__m128i *>(ptr),
426 _mm_castpd_si128(xmm));
427 }
428
429 inline operator double() const
430 {
431 return _mm_cvtsd_f64(xmm);
432 }
433};
434
435#else
436
437#ifndef NO_WARN_USE_SSE2_EMULATION
438#warning "Software emulation of SSE2 !"
439#endif
440
441class XMMReg2Double
442{
443 public:
444 double low;
445 double high;
446
447 XMMReg2Double()
448 {
449 }
450 XMMReg2Double(double val)
451 {
452 low = val;
453 high = 0.0;
454 }
455 XMMReg2Double(const XMMReg2Double &other) : low(other.low), high(other.high)
456 {
457 }
458
459 static inline XMMReg2Double Zero()
460 {
461 XMMReg2Double reg;
462 reg.Zeroize();
463 return reg;
464 }
465
466 static inline XMMReg2Double Load1ValHighAndLow(const double *ptr)
467 {
468 XMMReg2Double reg;
469 reg.nsLoad1ValHighAndLow(ptr);
470 return reg;
471 }
472
473 static inline XMMReg2Double Equals(const XMMReg2Double &expr1,
474 const XMMReg2Double &expr2)
475 {
476 XMMReg2Double reg;
477
478 if (expr1.low == expr2.low)
479 memset(&(reg.low), 0xFF, sizeof(double));
480 else
481 reg.low = 0;
482
483 if (expr1.high == expr2.high)
484 memset(&(reg.high), 0xFF, sizeof(double));
485 else
486 reg.high = 0;
487
488 return reg;
489 }
490
491 static inline XMMReg2Double NotEquals(const XMMReg2Double &expr1,
492 const XMMReg2Double &expr2)
493 {
494 XMMReg2Double reg;
495
496 if (expr1.low != expr2.low)
497 memset(&(reg.low), 0xFF, sizeof(double));
498 else
499 reg.low = 0;
500
501 if (expr1.high != expr2.high)
502 memset(&(reg.high), 0xFF, sizeof(double));
503 else
504 reg.high = 0;
505
506 return reg;
507 }
508
509 static inline XMMReg2Double Greater(const XMMReg2Double &expr1,
510 const XMMReg2Double &expr2)
511 {
512 XMMReg2Double reg;
513
514 if (expr1.low > expr2.low)
515 memset(&(reg.low), 0xFF, sizeof(double));
516 else
517 reg.low = 0;
518
519 if (expr1.high > expr2.high)
520 memset(&(reg.high), 0xFF, sizeof(double));
521 else
522 reg.high = 0;
523
524 return reg;
525 }
526
527 static inline XMMReg2Double And(const XMMReg2Double &expr1,
528 const XMMReg2Double &expr2)
529 {
530 XMMReg2Double reg;
531 int low1[2], high1[2];
532 int low2[2], high2[2];
533 memcpy(low1, &expr1.low, sizeof(double));
534 memcpy(high1, &expr1.high, sizeof(double));
535 memcpy(low2, &expr2.low, sizeof(double));
536 memcpy(high2, &expr2.high, sizeof(double));
537 low1[0] &= low2[0];
538 low1[1] &= low2[1];
539 high1[0] &= high2[0];
540 high1[1] &= high2[1];
541 memcpy(&reg.low, low1, sizeof(double));
542 memcpy(&reg.high, high1, sizeof(double));
543 return reg;
544 }
545
546 static inline XMMReg2Double Ternary(const XMMReg2Double &cond,
547 const XMMReg2Double &true_expr,
548 const XMMReg2Double &false_expr)
549 {
550 XMMReg2Double reg;
551 if (cond.low != 0)
552 reg.low = true_expr.low;
553 else
554 reg.low = false_expr.low;
555 if (cond.high != 0)
556 reg.high = true_expr.high;
557 else
558 reg.high = false_expr.high;
559 return reg;
560 }
561
562 static inline XMMReg2Double Min(const XMMReg2Double &expr1,
563 const XMMReg2Double &expr2)
564 {
565 XMMReg2Double reg;
566 reg.low = (expr1.low < expr2.low) ? expr1.low : expr2.low;
567 reg.high = (expr1.high < expr2.high) ? expr1.high : expr2.high;
568 return reg;
569 }
570
571 static inline XMMReg2Double Load2Val(const double *ptr)
572 {
573 XMMReg2Double reg;
574 reg.nsLoad2Val(ptr);
575 return reg;
576 }
577
578 static inline XMMReg2Double Load2ValAligned(const double *ptr)
579 {
580 XMMReg2Double reg;
581 reg.nsLoad2ValAligned(ptr);
582 return reg;
583 }
584
585 static inline XMMReg2Double Load2Val(const float *ptr)
586 {
587 XMMReg2Double reg;
588 reg.nsLoad2Val(ptr);
589 return reg;
590 }
591
592 static inline XMMReg2Double Load2Val(const unsigned char *ptr)
593 {
594 XMMReg2Double reg;
595 reg.nsLoad2Val(ptr);
596 return reg;
597 }
598
599 static inline XMMReg2Double Load2Val(const short *ptr)
600 {
601 XMMReg2Double reg;
602 reg.nsLoad2Val(ptr);
603 return reg;
604 }
605
606 static inline XMMReg2Double Load2Val(const unsigned short *ptr)
607 {
608 XMMReg2Double reg;
609 reg.nsLoad2Val(ptr);
610 return reg;
611 }
612
613 inline void nsLoad1ValHighAndLow(const double *ptr)
614 {
615 low = ptr[0];
616 high = ptr[0];
617 }
618
619 inline void nsLoad2Val(const double *ptr)
620 {
621 low = ptr[0];
622 high = ptr[1];
623 }
624
625 inline void nsLoad2ValAligned(const double *ptr)
626 {
627 low = ptr[0];
628 high = ptr[1];
629 }
630
631 inline void nsLoad2Val(const float *ptr)
632 {
633 low = ptr[0];
634 high = ptr[1];
635 }
636
637 inline void nsLoad2Val(const unsigned char *ptr)
638 {
639 low = ptr[0];
640 high = ptr[1];
641 }
642
643 inline void nsLoad2Val(const short *ptr)
644 {
645 low = ptr[0];
646 high = ptr[1];
647 }
648
649 inline void nsLoad2Val(const unsigned short *ptr)
650 {
651 low = ptr[0];
652 high = ptr[1];
653 }
654
655 static inline void Load4Val(const unsigned char *ptr, XMMReg2Double &low,
656 XMMReg2Double &high)
657 {
658 low.low = ptr[0];
659 low.high = ptr[1];
660 high.low = ptr[2];
661 high.high = ptr[3];
662 }
663
664 static inline void Load4Val(const short *ptr, XMMReg2Double &low,
665 XMMReg2Double &high)
666 {
667 low.nsLoad2Val(ptr);
668 high.nsLoad2Val(ptr + 2);
669 }
670
671 static inline void Load4Val(const unsigned short *ptr, XMMReg2Double &low,
672 XMMReg2Double &high)
673 {
674 low.nsLoad2Val(ptr);
675 high.nsLoad2Val(ptr + 2);
676 }
677
678 static inline void Load4Val(const double *ptr, XMMReg2Double &low,
679 XMMReg2Double &high)
680 {
681 low.nsLoad2Val(ptr);
682 high.nsLoad2Val(ptr + 2);
683 }
684
685 static inline void Load4Val(const float *ptr, XMMReg2Double &low,
686 XMMReg2Double &high)
687 {
688 low.nsLoad2Val(ptr);
689 high.nsLoad2Val(ptr + 2);
690 }
691
692 inline void Zeroize()
693 {
694 low = 0.0;
695 high = 0.0;
696 }
697
698 inline XMMReg2Double &operator=(const XMMReg2Double &other)
699 {
700 low = other.low;
701 high = other.high;
702 return *this;
703 }
704
705 inline XMMReg2Double &operator+=(const XMMReg2Double &other)
706 {
707 low += other.low;
708 high += other.high;
709 return *this;
710 }
711
712 inline XMMReg2Double &operator*=(const XMMReg2Double &other)
713 {
714 low *= other.low;
715 high *= other.high;
716 return *this;
717 }
718
719 inline XMMReg2Double operator+(const XMMReg2Double &other) const
720 {
721 XMMReg2Double ret;
722 ret.low = low + other.low;
723 ret.high = high + other.high;
724 return ret;
725 }
726
727 inline XMMReg2Double operator-(const XMMReg2Double &other) const
728 {
729 XMMReg2Double ret;
730 ret.low = low - other.low;
731 ret.high = high - other.high;
732 return ret;
733 }
734
735 inline XMMReg2Double operator*(const XMMReg2Double &other) const
736 {
737 XMMReg2Double ret;
738 ret.low = low * other.low;
739 ret.high = high * other.high;
740 return ret;
741 }
742
743 inline XMMReg2Double operator/(const XMMReg2Double &other) const
744 {
745 XMMReg2Double ret;
746 ret.low = low / other.low;
747 ret.high = high / other.high;
748 return ret;
749 }
750
751 inline double GetHorizSum() const
752 {
753 return low + high;
754 }
755
756 inline void Store2Val(double *ptr) const
757 {
758 ptr[0] = low;
759 ptr[1] = high;
760 }
761
762 inline void Store2ValAligned(double *ptr) const
763 {
764 ptr[0] = low;
765 ptr[1] = high;
766 }
767
768 inline void Store2Val(float *ptr) const
769 {
770 ptr[0] = static_cast<float>(low);
771 ptr[1] = static_cast<float>(high);
772 }
773
774 void Store2Val(unsigned char *ptr) const
775 {
776 ptr[0] = (unsigned char)(low + 0.5);
777 ptr[1] = (unsigned char)(high + 0.5);
778 }
779
780 void Store2Val(unsigned short *ptr) const
781 {
782 ptr[0] = (GUInt16)(low + 0.5);
783 ptr[1] = (GUInt16)(high + 0.5);
784 }
785
786 inline void StoreMask(unsigned char *ptr) const
787 {
788 memcpy(ptr, &low, 8);
789 memcpy(ptr + 8, &high, 8);
790 }
791
792 inline operator double() const
793 {
794 return low;
795 }
796};
797
798#endif /* defined(__x86_64) || defined(_M_X64) */
799
800#if defined(__AVX__) && !defined(USE_SSE2_EMULATION)
801
802#include <immintrin.h>
803
804class XMMReg4Double
805{
806 public:
807 __m256d ymm;
808
809 XMMReg4Double() : ymm(_mm256_setzero_pd())
810 {
811 }
812 XMMReg4Double(const XMMReg4Double &other) : ymm(other.ymm)
813 {
814 }
815
816 static inline XMMReg4Double Zero()
817 {
818 XMMReg4Double reg;
819 reg.Zeroize();
820 return reg;
821 }
822
823 inline void Zeroize()
824 {
825 ymm = _mm256_setzero_pd();
826 }
827
828 static inline XMMReg4Double Load1ValHighAndLow(const double *ptr)
829 {
830 XMMReg4Double reg;
831 reg.nsLoad1ValHighAndLow(ptr);
832 return reg;
833 }
834
835 inline void nsLoad1ValHighAndLow(const double *ptr)
836 {
837 ymm = _mm256_set1_pd(*ptr);
838 }
839
840 static inline XMMReg4Double Load4Val(const unsigned char *ptr)
841 {
842 XMMReg4Double reg;
843 reg.nsLoad4Val(ptr);
844 return reg;
845 }
846
847 inline void nsLoad4Val(const unsigned char *ptr)
848 {
849 __m128i xmm_i = GDALCopyInt32ToXMM(ptr);
850 xmm_i = _mm_cvtepu8_epi32(xmm_i);
851 ymm = _mm256_cvtepi32_pd(xmm_i);
852 }
853
854 static inline XMMReg4Double Load4Val(const short *ptr)
855 {
856 XMMReg4Double reg;
857 reg.nsLoad4Val(ptr);
858 return reg;
859 }
860
861 inline void nsLoad4Val(const short *ptr)
862 {
863 __m128i xmm_i = GDALCopyInt64ToXMM(ptr);
864 xmm_i = _mm_cvtepi16_epi32(xmm_i);
865 ymm = _mm256_cvtepi32_pd(xmm_i);
866 }
867
868 static inline XMMReg4Double Load4Val(const unsigned short *ptr)
869 {
870 XMMReg4Double reg;
871 reg.nsLoad4Val(ptr);
872 return reg;
873 }
874
875 inline void nsLoad4Val(const unsigned short *ptr)
876 {
877 __m128i xmm_i = GDALCopyInt64ToXMM(ptr);
878 xmm_i = _mm_cvtepu16_epi32(xmm_i);
879 ymm = _mm256_cvtepi32_pd(
880 xmm_i); // ok to use signed conversion since we are in the ushort
881 // range, so cannot be interpreted as negative int32
882 }
883
884 static inline XMMReg4Double Load4Val(const double *ptr)
885 {
886 XMMReg4Double reg;
887 reg.nsLoad4Val(ptr);
888 return reg;
889 }
890
891 inline void nsLoad4Val(const double *ptr)
892 {
893 ymm = _mm256_loadu_pd(ptr);
894 }
895
896 static inline XMMReg4Double Load4ValAligned(const double *ptr)
897 {
898 XMMReg4Double reg;
899 reg.nsLoad4ValAligned(ptr);
900 return reg;
901 }
902
903 inline void nsLoad4ValAligned(const double *ptr)
904 {
905 ymm = _mm256_load_pd(ptr);
906 }
907
908 static inline XMMReg4Double Load4Val(const float *ptr)
909 {
910 XMMReg4Double reg;
911 reg.nsLoad4Val(ptr);
912 return reg;
913 }
914
915 inline void nsLoad4Val(const float *ptr)
916 {
917 ymm = _mm256_cvtps_pd(_mm_loadu_ps(ptr));
918 }
919
920 static inline XMMReg4Double Equals(const XMMReg4Double &expr1,
921 const XMMReg4Double &expr2)
922 {
923 XMMReg4Double reg;
924 reg.ymm = _mm256_cmp_pd(expr1.ymm, expr2.ymm, _CMP_EQ_OQ);
925 return reg;
926 }
927
928 static inline XMMReg4Double NotEquals(const XMMReg4Double &expr1,
929 const XMMReg4Double &expr2)
930 {
931 XMMReg4Double reg;
932 reg.ymm = _mm256_cmp_pd(expr1.ymm, expr2.ymm, _CMP_NEQ_OQ);
933 return reg;
934 }
935
936 static inline XMMReg4Double Greater(const XMMReg4Double &expr1,
937 const XMMReg4Double &expr2)
938 {
939 XMMReg4Double reg;
940 reg.ymm = _mm256_cmp_pd(expr1.ymm, expr2.ymm, _CMP_GT_OQ);
941 return reg;
942 }
943
944 static inline XMMReg4Double And(const XMMReg4Double &expr1,
945 const XMMReg4Double &expr2)
946 {
947 XMMReg4Double reg;
948 reg.ymm = _mm256_and_pd(expr1.ymm, expr2.ymm);
949 return reg;
950 }
951
952 static inline XMMReg4Double Ternary(const XMMReg4Double &cond,
953 const XMMReg4Double &true_expr,
954 const XMMReg4Double &false_expr)
955 {
956 XMMReg4Double reg;
957 reg.ymm = _mm256_or_pd(_mm256_and_pd(cond.ymm, true_expr.ymm),
958 _mm256_andnot_pd(cond.ymm, false_expr.ymm));
959 return reg;
960 }
961
962 static inline XMMReg4Double Min(const XMMReg4Double &expr1,
963 const XMMReg4Double &expr2)
964 {
965 XMMReg4Double reg;
966 reg.ymm = _mm256_min_pd(expr1.ymm, expr2.ymm);
967 return reg;
968 }
969
970 inline XMMReg4Double &operator=(const XMMReg4Double &other)
971 {
972 ymm = other.ymm;
973 return *this;
974 }
975
976 inline XMMReg4Double &operator+=(const XMMReg4Double &other)
977 {
978 ymm = _mm256_add_pd(ymm, other.ymm);
979 return *this;
980 }
981
982 inline XMMReg4Double &operator*=(const XMMReg4Double &other)
983 {
984 ymm = _mm256_mul_pd(ymm, other.ymm);
985 return *this;
986 }
987
988 inline XMMReg4Double operator+(const XMMReg4Double &other) const
989 {
990 XMMReg4Double ret;
991 ret.ymm = _mm256_add_pd(ymm, other.ymm);
992 return ret;
993 }
994
995 inline XMMReg4Double operator-(const XMMReg4Double &other) const
996 {
997 XMMReg4Double ret;
998 ret.ymm = _mm256_sub_pd(ymm, other.ymm);
999 return ret;
1000 }
1001
1002 inline XMMReg4Double operator*(const XMMReg4Double &other) const
1003 {
1004 XMMReg4Double ret;
1005 ret.ymm = _mm256_mul_pd(ymm, other.ymm);
1006 return ret;
1007 }
1008
1009 inline XMMReg4Double operator/(const XMMReg4Double &other) const
1010 {
1011 XMMReg4Double ret;
1012 ret.ymm = _mm256_div_pd(ymm, other.ymm);
1013 return ret;
1014 }
1015
1016 void AddToLow(const XMMReg2Double &other)
1017 {
1018 __m256d ymm2 = _mm256_setzero_pd();
1019 ymm2 = _mm256_insertf128_pd(ymm2, other.xmm, 0);
1020 ymm = _mm256_add_pd(ymm, ymm2);
1021 }
1022
1023 inline double GetHorizSum() const
1024 {
1025 __m256d ymm_tmp1, ymm_tmp2;
1026 ymm_tmp2 = _mm256_hadd_pd(ymm, ymm);
1027 ymm_tmp1 = _mm256_permute2f128_pd(ymm_tmp2, ymm_tmp2, 1);
1028 ymm_tmp1 = _mm256_add_pd(ymm_tmp1, ymm_tmp2);
1029 return _mm_cvtsd_f64(_mm256_castpd256_pd128(ymm_tmp1));
1030 }
1031
1032 inline void Store4Val(unsigned char *ptr) const
1033 {
1034 __m128i xmm_i =
1035 _mm256_cvttpd_epi32(_mm256_add_pd(ymm, _mm256_set1_pd(0.5)));
1036 // xmm_i = _mm_packs_epi32(xmm_i, xmm_i); // Pack int32 to int16
1037 // xmm_i = _mm_packus_epi16(xmm_i, xmm_i); // Pack int16 to uint8
1038 xmm_i =
1039 _mm_shuffle_epi8(xmm_i, _mm_cvtsi32_si128(0 | (4 << 8) | (8 << 16) |
1040 (12 << 24))); // SSSE3
1041 GDALCopyXMMToInt32(xmm_i, reinterpret_cast<GInt32 *>(ptr));
1042 }
1043
1044 inline void Store4Val(unsigned short *ptr) const
1045 {
1046 __m128i xmm_i =
1047 _mm256_cvttpd_epi32(_mm256_add_pd(ymm, _mm256_set1_pd(0.5)));
1048 xmm_i = _mm_packus_epi32(xmm_i, xmm_i); // Pack uint32 to uint16
1049 GDALCopyXMMToInt64(xmm_i, reinterpret_cast<GInt64 *>(ptr));
1050 }
1051
1052 inline void Store4Val(float *ptr) const
1053 {
1054 _mm_storeu_ps(ptr, _mm256_cvtpd_ps(ymm));
1055 }
1056
1057 inline void Store4Val(double *ptr) const
1058 {
1059 _mm256_storeu_pd(ptr, ymm);
1060 }
1061
1062 inline void StoreMask(unsigned char *ptr) const
1063 {
1064 _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr),
1065 _mm256_castpd_si256(ymm));
1066 }
1067};
1068
1069#else
1070
1071class XMMReg4Double
1072{
1073 public:
1074 XMMReg2Double low, high;
1075
1076#if defined(__GNUC__)
1077#pragma GCC diagnostic push
1078#pragma GCC diagnostic ignored "-Weffc++"
1079#endif
1080 /* coverity[uninit_member] */
1081 XMMReg4Double() = default;
1082#if defined(__GNUC__)
1083#pragma GCC diagnostic pop
1084#endif
1085
1086 XMMReg4Double(const XMMReg4Double &other) : low(other.low), high(other.high)
1087 {
1088 }
1089
1090 static inline XMMReg4Double Zero()
1091 {
1092 XMMReg4Double reg;
1093 reg.low.Zeroize();
1094 reg.high.Zeroize();
1095 return reg;
1096 }
1097
1098 static inline XMMReg4Double Load1ValHighAndLow(const double *ptr)
1099 {
1100 XMMReg4Double reg;
1101 reg.low.nsLoad1ValHighAndLow(ptr);
1102 reg.high = reg.low;
1103 return reg;
1104 }
1105
1106 static inline XMMReg4Double Load4Val(const unsigned char *ptr)
1107 {
1108 XMMReg4Double reg;
1109 XMMReg2Double::Load4Val(ptr, reg.low, reg.high);
1110 return reg;
1111 }
1112
1113 static inline XMMReg4Double Load4Val(const short *ptr)
1114 {
1115 XMMReg4Double reg;
1116 reg.low.nsLoad2Val(ptr);
1117 reg.high.nsLoad2Val(ptr + 2);
1118 return reg;
1119 }
1120
1121 static inline XMMReg4Double Load4Val(const unsigned short *ptr)
1122 {
1123 XMMReg4Double reg;
1124 reg.low.nsLoad2Val(ptr);
1125 reg.high.nsLoad2Val(ptr + 2);
1126 return reg;
1127 }
1128
1129 static inline XMMReg4Double Load4Val(const double *ptr)
1130 {
1131 XMMReg4Double reg;
1132 reg.low.nsLoad2Val(ptr);
1133 reg.high.nsLoad2Val(ptr + 2);
1134 return reg;
1135 }
1136
1137 static inline XMMReg4Double Load4ValAligned(const double *ptr)
1138 {
1139 XMMReg4Double reg;
1140 reg.low.nsLoad2ValAligned(ptr);
1141 reg.high.nsLoad2ValAligned(ptr + 2);
1142 return reg;
1143 }
1144
1145 static inline XMMReg4Double Load4Val(const float *ptr)
1146 {
1147 XMMReg4Double reg;
1148 XMMReg2Double::Load4Val(ptr, reg.low, reg.high);
1149 return reg;
1150 }
1151
1152 static inline XMMReg4Double Equals(const XMMReg4Double &expr1,
1153 const XMMReg4Double &expr2)
1154 {
1155 XMMReg4Double reg;
1156 reg.low = XMMReg2Double::Equals(expr1.low, expr2.low);
1157 reg.high = XMMReg2Double::Equals(expr1.high, expr2.high);
1158 return reg;
1159 }
1160
1161 static inline XMMReg4Double NotEquals(const XMMReg4Double &expr1,
1162 const XMMReg4Double &expr2)
1163 {
1164 XMMReg4Double reg;
1165 reg.low = XMMReg2Double::NotEquals(expr1.low, expr2.low);
1166 reg.high = XMMReg2Double::NotEquals(expr1.high, expr2.high);
1167 return reg;
1168 }
1169
1170 static inline XMMReg4Double Greater(const XMMReg4Double &expr1,
1171 const XMMReg4Double &expr2)
1172 {
1173 XMMReg4Double reg;
1174 reg.low = XMMReg2Double::Greater(expr1.low, expr2.low);
1175 reg.high = XMMReg2Double::Greater(expr1.high, expr2.high);
1176 return reg;
1177 }
1178
1179 static inline XMMReg4Double And(const XMMReg4Double &expr1,
1180 const XMMReg4Double &expr2)
1181 {
1182 XMMReg4Double reg;
1183 reg.low = XMMReg2Double::And(expr1.low, expr2.low);
1184 reg.high = XMMReg2Double::And(expr1.high, expr2.high);
1185 return reg;
1186 }
1187
1188 static inline XMMReg4Double Ternary(const XMMReg4Double &cond,
1189 const XMMReg4Double &true_expr,
1190 const XMMReg4Double &false_expr)
1191 {
1192 XMMReg4Double reg;
1193 reg.low =
1194 XMMReg2Double::Ternary(cond.low, true_expr.low, false_expr.low);
1195 reg.high =
1196 XMMReg2Double::Ternary(cond.high, true_expr.high, false_expr.high);
1197 return reg;
1198 }
1199
1200 static inline XMMReg4Double Min(const XMMReg4Double &expr1,
1201 const XMMReg4Double &expr2)
1202 {
1203 XMMReg4Double reg;
1204 reg.low = XMMReg2Double::Min(expr1.low, expr2.low);
1205 reg.high = XMMReg2Double::Min(expr1.high, expr2.high);
1206 return reg;
1207 }
1208
1209 inline XMMReg4Double &operator=(const XMMReg4Double &other)
1210 {
1211 low = other.low;
1212 high = other.high;
1213 return *this;
1214 }
1215
1216 inline XMMReg4Double &operator+=(const XMMReg4Double &other)
1217 {
1218 low += other.low;
1219 high += other.high;
1220 return *this;
1221 }
1222
1223 inline XMMReg4Double &operator*=(const XMMReg4Double &other)
1224 {
1225 low *= other.low;
1226 high *= other.high;
1227 return *this;
1228 }
1229
1230 inline XMMReg4Double operator+(const XMMReg4Double &other) const
1231 {
1232 XMMReg4Double ret;
1233 ret.low = low + other.low;
1234 ret.high = high + other.high;
1235 return ret;
1236 }
1237
1238 inline XMMReg4Double operator-(const XMMReg4Double &other) const
1239 {
1240 XMMReg4Double ret;
1241 ret.low = low - other.low;
1242 ret.high = high - other.high;
1243 return ret;
1244 }
1245
1246 inline XMMReg4Double operator*(const XMMReg4Double &other) const
1247 {
1248 XMMReg4Double ret;
1249 ret.low = low * other.low;
1250 ret.high = high * other.high;
1251 return ret;
1252 }
1253
1254 inline XMMReg4Double operator/(const XMMReg4Double &other) const
1255 {
1256 XMMReg4Double ret;
1257 ret.low = low / other.low;
1258 ret.high = high / other.high;
1259 return ret;
1260 }
1261
1262 void AddToLow(const XMMReg2Double &other)
1263 {
1264 low += other;
1265 }
1266
1267 inline double GetHorizSum() const
1268 {
1269 return (low + high).GetHorizSum();
1270 }
1271
1272 inline void Store4Val(unsigned char *ptr) const
1273 {
1274#ifdef USE_SSE2_EMULATION
1275 low.Store2Val(ptr);
1276 high.Store2Val(ptr + 2);
1277#else
1278 __m128i tmpLow = _mm_cvttpd_epi32(_mm_add_pd(
1279 low.xmm,
1280 _mm_set1_pd(0.5))); /* Convert the 2 double values to 2 integers */
1281 __m128i tmpHigh = _mm_cvttpd_epi32(_mm_add_pd(
1282 high.xmm,
1283 _mm_set1_pd(0.5))); /* Convert the 2 double values to 2 integers */
1284 auto tmp = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(tmpLow),
1285 _mm_castsi128_ps(tmpHigh),
1286 _MM_SHUFFLE(1, 0, 1, 0)));
1287 tmp = _mm_packs_epi32(tmp, tmp);
1288 tmp = _mm_packus_epi16(tmp, tmp);
1289 GDALCopyXMMToInt32(tmp, reinterpret_cast<GInt32 *>(ptr));
1290#endif
1291 }
1292
1293 inline void Store4Val(unsigned short *ptr) const
1294 {
1295#if 1
1296 low.Store2Val(ptr);
1297 high.Store2Val(ptr + 2);
1298#else
1299 __m128i xmm0 = _mm_cvtpd_epi32(low.xmm);
1300 __m128i xmm1 = _mm_cvtpd_epi32(high.xmm);
1301 xmm0 = _mm_or_si128(xmm0, _mm_slli_si128(xmm1, 8));
1302#if __SSE4_1__
1303 xmm0 = _mm_packus_epi32(xmm0, xmm0); // Pack uint32 to uint16
1304#else
1305 xmm0 = _mm_add_epi32(xmm0, _mm_set1_epi32(-32768));
1306 xmm0 = _mm_packs_epi32(xmm0, xmm0);
1307 xmm0 = _mm_sub_epi16(xmm0, _mm_set1_epi16(-32768));
1308#endif
1309 GDALCopyXMMToInt64(xmm0, (GInt64 *)ptr);
1310#endif
1311 }
1312
1313 inline void Store4Val(float *ptr) const
1314 {
1315 low.Store2Val(ptr);
1316 high.Store2Val(ptr + 2);
1317 }
1318
1319 inline void Store4Val(double *ptr) const
1320 {
1321 low.Store2Val(ptr);
1322 high.Store2Val(ptr + 2);
1323 }
1324
1325 inline void StoreMask(unsigned char *ptr) const
1326 {
1327 low.StoreMask(ptr);
1328 high.StoreMask(ptr + 16);
1329 }
1330};
1331
1332#endif
1333
1334#endif /* #ifndef DOXYGEN_SKIP */
1335
1336#endif /* GDALSSE_PRIV_H_INCLUDED */
Core portability definitions for CPL.
short GInt16
Int16 type.
Definition cpl_port.h:201
GIntBig GInt64
Signed 64 bit integer type.
Definition cpl_port.h:254
unsigned short GUInt16
Unsigned int16 type.
Definition cpl_port.h:203
int GInt32
Int32 type.
Definition cpl_port.h:195