Ответ 1
SSE2 имеет прямую поддержку некоторых 64-битных целочисленных операций:
Установите оба элемента на 0:
__m128i z = _mm_setzero_si128();
Установите оба элемента в 1:
__m128i z = _mm_set1_epi64x(1); // also works for variables.
__m128i z = _mm_set_epi64x(hi, lo); // elements can be different
__m128i z = _mm_set_epi32(0,1,0,1); // if any compilers refuse int64_t in 32-bit mode. (None of the major ones do.)
Установить/загрузить младшие 64 бита с расширением нуля до __m128i
// supported even in 32-bit mode, and listed as an intrinsic for MOVQ
// so it should be atomic on aligned integers.
_mm_loadl_epi64((const __m128i*)p); // movq or movsd 64-bit load
_mm_cvtsi64x_si128(a); // only ICC, others refuse in 32-bit mode
_mm_loadl_epi64((const __m128i*)&a); // portable for a value instead of pointer
Вещи, основанные на _mm_set_epi32
могут быть скомпилированы в беспорядок некоторыми компиляторами, поэтому _mm_loadl_epi64
кажется, лучшая ставка для MSVC и ICC, а также gcc/clang, и на самом деле должна быть безопасной для ваших требований атомарных 64-битных нагрузок в 32- битовый режим. Смотрите это в проводнике компилятора Godbolt
Вертикально складывать/вычитать каждое 64-битное целое число:
__m128i z = _mm_add_epi64(x,y)
__m128i z = _mm_sub_epi64(x,y)
Сдвиг влево:
__m128i z = _mm_slli_epi64(x,i) // i must be an immediate
Битовые операторы:
__m128i z = _mm_and_si128(x,y)
__m128i z = _mm_or_si128(x,y)
SSE не имеет приращений, поэтому вам придется использовать константу с 1
.
Сравнения сложнее, поскольку 64-битная поддержка отсутствует до SSE4.1 pcmpeqq
и SSE4.2 pcmpgtq
Здесь один для равенства:
__m128i t = _mm_cmpeq_epi32(a,b);
__m128i z = _mm_and_si128(t,_mm_shuffle_epi32(t,177));
Это установит для каждого 64-битного элемента значение 0xffffffffffff
(он же -1)
если они равны. Если вы хотите, чтобы оно было 0
или 1
в int
, вы можете вытащить его с помощью _mm_cvtsi32_si128()
и добавить 1
. (Но иногда вы можете сделать total -= cmp_result;
вместо преобразования и добавления.)
И меньше, чем: (не полностью проверено)
a = _mm_xor_si128(a,_mm_set1_epi32(0x80000000));
b = _mm_xor_si128(b,_mm_set1_epi32(0x80000000));
__m128i t = _mm_cmplt_epi32(a,b);
__m128i u = _mm_cmpgt_epi32(a,b);
__m128i z = _mm_or_si128(t,_mm_shuffle_epi32(t,177));
z = _mm_andnot_si128(_mm_shuffle_epi32(u,245),z);
Это установит для каждого 64-битного элемента значение 0xffffffffffff
если соответствующий элемент в a
меньше, чем b
.
Вот версии "равно" и "меньше чем", которые возвращают логическое значение. Они возвращают результат сравнения для нижнего 64-разрядного целого числа.
inline bool equals(__m128i a,__m128i b){
__m128i t = _mm_cmpeq_epi32(a,b);
__m128i z = _mm_and_si128(t,_mm_shuffle_epi32(t,177));
return _mm_cvtsi128_si32(z) & 1;
}
inline bool lessthan(__m128i a,__m128i b){
a = _mm_xor_si128(a,_mm_set1_epi32(0x80000000));
b = _mm_xor_si128(b,_mm_set1_epi32(0x80000000));
__m128i t = _mm_cmplt_epi32(a,b);
__m128i u = _mm_cmpgt_epi32(a,b);
__m128i z = _mm_or_si128(t,_mm_shuffle_epi32(t,177));
z = _mm_andnot_si128(_mm_shuffle_epi32(u,245),z);
return _mm_cvtsi128_si32(z) & 1;
}