C++ MSVC&x2B+;2015-SSE编译器错误或程序中的错误/未定义行为?
在使用SIMD color lerp函数时,我遇到了一些奇怪的行为,我将其精简为一个最小的程序。本例中的SIMD代码不再执行lerp,而是执行从32位颜色到XMM寄存器的解包,然后再回到32位 在MSVC++2015(更新3)中,在版本x64模式下,以下代码不会产生正确的结果,但在调试x64或版本/调试x86中,它工作正常。这是空的Win32 C++控制台应用程序中的唯一代码:C++ MSVC&x2B+;2015-SSE编译器错误或程序中的错误/未定义行为?,c++,visual-c++,visual-studio-2015,sse,C++,Visual C++,Visual Studio 2015,Sse,在使用SIMD color lerp函数时,我遇到了一些奇怪的行为,我将其精简为一个最小的程序。本例中的SIMD代码不再执行lerp,而是执行从32位颜色到XMM寄存器的解包,然后再回到32位 在MSVC++2015(更新3)中,在版本x64模式下,以下代码不会产生正确的结果,但在调试x64或版本/调试x86中,它工作正常。这是空的Win32 C++控制台应用程序中的唯一代码: #include <stdint.h> #include <stdio.h> #include
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "emmintrin.h"
struct Color4
{
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t alpha;
Color4(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255)
: red(red), green(green), blue(blue), alpha(alpha) {}
explicit Color4(uint32_t rgba)
{
red = (uint8_t)(rgba & 0xFF);
green = (uint8_t)((rgba >> 8)&0xFF);
blue = (uint8_t)((rgba >> 16) & 0xFF);
alpha = (uint8_t)((rgba >> 24) & 0xFF);
}
};
Color4 PackUnpack(Color4 col)
{
uint32_t tmp;
memcpy(&tmp, &col, sizeof(tmp));
__m128 aFloat = _mm_cvtepi32_ps(
_mm_unpacklo_epi16(
_mm_unpacklo_epi8(
_mm_set1_epi32(tmp),
_mm_setzero_si128()
),
_mm_setzero_si128()
)
);
__m128i ret = _mm_packus_epi16(
_mm_packs_epi32(
_mm_cvtps_epi32(aFloat),
_mm_setzero_si128()
),
_mm_setzero_si128()
);
return Color4((uint32_t)_mm_cvtsi128_si32(ret));
}
int main()
{
#ifdef _DEBUG
printf("DEBUG\n");
#else
printf("RELEASE\n");
#endif
Color4 c = PackUnpack(Color4(32, 64, 128, 255));
// Debug x64 or Debug/Release x86: Prints "32 64 128 255"
// Release x64: Prints "255 0 0 0"
printf("%d %d %d %d\n", c.red, c.green, c.blue, c.alpha);
return 0;
}
调试x64和所有x86输出为:
RELEASE
255 0 0 0
DEBUG
32 64 128 255
反汇编看起来像是在预先计算一个常量值以加载到XMM寄存器以跳过\u mm\u set1\u epi32
(请参阅第一条movdqa
指令)时出错了
我已经在Ubuntu 14.04 x64上的g++
4.8.4上试过了,它可以在-O3
打开或关闭的情况下正常工作
所以我的问题是,这是一个编译器错误,是使用未定义/实现定义的行为的结果,还是代码中更普通的错误?
(代码使用通过联合的类型双关来从Color4中获取uint32_t值,我用memcpy替换了它,因为这不是标准的…仍然没有骰子。)这是由于编译器错误造成的。解决方法是使用
tmp = color.red + 256 * (col.blue + 256 * (col.green + 256 * col.alpha)));
代替了
memcpy
或键入双关语。实际上不是答案,但是,由于我不喜欢在注释中添加太多文本,这是我可以复制问题的最小代码:
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "emmintrin.h"
int main()
{
uint8_t src[4] = { 32, 64, 128, 255 };
uint32_t tmp = 0;
memcpy( &tmp, &src, sizeof( tmp ) );
auto a = _mm_set1_epi32( tmp );
printf( "tmp = 0x%08x\n", tmp );
printf( "a.m128i_i32[0] = 0x%08x\n", a.m128i_i32[0] );
return 0;
}
x64版本的输出:
tmp = 0xff804020
a.m128i_i32[0] = 0x000000ff
这可能会有所帮助:VS 2017中的意外行为是相同的,看起来像是编译器错误。如果使用
tmp=color.red+256*(col.blue+256*(col.green+256*col.alpha)),会发生什么
而不是memcpy
?@severinpapadeux谢谢你的提示,我下载并安装了这个,但它似乎没有修复它:(@1201programalam,当我用tmp=col.red+256*(col.blue+256*(col.green+256*col.alpha)]替换该行时;
它工作得很好。感谢您将这一切放在一起。再仔细修改一下,就会发现到达SSE寄存器的值实际上是数组的第4字节。有趣:-)谢谢——我有点震惊,因为我碰到了一个真正的编译器错误,所以我将把这个打开一段时间,以防有人能证明不是这样,但如果他们不能证明,我会将这个标记为已接受!:)(和MS.一起存档)是否已经有一个关于这个的bug存档了,或者这是一个编译器bug只是一个偶然的观察?我不知道它是否存档了,但是生成的错误代码将是一个bug。
tmp = 0xff804020
a.m128i_i32[0] = 0xff804020
tmp = 0xff804020
a.m128i_i32[0] = 0x000000ff