Yak*_*ont 17 c++ sse c++11 visual-studio-2013
如果我在VS 2013 Update 2或Update 3中编译此代码:(以下来自Update 3)
#include "stdafx.h"
#include <iostream>
#include <random>
struct Buffer
{
long* data;
int count;
};
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
long Code(long* data, int count)
{
long nMaxY = data[0];
for (int nNode = 0; nNode < count; nNode++)
{
nMaxY = max(data[nNode], nMaxY);
}
return(nMaxY);
}
int _tmain(int argc, _TCHAR* argv[])
{
#ifdef __AVX__
static_assert(false, "AVX should be disabled");
#endif
#ifdef __AVX2__
static_assert(false, "AVX2 should be disabled");
#endif
static_assert(_M_IX86_FP == 2, "SSE2 instructions should be enabled");
Buffer buff;
std::mt19937 engine;
engine.seed(std::random_device{}());
std::uniform_int_distribution<int> distribution(0, 100);
buff.count = 1;
buff.data = new long[1];
buff.data[0] = distribution(engine);
long result = Code(buff.data, buff.count);
std::cout << result; // ensure result is used
return result;
}
Run Code Online (Sandbox Code Playgroud)
启用SSE2指令但不启用AVX/AVX2,发行版中的编译器生成:
{
nMaxY = max(data[nNode], nMaxY);
010612E1 movdqu xmm0,xmmword ptr [eax]
010612E5 add esi,8
010612E8 lea eax,[eax+20h]
010612EB pmaxsd xmm1,xmm0
010612F0 movdqu xmm0,xmmword ptr [eax-10h]
010612F5 pmaxsd xmm2,xmm0
010612FA cmp esi,ebx
010612FC jl Code+41h (010612E1h)
010612FE pmaxsd xmm1,xmm2
01061303 movdqa xmm0,xmm1
01061307 psrldq xmm0,8
0106130C pmaxsd xmm1,xmm0
01061311 movdqa xmm0,xmm1
01061315 psrldq xmm0,4
0106131A pmaxsd xmm1,xmm0
0106131F movd eax,xmm1
01061323 pop ebx
long nMaxY = data[0];
Run Code Online (Sandbox Code Playgroud)
其中包含pmaxsd指令.
pmaxsd据我所知,指令是SSE4_1指令或AVX指令,而不是SSE2指令.
英特尔core2s支持sse3,但不支持sse4,而不支持sse4 pmaxsd.
在VS2013更新1或更新0中不会发生这种情况.
有没有办法让Visual Studio生成SSE2指令而不是SSE4指令pmaxsd?这是Visual Studio更新2/3中的已知错误吗?有解决方法吗?Visual Studio是否不再支持Core2处理器?
以下是上述代码的更复杂版本,它将(在默认版本设置下)编译为崩溃Core2 CPU的代码:
#include "stdafx.h"
#include <iostream>
#include <random>
#include <array>
enum unused_name {
_nNumPolygons = 10,
};
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
struct Buffer
{
std::array<long*, _nNumPolygons> data;
std::array<int, _nNumPolygons> count;
};
long Code(Buffer* buff)
{
long nMaxY = buff->data[0][0];
for (int nPoly = 0; nPoly < _nNumPolygons; nPoly++)
{
for (int nNode = 0; nNode < buff->count[nPoly]; nNode++)
{
nMaxY = max(buff->data[nPoly][nNode], nMaxY);
}
}
return(nMaxY);
}
extern "C" __int32 __isa_available;
int _tmain(int argc, _TCHAR* argv[])
{
#ifdef __AVX__
static_assert(false, "AVX should be disabled");
#endif
#ifdef __AVX2__
static_assert(false, "AVX2 should be disabled");
#endif
#if !( defined( _M_AMD64 ) || defined( _M_X64 ) )
static_assert(_M_IX86_FP == 2, "SSE2 instructions should be enabled");
#endif
// __isa_available = 1; // to force code to act as if SSE4_2 is not available
Buffer buff;
std::mt19937 engine;
engine.seed(std::random_device{}());
std::uniform_int_distribution<int> distribution(0, 100);
for (int i = 0; i < _nNumPolygons; ++i) {
buff.count[i] = 10;
buff.data[i] = new long[10];
for (int k = 0; k < 10; ++k)
{
buff.data[i][k] = distribution(engine);
}
}
long result = Code(&buff);
std::cout << result; // ensure result is used
return result;
}
Run Code Online (Sandbox Code Playgroud)
这是一个链接到这个问题的错误,其他人在我发布这个问题的同时打开了这个问题.
这是生成的.asm:
?Code2@@YAJPAUBuffer@@@Z PROC ; Code2, COMDAT
; _buff$ = ecx
; File c:\users\adam.nevraumont.corelcorp.000\documents\visual studio 2013\projects\consoleapplication1\consoleapplication1\consoleapplication1.cpp
; Line 22
push ebp
mov ebp, esp
sub esp, 12 ; 0000000cH
push ebx
push esi
push edi
mov edi, ecx
; Line 26
xor ebx, ebx
mov DWORD PTR _buff$1$[ebp], edi
mov DWORD PTR _nPoly$1$[ebp], ebx
mov eax, DWORD PTR [edi]
mov edx, DWORD PTR [eax]
; Line 28
movd xmm0, edx
pshufd xmm1, xmm0, 0
movdqa xmm2, xmm1
npad 12
$LL6@Code2:
lea ecx, DWORD PTR [ebx*4]
xor eax, eax
mov esi, DWORD PTR [ecx+edi+40]
mov DWORD PTR tv443[ebp], ecx
test esi, esi
jle SHORT $LN5@Code2
cmp esi, 8
jb SHORT $LN25@Code2
cmp DWORD PTR ___isa_available, 2
jl SHORT $LN25@Code2
; Line 26
mov ebx, DWORD PTR [ecx+edi]
mov ecx, esi
and ecx, -2147483641 ; 80000007H
jns SHORT $LN33@Code2
dec ecx
or ecx, -8 ; fffffff8H
inc ecx
$LN33@Code2:
mov edi, esi
sub edi, ecx
npad 8
$LL3@Code2:
; Line 30
movdqu xmm0, XMMWORD PTR [ebx+eax*4]
pmaxsd xmm1, xmm0
movdqu xmm0, XMMWORD PTR [ebx+eax*4+16]
add eax, 8
pmaxsd xmm2, xmm0
cmp eax, edi
jl SHORT $LL3@Code2
mov ebx, DWORD PTR _nPoly$1$[ebp]
mov ecx, DWORD PTR tv443[ebp]
mov edi, DWORD PTR _buff$1$[ebp]
$LN25@Code2:
; Line 28
cmp eax, esi
jge SHORT $LN5@Code2
; Line 26
mov edi, DWORD PTR [ecx+edi]
npad 4
$LL23@Code2:
; Line 30
cmp DWORD PTR [edi+eax*4], edx
cmovg edx, DWORD PTR [edi+eax*4]
inc eax
cmp eax, esi
jl SHORT $LL23@Code2
$LN5@Code2:
; Line 26
mov edi, DWORD PTR _buff$1$[ebp]
inc ebx
mov DWORD PTR _nPoly$1$[ebp], ebx
cmp ebx, 10 ; 0000000aH
jl $LL6@Code2
; Line 28
movd xmm0, edx
pshufd xmm0, xmm0, 0
pmaxsd xmm1, xmm0
pmaxsd xmm1, xmm2
movdqa xmm0, xmm1
psrldq xmm0, 8
pmaxsd xmm1, xmm0
movdqa xmm0, xmm1
pop edi
psrldq xmm0, 4
pmaxsd xmm1, xmm0
pop esi
movd eax, xmm1
pop ebx
; Line 35
mov esp, ebp
pop ebp
ret 0
Run Code Online (Sandbox Code Playgroud)
这里:
cmp esi, 8
jb SHORT $LN25@Code2
cmp DWORD PTR ___isa_available, 2
jl SHORT $LN25@Code2
Run Code Online (Sandbox Code Playgroud)
如果(A)循环小于8长,或者(B)我们没有SSE3/SSE4支持,我们有分支到"单步"版本的测试.
单步版本是:
$LN5@Code2:
; Line 26
mov edi, DWORD PTR _buff$1$[ebp]
inc ebx
mov DWORD PTR _nPoly$1$[ebp], ebx
cmp ebx, 10 ; 0000000aH
jl $LL6@Code2
Run Code Online (Sandbox Code Playgroud)
没有SSE指令.然而,重要的部分是堕落.如果eax(迭代参数)通过10,它会进入:
; Line 28
movd xmm0, edx
pshufd xmm0, xmm0, 0
pmaxsd xmm1, xmm0
Run Code Online (Sandbox Code Playgroud)
这是找到单步版本结果和SSE4结果的最大值的代码.第3条指令是pmaxsdSSE4_1指令,不受保护__isa_available.
是否有编译器设置或解决方法可以保持自动矢量化不变,而不在启用Core2 SSE2的计算机上调用SSE4_1指令?我的代码中是否存在导致此问题发生的错误?
请注意,我尝试删除循环的双嵌套特性似乎会使问题消失.
Ros*_*dge 15
这是记录在案的行为:
如果您的计算机支持,Auto-Vectorizer也会使用较新的SSE4.2指令集.
如果仔细观察编译器生成的代码,您会发现SSE4.2指令的使用依赖于运行时测试:
cmp DWORD PTR ___isa_available, 2
jl SHORT $LN11@Code
Run Code Online (Sandbox Code Playgroud)
这里的值2 显然意味着SSE4.2.
然而,我能够在第二个例子中确认错误.事实证明我使用的Core 2 PC支持SSE4.1和PMAXSD指令,所以我不得不在带有Pentium 4 CPU的PC上测试它以获得非法指令异常.您应该向Microsoft Connect提交错误报告.请务必提及您的示例代码失败的特定Core 2 CPU型号.
至于解决方法,我只能建议更改受影响功能的优化级别.从优化速度切换到优化尺寸似乎产生与仅使用SSE2指令大致相同的代码.您可以使用#pragma optimize这样切换优化级别:
#pragma optimize("s", on)
long Code(Buffer* buff)
{
...
}
#pragma optimize("", on)
Run Code Online (Sandbox Code Playgroud)
正如此错误报告中/d2Qvec-sse2only所记录的那样,是一个未记录的标志,可用于更新3(可能还有更新2),以防止编译器输出SSE4指令.这可以自然地防止某些循环被矢量化. /d2Qvec-sse2only可能在VC的未来版本中,可能在任何时候停止工作(它"将在未经通知的情况下进行更改").
Microsoft声称此问题已在Update 4和Update 4 CTP 2中修复(不适用于生产用途).