检测CPU指令集支持级别(SSE、AVX、AVX2、F16C、FMA、FMA4、XOP)

来源:互联网 发布:mac终端显示当前路径 编辑:程序博客网 时间:2024/04/29 20:27

http://blog.csdn.net/zyl910/article/details/7715558

 

从2011年的Sandy Bridge微架构处理器开始,现在支持AVX系列指令集的处理器越来越多了。本文探讨如何用VC编写检测AVX系列指令集的程序,并利用了先前的CPUIDFIELD方案。

一、AVX系列指令集简介

  SSE5 指令:SSE5 是一个纸面上的指令集,并没有最终实现,AMD 在 2007 年 8 月公布 SSE5 指令集规范,在 2009 年 5 月 AMD 推出了 XOP,FMA4 以及 CVT16 来取代 SSE5 指令。
  AVX 指令:2008 年 3 月 Intel 发布了 AVX(Advanced Vector Extensions)指令集规范,首次在 Sandy Bridge 微架构的处理器上使用。AMD 首次在 Bulldozer 微架构的处理器上加入 AVX 指令的支持。
  FMA 指令:FMA 指令是 AVX 指令集中的一部分,Intel 将在 2013 年的 Haswell 微架构处理器上使用。据说AMD将在2012年的Piledriver微架构处理器上支持FMA。
  XOP,FMA4 以及 CVT16 指令:AMD 在 2009 年 5 月发布了 XOP,FMA4 以及 CVT16 指令集规范,这些指令集取代了 SSE5 指令,在原有的 SSE5 指令基础上,使用了兼容 AVX 指令的设计方案重新进行了设计,因此,XOP,FMA4 以及 CVT16 在指令的编码方面是兼容于 AVX 的方案。这使得 AVX/FAM4/CVT16 指令与 AVX 指令同时存在,而不会产生冲突。AMD首次在 Bulldozer 微架构的处理器上使用。
  F16C 指令:F16C指令就是AMD的CVT16指令,Intel换了一个名称,随后AMD也接收了这一称呼。Intel 首次在 2012 年的 Ivy Bridge 微架构处理器上使用。
  AVX2 指令:2011 年 6 月,Intel 发布了 AVX2 指令集规范,将在 2013 年的 Haswell 微架构处理器上使用。


二、检测AVX、AVX2

2.1 应用程序如何检测AVX

  在Intel手册第一卷的“13.5 DETECTION OF AVX INSTRUCTIONS”中介绍了AVX指令集的检测办法,具体步骤为——
1) Detect CPUID.1:ECX.OSXSAVE[bit 27] = 1 (XGETBV enabled for application use)
2) Issue XGETBV and verify that XCR0[2:1] = ‘11b’ (XMM state and YMM state are enabled by OS).
3) detect CPUID.1:ECX.AVX[bit 28] = 1 (AVX instructions supported).
(Step 3 can be done in any order relative to 1 and 2)

  Intel还给出了汇编伪代码——

[plain] view plaincopyprint?
  1. INT supports_AVX()  
  2. {   mov eax, 1  
  3.     cpuid  
  4.     and ecx, 018000000H  
  5.     cmp ecx, 018000000H; check both OSXSAVE and AVX feature flags  
  6.     jne not_supported  
  7.     ; processor supports AVX instructions and XGETBV is enabled by OS  
  8.     mov ecx, 0; specify 0 for XCR0 register  
  9.     XGETBV ; result in EDX:EAX  
  10.     and eax, 06H  
  11.     cmp eax, 06H; check OS has enabled both XMM and YMM state support  
  12.     jne not_supported  
  13.     mov eax, 1  
  14.     jmp done  
  15. NOT_SUPPORTED:  
  16.     mov eax, 0  
  17. done:  


 

  解释一下它的检测步骤——
1) 检测CPUID.1:ECX.OSXSAVE[bit 27] = 1。该位为1表示操作系统支持XSAVE系列指令,于是在应用程序中可以使用XGETBV等XSAVE系列指令。
2) 使用XGETBV指令获取XCR0寄存器的值,并检查第1位至第2位是否都为1。即检查操作系统是否支持XMM和YMM状态。
3) 检测CPUID.1:ECX.OSXSAVE[bit 27] = 1。该位为1表示硬件支持AVX指令集。

  XCR0叫做XFEATURE_ENABLED_MASK寄存器,它是一个64位寄存器。它的第0位是x87 FPU/MMX状态,第1位是XMM状态,第2位是YMM状态。如果操作系统支持AVX指令集,它就会将XMM和YMM状态均置为1。详见Intel手册第3卷的“2.6 EXTENDED CONTROL REGISTERS (INCLUDING XCR0)”——

  AMD对XCR0寄存器做了扩展,第62位是LWP状态。详见AMD手册第3卷的“11.5.2 XFEATURE_ENABLED_MASK”——


