我需要为不同的指令集创建多个可执行文件吗?

6

假设我有一个执行AES操作的程序。

一些先进的CPU具有AES-NI指令集,而其他CPU则没有。

我必须将我的程序编译为两个可执行文件吗:A_with_aes_ni.exe 和 B_without_aes_ni.exe?


另一种选择是将加密部分分离到一个动态/共享库中,然后有两个版本。 - Cheers and hth. - Alf
你可以在同一个可执行文件中编写两个函数,一个使用AES指令,另一个在软件中执行AES操作,然后根据CPUID的输出在运行时选择它们之间的切换。 - Andrew Tomazos
@AndrewTomazos,您的意思是我必须在我的C源文件中嵌入汇编代码吗? - xmllmx
GCC支持函数多版本化,这似乎是你想要的。 - Jesper Juhl
2个回答

7

你所需要的是一个CPU调度器。Agner Fog在他的《优化C++手册》第13章“为不同指令集制作多个版本的关键代码”中有10页的文本可供参考Optimizing C++ manual。他讨论了如何在GCC和ICC中实现这一点。

你只需要一个可执行文件,但需要使用开启和关闭AES功能来编译两个不同的目标文件。然后根据可用的指令集,调度器确定选择哪条代码路径。

我曾尝试在MSVC2010 cpu dispatcher for visual studio for AVX and SSE中实现这一点,但没有成功。不过我觉得现在可能可以实现。

编辑: 在Agner Fog的vectorclass中,他有一个文件dispatch_example.cppinstrset_detech.cpp,其中应该包含大部分你需要制作调度程序所需的内容。你仍然需要找出如何检测CPU是否支持AES。你需要增强intrset_detect.cpp文件。根据wikipedia,当你读取寄存器ECX中的CPUID位23时,如果CPU支持AES,则会被置位。维基百科还有代码示例来读取CPUID(除了instrset_detech.cpp之外,另一个很好的示例可以在https://github.com/Mysticial/Flops的cpuid.c文件中找到)


2
在Solaris中,我们一种方法是使用硬件能力库,这些库由链接器在运行时动态加载。 另一个选择是首先加载非法指令的陷阱处理程序,然后测试所需的机器语言指令。如果触发了陷阱,那么您就知道不能使用优化版本,必须加载非优化(或较少优化)版本。
虽然我喜欢上面安德鲁的建议,但我认为更安全的方法是测试所需的特定指令。这样,您就不必为更新的CPUID输出不断更新应用程序。
编辑以添加: 我意识到我应该提供一个例子。对于x64平台上的Solaris libc,我们提供了库的硬件优化版本-其中三个是32位,一个是64位。我们可以通过在感兴趣的文件上运行elfdump -H来查看差异。
s11u1:jmcp $ elfdump -H /usr/lib/libc/libc_hwcap1.so.1 

Capabilities Section:  .SUNW_cap

 Object Capabilities:
     index  tag               value
       [0]  CA_SUNW_HW_1     0x86d  [ SSE MMX CMOV SEP CX8 FPU ]

 Symbol Capabilities:
     index  tag               value
       [2]  CA_SUNW_ID       hrt
       [3]  CA_SUNW_HW_1     0x40002  [ TSCP TSC ]

  Symbols:
     index    value      size      type bind oth ver shndx          name
       [1]  0x000f306c 0x00000225  FUNC LOCL  D    0 .text          gettimeofday%hrt
       [2]  0x000f2efc 0x00000165  FUNC LOCL  D    0 .text          gethrtime%hrt

Capabilities Chain Section:  .SUNW_capchain

 Capabilities family: gettimeofday
  chainndx  symndx      name
         1  [702]       gettimeofday
         2  [1]         gettimeofday%hrt

 Capabilities family: gethrtime
  chainndx  symndx      name
         4  [1939]      gethrtime
         5  [2]         gethrtime%hrt

s11u1:jmcp $ elfdump -H /usr/lib/libc/libc_hwcap2.so.1 

Capabilities Section:  .SUNW_cap

 Object Capabilities:
     index  tag               value
       [0]  CA_SUNW_HW_1     0x1875  [ SSE2 SSE MMX CMOV AMD_SYSC CX8 FPU ]

 Symbol Capabilities:
     index  tag               value
       [2]  CA_SUNW_ID       hrt
       [3]  CA_SUNW_HW_1     0x40002  [ TSCP TSC ]

  Symbols:
     index    value      size      type bind oth ver shndx          name
       [1]  0x000f253c 0x00000225  FUNC LOCL  D    0 .text              gettimeofday%hrt
       [2]  0x000f23cc 0x00000165  FUNC LOCL  D    0 .text          gethrtime%hrt

Capabilities Chain Section:  .SUNW_capchain

 Capabilities family: gettimeofday
  chainndx  symndx      name
         1  [702]       gettimeofday
         2  [1]         gettimeofday%hrt

 Capabilities family: gethrtime
  chainndx  symndx      name
         4  [1939]      gethrtime
         5  [2]         gethrtime%hrt

猜一下上面的哪个是针对AMD系统的,哪个是针对Intel系统的?

Solaris链接器具有智能功能,在调用进程的_init()之前运行正确的hwcap库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接