Search code examples
assemblyx86inline-assemblycpuid

Check exist on my platform commands AVX, SSE (SSE1-SSE4.2), FPU


i have a task: Check exist on my platform commands AVX, SSE (SSE1-SSE4.2), FPU. My assembler code do not work, and i can not unterstand why. I think so the thing in not correct description output modifiers.

    unsigned int AVX;
unsigned int SSE1;
unsigned int SSE2;
unsigned int SSE3;
unsigned int SSSE3;
unsigned int SSE41;
unsigned int SSE42;
unsigned int FPU;
__asm__(
    "cpuid\n\t"
    "movl %%edx, %[AVX]\n\t"
    "and $(1<<28), %[AVX]\n\t"
    "movl %%edx, %[SSE1]\n\t"
    "and $(1<<25), %[SSE1]\n\t"
    "movl %%edx, %[SSE2]\n\t"
    "and $(1<<26), %[SSE2]\n\t"
    "movl %%ecx, %[SSE3]\n\t"
    "and $(1<<9), %[SSE3]\n\t"
    "movl %%ecx, %[SSSE3]\n\t"
    "and $(1<<9), %[SSSE3]\n\t"
    "movl %%edx, %[SSE41]\n\t"
    "and $(1<<19), %[SSE41]\n\t"
    "movl %%ecx, %[SSE42]\n\t"
    "and $(1<<20), %[SSE42]\n\t"
    "movl %%edx, %[FPU]\n\t"
    "and $(1<<0), %[FPU]\n\t"
    : [AVX] "=&r"(AVX), [SSE1]"=&r"(SSE1), 
      [SSE2]"=&r"(SSE2), [SSE3]"=&r"(SSE3), 
      [SSSE3]"=&r"(SSSE3), [SSE41]"=&r"(SSE41),
      [SSE42]"=&r"(SSE42), [FPU]"=&r"(FPU)
    : "a"(1)
    : "cc"
);
cout << "AVX:" << (bool)AVX << endl;
cout << "SSE1:" << (bool)SSE1 << endl;
cout << "SSE2:" << (bool)SSE2 << endl;
cout << "SSE3:" << (bool)SSE3 << endl;
cout << "SSSE3:" << (bool)SSSE3 << endl;
cout << "SSE41:" << (bool)SSE41 << endl;
cout << "SSE42:" << (bool)SSE42 << endl;
cout << "FPU:" << (bool)FPU << endl;

error: 'asm' operand has impossible constraints


Solution

  • When programming in 32 bit mode, there are only six or seven general purpose registers, so the compiler cannot allocate eight output operands and one input operand to registers. You have also forgotten to declare correct clobbers (cpuid clobbers eax, ebx, ecx, and edx) which will cause unexpected behaviour at runtime if not corrected.

    It is best to do this sort of thing without inline assembly, e.g. using some sort of library or gcc's built in functions:

    int AVX, SSE1, SSE2, SSE3, SSSE3, SSE41, SSE42;
    
    __builtin_cpu_init();
    SSE1 = __builtin_cpu_supports("sse");
    SSE2 = __builtin_cpu_supports("sse2");
    SSE3 = __builtin_cpu_supports("sse3");
    SSSE3 = __builtin_cpu_supports("ssse3");
    SSE41 = __builtin_cpu_supports("sse4.1");
    SSE42 = __builtin_cpu_supports("sse4.2");
    AVX = __builtin_cpu_supports("avx");
    

    Also consider using the Intel intrinsic function _may_i_use_cpu_feature and the <cpuid.h> interface.

    Note that checking these bits does not tell you if the operating system has actually enabled the CPU feature, so code attempting to use the features might still crash at runtime.

    If you have to do it with inline assembly, try to do as little work as possible in inline assembly.

    int eax = 1, ecx, edx;
    int AVX, FPU, SSE1, SSE2, SSE3, SSSE3, SSE41, SSE42;
    
    asm ("cpuid" : "+a"(eax), "=c"(ecx), "=d"(edx) :: "ebx");
    FPU   = edx & 1 <<  0;
    AVX   = ecx & 1 << 28;
    SSE1  = edx & 1 << 25;
    SSE2  = edx & 1 << 26;
    SSE3  = ecx & 1 <<  0;
    SSSE3 = ecx & 1 <<  9;
    SSE41 = ecx & 1 << 19;
    SSE42 = ecx & 1 << 20;