開源庫中檢測當前系統是否支援AVX指令集的一個坑

blowfish發表於2017-12-08
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引數才穩妥。

參考:

x86 Intrinsics List


相關文章