2.2 应用程序如何检测AVX2

  在《Intel® Architecture Instruction Set Extensions Programming Reference》的“2.2.3 Detection of AVX2”中介绍了AVX2指令集的检测方法和汇编伪代码,摘录如下——

[plain] view plaincopyprint?
  1. Hardware support for AVX2 is indicated by CPUID.(EAX=07H,ECX=0H):EBX.AVX2[bit 5]=1.  
  2. Application Software must identify that hardware supports AVX as explained in Section 2.2, after that it must also detect support for AVX2 by checking  
  3. CPUID.(EAX=07H, ECX=0H):EBX.AVX2[bit 5]. The recommended pseudocode sequence for detection of AVX2 is:  
  4. ----------------------------------------------------------------------------------------  
  5. INT supports_avx2()  
  6. {   ; result in eax  
  7.     mov eax, 1  
  8.     cpuid  
  9.     and ecx, 018000000H  
  10.     cmp ecx, 018000000H; check both OSXSAVE and AVX feature flags  
  11.     jne not_supported  
  12.     ; processor supports AVX instructions and XGETBV is enabled by OS  
  13.     mov eax, 7  
  14.     mov ecx, 0  
  15.     cpuid  
  16.     and ebx, 20H  
  17.     cmp ebx, 20H; check AVX2 feature flags  
  18.     jne not_supported  
  19.     mov ecx, 0; specify 0 for XFEATURE_ENABLED_MASK register  
  20.     XGETBV; result in EDX:EAX  
  21.     and eax, 06H  
  22.     cmp eax, 06H; check OS has enabled both XMM and YMM state support  
  23.     jne not_supported  
  24.     mov eax, 1  
  25.     jmp done  
  26. NOT_SUPPORTED:  
  27.     mov eax, 0  
  28. done:  
  29. }  


 

  可以看出,它是通过三个步奏来检查AVX2指令集的——
1) 使用cpuid指令的功能1,检测OSXSAVE和AVX标志。
2) 使用cpuid指令的功能7,检测AVX2标志。
3) 使用XGETBV指令获取XCR0寄存器的值,判断操作系统是否支持XMM和YMM状态。


2.3 如何获取XCR0寄存器的值

  官方推荐使用XGETBV指令来获取XCR0寄存器的值。输入寄存器是ECX,是XCR系列寄存器的索引,对于XCR0来说应填0。输出寄存器是EDX和EAX,分别是高32位和低32位。
  XGETBV指令是在任何访问级别均可调用的指令,即在Ring3的应用程序层也可使用XGETBV指令。
  虽然应用程序层可以使用XGETBV指令,但在实际使用时会遇到问题。这是因为XGETBV是最近才出现的指令,大多数编译器还不支持XGETBV指令。
  该怎么办呢?

  cpuid的0Dh号功能(Processor Extended State Enumeration)就是为这种情况设计的。当使用功能号0Dh、子功能号0调用cpuid指令时,返回的EDX和EAX就是XCR0的值。


2.4 编写检测函数

  前面我们看到了Intel的检测AVX与AVX2的汇编伪代码。虽然将其直接翻译为VC中的内嵌汇编并不复杂,但存在两个问题——
1. VC在x64平台不支持内嵌汇编;
2. 使用不方便。它比较适合在编写汇编代码时使用,但对于C语言程序来说,我们希望能以更好的方式组织代码。

  这时可以参考先前的simd_sse_level函数的设计,函数的返回值是操作系统对AVX指令集的支持级别,还提供一个指针参数来接收硬件对AVX指令集的支持级别。于是,定义了这些常数——
#define SIMD_AVX_NONE 0 // 不支持
#define SIMD_AVX_1 1 // AVX
#define SIMD_AVX_2 2 // AVX2

  我们可以利用先前的CPUIDFIELD方案来简化检测代码的编写。先定义好相关的常数——
