【问题标题】:SSE2 integer overflow checkingSSE2 整数溢出检查
【发布时间】:2012-05-17 16:01:48
【问题描述】:

当使用诸如PADDD(即_mm_add_epi32内在函数)等SSE2指令时,有没有办法检查是否有任何操作溢出?

我认为 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 是否溢出?

【问题讨论】:

  • 您可以在饱和模式 (PADDS) 下重复计算并比较结果。
  • 您对有符号或无符号溢出感兴趣吗?
  • @Dmitri:SSE2 中没有 32 位饱和添加(只有 8 位和 16 位)
  • 这可能有编译器的内部结构。我知道 Clang 正在添加它们,我想 gcc 已经有了它们,你检查了吗?
  • Alex:啊,好点子。我没有意识到这两种情况是不同的。理想情况下,我想为这两种情况提供解决方案。我正在研究与 SSE 相关的库,因此这两种情况都是相关的。

标签: c++ x86 sse simd sse2


【解决方案1】:

这是@hirschhornsalz's sum_and_overflow function 的一个更高效的版本:

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。

注意:此解决方案以及 previous solution on which it is based 用于有符号整数值。无符号值的解决方案需要稍微不同的方法(请参阅@Stephen Canonanswer)。

【讨论】:

  • 谢谢!这些都是一些不错的技巧,尤其是。复制符号位的右移。
  • 值得强调的是,此解决方案仅对 SIGNED 值有效 - 而不是 UNSIGNED。
  • @SquareRootOfTwentyThree:谢谢 - 从所有对符号位等的引用中应该可以看出这一点,但我会再添加一句话以使其 100% 清楚。
【解决方案2】:

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

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

【讨论】:

  • AVX512 终于添加了无符号比较:_mm512_cmp[eq|ge|gt|le|lt|neq]_epu32_mask。完整范围的 8/16/32/64 位元素大小可用于有符号和无符号(epiepu),具有任意比较谓词,而不仅仅是 gteq 用于 AVX2 和更早版本整数比较。
  • @PeterCordes 终于
【解决方案3】:

由于您有 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,则必须将 ^ &amp; + 运算符替换为适当的 SSE 内在函数,例如 _mm_and_si128()_mm_add_epi32() 等。

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

【讨论】:

  • 谢谢,drhirsch!我接受了 Paul R 的回答,因为这是为后代强调的最有意义的回答,但您的回答非常有帮助,我很感激您花时间写下来。
【解决方案4】:

底层PADDD 指令没有触及任何标志。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多