首页 文章

使用SIGILL与CPU探测进行AVX功能检测

提问于
浏览
3

我正在尝试确定一种检测英特尔和AMD处理器上AVX和AVX2可用性的有效方法 . 在阅读英特尔软件开发人员手册第一卷(使用XSAVE功能集管理状态,第310页)时,我更加惊讶地发现它更接近SSE和XSAVE .

英特尔在Is AVX enabled?发布了一些用于检测AVX可用性的代码 . 代码如下所示,并且不会太痛苦 . 问题是,Visual Studio是一个痛点,因为我们需要将代码从C / C文件中移出到X64的ASM文件中 .

其他人似乎采用 SIGILL 方法来检测AVX可用性 . 或者他们无意中使用了 SIGILL 方法 . 例如,参见SIGILL on AVX instruction .

我的问题是,使用 SIGILL 方法检测AVX可用性是否安全?这里,"safe"表示当CPU和OS支持AVX时,AVX指令不会生成 SIGILL ;否则它将生成一个 SIGILL .


下面的代码适用于32位机器,它来自英特尔博客Is AVX enabled?令我担心的是操纵控制寄存器 . 读取和写入某些X86和ARM控制寄存器有时需要超级用户/管理员权限 . 这是我更喜欢 SIGILL (并避免控制寄存器)的原因 .

; int isAvxSupported();
isAvxSupported proc

  xor eax, eax
  cpuid
  cmp eax, 1           ; does CPUID support eax = 1?
  jb not_supported

  mov eax, 1
  cpuid
  and ecx, 018000000h  ; check 27 bit (OS uses XSAVE/XRSTOR)
  cmp ecx, 018000000h  ; and 28       (AVX supported by CPU)
  jne not_supported

  xor ecx, ecx         ; XFEATURE_ENABLED_MASK/XCR0 register number = 0
  xgetbv               ; XFEATURE_ENABLED_MASK register is in edx:eax
  and eax, 110b
  cmp eax, 110b        ; check the AVX registers restore at context switch
  jne not_supported

supported:
  mov eax, 1
  ret

not_supported:
  xor eax, eax
  ret

isAvxSupported endp

1 回答

  • 5

    首先是一点理论 .

    要使用AVX指令集,必须满足以下几个条件:

    • CR4.OSXSAVE[bit 18] 必须是1 .
      该标志由OS设置,以通知处理器它支持 xsave 扩展 .
      xsave 扩展是保存AVX状态的唯一方法( fxsave 不保存 ymm 寄存器),因此操作系统必须支持它们 .

    • XCR0.SSE[bit 1]XCR0.AVX[bit 2] 必须为1 .
      这些标志由OS设置,以通知处理器它支持保存和恢复SSE和AVX状态(通过 xsave ) .

    • CPUID.1:ECX.AVX[bit 28] = 1
      当然,处理器必须首先支持AVX扩展 .

    所有这些寄存器都是用户模式可读的,但对于 CR4 .
    幸运的是,位 CR4.OSXSAVE 反映在 CPUID.1:ECX.OSXSAVE[bit 27] 中,因此所有信息都是用户模式可访问的 . 不涉及特权指示 .

    要使用AVX扩展,必须同时支持硬件( CPUID.1:ECX.AVXCPUID.1:ECX.XSAVE )和OS( CPUID.1:ECX.OSXSAVEXCR0.SSEXCR0.AVX ) .
    由于操作系统仅在存在硬件支持的情况下表示支持 xsave ,因此测试前者就足够了 .
    对于AVX扩展,仍建议测试 CPUID.1:ECX.AVX ,因为即使不支持AVX,操作系统也可能设置 XCR0.AVX .

    这导致英特尔官方强烈推荐的算法:

    Official Intel algorithm for AVX detection. Section 14.3 of manual 1

    这与您发布的完全相同 .


    捕获异常以检测对AVX扩展的支持也将授予您可以保证捕获的异常是#UD .
    例如,通过执行 vzeroall ,唯一可能的例外是#UD和#NM .
    第一个只在以下情况下抛出:

    如果XCR0 [2:1]≠'11b' . 如果CR4.OSXSAVE [bit 18] = 0 . 如果CPUID.01H.ECX.AVX [bit 28] = 0 . 如果VEX.vvvv≠1111B .

    因此,除非你有一个破坏的汇编程序/编译器,否则它完全等同于开头所述的条件 .

    后者被抛出作为保存AVX状态的优化,因此,它不会被OS暴露给用户模式程序 .

    从而在 vzeroall 或类似的地方捕捉 SIGILL 也可以 .

相关问题