Myr*_*ria 5 c undefined-behavior sse2 language-lawyer
在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;
}
Run Code Online (Sandbox Code Playgroud)
[myria@polaris tests]$ gcc -m64 -msse2 -std=c11 -O3 sse2defined.c -o sse2defined
[myria@polaris tests]$ ./sse2defined
80000000
Run Code Online (Sandbox Code Playgroud)
请注意,SSE2的4字节大小的字段__m128i被视为已签名.
这个问题大概有三个问题(不是以低调的方式,在"你缺乏理解"的方式......这就是为什么我猜你来到这里).
1)您询问的是具体的实施问题(使用SSE2)而不是标准.你已经回答了自己的问题"在C中未定义有符号整数溢出".
2)当你处理c内在函数时,你甚至不用C编程!这些是插入汇编指令.它是以便携式方式进行的,但您的数据不再是有符号整数.它是传递给SSE内在函数的向量类型.然后你将它转换为一个整数并告诉C你想要查看该操作的结果. 无论你在演员阵容中发生什么字节都是你将看到的,与C标准中的带符号算术无关.
3)只有两个错误的假设.我对错误的数量做了一个假设并且错了.
如果编译器插入SSE指令(比如在循环中),情况会有所不同.现在编译器保证结果与带符号的32位操作相同......除非有未定义的行为(例如溢出),在这种情况下它可以做任何它喜欢的事情.
另请注意,undefined并不意味着意外......无论你观察到的自动矢量化的行为是否一致且可重复(也许它总是包裹在你的机器上......对于周围代码的所有情况可能都不是这样,或者所有编译器.或者如果编译器根据SSSE3,SSE4或AVX*的可用性选择不同的指令,甚至可能不是所有处理器,如果它为不同的指令集做出不同的代码选择,这些指令集有或没有利用签名溢出是UB).
编辑:
好吧,现在我们正在询问"英特尔标准"(我认为你的意思是x86标准),我可以在我的答案中添加一些内容.事情有点令人费解.
首先,内部_mm_add_epi32由Microsoft定义,以匹配英特尔的内在API定义(https://software.intel.com/sites/landingpage/IntrinsicsGuide/和英特尔x86汇编手册中的内在注释).他们巧妙地将它定义为__m128ix86 PADDD指令对XMM寄存器执行的操作,不再讨论(例如,它是ARM上的编译错误还是应该模拟?).
其次,PADDD不仅是签名的补充!它是32位二进制加法.x86对有符号整数使用二进制补码,并且添加它们与无符号基数2的二进制运算相同. 所以是的,paddd保证包装.这里是所有的x86指令一个很好的参考这里.
那么这意味着什么:再次,你的问题中的假设是有缺陷的,因为甚至没有任何溢出.所以你看到的输出应该是定义的行为.请注意,它由Microsoft和x86(不是C标准)定义.
其他x86编译器也以相同的方式实现了英特尔的内在函数API,因此_mm_add_epi32可以保证只包装.
| 归档时间: |
|
| 查看次数: |
387 次 |
| 最近记录: |