@@ -23,6 +23,14 @@ limitations under the License.
2323#include < cstring>
2424#include " types.hpp"
2525
26+ #if !defined(NO_INTRINSICS)
27+ #if defined(__ARM_NEON) || defined(__aarch64__)
28+ #include < arm_neon.h>
29+ #elif defined(__AVX512F__) || defined(__AVX2__) || defined(__SSE2__)
30+ #include < immintrin.h>
31+ #endif
32+ #endif
33+
2634
2735namespace kanzi {
2836
@@ -93,16 +101,158 @@ static KANZI_ALWAYS_INLINE uint64 knz_bswap64(uint64 x) {
93101#endif
94102}
95103
96- #ifdef AGGRESSIVE_OPTIMIZATION
97- // There be dragons!
98- // User assumes responsibility for alignment and aliasing constraints.
99- #define KANZI_MEM_EQ4 (x, y ) (*(const uint32*)(x) == *(const uint32*)(y))
100- #define KANZI_MEM_EQ8 (x, y ) (*(const uint64*)(x) == *(const uint64*)(y))
104+ #if !defined(NO_INTRINSICS) && (defined(__ARM_NEON) || defined(__aarch64__))
105+
106+ static KANZI_ALWAYS_INLINE bool memEq4 (const byte* x, const byte* y)
107+ {
108+ const uint32x2_t a = vld1_dup_u32 (reinterpret_cast <const uint32_t *>(x));
109+ const uint32x2_t b = vld1_dup_u32 (reinterpret_cast <const uint32_t *>(y));
110+ return vget_lane_u32 (vceq_u32 (a, b), 0 ) != 0 ;
111+ }
112+
113+ static KANZI_ALWAYS_INLINE bool memEq8 (const byte* x, const byte* y)
114+ {
115+ #if defined(__aarch64__)
116+ const uint64x1_t a = vld1_u64 (reinterpret_cast <const uint64_t *>(x));
117+ const uint64x1_t b = vld1_u64 (reinterpret_cast <const uint64_t *>(y));
118+ return vget_lane_u64 (vceq_u64 (a, b), 0 ) != 0 ;
119+ #else
120+ const uint32x2_t a = vld1_u32 (reinterpret_cast <const uint32_t *>(x));
121+ const uint32x2_t b = vld1_u32 (reinterpret_cast <const uint32_t *>(y));
122+ const uint32x2_t eq = vceq_u32 (a, b);
123+ return (vget_lane_u32 (eq, 0 ) != 0 ) && (vget_lane_u32 (eq, 1 ) != 0 );
124+ #endif
125+ }
126+
127+ static KANZI_ALWAYS_INLINE void memCp8 (byte* dst, const byte* src)
128+ {
129+ vst1_u8 (reinterpret_cast <uint8_t *>(dst), vld1_u8 (reinterpret_cast <const uint8_t *>(src)));
130+ }
131+
132+ static KANZI_ALWAYS_INLINE void memCp16 (byte* dst, const byte* src)
133+ {
134+ vst1q_u8 (reinterpret_cast <uint8_t *>(dst), vld1q_u8 (reinterpret_cast <const uint8_t *>(src)));
135+ }
136+
137+ #elif !defined(NO_INTRINSICS) && defined(__AVX512F__)
138+
139+ static KANZI_ALWAYS_INLINE bool memEq4 (const byte* x, const byte* y)
140+ {
141+ const __mmask16 mask = 0x0001 ;
142+ const __m512i a = _mm512_maskz_loadu_epi32 (mask, x);
143+ const __m512i b = _mm512_maskz_loadu_epi32 (mask, y);
144+ return _mm512_mask_cmpeq_epi32_mask (mask, a, b) == mask;
145+ }
146+
147+ static KANZI_ALWAYS_INLINE bool memEq8 (const byte* x, const byte* y)
148+ {
149+ const __mmask8 mask = 0x01 ;
150+ const __m512i a = _mm512_maskz_loadu_epi64 (mask, x);
151+ const __m512i b = _mm512_maskz_loadu_epi64 (mask, y);
152+ return _mm512_mask_cmpeq_epi64_mask (mask, a, b) == mask;
153+ }
154+
155+ static KANZI_ALWAYS_INLINE void memCp8 (byte* dst, const byte* src)
156+ {
157+ const __m512i value = _mm512_maskz_loadu_epi64 (0x01 , src);
158+ _mm512_mask_storeu_epi64 (dst, 0x01 , value);
159+ }
160+
161+ static KANZI_ALWAYS_INLINE void memCp16 (byte* dst, const byte* src)
162+ {
163+ const __m512i value = _mm512_maskz_loadu_epi64 (0x03 , src);
164+ _mm512_mask_storeu_epi64 (dst, 0x03 , value);
165+ }
166+
167+ #elif !defined(NO_INTRINSICS) && defined(__AVX2__)
168+
169+ static KANZI_ALWAYS_INLINE bool memEq4 (const byte* x, const byte* y)
170+ {
171+ const __m256i mask = _mm256_set_epi32 (0 , 0 , 0 , 0 , 0 , 0 , 0 , -1 );
172+ const __m256i a = _mm256_maskload_epi32 (reinterpret_cast <const int *>(x), mask);
173+ const __m256i b = _mm256_maskload_epi32 (reinterpret_cast <const int *>(y), mask);
174+ return (_mm256_movemask_epi8 (_mm256_cmpeq_epi32 (a, b)) & 0x0F ) == 0x0F ;
175+ }
176+
177+ static KANZI_ALWAYS_INLINE bool memEq8 (const byte* x, const byte* y)
178+ {
179+ const __m256i mask = _mm256_set_epi64x (0 , 0 , 0 , -1 );
180+ const __m256i a = _mm256_maskload_epi64 (reinterpret_cast <const long long *>(x), mask);
181+ const __m256i b = _mm256_maskload_epi64 (reinterpret_cast <const long long *>(y), mask);
182+ return (_mm256_movemask_epi8 (_mm256_cmpeq_epi64 (a, b)) & 0xFF ) == 0xFF ;
183+ }
184+
185+ static KANZI_ALWAYS_INLINE void memCp8 (byte* dst, const byte* src)
186+ {
187+ const __m256i mask = _mm256_set_epi64x (0 , 0 , 0 , -1 );
188+ const __m256i value = _mm256_maskload_epi64 (reinterpret_cast <const long long *>(src), mask);
189+ _mm256_maskstore_epi64 (reinterpret_cast <long long *>(dst), mask, value);
190+ }
191+
192+ static KANZI_ALWAYS_INLINE void memCp16 (byte* dst, const byte* src)
193+ {
194+ const __m256i mask = _mm256_set_epi64x (0 , 0 , -1 , -1 );
195+ const __m256i value = _mm256_maskload_epi64 (reinterpret_cast <const long long *>(src), mask);
196+ _mm256_maskstore_epi64 (reinterpret_cast <long long *>(dst), mask, value);
197+ }
198+
199+ #elif !defined(NO_INTRINSICS) && defined(__SSE2__)
200+
201+ static KANZI_ALWAYS_INLINE bool memEq4 (const byte* x, const byte* y)
202+ {
203+ const __m128i va = _mm_loadu_si32 (x);
204+ const __m128i vb = _mm_loadu_si32 (y);
205+ return _mm_cvtsi128_si32 (_mm_cmpeq_epi32 (va, vb)) != 0 ;
206+ }
207+
208+ static KANZI_ALWAYS_INLINE bool memEq8 (const byte* x, const byte* y)
209+ {
210+ const __m128i a = _mm_loadl_epi64 (reinterpret_cast <const __m128i*>(x));
211+ const __m128i b = _mm_loadl_epi64 (reinterpret_cast <const __m128i*>(y));
212+ return _mm_movemask_epi8 (_mm_cmpeq_epi8 (a, b)) == 0xFF ;
213+ }
214+
215+ static KANZI_ALWAYS_INLINE void memCp8 (byte* dst, const byte* src)
216+ {
217+ _mm_storel_epi64 (reinterpret_cast <__m128i*>(dst),
218+ _mm_loadl_epi64 (reinterpret_cast <const __m128i*>(src)));
219+ }
220+
221+ static KANZI_ALWAYS_INLINE void memCp16 (byte* dst, const byte* src)
222+ {
223+ _mm_storeu_si128 (reinterpret_cast <__m128i*>(dst),
224+ _mm_loadu_si128 (reinterpret_cast <const __m128i*>(src)));
225+ }
226+
101227#else
102- #define KANZI_MEM_EQ4 (x, y ) (std::memcmp((x), (y), 4 ) == 0 )
103- #define KANZI_MEM_EQ8 (x, y ) (std::memcmp((x), (y), 8 ) == 0 )
228+
229+ static KANZI_ALWAYS_INLINE bool memEq4 (const byte* x, const byte* y)
230+ {
231+ return std::memcmp (x, y, 4 ) == 0 ;
232+ }
233+
234+ static KANZI_ALWAYS_INLINE bool memEq8 (const byte* x, const byte* y)
235+ {
236+ return std::memcmp (x, y, 8 ) == 0 ;
237+ }
238+
239+ static KANZI_ALWAYS_INLINE void memCp8 (byte* dst, const byte* src)
240+ {
241+ memcpy (dst, src, 8 );
242+ }
243+
244+ static KANZI_ALWAYS_INLINE void memCp16 (byte* dst, const byte* src)
245+ {
246+ memcpy (dst, src, 16 );
247+ }
248+
104249#endif
105250
251+ #define KANZI_MEM_EQ4 (x, y ) (::kanzi::memEq4((x), (y)))
252+ #define KANZI_MEM_EQ8 (x, y ) (::kanzi::memEq8((x), (y)))
253+ #define KANZI_MEM_CP8 (dst, src ) (::kanzi::memCp8((dst), (src)))
254+ #define KANZI_MEM_CP16 (dst, src ) (::kanzi::memCp16((dst), (src)))
255+
106256// Detect host endianness
107257
108258#if __cplusplus >= 202002L
0 commit comments