开源库中检测当前系统是否支持AVX指令集的一个坑

发布者:blowfish
发布于:2017-12-08 17:39
Intel、AMD的处理器都是在2011年才开始支持AVX指令集的,所以现在还有不少用户的机器是不支持AVX的。AVX指令集,除了需要CPU硬件支持,还需要OS的支持,为什么呢?因为OS在做context switch时,需要将AVX寄存器给save/restore好,要不就可能出现错误的执行结果。

微软在其官方文档中公开宣称,从Windows 7 SP1和Windows Server 2008 R2 SP1开始,才能支持AVX,但是根据实际的测试结果,在某些Windows XP物理机上,cpuid指令会一会儿返回支持AVX,一会儿返回不支持AVX,也就是不稳定。_xgetbv( )返回的结果也不稳定。

开源库如OpenSSL、Chromium/CEF中都有只利用cpuid指令检测是否支持AVX的逻辑,导致进坑,在这种不稳定的XP上可能会崩掉。

要解决这个问题也很简单,先判断OS版本,在低于Windows 7 SP1、Windows Server 2008 R2 SP1的系统上,直接禁用AVX就行了;OS版本满足AVX支持的要求的,再用cpuid、_xgetbv指令检测。

对于 Chromium/CEF,只能修改它的代码重新编译。要修改的文件是:
chromium\src\v8\src\ia32\assembler-ia32.cc
chromium\src\v8\src\x64\assembler-x64.cc
要修改的函数:
bool OSHasAVXSupport() {
 //此处省略一堆代码
 //在此处加上OS版本判断
 // Check whether OS claims to support AVX.
  uint64_t feature_mask = _xgetbv(_XCR_XFEATURE_ENABLED_MASK);
  return (feature_mask & 0x6) == 0x6;
}

}  // namespace

void CpuFeatures::ProbeImpl(bool cross_compile) {
  base::CPU cpu;
  //此处省略一堆代码
  if (cpu.has_avx() && FLAG_enable_avx && cpu.has_osxsave() &&
      OSHasAVXSupport()) {
    supported_ |= 1u << AVX;
  }

对于OpenSSL,由于OpenSSL是优先使用调用者传递过来的环境变量OPENSSL_ia32cap的,所以在不方便直接修改OpenSSL的实现代码时,可以自己先做OS检测,再做 cpuid、_xgetbv指令检测,然后生成合适的 OPENSSL_ia32cap 环境变量传递给OpenSSL库。当然,如果编译OpenSSL时限定了只使用386指令,就无需担心这个问题了。要正确限定OpenSSL只使用386指令,除了要在perl编译脚本命令行中加386参数外,还需要修改perl脚本生成的ms\nt.mak或者 ms\ntdll.mak 文件,在cl.exe的命令行中加上/arch:IA32参数才稳妥。

参考:
Enable Windows 7 Support for Intel AVX

Windows 7 and Windows Server 2008 R2 Service Pack 1 Bring AVX Support

OPENSSL_ia32cap

x86 Intrinsics List
https://msdn.microsoft.com/en-us/library/hh977023.aspx



声明:该文观点仅代表作者本人,转载请注明来自看雪