SSE2 有符号整数溢出未定义吗?

2023-11-22

有符号整数溢出在 C 和 C++ 中未定义。但是,有符号整数在单个字段内溢出又如何呢?__m128i?换句话说,这种行为是在英特尔标准中定义的吗?

#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
#include <emmintrin.h>

union SSE2
{
    __m128i m_vector;
    uint32_t m_dwords[sizeof(__m128i) / sizeof(uint32_t)];
};

int main()
{
    union SSE2 reg = {_mm_set_epi32(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX)};
    reg.m_vector = _mm_add_epi32(reg.m_vector, _mm_set_epi32(1, 1, 1, 1));

    printf("%08" PRIX32 "\n", (uint32_t) reg.m_dwords[0]);
    return 0;
}
[myria@polaris tests]$ gcc -m64 -msse2 -std=c11 -O3 sse2defined.c -o sse2defined
[myria@polaris tests]$ ./sse2defined
80000000

请注意,SSE2 的 4 字节大小的字段__m128i被视为已签署。


  1. 您询问的是特定的实施问题(使用 SSE2),而不是标准。您已经回答了自己的问题“C 中未定义有符号整数溢出”。

  2. 当您处理 C 内在函数时,您甚至不是在用 C 进行编程!这些是在行中插入汇编指令。它以某种可移植的方式执行此操作,但您的数据不再是有符号整数。它是传递给 SSE 内在函数的向量类型。然后,您将其转换为整数并告诉 C 您希望查看该操作的结果。当你进行强制转换时,无论出现什么字节,你都会看到什么,与 C 标准中的有符号算术无关。

如果编译器插入 SSE 指令(比如在循环中),情况会有所不同。现在编译器保证结果与带符号的 32 位操作相同...除非存在未定义的行为(例如溢出),在这种情况下它可以做任何它喜欢的事情。

另请注意,未定义并不意味着意外...您观察到的自动矢量化行为可能是一致且可重复的(也许它总是包装在您的机器上...这可能不适用于周围代码的所有情况,或者或者,如果编译器根据 SSSE3、SSE4 或 AVX* 的可用性选择不同的指令,甚至可能不是所有处理器(如果它为不同的指令集(使用或不使用带符号的指令集)做出不同的代码生成选择溢出为 UB)。

EDIT:

好吧,现在我们正在询问“英特尔标准”(该标准不存在,我认为您指的是 x86 标准),我可以在我的答案中添加一些内容。事情有点复杂。

首先,内在的 _mm_add_epi32 定义为微软以匹配 Intel 的内在函数 API 定义(https://software.intel.com/sites/landingpage/IntrinsicsGuide/以及 Intel x86 汇编手册中的内在注释)。他们巧妙地将其定义为对__m128ix86也是一样的PADDD指令对 XMM 寄存器执行操作,无需更多讨论(例如,它是 ARM 上的编译错误还是应该对其进行模拟?)。

其次,PADDD不仅仅是一个签名补充!它是一个 32 位二进制加法。 x86 对有符号整数使用二进制补码,将它们相加与无符号基数 2 的二进制运算相同。So yes, paddd保证包裹。所有 x86 指令都有一个很好的参考here.

那么这意味着什么:同样,您问题中的假设是有缺陷的,因为甚至没有任何溢出。所以你看到的输出应该是定义的行为。请注意,它是由 Microsoft 和 x86 定义的(而不是由 C 标准定义的)。

其他x86编译器也以同样的方式实现Intel的intrinsic API,所以_mm_add_epi32可移植地保证只是包装。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SSE2 有符号整数溢出未定义吗? 的相关文章

随机推荐