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