#define CPUF_AVX CPUIDFIELD_MAKE(1,0,2,28,1)
#define CPUF_AVX2 CPUIDFIELD_MAKE(7,0,1,5,1)
#define CPUF_XSAVE CPUIDFIELD_MAKE(1,0,2,26,1)
#define CPUF_OSXSAVE CPUIDFIELD_MAKE(1,0,2,27,1)
#define CPUF_XFeatureSupportedMaskLo CPUIDFIELD_MAKE(0xD,0,0,0,32)

  在编写具体的检测代码时,没必要拘泥于官方的那三个步骤,可以先检查硬件支持性,然后再检查操作系统支持性。函数代码如下——

[cpp] view plaincopyprint?
  1. int simd_avx_level(int* phwavx)  
  2. {  
  3.     int rt = SIMD_AVX_NONE; // result  
  4.   
  5.     // check processor support  
  6.     if (0!=getcpuidfield(CPUF_AVX))  
  7.     {  
  8.         rt = SIMD_AVX_1;  
  9.         if (0!=getcpuidfield(CPUF_AVX2))  
  10.         {  
  11.             rt = SIMD_AVX_2;  
  12.         }  
  13.     }  
  14.     if (NULL!=phwavx)   *phwavx=rt;  
  15.   
  16.     // check OS support   
  17.     if (0!=getcpuidfield(CPUF_OSXSAVE)) // XGETBV enabled for application use.  
  18.     {  
  19.         UINT32 n = getcpuidfield(CPUF_XFeatureSupportedMaskLo); // XCR0: XFeatureSupportedMask register.  
  20.         if (6==(n&6))   // XCR0[2:1] = ‘11b’ (XMM state and YMM state are enabled by OS).  
  21.         {  
  22.             return rt;  
  23.         }  
  24.     }  
  25.     return SIMD_AVX_NONE;  
  26. }  


 


三、检测F16C、FMA、FMA4、XOP

  在《Intel® Architecture Instruction Set Extensions Programming Reference》的“2.2.1 Detection of FMA”中介绍了FMA指令的检测方法和汇编伪代码,摘录如下——

[plain] view plaincopyprint?
  1. Hardware support for FMA is indicated by CPUID.1:ECX.FMA[bit 12]=1.  
  2. Application Software must identify that hardware supports AVX as explained in Section 2.2, after that it must also detect support for FMA by CPUID.1:ECX.FMA[bit 12]. The recommended pseudocode sequence for detection of FMA is:  
  3. ----------------------------------------------------------------------------------------  
  4. INT supports_fma()  
  5. {   ; result in eax  
  6.     mov eax, 1  
  7.     cpuid  
  8.     and ecx, 018001000H  
  9.     cmp ecx, 018001000H; check OSXSAVE, AVX, FMA feature flags  
  10.     jne not_supported  
  11.     ; processor supports AVX,FMA instructions and XGETBV is enabled by OS  
  12.     mov ecx, 0; specify 0 for XFEATURE_ENABLED_MASK register  
  13.     XGETBV; result in EDX:EAX  
  14.     and eax, 06H  
  15.     cmp eax, 06H; check OS has enabled both XMM and YMM state support  
  16.     jne not_supported  
  17.     mov eax, 1  
  18.     jmp done  
  19. NOT_SUPPORTED:  
  20.     mov eax, 0  
  21. done:  
  22. }  
  23. -------------------------------------------------------------------------------  
  24. Note that FMA comprises 256-bit and 128-bit SIMD instructions operating on YMM states.  


 

  可以看出上面的代码与AVX2的检测代码很相似,只是多了对FMA标志位的检查。
  所以我们可以将其分解为两个步骤,先调用simd_avx_level检查AVX的支持性,然后再调用getcpuidfield检查硬件是否支持FMA,即这样的代码——

[cpp] view plaincopyprint?
  1. if (simd_avx_level(NULL)>0)  
  2. {  
  3.     if (getcpuidfield(CPUF_FMA))  
  4.     {  
  5.         支持FMA  
  6.     }  
  7. }  


 

  这样就只需定义F16C、FMA、FMA4、XOP的常数就够了——
#define CPUF_F16C CPUIDFIELD_MAKE(1,0,2,29,1)
#define CPUF_FMA CPUIDFIELD_MAKE(1,0,2,12,1)
#define CPUF_FMA4 CPUIDFIELD_MAKE(0x80000001,0,2,16,1)
#define CPUF_XOP CPUIDFIELD_MAKE(0x80000001,0,2,11,1)


