[VC] 检测AVX系列指令集的支持级别(AVX、AVX2、F16C、FMA、FMA4、XOP)

从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还给出了汇编伪代码——

复制代码
INT supports_AVX()
{    mov eax, 1
    cpuid
    and ecx, 018000000H
    cmp ecx, 018000000H; check both OSXSAVE and AVX feature flags
    jne not_supported
    ; processor supports AVX instructions and XGETBV is enabled by OS
    mov ecx, 0; specify 0 for XCR0 register
    XGETBV ; result in EDX:EAX
    and eax, 06H
    cmp eax, 06H; check OS has enabled both XMM and YMM state support
    jne not_supported
    mov eax, 1
    jmp done
NOT_SUPPORTED:
    mov eax, 0
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指令集的检测方法和汇编伪代码,摘录如下——

复制代码
Hardware support for AVX2 is indicated by CPUID.(EAX=07H,ECX=0H):EBX.AVX2[bit 5]=1.
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
CPUID.(EAX=07H, ECX=0H):EBX.AVX2[bit 5]. The recommended pseudocode sequence for detection of AVX2 is:
----------------------------------------------------------------------------------------
INT supports_avx2()
{    ; result in eax
    mov eax, 1
    cpuid
    and ecx, 018000000H
    cmp ecx, 018000000H; check both OSXSAVE and AVX feature flags
    jne not_supported
    ; processor supports AVX instructions and XGETBV is enabled by OS
    mov eax, 7
    mov ecx, 0
    cpuid
    and ebx, 20H
    cmp ebx, 20H; check AVX2 feature flags
    jne not_supported
    mov ecx, 0; specify 0 for XFEATURE_ENABLED_MASK register
    XGETBV; result in EDX:EAX
    and eax, 06H
    cmp eax, 06H; check OS has enabled both XMM and YMM state support
    jne not_supported
    mov eax, 1
    jmp done
NOT_SUPPORTED:
    mov eax, 0
done:
}
复制代码

 

  可以看出,它是通过三个步奏来检查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)

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

复制代码
int    simd_avx_level(int* phwavx)
{
    int    rt = SIMD_AVX_NONE;    // result

    // check processor support
    if (0!=getcpuidfield(CPUF_AVX))
    {
        rt = SIMD_AVX_1;
        if (0!=getcpuidfield(CPUF_AVX2))
        {
            rt = SIMD_AVX_2;
        }
    }
    if (NULL!=phwavx)    *phwavx=rt;

    // check OS support
    if (0!=getcpuidfield(CPUF_OSXSAVE))    // XGETBV enabled for application use.
    {
        UINT32 n = getcpuidfield(CPUF_XFeatureSupportedMaskLo);    // XCR0: XFeatureSupportedMask register.
        if (6==(n&6))    // XCR0[2:1] = ‘11b’ (XMM state and YMM state are enabled by OS).
        {
            return rt;
        }
    }
    return SIMD_AVX_NONE;
}
复制代码

 


三、检测F16C、FMA、FMA4、XOP

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

复制代码
Hardware support for FMA is indicated by CPUID.1:ECX.FMA[bit 12]=1.
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:
----------------------------------------------------------------------------------------
INT supports_fma()
{    ; result in eax
    mov eax, 1
    cpuid
    and ecx, 018001000H
    cmp ecx, 018001000H; check OSXSAVE, AVX, FMA feature flags
    jne not_supported
    ; processor supports AVX,FMA instructions and XGETBV is enabled by OS
    mov ecx, 0; specify 0 for XFEATURE_ENABLED_MASK register
    XGETBV; result in EDX:EAX
    and eax, 06H
    cmp eax, 06H; check OS has enabled both XMM and YMM state support
    jne not_supported
    mov eax, 1
    jmp done
NOT_SUPPORTED:
    mov eax, 0
done:
}
-------------------------------------------------------------------------------
Note that FMA comprises 256-bit and 128-bit SIMD instructions operating on YMM states.
复制代码

 

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

复制代码
if (simd_avx_level(NULL)>0)
{
    if (getcpuidfield(CPUF_FMA))
    {
        支持FMA
    }
}
复制代码

 

  这样就只需定义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)


四、全部代码

  全部代码——

按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

 

  在以下编译器中成功编译——
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

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页