21

当使用 SSE2 指令(例如PADDD_mm_add_epi32内在指令)时,有没有办法检查是否有任何操作溢出?

我认为在溢出后可能会设置 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 是否溢出?

4

4 回答 4

13

这是@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's Delight第 27 页的溢出检测表达式:

sum = a + b;
overflow = (sum ^ a) & (sum ^ b);               // overflow flag in sign bit

请注意,溢出向量将包含更传统的 SIMD 布尔值,即 -1 表示 TRUE(溢出)和 0 表示 FALSE(无溢出)。如果您只需要符号位溢出而其他位“无关紧要”,那么您可以省略函数的最后一行,将 SIMD 指令的数量从 5 减少到 4。

注意:此解决方案以及它所基于的先前解决方案都是针对有符号整数值的。无符号值的解决方案需要稍微不同的方法(请参阅@Stephen Canon回答)。

于 2012-05-09T11:42:00.460 回答
9

由于您有 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()

编辑:我刚刚注意到andwith mask 当然可以在函数的最后完成,节省了两个and操作。但是编译器很可能足够聪明,可以自己完成。

于 2012-05-09T09:17:23.393 回答
6

我注意到您也要求提供未签名的解决方案;幸运的是,这也很容易:

__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 < asum < b。但是,SSE 没有无符号比较;xor-ing 参数0x80000000允许您使用带符号的比较来获得相同的结果。

于 2012-05-13T13:07:50.407 回答
2

底层PADDD指令不涉及任何标志。

因此,要对此进行测试,您必须编写额外的代码,具体取决于您想要做什么。

注意:由于缺少 Epi32 内部结构,您有点受阻

于 2012-05-09T08:18:37.937 回答