Проверка целостности целых SSE2
При использовании инструкций SSE2, таких как PADDD
(т.е. _mm_add_epi32
intrinsic), есть ли способ проверить, не переполнилось ли какое-либо из операций?
Я думал, что, возможно, флаг в регистре управления MXCSR может быть установлен после переполнения, но я этого не вижу. Например, _mm_getcsr()
печатает одно и то же значение в обоих случаях ниже (8064):
#include <iostream>
#include <emmintrin.h>
using namespace std;
void main()
{
__m128i a = _mm_set_epi32(1, 0, 0, 0);
__m128i b = _mm_add_epi32(a, a);
cout << "MXCSR: " << _mm_getcsr() << endl;
cout << "Result: " << b.m128i_i32[3] << endl;
__m128i c = _mm_set_epi32((1<<31)-1, 3, 2, 1);
__m128i d = _mm_add_epi32(c, c);
cout << "MXCSR: " << _mm_getcsr() << endl;
cout << "Result: " << d.m128i_i32[3] << endl;
}
Есть ли другой способ проверить переполнение с помощью SSE2?
Ответы
Ответ 1
Вот несколько более эффективная версия @hirschhornsalz sum_and_overflow
функция:
void sum_and_overflow(__v4si a, __v4si b, __v4si& sum, __v4si& overflow)
{
__v4si sa, sb;
sum = _mm_add_epi32(a, b); // calculate sum
sa = _mm_xor_si128(sum, a); // compare sign of sum with sign of a
sb = _mm_xor_si128(sum, b); // compare sign of sum with sign of b
overflow = _mm_and_si128(sa, sb); // get overflow in sign bit
overflow = _mm_srai_epi32(overflow, 31); // convert to SIMD boolean (-1 == TRUE, 0 == FALSE)
}
Он использует выражение для обнаружения переполнения из Hacker Delight стр. 27:
sum = a + b;
overflow = (sum ^ a) & (sum ^ b); // overflow flag in sign bit
Обратите внимание, что вектор переполнения будет содержать более обычные логические значения SIMD -1 для TRUE (переполнение) и 0 для FALSE (без переполнения). Если вам нужно только переполнение знакового бита, а остальные бит "не заботятся", вы можете опустить последнюю строку функции, уменьшив количество инструкций SIMD от 5 до 4.
NB: это решение, а также предыдущее решение на котором оно основано, предназначены для целых значений со знаком. Решение для значений без знака потребует немного другого подхода (см. @Stephen Canon answer).
Ответ 2
Так как у вас есть 4 возможных переполнения, регистр управления будет очень быстро заканчиваться из бит, особенно если вы хотите носить, подписывать и т.д., и даже для добавления вектора, состоящего из 16 байт:-)
Флаг переполнения установлен, если входные знаковые биты равны, а бит знака результата отличается от входного знакового бита.
Эти функции вычисляют sum = a+b
и переполняют вручную. Для каждого переполнения 0x80000000 возвращается в overflow
.
void sum_and_overflow(__v4si a, __v4si b, __v4si& sum, __v4si& overflow) {
__v4si signmask = _mm_set1_epi32(0x80000000);
sum = a+b;
a &= signmask;
b &= signmask;
overflow = sum & signmask;
overflow = ~(a^b) & (overflow^a); // overflow is 1 if (a==b) and (resultbit has changed)
}
Примечание. Если у вас нет gcc, вы должны заменить операторы ^
&
+
соответствующими функциями SSE, такими как _mm_and_si128()
, _mm_add_epi32()
и т.д.
Изменить: я заметил, что and
с маской, конечно, может быть выполнена в самом конце функции, экономя две операции and
. Но компилятор, скорее всего, будет достаточно умным, чтобы сделать это сам.
Ответ 3
Я заметил, что вы также попросили решение для неподписанных; к счастью, это тоже довольно легко:
__v4si mask = _mm_set1_epi32(0x80000000);
sum = _mm_add_epi32(a, b);
overflow = _mm_cmpgt_epi32(_mm_xor_si128(mask, a), _mm_xor_si128(mask, sum));
Обычно для обнаружения неподписанного переполнения вы просто проверяете либо sum < a
, либо sum < b
. Однако SSE не имеет неподписанных сравнений; xor
- аргументы с помощью 0x80000000
позволяют использовать сопоставленное сравнение для получения того же результата.
Ответ 4
Никакие флаги не затрагиваются базовой инструкцией PADDD.
Итак, чтобы проверить это, вам нужно написать дополнительный код, в зависимости от того, что вы хотите сделать.
Примечание. Вам немного мешает отсутствие интрисий в epi32.