#include "defs.h"
#include <smmintrin.h>
#include <stdio.h>
/* Straight-line branchless SSE 4.2 code for searching an array of uint16_t
hash codes. */
static inline int32_t mask(int32_t a, int32_t b) { return -(a == b); }
#if defined(__GNUC__)
static inline int32_t first_bit_set(int32_t a) {
return __builtin_ffs(a) - 1;
}
#else
static uint8_t de_bruijn_table[] = {
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
static inline int32_t first_bit_set(int32_t a) {
int32_t zero_case = mask(0, a);
uint32_t x = (uint32_t) (a & -a);
x *= 0x077CB531;
x >>= 27;
return zero_case | de_bruijn_table[x];
}
#endif
static inline __m128i fill(small_hash_t v) {
int32_t v1 = (((int)v) << 16) | v;
__m128i x = _mm_cvtsi32_si128(0);
x = _mm_insert_epi32(x, v1, 0);
return _mm_shuffle_epi32(x, _MM_SHUFFLE(0,0,0,0));
}
#ifndef SIDD_UWORD_OPS
#define SIDD_UWORD_OPS _SIDD_UWORD_OPS
#endif
#ifndef SIDD_CMP_EQUAL_EACH
#define SIDD_CMP_EQUAL_EACH _SIDD_CMP_EQUAL_EACH
#endif
#ifndef SIDD_BIT_MASK
#define SIDD_BIT_MASK _SIDD_BIT_MASK
#endif
#define _MODE (SIDD_UWORD_OPS | SIDD_CMP_EQUAL_EACH)
static inline __m128i cmp_mask(__m128i a, __m128i b) {
const int mode = SIDD_UWORD_OPS | SIDD_CMP_EQUAL_EACH | SIDD_BIT_MASK;
return _mm_cmpistrm(a, b, mode);
}
static inline int32_t line_result(uint32_t m, int start) {
int32_t p = first_bit_set((int32_t) m);
int32_t mm = mask(p, -1);
return mm | (start + p);
}
#define DUMP(xval) do { \
uint16_t xval##_x0 = _mm_extract_epi16(xval, 0); \
uint16_t xval##_x1 = _mm_extract_epi16(xval, 1); \
uint16_t xval##_x2 = _mm_extract_epi16(xval, 2); \
uint16_t xval##_x3 = _mm_extract_epi16(xval, 3); \
uint16_t xval##_x4 = _mm_extract_epi16(xval, 4); \
uint16_t xval##_x5 = _mm_extract_epi16(xval, 5); \
uint16_t xval##_x6 = _mm_extract_epi16(xval, 6); \
uint16_t xval##_x7 = _mm_extract_epi16(xval, 7); \
printf(" % 10s: %04x-%04x-%04x-%04x-%04x-%04x-%04x-%04x\n", \
#xval, xval##_x0, xval##_x1, xval##_x2, xval##_x3, \
xval##_x4, xval##_x5, xval##_x6, xval##_x7); \
} while(0);
int line_search(small_hash_t* array, int start0, small_hash_t v1) {
int offset = start0 & 31;
int start = start0 & ~31;
__m128i* p = (__m128i*) &array[start];
__m128i x1, val1, val2, val3, val4;
__m128i m1, m2, m3, m4, dmask;
x1 = fill(v1);
val1 = *p++;
m1 = cmp_mask(x1, val1);
val2 = *p++;
m2 = _mm_slli_si128(cmp_mask(x1, val2), 1);
val3 = *p++;
m3 = _mm_slli_si128(cmp_mask(x1, val3), 2);
val4 = *p;
m4 = _mm_slli_si128(cmp_mask(x1, val4), 3);
dmask = _mm_or_si128(_mm_or_si128(m1, m2),
_mm_or_si128(m3, m4));
uint32_t imask = _mm_extract_epi32(dmask, 0);
const uint32_t p2 = 1 << offset;
const uint32_t dest_mask = imask & ~(p2 - 1);
return line_result(dest_mask, start);
}
int line_search_2(small_hash_t* array, int start0, small_hash_t v1,
small_hash_t v2) {
int offset = start0 & 31;
int start = start0 & ~31;
__m128i* p = (__m128i*) &array[start];
__m128i x1, x2, val1, val2, val3, val4;
__m128i m1, m2, m3, m4, dmask;
x1 = fill(v1);
x2 = fill(v2);
#define M(v) _mm_or_si128(cmp_mask(x1,(v)), \
cmp_mask(x2,(v)))
val1 = *p++;
m1 = M(val1);
val2 = *p++;
m2 = _mm_slli_si128(M(val2), 1);
val3 = *p++;
m3 = _mm_slli_si128(M(val3), 2);
val4 = *p;
m4 = _mm_slli_si128(M(val4), 3);
#undef M
dmask = _mm_or_si128(_mm_or_si128(m1, m2),
_mm_or_si128(m3, m4));
uint32_t imask = _mm_extract_epi32(dmask, 0);
const uint32_t p2 = 1 << offset;
const uint32_t dest_mask = imask & ~(p2 - 1);
return line_result(dest_mask, start);
}
int line_search_3(small_hash_t* array, int start0, small_hash_t v1,
small_hash_t v2, small_hash_t v3) {
int offset = start0 & 31;
int start = start0 & ~31;
__m128i* p = (__m128i*) &array[start];
__m128i x1, x2, x3, val1, val2, val3, val4;
__m128i m1, m2, m3, m4, dmask;
x1 = fill(v1);
x2 = fill(v2);
x3 = fill(v3);
#define M(v) _mm_or_si128( \
cmp_mask(x1,(v)), \
_mm_or_si128(cmp_mask(x2,(v)), \
cmp_mask(x3,(v))))
val1 = *p++;
m1 = M(val1);
val2 = *p++;
m2 = _mm_slli_si128(M(val2), 1);
val3 = *p++;
m3 = _mm_slli_si128(M(val3), 2);
val4 = *p;
m4 = _mm_slli_si128(M(val4), 3);
#undef M
dmask = _mm_or_si128(_mm_or_si128(m1, m2),
_mm_or_si128(m3, m4));
uint32_t imask = _mm_extract_epi32(dmask, 0);
const uint32_t p2 = 1 << offset;
const uint32_t dest_mask = imask & ~(p2 - 1);
return line_result(dest_mask, start);
}