9. AVX 指令的使用


应用程序在使用 AVX 指令之前要经过一些检测是否支持,OS 要经过一些设置允许使用 AVX 指令。

9.1 OS 层的设置

尽管 CPU 在硬件层上是支持 AVX 指令的,但是如果 OS 并没有去设置来允许使用,应用程序将不能使用 AVX 指令。

        mov eax, 1                             
        cpuid
        bt ecx, 26                                ; verify XSAVE/XRSTOR/XSETBV/XGETBV and XCR0 support
        jnc no_support
       
        mov eax, 0xd
        mov ecx, 0
        cpuid
        and eax, 7
        cmp eax, 7                                ; verify XCR0.YMM, XCRO.SSE and XCRO.X87 support
        jnz no_support
       
        mov eax, cr4
        bts eax, 18                                ; enable CR4.OSXSAVE to enable XSETBV and XGETBV instruction
        mov cr4, eax
       
        mov edx, 0
        mov eax, 7
        mov ecx, 0
        xsetbv                                     ; set XCR0.x87 = 1,  XCR0.SSE = 1 and XCR0.YMM = 1
       
        mov eax, 0xd
        mov ecx, 0                       
        cpuid                                      ; get XSAVE/XRSTOR buffer size

上面的代码是在 OS 层上进行的设置。

9.1.1 确认是否支持 XSAVE 指令

        mov eax, 1                             
        cpuid
        bt ecx, 26                                ; verify XSAVE/XRSTOR/XSETBV/XGETBV and XCR0 support
        jnc no_support

在这一步里,通过 cpuid 的功能号 1 来检测 ECX 的 bit 26 位的 ECX[XSAVE] 标志位,这个标志位可以检测:

实际上在 OS 层这里不需要检测 cpuid 是否支持 AVX 指令,在应用程序需要使用 AVX 指令时,由应用程序来检测是否支持 AVX 指令,这个规则是 Intel 和 AMD 都推崇的。当然在这里检测一下也是好的。

9.1.2 检测 XCR0 寄存器的支持度

来看看 XCR0 寄存器的结构:

这个 XCR0 寄存器的 Bit 62 对 AMD 来说是 LWP 指令支持位,对 Intel 来说是保留的

XCR0 寄存器实际上是 XFEATURE_ENABLED_MASK 寄存器,用来为 XSAVA XRSTOR 指令提供一个 mask 值,这个 mask 值允许 XSAVA 和 XRSTOR 指令保存哪个 cpu 状态,分别为:

在 OS 层里需要对 XCR0 寄存器进行设置,因此,在设置 XCR0 寄存器之前需要检测 XCR0 寄存器支持哪个 mask 位

        mov eax, 0xd
        mov ecx, 0
        cpuid
        and eax, 7
        cmp eax, 7                                ; verify XCR0.YMM, XCRO.SSE and XCRO.X87 support
        jnz no_support

这段代码,通过 CPUIDEDX=0DH,ECX=0H)来检测 XCR0 的支持度,XCR0 的低 32 位返回在 eax 寄存器,高 32 位返回在 edx 寄存器,因此检测 eax 寄存器的低 3 位是否为 111b 值(即:07H)。不被支持的位是 reserved 位,如果对一个保留进行写的话将会产生 #GP 异常。在一个不支持 AVX 指令的 CPU 上 XCR0 寄存器也是不支持的。

9.1.3 开启 CR4.OSXSAVE 标志位

当上面两个功能都检测通过后,接着可以开启 CR4.OSXSAVE 标志,打开 CR4.OSXSAVE 意味着:

应用层上的软件可以通过 cpuid(EAX=01H)功能号来查询 OSXSAVE 的状态是否可用。OS 层设置了 CR4.OSXSAVE 标志位,它将会反应在 cpuid(EAX=01H) ECX[OSXSAVE] 标志位上。

        mov eax, cr4
        bts eax, 18                                ; enable CR4.OSXSAVE to enable XSETBV and XGETBV instruction
        mov cr4, eax

CR4.OSXSAVE 是 CR4 寄存的 BIT 18 位,开启后表示 XSETBV 和 SGETBV 是可以使用了。

9.1.4 设置 XFEATURE_ENABLED_MASK

在这一步应该要使用 XSETBV 指令来设置 XCR0 寄存器的 mask 值,典型的做法如下:

        mov edx, 0
        mov eax, 7
        mov ecx, 0
        xsetbv                                     ; set XCR0.x87 = 1,  XCR0.SSE = 1 and XCR0.YMM = 1

