title: SSE 4 instructions generated by Visual Studio 2013 Update 2 and Update 3
body: <p>If I compile this code in VS 2013 Update 2 or Update 3: (below comes from Update 3)</p>
  
  <pre><code>#include "stdafx.h"
  #include &lt;iostream&gt;
  #include &lt;random&gt;
  
  struct Buffer
  {
    long* data;
    int   count;
  };
  
  #ifndef max
  #define max(a,b)            (((a) &gt; (b)) ? (a) : (b))
  #endif
  
  long Code(long* data, int count)
  {
    long nMaxY = data[0];
  
    for (int nNode = 0; nNode &lt; 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&lt;int&gt; 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 &lt;&lt; result; // ensure result is used
    return result;
  }
  </code></pre>
  
  <p>with SSE2 instructions enabled, but not AVX/AVX2, the compiler in release generates:</p>
  
  <pre><code>  {
      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];
  </code></pre>
  
  <p>which contains, among other things, <code>pmaxsd</code> instructions.</p>
  
  <p><code>pmaxsd</code> instructions are <a href="http://www.felixcloutier.com/x86/PMAXSD.html">SSE4_1 instructions</a> or AVX instructions as far as I can tell, not SSE2 instructions.</p>
  
  <p>Intel core2s support sse3, but not sse4, and not <code>pmaxsd</code>.</p>
  
  <p>This does not occur in VS2013 update 1 or update 0.</p>
  
  <p>Is there a way to get Visual Studio to generate SSE2 instructions but not SSE4 instructions like <code>pmaxsd</code>?  Is this a known bug in Visual Studio update 2/3?  Is there a workaround?  Does Visual Studio no longer support Core2 processors?</p>
  
  <hr>
  
  <p>Here is a more complex version of the above code that compiles (under default release settings) to code that crashes a Core2 CPU:</p>
  
  <pre><code>#include "stdafx.h"
  #include &lt;iostream&gt;
  #include &lt;random&gt;
  #include &lt;array&gt;
  
  enum unused_name {
    _nNumPolygons = 10,
  };
  
  
  #ifndef max
  #define max(a,b)            (((a) &gt; (b)) ? (a) : (b))
  #endif
  
  struct Buffer
  {
    std::array&lt;long*, _nNumPolygons&gt; data;
    std::array&lt;int, _nNumPolygons&gt;   count;
  };
  
  long Code(Buffer* buff)
  {
    long  nMaxY = buff-&gt;data[0][0];
  
  
    for (int nPoly = 0; nPoly &lt; _nNumPolygons; nPoly++)
    {
      for (int nNode = 0; nNode &lt; buff-&gt;count[nPoly]; nNode++)
      {
        nMaxY = max(buff-&gt;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&lt;int&gt; distribution(0, 100);
  
    for (int i = 0; i &lt; _nNumPolygons; ++i) {
      buff.count[i] = 10;
      buff.data[i] = new long[10];
      for (int k = 0; k &lt; 10; ++k)
      {
        buff.data[i][k] = distribution(engine);
      }
    }
  
    long result = Code(&amp;buff);
    std::cout &lt;&lt; result; // ensure result is used
    return result;
  }
  </code></pre>
  
  <p><a href="https://connect.microsoft.com/VisualStudio/feedbackdetail/view/956254">Here is a link to a bug for this issue</a> that someone else opened around the same time I posted this question.</p>
  
  <p>Here is the generated .asm:</p>
  
  <pre><code>?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
  </code></pre>
  
  <p>Here:</p>
  
  <pre><code>  cmp  esi, 8
    jb  SHORT $LN25@Code2
    cmp  DWORD PTR ___isa_available, 2
    jl  SHORT $LN25@Code2
  </code></pre>
  
  <p>we have the test that branches to the "single step" version if either (A) the loop is less than 8 long, or (B) we don't have SSE3/SSE4 support.</p>
  
  <p>The single step version is:</p>
  
  <pre><code>$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
  </code></pre>
  
  <p>which has no SSE instructions.  However, the important part is the fall through.  If <code>eax</code> (the iteration parameter) passes <code>10</code>, it falls through into:</p>
  
  <pre><code>; Line 28
    movd  xmm0, edx
    pshufd  xmm0, xmm0, 0
    pmaxsd  xmm1, xmm0
  </code></pre>
  
  <p>which is code that finds the max of both the single step version results and the SSE4 results.  The 3rd instruction is <code>pmaxsd</code>, which is an SSE4_1 instruction, and it is not guarded by <code>__isa_available</code>.</p>
  
  <p>Is there a compiler setting or workaround that can leave the auto-vectorization intact, while not invoking SSE4_1 instructions on Core2 SSE2 enabled computers?  Is there a bug in my code that is causing this to happen?</p>
  
  <p>Note that my attempts to remove the double-nested nature of the loop seem to make the problem go away.</p>