C++ Visual Studio 2013更新2和更新3生成的SSE 4指令

C++ Visual Studio 2013更新2和更新3生成的SSE 4指令,c++,c++11,visual-studio-2013,sse,C++,C++11,Visual Studio 2013,Sse,如果我在VS2013更新2或更新3中编译此代码:(下面来自更新3) 其中包括pmaxsd指令 pmaxsd就我所知,指令是或AVX指令,而不是SSE2指令 英特尔core2s支持sse3,但不支持sse4,也不支持pmaxsd 这在VS2013更新1或更新0中不会发生 有没有办法让Visual Studio生成SSE2指令而不是SSE4指令,如pmaxsd?这是Visual Studio update 2/3中的已知错误吗?有解决办法吗?Visual Studio是否不再支持Core2处理器

如果我在VS2013更新2或更新3中编译此代码:(下面来自更新3)

其中包括
pmaxsd
指令

pmaxsd
就我所知,指令是或AVX指令,而不是SSE2指令

英特尔core2s支持sse3,但不支持sse4,也不支持pmaxsd

这在VS2013更新1或更新0中不会发生

有没有办法让Visual Studio生成SSE2指令而不是SSE4指令,如
pmaxsd
?这是Visual Studio update 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;
}
在这里:

我们有一个测试,如果(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
没有SSE指令。然而,重要的部分是失败。如果
eax
(迭代参数)通过
10
,则其落入:

; Line 28
  movd  xmm0, edx
  pshufd  xmm0, xmm0, 0
  pmaxsd  xmm1, xmm0
这是查找单步版本结果和SSE4结果的最大值的代码。第三条指令是
pmaxsd
,它是一条SSE4_1指令,不受可用的
\u isa_
保护

是否有一种编译器设置或解决方法可以使自动矢量化保持不变,同时不在启用Core2 SSE2的计算机上调用SSE4_1指令?我的代码中是否存在导致这种情况发生的bug

请注意,我尝试删除循环的双重嵌套性质似乎可以解决问题。

这是:

如果您的计算机支持,自动矢量器还使用更新的SSE4.2指令集

如果仔细查看编译器生成的代码,您会发现SSE4.2指令的使用取决于运行时测试:

cmp DWORD PTR ___isa_available, 2
jl  SHORT $LN11@Code
这里的值为2

但是,我能够在您的第二个示例中确认错误。事实证明,我使用的Core 2 PC支持SSE4.1和
PMAXSD
指令,因此我必须在配备奔腾4 CPU的PC上测试它,以获得非法指令异常。您应该向提交错误报告。请务必提及您的示例代码失败的特定Core 2 CPU型号

至于解决方法,我只能建议更改受影响函数的优化级别。从速度优化切换到大小优化似乎生成的代码与仅用于SSE2指令的代码大致相同。您可以使用
#pragma optimize
如下切换优化级别:

#pragma optimize("s", on)

long Code(Buffer* buff)
{
     ...
}

#pragma optimize("", on)
如所示,
/d2Qvec-sse2only
是一个未记录的标志,用于更新3(可能还有更新2),以防止编译器输出SSE4指令。这自然可以防止某些循环被矢量化
/d2Qvec-sse2only
在任何时候都可能停止工作(可能在未来版本的VC上“随时可能更改,恕不另行通知”)


Microsoft声称此问题已在更新4和更新4 CTP 2(不用于生产)中修复。

我在反汇编中的任何地方都看不到可用的
\uuuuuuu isa_
。有
cmp dword ptr ds:[33B324h],2
--我应该使用不同的反汇编程序吗?我使用命令行编译器的
/Fa
选项让它生成一个汇编文件。啊哈!当我做一些稍微复杂一点的事情(一个嵌套的maxes循环,带有内部动态数组)时,结果是在函数的后期缓冲中有一些指令没有被
\u isa\u available
检查保护。如果你好奇的话,现在我的问题中包括导致崩溃的更复杂的代码。不幸的是,新示例中的崩溃是由代码中的缓冲区溢出引起的。使用
new long[8]
为8个long分配空间,然后将10个long写入缓冲区。一旦这个错误被修复,你的代码在我的Core 2 PC上就可以正常工作。如果你想知道的话,你可以设法拨动MS,从中取出一个未记录的标志来禁用这个问题--
/d2Qvec-sse2only
。介意我把它编辑成你的好答案,然后删除我的吗?澄清:第一代Core2(65nm Merom/Conroe)只支持SSSE3和更早版本。第二代Core2(45nm Penryn/Wolfdale)支持SSE4.1及更早版本。Nehalem(第一个Core-i7)支持SSE4.2及更早版本。
$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
cmp DWORD PTR ___isa_available, 2
jl  SHORT $LN11@Code
#pragma optimize("s", on)

long Code(Buffer* buff)
{
     ...
}

#pragma optimize("", on)