四、全部代码

  全部代码——

[cpp] view plaincopyprint?
  1. #include <windows.h>   
  2. #include <stdio.h>   
  3. #include <conio.h>   
  4. #include <tchar.h>   
  5.   
  6. #if _MSC_VER >=1400  // VC2005才支持intrin.h  
  7. #include <intrin.h>   // 所有Intrinsics函数  
  8. #else   
  9. #include <emmintrin.h>    // MMX, SSE, SSE2  
  10. #endif   
  11.   
  12.   
  13. // CPUIDFIELD   
  14. typedef INT32 CPUIDFIELD;  
  15.   
  16. #define  CPUIDFIELD_MASK_POS    0x0000001F  // 位偏移. 0~31.  
  17. #define  CPUIDFIELD_MASK_LEN    0x000003E0  // 位长. 1~32  
  18. #define  CPUIDFIELD_MASK_REG    0x00000C00  // 寄存器. 0=EAX, 1=EBX, 2=ECX, 3=EDX.  
  19. #define  CPUIDFIELD_MASK_FIDSUB 0x000FF000  // 子功能号(低8位).  
  20. #define  CPUIDFIELD_MASK_FID    0xFFF00000  // 功能号(最高4位 和 低8位).  
  21.   
  22. #define CPUIDFIELD_SHIFT_POS    0   
  23. #define CPUIDFIELD_SHIFT_LEN    5  
  24. #define CPUIDFIELD_SHIFT_REG    10   
  25. #define CPUIDFIELD_SHIFT_FIDSUB 12  
  26. #define CPUIDFIELD_SHIFT_FID    20   
  27.   
  28. #define CPUIDFIELD_MAKE(fid,fidsub,reg,pos,len) (((fid)&0xF0000000) \  
  29.     | ((fid)<<CPUIDFIELD_SHIFT_FID & 0x0FF00000) \  
  30.     | ((fidsub)<<CPUIDFIELD_SHIFT_FIDSUB & CPUIDFIELD_MASK_FIDSUB) \  
  31.     | ((reg)<<CPUIDFIELD_SHIFT_REG & CPUIDFIELD_MASK_REG) \  
  32.     | ((pos)<<CPUIDFIELD_SHIFT_POS & CPUIDFIELD_MASK_POS) \  
  33.     | (((len)-1)<<CPUIDFIELD_SHIFT_LEN & CPUIDFIELD_MASK_LEN) \  
  34.     )  
  35. #define CPUIDFIELD_FID(cpuidfield)  ( ((cpuidfield)&0xF0000000) | (((cpuidfield) & 0x0FF00000)>>CPUIDFIELD_SHIFT_FID) )  
  36. #define CPUIDFIELD_FIDSUB(cpuidfield)   ( ((cpuidfield) & CPUIDFIELD_MASK_FIDSUB)>>CPUIDFIELD_SHIFT_FIDSUB )  
  37. #define CPUIDFIELD_REG(cpuidfield)  ( ((cpuidfield) & CPUIDFIELD_MASK_REG)>>CPUIDFIELD_SHIFT_REG )  
  38. #define CPUIDFIELD_POS(cpuidfield)  ( ((cpuidfield) & CPUIDFIELD_MASK_POS)>>CPUIDFIELD_SHIFT_POS )  
  39. #define CPUIDFIELD_LEN(cpuidfield)  ( (((cpuidfield) & CPUIDFIELD_MASK_LEN)>>CPUIDFIELD_SHIFT_LEN) + 1 )  
  40.   
  41. // 取得位域   
  42. #ifndef __GETBITS32   
  43. #define __GETBITS32(src,pos,len)    ( ((src)>>(pos)) & (((UINT32)-1)>>(32-len)) )  
  44. #endif   
  45.   
  46.   
  47. #define CPUF_SSE4A  CPUIDFIELD_MAKE(0x80000001,0,2,6,1)  
  48. #define CPUF_AES    CPUIDFIELD_MAKE(1,0,2,25,1)  
  49. #define CPUF_PCLMULQDQ  CPUIDFIELD_MAKE(1,0,2,1,1)  
  50.   
  51. #define CPUF_AVX    CPUIDFIELD_MAKE(1,0,2,28,1)  
  52. #define CPUF_AVX2   CPUIDFIELD_MAKE(7,0,1,5,1)  
  53. #define CPUF_OSXSAVE    CPUIDFIELD_MAKE(1,0,2,27,1)  
  54. #define CPUF_XFeatureSupportedMaskLo    CPUIDFIELD_MAKE(0xD,0,0,0,32)  
  55. #define CPUF_F16C   CPUIDFIELD_MAKE(1,0,2,29,1)  
  56. #define CPUF_FMA    CPUIDFIELD_MAKE(1,0,2,12,1)  
  57. #define CPUF_FMA4   CPUIDFIELD_MAKE(0x80000001,0,2,16,1)  
  58. #define CPUF_XOP    CPUIDFIELD_MAKE(0x80000001,0,2,11,1)  
  59.   
  60.   
  61. // SSE系列指令集的支持级别. simd_sse_level 函数的返回值。  
  62. #define SIMD_SSE_NONE   0   // 不支持   
  63. #define SIMD_SSE_1  1   // SSE  
  64. #define SIMD_SSE_2  2   // SSE2   
  65. #define SIMD_SSE_3  3   // SSE3  
  66. #define SIMD_SSE_3S 4   // SSSE3   
  67. #define SIMD_SSE_41 5   // SSE4.1  
  68. #define SIMD_SSE_42 6   // SSE4.2   
  69.   
  70. const char* simd_sse_names[] = {  
  71.     "None",  
  72.     "SSE",  
  73.     "SSE2",  
  74.     "SSE3",  
  75.     "SSSE3",  
  76.     "SSE4.1",  
  77.     "SSE4.2",  
  78. };  
  79.   
  80.   
  81. // AVX系列指令集的支持级别. simd_avx_level 函数的返回值。  
  82. #define SIMD_AVX_NONE   0   // 不支持   
  83. #define SIMD_AVX_1  1   // AVX  
  84. #define SIMD_AVX_2  2   // AVX2   
  85.   
  86. const char* simd_avx_names[] = {  
  87.     "None",  
  88.     "AVX",  
  89.     "AVX2"  
  90. };  
  91.   
  92.   
  93.   
  94. char szBuf[64];  
  95. INT32 dwBuf[4];  
  96.   
  97. #if defined(_WIN64)   
  98. // 64位下不支持内联汇编. 应使用__cpuid、__cpuidex等Intrinsics函数。  
  99. #else   
  100. #if _MSC_VER < 1600  // VS2010. 据说VC2008 SP1之后才支持__cpuidex  
  101. void __cpuidex(INT32 CPUInfo[4], INT32 InfoType, INT32 ECXValue)  
  102. {  
  103.     if (NULL==CPUInfo)  return;  
  104.     _asm{  
  105.         // load. 读取参数到寄存器   
  106.         mov edi, CPUInfo;   // 准备用edi寻址CPUInfo  
  107.         mov eax, InfoType;  
  108.         mov ecx, ECXValue;  
  109.         // CPUID   
  110.         cpuid;  
  111.         // save. 将寄存器保存到CPUInfo  
  112.         mov [edi], eax;  
  113.         mov [edi+4], ebx;  
  114.         mov [edi+8], ecx;  
  115.         mov [edi+12], edx;  
  116.     }  
  117. }  
  118. #endif  // #if _MSC_VER < 1600   // VS2010. 据说VC2008 SP1之后才支持__cpuidex  
  119.   
  120. #if _MSC_VER < 1400  // VC2005才支持__cpuid  
  121. void __cpuid(INT32 CPUInfo[4], INT32 InfoType)  
  122. {  
  123.     __cpuidex(CPUInfo, InfoType, 0);  
  124. }  
  125. #endif  // #if _MSC_VER < 1400   // VC2005才支持__cpuid  
  126.   
  127. #endif  // #if defined(_WIN64)  
  128.   
  129. // 根据CPUIDFIELD从缓冲区中获取字段.   
  130. UINT32  getcpuidfield_buf(const INT32 dwBuf[4], CPUIDFIELD cpuf)  
  131. {  
  132.     return __GETBITS32(dwBuf[CPUIDFIELD_REG(cpuf)], CPUIDFIELD_POS(cpuf), CPUIDFIELD_LEN(cpuf));  
  133. }  
  134.   
  135. // 根据CPUIDFIELD获取CPUID字段.   
  136. UINT32  getcpuidfield(CPUIDFIELD cpuf)  
  137. {  
  138.     INT32 dwBuf[4];  
  139.     __cpuidex(dwBuf, CPUIDFIELD_FID(cpuf), CPUIDFIELD_FIDSUB(cpuf));  
  140.     return getcpuidfield_buf(dwBuf, cpuf);  
  141. }  
  142.   
  143. // 取得CPU厂商(Vendor)   
  144. //   
  145. // result: 成功时返回字符串的长度(一般为12)。失败时返回0。  
  146. // pvendor: 接收厂商信息的字符串缓冲区。至少为13字节。   
  147. int cpu_getvendor(char* pvendor)  
  148. {  
  149.     INT32 dwBuf[4];  
  150.     if (NULL==pvendor)  return 0;  
  151.     // Function 0: Vendor-ID and Largest Standard Function  
  152.     __cpuid(dwBuf, 0);  
  153.     // save. 保存到pvendor   
  154.     *(INT32*)&pvendor[0] = dwBuf[1];    // ebx: 前四个字符  
  155.     *(INT32*)&pvendor[4] = dwBuf[3];    // edx: 中间四个字符  
  156.     *(INT32*)&pvendor[8] = dwBuf[2];    // ecx: 最后四个字符  
  157.     pvendor[12] = '\0';  
  158.     return 12;  
  159. }  
  160.   
  161. // 取得CPU商标(Brand)   
  162. //   
  163. // result: 成功时返回字符串的长度(一般为48)。失败时返回0。  
  164. // pbrand: 接收商标信息的字符串缓冲区。至少为49字节。   
  165. int cpu_getbrand(char* pbrand)  
  166. {  
  167.     INT32 dwBuf[4];  
  168.     if (NULL==pbrand)   return 0;  
  169.     // Function 0x80000000: Largest Extended Function Number  
  170.     __cpuid(dwBuf, 0x80000000);  
  171.     if (dwBuf[0] < 0x80000004)   return 0;  
  172.     // Function 80000002h,80000003h,80000004h: Processor Brand String  
  173.     __cpuid((INT32*)&pbrand[0], 0x80000002);    // 前16个字符  
  174.     __cpuid((INT32*)&pbrand[16], 0x80000003);   // 中间16个字符  
  175.     __cpuid((INT32*)&pbrand[32], 0x80000004);   // 最后16个字符  
  176.     pbrand[48] = '\0';  
  177.     return 48;  
  178. }  
  179.   
  180.   
  181. // 是否支持MMX指令集   
  182. BOOL    simd_mmx(BOOL* phwmmx)  
  183. {  
  184.     const INT32 BIT_D_MMX = 0x00800000; // bit 23  
  185.     BOOL    rt = FALSE; // result  
  186.     INT32 dwBuf[4];  
  187.   
  188.     // check processor support   
  189.     __cpuid(dwBuf, 1);  // Function 1: Feature Information  
  190.     if ( dwBuf[3] & BIT_D_MMX ) rt=TRUE;  
  191.     if (NULL!=phwmmx)   *phwmmx=rt;  
  192.   
  193.     // check OS support   
  194.     if ( rt )  
  195.     {  
  196. #if defined(_WIN64)   
  197.         // VC编译器不支持64位下的MMX。   
  198.         rt=FALSE;  
  199. #else   
  200.         __try   
  201.         {  
  202.             _mm_empty();    // MMX instruction: emms  
  203.         }  
  204.         __except (EXCEPTION_EXECUTE_HANDLER)  
  205.         {  
  206.             rt=FALSE;  
  207.         }  
  208. #endif  // #if defined(_WIN64)   
  209.     }  
  210.   
  211.     return rt;  
  212. }  
  213.   
  214. // 检测SSE系列指令集的支持级别   
  215. int simd_sse_level(int* phwsse)  
  216. {  
  217.     const INT32 BIT_D_SSE = 0x02000000; // bit 25  
  218.     const INT32 BIT_D_SSE2 = 0x04000000;    // bit 26  
  219.     const INT32 BIT_C_SSE3 = 0x00000001;    // bit 0  
  220.     const INT32 BIT_C_SSSE3 = 0x00000100;   // bit 9  
  221.     const INT32 BIT_C_SSE41 = 0x00080000;   // bit 19  
  222.     const INT32 BIT_C_SSE42 = 0x00100000;   // bit 20  
  223.     int rt = SIMD_SSE_NONE; // result  
  224.     INT32 dwBuf[4];  
  225.   
  226.     // check processor support   
  227.     __cpuid(dwBuf, 1);  // Function 1: Feature Information  
  228.     if ( dwBuf[3] & BIT_D_SSE )  
  229.     {  
  230.         rt = SIMD_SSE_1;  
  231.         if ( dwBuf[3] & BIT_D_SSE2 )  
  232.         {  
  233.             rt = SIMD_SSE_2;  
  234.             if ( dwBuf[2] & BIT_C_SSE3 )  
  235.             {  
  236.                 rt = SIMD_SSE_3;  
  237.                 if ( dwBuf[2] & BIT_C_SSSE3 )  
  238.                 {  
  239.                     rt = SIMD_SSE_3S;  
  240.                     if ( dwBuf[2] & BIT_C_SSE41 )  
  241.                     {  
  242.                         rt = SIMD_SSE_41;  
  243.                         if ( dwBuf[2] & BIT_C_SSE42 )  
  244.                         {  
  245.                             rt = SIMD_SSE_42;  
  246.                         }  
  247.                     }  
  248.                 }  
  249.             }  
  250.         }  
  251.     }  
  252.     if (NULL!=phwsse)   *phwsse=rt;  
  253.   
  254.     // check OS support   
  255.     __try   
  256.     {  
  257.         __m128 xmm1 = _mm_setzero_ps(); // SSE instruction: xorps  
  258.         if (0!=*(int*)&xmm1)    rt = SIMD_SSE_NONE; // 避免Release模式编译优化时剔除上一条语句  
  259.     }  
  260.     __except (EXCEPTION_EXECUTE_HANDLER)  
  261.     {  
  262.         rt = SIMD_SSE_NONE;  
  263.     }  
  264.   
  265.     return rt;  
  266. }  
  267.   
  268. // 检测AVX系列指令集的支持级别.   
  269. int simd_avx_level(int* phwavx)  
  270. {  
  271.     int rt = SIMD_AVX_NONE; // result  
  272.   
  273.     // check processor support  
  274.     if (0!=getcpuidfield(CPUF_AVX))  
  275.     {  
  276.         rt = SIMD_AVX_1;  
  277.         if (0!=getcpuidfield(CPUF_AVX2))  
  278.         {  
  279.             rt = SIMD_AVX_2;  
  280.         }  
  281.     }  
  282.     if (NULL!=phwavx)   *phwavx=rt;  
  283.   
  284.     // check OS support   
  285.     if (0!=getcpuidfield(CPUF_OSXSAVE)) // XGETBV enabled for application use.  
  286.     {  
  287.         UINT32 n = getcpuidfield(CPUF_XFeatureSupportedMaskLo); // XCR0: XFeatureSupportedMask register.  
  288.         if (6==(n&6))   // XCR0[2:1] = ‘11b’ (XMM state and YMM state are enabled by OS).  
  289.         {  
  290.             return rt;  
  291.         }  
  292.     }  
  293.     return SIMD_AVX_NONE;  
  294. }  
  295.   
  296.   
  297.   
  298. int _tmain(int argc, _TCHAR* argv[])  
  299. {  
  300.     int i;  
  301.   
  302.     //__cpuidex(dwBuf, 0,0);   
  303.     //__cpuid(dwBuf, 0);   
  304.     //printf("%.8X\t%.8X\t%.8X\t%.8X\n", dwBuf[0],dwBuf[1],dwBuf[2],dwBuf[3]);  
  305.   
  306.     cpu_getvendor(szBuf);  
  307.     printf("CPU Vendor:\t%s\n", szBuf);  
  308.   
  309.     cpu_getbrand(szBuf);  
  310.     printf("CPU Name:\t%s\n", szBuf);  
  311.   
  312.     BOOL bhwmmx;    // 硬件支持MMX.  
  313.     BOOL bmmx;  // 操作系统支持MMX.  
  314.     bmmx = simd_mmx(&bhwmmx);  
  315.     printf("MMX: %d\t// hw: %d\n", bmmx, bhwmmx);  
  316.   
  317.     int nhwsse; // 硬件支持SSE.  
  318.     int nsse;   // 操作系统支持SSE.  
  319.     nsse = simd_sse_level(&nhwsse);  
  320.     printf("SSE: %d\t// hw: %d\n", nsse, nhwsse);  
  321.     for(i=1; i<sizeof(simd_sse_names)/sizeof(simd_sse_names[0]); ++i)  
  322.     {  
  323.         if (nhwsse>=i)   printf("\t%s\n", simd_sse_names[i]);  
  324.     }  
  325.   
  326.     // test SSE4A/AES/PCLMULQDQ   
  327.     printf("SSE4A: %d\n", getcpuidfield(CPUF_SSE4A));  
  328.     printf("AES: %d\n", getcpuidfield(CPUF_AES));  
  329.     printf("PCLMULQDQ: %d\n", getcpuidfield(CPUF_PCLMULQDQ));  
  330.   
  331.     // test AVX   
  332.     int nhwavx; // 硬件支持AVX.  
  333.     int navx;   // 操作系统支持AVX.  
  334.     navx = simd_avx_level(&nhwavx);  
  335.     printf("AVX: %d\t// hw: %d\n", navx, nhwavx);  
  336.     for(i=1; i<sizeof(simd_avx_names)/sizeof(simd_avx_names[0]); ++i)  
  337.     {  
  338.         if (nhwavx>=i)   printf("\t%s\n", simd_avx_names[i]);  
  339.     }  
  340.   
  341.     // test F16C/FMA/FMA4/XOP   
  342.     printf("F16C: %d\n", getcpuidfield(CPUF_F16C));  
  343.     printf("FMA: %d\n", getcpuidfield(CPUF_FMA));  
  344.     printf("FMA4: %d\n", getcpuidfield(CPUF_FMA4));  
  345.     printf("XOP: %d\n", getcpuidfield(CPUF_XOP));  
  346.   
  347.     return 0;  
  348. }  


 

  在以下编译器中成功编译——
