SSE2整数溢出检查

Igo*_*sky 19 c++ x86 sse simd sse2

当使用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;
}
Run Code Online (Sandbox Code Playgroud)

有没有其他方法来检查SSE2的溢出?

Pau*_*l R 10

这是一个更有效的@ 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)
}
Run Code Online (Sandbox Code Playgroud)

它使用来自Hacker's Delight第27页的溢出检测表达式:

sum = a + b;
overflow = (sum ^ a) & (sum ^ b);               // overflow flag in sign bit
Run Code Online (Sandbox Code Playgroud)

请注意,对于TRUE(溢出),溢出向量将包含更常规的SIMD布尔值-1,对于FALSE(无溢出),将包含0.如果您只需要符号位中的溢出而其他位是"不关心",那么您可以省略该函数的最后一行,将SIMD指令的数量从5减少到4.

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


Gun*_*iez 9

由于你有4个可能的溢出,控制寄存器会非常快地耗尽位,特别是,如果你想要carrys,sign等等,即使是一个由16个字节组成的向量加法:-)

如果输入符号位相等且结果符号位与输入符号位不同,则设置溢出标志.

此函数sum = a+b手动计算和溢出.对于每个溢出0x80000000都是returend 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)
}
Run Code Online (Sandbox Code Playgroud)

注意:如果你没有GCC,你必须更换^ & +由适当上证所内部函数运算符,如_mm_and_si128(),_mm_add_epi32()等.

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


Ste*_*non 5

我注意到你要求一个无符号的解决方案; 幸运的是,这也很容易:

__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));
Run Code Online (Sandbox Code Playgroud)

通常要检测无符号溢出,只需检查sum < asum < b.但是,SSE没有未签名的比较; xor-the with with 0x80000000允许您使用带符号的比较来获得相同的结果.

  • AVX512最终添加了无符号比较:[`_mm512_cmp [eq | ge | gt | le | lt | neq] _epu32_mask`](https://github.com/HJLebbink/asm-dude/wiki/VPCMPD_VPCMPUD).全系列的8/16/32/64位元素大小可用于有符号和无符号(`epi`与`epu`),具有任意比较谓词,而不仅仅是AVX2及更早版本的`gt`和`eq`整数比较. (2认同)