我在处理SIMD颜色lerp函数时遇到了一些奇怪的行为,并将其修剪为最小程序。本示例中的SIMD代码不再执行lerp,但是它执行从32位颜色到XMM寄存器的解包,然后返回到32位。MSVC++ 2015 - SSE编译器错误或错误/未定义在我的程序中的行为?
在MSVC++ 2015(更新3)中,在发布x64模式下,以下代码不会生成正确的结果,但在Debug 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("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输出是:
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 "RELEASE\n" (07FF674392200h)]
00007FF67439107B call printf (07FF674391010h)
00007FF674391080 movdqa xmm0,xmmword ptr [[email protected] (07FF674392220h)]
00007FF674391088 lea rcx,[string "%d %d %d %d\n" (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 14.04 64 g++
4.8.4尝试这样做,它开启或关闭正常工作与-O3
。
所以我的问题是,这是一个编译器错误,使用未定义/实现定义的行为,或在我的代码中更普通的错误的结果?
(代码中使用使用类型双关通过工会得到uint32_t的值了Color4,我用的memcpy替代的,因为这不是标准......仍然没有骰子。)
这可能会帮助:https://support.microsoft.com/en-us/help/3207317/visual-co ptimizer-fixes-for-visual-studio-2015-update-3 –
在VS 2017中出乎意料的行为是相同的 – Timbo
看起来像一个编译器错误。如果使用'tmp = color.red + 256 *(col.blue + 256 *(col.green + 256 * col.alpha)));'而不是'memcpy'会发生什么? – 1201ProgramAlarm