Search code examples
assemblysse2cpuid

Test for SSE2 using CPUID versus trying SSE2 instruction and SIGILL?


I'm looking at some library code that performs the following. The CpuId function operates as expected. It loads EAX (function), ECX (subfunction) and then calls CPUID.

struct CPUIDinfo
{
    word32 EAX;
    word32 EBX;
    word32 ECX;
    word32 EDX;
};
...

CPUIDinfo info;
CpuId(1 /*EAX=1*/, 0 /*ECX=0*, info);

if ((info.EDX & (1 << 26)) != 0)
    s_hasSSE2 = TrySSE2();

Then, this is what the code does in TrySSE2:

bool TrySSE2()
{
    /* SIG handlers in place */

    // Sets XMM0 to 0
    por xmm0, xmm0;

    #if ... Microsoft and instrinsics available ...
      // Exercises MOVD instruction
      word32 x = _mm_cvtsi128_si32(xmm0);
      return x == 0;
    #endif

    return true;
}

Calling CPUID and testing bit 26 of EDX is correct per Intel® 64 and IA-32 Architectures Software Developer Manual, Volume 2, Figure 3-8, page 3-192. So I'm not sure about the TrySSE2 part...

I have looked at other similar questions, like Determine processor support for SSE2?. None of them say testing EDX:26 is unreliable.

Why would the code call TrySSE2 rather than using CPUID/EDX:26? Is the test unreliable on some non-Intel processors?


Solution

  • When the SSE instructions were added, they introduced new registers which need to be saved/restored during context switches... since OSes at the time didn't have the code to do this, the SSE instructions were disabled by default.

    Once OSes were updated to support saving/restoring these new registers, the OS would then enable the SSE instructions. Nowadays all OSes have SSE support, but I suspect this code is checking that:

    • the CPU supports SSE2
    • the OS has enabled SSE2

    See here for a bit more info: http://wiki.osdev.org/SSE#Checking_for_SSE