XCR0 寄存器的地址编号为 0H,通过 ECX=0H 来传递,edx:eax 是存放需要设置 64 位的内容, edx 存放高 32 位,eax 存放低 32 位,因此,edx=0,而 eax=7(即:111b 值),这个值正是开启 XCR0 寄存器的值,即:x87 mask,SSE mask 以及 YMM mask,正如前面所说,如果 XCR0 不支持某些位,在这里进行写操作的话将会产生 #GP 异常。

9.1.5 获得 XSAVE/XRSTOR 指令所需要的 buffer size

在 Intel 和 AMD 的推荐做法中,需要检测 XSAVE 和 XRSTOR 指令在保存状态时所需要的内存大小,通过 cpuid(EAX=0DH,ECX=0H)来获得。

        mov eax, 0xd
        mov ecx, 0                       
        cpuid                                      ; get XSAVE/XRSTOR buffer size

XSAVE/XRSTOR 所需要的 buffer size 返回在 ebx 寄存器和 ecx 寄存器中,在 Intel 的文档中说到 ebx 寄存器是返回

Maximum size (bytes, from the beginning of the XSAVE/XRSTOR save area) required by enabled features in XCR0. May be different than ECX if some features at the end of the XSAVE save area are not enabled.

而 ecx 则是返回:

Maximum size (bytes, from the beginning of the XSAVE/XRSTOR save area) of the XSAVE/XRSTOR save area required by all supported features in the processor, i.e all the valid bit fields in XCR0.

ebx 和 ecx 的值或许会不同的,从上面的两段话来看,似乎 ecx 寄存器返回更全面的内存大小。由于我的机子上并不支持 AVX 指令,所以我并不能更详细地知道 ebx 返回值与 ecx 返回值的区别。

到此为止,OS 层上所需要做的工作就是这些,OS 层已经为 AVX 指令准备好了使用环境。可以应用程序在 AVX 指令之前还应该做一些检测。

9.2 应用程序层上的使用

应用程序在使用之前进行的一些检测,典型做法如下:

        mov eax, 1                               
        cpuid
        bt ecx, 28                                ; check CPU is support for AVX instruction
        jnc no_support       
        bt ecx, 27                                ; check OS is ready for OSXSAVE
        jnc no_support               
                                       
        mov ecx, 0
        xgetbv                                    ; get XCR0
        and eax, 6
        cmp eax, 6                                ; check XCRO for SSE and YMM mask support
        jnz no_support

        ... ...                                        ; OK!

no_support:
        ... ...                                        ; failure

应用程序的检测相对简单多了,主要是检测两个功能:

除了 AVX 标志是 CPU 硬件上支持的外,OSXSAVE 位和 XCR0 mask 值都是由 OS 进行设置的。

9.2.1 检测硬件上的支持

        mov eax, 1                               
        cpuid
        bt ecx, 28                                ; check CPU is support for AVX instruction
        jnc no_support       
        bt ecx, 26                                ; check CPU is support for XSAVE instruction
        jnc no_support

这里检测了 CPUID.1.ECX.AVX[bit 28] 以及 CPUID.1.ECX.XSAVE[bit 26],它们代表了 CPU 在硬件上的支持,分别是 AVX 指令和 XSAVE 系列指令。

其中,这个 XSAVE 标志位可以不用检测,这是因为 OS 层上必须要 XSAVE 指令是否得到支持

9.2.2 检测 OS 层上的支持

这个检测是必须的,必须要明白:

硬件上支持,不等于 OS 上支持

因此,只检测硬件上的支持而忽略 OS 上的支持,是非常错误的。

        bt ecx, 27                                ; check OS is ready for OSXSAVE
        jnc no_support  

通过检测 CPUID.1.ECX.OSXSAVE[bit 27] 来确定 OS 是否已经准备好了,正如前面所说的,OS 层上需要设置 CR4.OSXSAVE 标志位来告诉应用层 AVX 的使用环境已经准备好了。这个标志位将反应到 CPUID.1.ECX.OSXSAVE[bit 27] 上,这是应用程序能够访问的。

9.2.3 检测是否可以为 XMM 和 YMM 保存状态

AVX 指令是使用 YMM 和 XMM 寄存器的,因此这些状态必须能够被保存,否则同样不能使用 AVX 指令,通过 XGETBV 指令可以读取 XCR0 寄存器的 mask 值:

        mov ecx, 0
        xgetbv                                    ; get XCR0
        and eax, 6
        cmp eax, 6                                ; check XCRO for SSE and YMM mask support
        jnz no_support

到现在为止,应用程序的检测通过后,就可以正确地使用 AVX 指令了。

 


版权所有 mik 2009-2011