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