VC6(32位)
VC2003(32位)
VC2005(32位)
VC2010(32位、64位)


五、测试

  在64位的win7中运行“x64\Release\getcpuidfield_2010.exe”,运行效果——

  利用cmdarg_ui运行“Debug\getcpuidfield.exe”,顺便测试WinXP与VC6——

 

参考文献——
《Intel® 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes:1, 2A, 2B, 2C, 3A, 3B, and 3C》. May 2012.http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
《Intel® Architecture Instruction Set Extensions Programming Reference》. FEBRUARY 2012.http://software.intel.com/file/41604
《Intel® Processor Identification and the CPUID Instruction》. April 2012. http://developer.intel.com/content/www/us/en/processors/processor-identification-cpuid-instruction-note.html
《AMD64 Architecture Programmer's Manual Volume 3: General Purpose and System Instructions》. December 2011.http://support.amd.com/us/Processor_TechDocs/24594_APM_v3.pdf
《AMD CPUID Specification》. September 2010. http://support.amd.com/us/Embedded_TechDocs/25481.pdf
《x86 architecture CPUID》. http://www.sandpile.org/x86/cpuid.htm
《Haswell New Instruction Descriptions Now Available! 》. Mark Buxton. http://software.intel.com/en-us/blogs/2011/06/13/haswell-new-instruction-descriptions-now-available/
[IDF2012]ARCS002《Introduction to the upcoming Intel® Advanced Vector Extensions 2 (Intel® AVX2)》. 王有伟, Henry Ou. 2012-4.
[IDF2012]ARCS002《即将推出的英特尔® 高级矢量扩展指令集2(英特尔® AVX2)介绍》. 王有伟, Henry Ou. 2012-4.
《x86/x64 指令系统》. mik(邓志). http://www.mouseos.com/x64/default.html
《[x86]SIMD指令集发展历程表(MMX、SSE、AVX等)》. http://www.cnblogs.com/zyl910/archive/2012/02/26/x86_simd_table.html
《如何在各个版本的VC及64位下使用CPUID指令》. http://www.cnblogs.com/zyl910/archive/2012/05/21/vcgetcpuid.html
《[VC兼容32位和64位] 检查MMX和SSE系列指令集的支持级别》. http://www.cnblogs.com/zyl910/archive/2012/05/25/checksimd64.html
《[VC] CPUIDFIELD:CPUID字段的统一编号、读取方案。范例:检查SSE4A、AES、PCLMULQDQ指令》. http://www.cnblogs.com/zyl910/archive/2012/06/29/getcpuidfield.html
《[C#] cmdarg_ui:“简单参数命令行程序”的通用图形界面》.  http://www.cnblogs.com/zyl910/archive/2012/06/19/cmdarg_ui.html

 

源码下载——
http://files.cnblogs.com/zyl910/checkavx.rar

0 0
原创粉丝点击