应用程序在使用 AVX 指令之前要经过一些检测是否支持,OS 要经过一些设置允许使用 AVX 指令。
尽管 CPU 在硬件层上是支持 AVX 指令的,但是如果 OS 并没有去设置来允许使用,应用程序将不能使用 AVX 指令。
|
mov eax, 1 |
上面的代码是在 OS 层上进行的设置。
|
mov eax, 1 |
在这一步里,通过 cpuid 的功能号 1 来检测 ECX 的 bit 26 位的 ECX[XSAVE] 标志位,这个标志位可以检测:
实际上在 OS 层这里不需要检测 cpuid 是否支持 AVX 指令,在应用程序需要使用 AVX 指令时,由应用程序来检测是否支持 AVX 指令,这个规则是 Intel 和 AMD 都推崇的。当然在这里检测一下也是好的。
来看看 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 |
这段代码,通过 CPUID(EDX=0DH,ECX=0H)来检测 XCR0 的支持度,XCR0 的低 32 位返回在 eax 寄存器,高 32 位返回在 edx 寄存器,因此检测 eax 寄存器的低 3 位是否为 111b 值(即:07H)。不被支持的位是 reserved 位,如果对一个保留进行写的话将会产生 #GP 异常。在一个不支持 AVX 指令的 CPU 上 XCR0 寄存器也是不支持的。
当上面两个功能都检测通过后,接着可以开启 CR4.OSXSAVE 标志,打开 CR4.OSXSAVE 意味着:
应用层上的软件可以通过 cpuid(EAX=01H)功能号来查询 OSXSAVE 的状态是否可用。OS 层设置了 CR4.OSXSAVE 标志位,它将会反应在 cpuid(EAX=01H) 的 ECX[OSXSAVE] 标志位上。
|
mov eax, cr4 |
CR4.OSXSAVE 是 CR4 寄存的 BIT 18 位,开启后表示 XSETBV 和 SGETBV 是可以使用了。
在这一步应该要使用 XSETBV 指令来设置 XCR0 寄存器的 mask 值,典型的做法如下:
|
mov edx, 0 |
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 异常。
在 Intel 和 AMD 的推荐做法中,需要检测 XSAVE 和 XRSTOR 指令在保存状态时所需要的内存大小,通过 cpuid(EAX=0DH,ECX=0H)来获得。
|
mov eax, 0xd |
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 指令之前还应该做一些检测。
应用程序在使用之前进行的一些检测,典型做法如下:
|
mov eax, 1 ... ... ; OK! no_support: |
应用程序的检测相对简单多了,主要是检测两个功能:
除了 AVX 标志是 CPU 硬件上支持的外,OSXSAVE 位和 XCR0 mask 值都是由 OS 进行设置的。
|
mov eax, 1 |
这里检测了 CPUID.1.ECX.AVX[bit 28] 以及 CPUID.1.ECX.XSAVE[bit 26],它们代表了 CPU 在硬件上的支持,分别是 AVX 指令和 XSAVE 系列指令。
其中,这个 XSAVE 标志位可以不用检测,这是因为 OS 层上必须要 XSAVE 指令是否得到支持。
这个检测是必须的,必须要明白:
| 硬件上支持,不等于 OS 上支持 |
因此,只检测硬件上的支持而忽略 OS 上的支持,是非常错误的。
|
bt ecx, 27 ; check OS is ready for OSXSAVE |
通过检测 CPUID.1.ECX.OSXSAVE[bit 27] 来确定 OS 是否已经准备好了,正如前面所说的,OS 层上需要设置 CR4.OSXSAVE 标志位来告诉应用层 AVX 的使用环境已经准备好了。这个标志位将反应到 CPUID.1.ECX.OSXSAVE[bit 27] 上,这是应用程序能够访问的。
AVX 指令是使用 YMM 和 XMM 寄存器的,因此这些状态必须能够被保存,否则同样不能使用 AVX 指令,通过 XGETBV 指令可以读取 XCR0 寄存器的 mask 值:
|
mov ecx, 0 |
到现在为止,应用程序的检测通过后,就可以正确地使用 AVX 指令了。