MSVC 2015-我程序中的SSE编译器错误或错误/未定义的行为
MSVC++ 2015 - SSE compiler bug or bug/undefined behavior in my program?
我在处理SIMD颜色LERP函数时遇到了一些怪异的行为,然后将其缩小到最小程序中。此示例中的SIMD代码不再执行LERP,但它执行从32位颜色到XMM寄存器的打开包装,然后返回32位。
在MSVC 2015(更新3)中,在Release X64模式中,以下代码不会产生正确的结果,但是在调试X64或Release/debug X86中,它可以正常工作。这是原本空的Win32 C 控制台应用程序项目中唯一的代码:
#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("DEBUGn");
#else
printf("RELEASEn");
#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 %dn", c.red, c.green, c.blue, c.alpha);
return 0;
}
版本x64输出是:
RELEASE
255 0 0 0
调试X64,所有x86输出均为:
DEBUG
32 64 128 255
拆卸看起来好像已经弄乱了预计恒定值以加载到XMM寄存器中以跳过_mm_set1_epi32
(请参阅第一个movdqa
指令。)
main:
00007FF674391070 sub rsp,38h
00007FF674391074 lea rcx,[string "RELEASEn" (07FF674392200h)]
00007FF67439107B call printf (07FF674391010h)
00007FF674391080 movdqa xmm0,xmmword ptr [__xmm@000000ff000000ff000000ff000000ff (07FF674392220h)]
00007FF674391088 lea rcx,[string "%d %d %d %dn" (07FF674392210h)]
00007FF67439108F xorps xmm2,xmm2
00007FF674391092 mov dword ptr [rsp+40h],0FF804020h
00007FF67439109A punpcklbw xmm0,xmm2
00007FF67439109E punpcklwd xmm0,xmm2
00007FF6743910A2 cvtdq2ps xmm0,xmm0
00007FF6743910A5 cvtps2dq xmm1,xmm0
00007FF6743910A9 packssdw xmm1,xmm2
00007FF6743910AD packuswb xmm1,xmm2
00007FF6743910B1 movd r10d,xmm1
00007FF6743910B6 mov edx,r10d
00007FF6743910B9 mov r8d,r10d
00007FF6743910BC shr edx,10h
00007FF6743910BF mov eax,r10d
00007FF6743910C2 shr r8d,8
00007FF6743910C6 movzx r9d,dl
00007FF6743910CA shr eax,18h
00007FF6743910CD movzx edx,r10b
00007FF6743910D1 movzx r8d,r8b
00007FF6743910D5 mov dword ptr [rsp+20h],eax
00007FF6743910D9 call printf (07FF674391010h)
00007FF6743910DE xor eax,eax
00007FF6743910E0 add rsp,38h
00007FF6743910E4 ret
我在Ubuntu上使用g++
4.8.4尝试了此操作,在14.04 x64上,它可以与-O3
打开或关闭。
所以我的问题是,这是编译器错误,是使用未定义/实现定义的行为的结果,还是我的代码中的一个更平凡的错误?
(用于使用工会使用类型的代码将UINT32_T值从颜色中获取uint32_t值,我用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%08xn", tmp );
printf( "a.m128i_i32[0] = 0x%08xn", a.m128i_i32[0] );
return 0;
}
预期输出:
tmp = 0xff804020
a.m128i_i32[0] = 0xff804020
用版本x64输出:
tmp = 0xff804020
a.m128i_i32[0] = 0x000000ff
这是由于编译器错误。解决方法是使用
tmp = color.red + 256 * (col.blue + 256 * (col.green + 256 * col.alpha)));
代替memcpy
或键入双关。
相关文章:
- 错误:未定义对cv::cudacodec::createVideoReader的引用
- Pybind11 错误未定义对"Py_GetVersion"的引用
- 纯虚拟类和错误未定义对 'vtable 的引用
- 为什么我会收到链接器错误:未定义对 ..?
- 链接错误:未定义对 stdscr 和 wgetch 的引用
- 尝试构建 xerces c++ 解析器。Netbeans 和命令行构建错误未定义对"xercesc_3_2..."的引用
- 错误:未定义的符号:gzclose opencv emscripten
- 我不断收到对项目"xxx"的错误未定义引用
- 我仍然收到此错误未定义的引用:boost::log::v2_mt_posix::trivial::logger::get
- 符号查找错误未定义的符号:curl_mime_init
- Omnet 和 Inet 链接错误:未定义对 typinfo 的引用
- 模拟 __name__ = __main__ 在 c++ 中会导致错误"未定义类似函数的宏"
- 链接器错误:未定义的引用C++
- OpenCv3.3安装错误-未定义对的引用
- CUDA 编译错误:未定义__hmul和__hneg
- C++错误:未定义对 (Winsock2.h) 的引用
- 错误:未定义对 .. 的引用.怎么了
- 无法生成项目,出现错误 - 未定义的体系结构符号 x86_64:"_JNI_CreateJavaVM"
- 编译错误:未定义的拱门
- C 编程原理和练习第7章练习4 symber_table :: get_value始终显示错误未定义