在预处理器中检测 -xarch 选项?

Detect -xarch option in the preprocessor?

我在 Solaris 11 上使用 Sun Studio 12.4 和 12.5。我们有一个源文件,它提供了 CRC32 的直接 C/C++ 实现,或使用英特尔内在函数的优化版本的 CRC32。在 运行 时,函数指针被填充为正确的实现。

在具有双 Xeon 的 x86 服务器上进行的测试产生了以下结果,因为我们正在根据编译器版本提供可用的代码路径。 SunCC 12.1 添加了对 SSE4 的支持(如果我正确解析了矩阵),因此我们尝试在 __SUNPRO_CC >= 0x5100.

时启用它
"crc.cpp", line 311: ube: error: _mm_crc32_u8 intrinsic requires at least -xarch=sse4_2.

SunCC 定义习惯的 GCC 定义,如 __SSE4_1____SSE4_2__。此外,SunCC 似乎没有像 MS VC++ 那样提供内部函数,其中编译器版本表示支持。

SunCC 似乎启用了基于 -xarch 选项的功能,但我不清楚如何在预处理器中检测它。此外,使用 -xarch 设置了一些导致程序无法在下层处理器上执行的位(类似于 "minimum" 平台)。

我有两个问题。


以下来自使用 -xarch=aes 编译的宏转储。请注意,没有任何内容表明可用的功能。

$ /opt/solarisstudio12.4/bin/CC -native -m64 -xarch=aes -xdumpmacros -E /dev/null 2>&1 | /usr/gnu/bin/sort --ignore-case

#1 "/dev/null"
#define __alignof__ __alignof
#define __amd64 1
#define __amd64__ 1
#define __ARRAYNEW 1
#define __asm asm
#define __asm__ asm
#define __attribute __attribute__
#define __builtin_constant_p __oracle_builtin_constant_p
#define __builtin_fpclassify __oracle_builtin_fpclassify
#define __builtin_huge_val __oracle_builtin_huge_val
#define __builtin_huge_valf __oracle_builtin_huge_valf
#define __builtin_huge_vall __oracle_builtin_huge_vall
#define __builtin_infinity __oracle_builtin_infinity
#define __builtin_isfinite __oracle_builtin_isfinite
#define __builtin_isgreater __oracle_builtin_isgreater
#define __builtin_isgreaterequal __oracle_builtin_isgreaterequal
#define __builtin_isinf __oracle_builtin_isinf
#define __builtin_isless __oracle_builtin_isless
#define __builtin_islessequal __oracle_builtin_islessequal
#define __builtin_islessgreater __oracle_builtin_islessgreater
#define __builtin_isnan __oracle_builtin_isnan
#define __builtin_isnormal __oracle_builtin_isnormal
#define __builtin_isunordered __oracle_builtin_isunordered
#define __builtin_nan __oracle_builtin_nan
#define __builtin_signbit __oracle_builtin_signbit
#define __BUILTIN_VA_STRUCT 1
#define __cplusplus 199711L
#define __DATE__ "Jul 11 2016"
#define __FILE__ 
#define __has_attribute(x) __oracle_has_attribute(x)
#define __has_nothrow_assign(x) __oracle_has_nothrow_assign(x)
#define __has_nothrow_constructor(x) __oracle_has_nothrow_constructor(x)
#define __has_nothrow_copy(x) __oracle_has_nothrow_copy(x)
#define __has_trivial_assign(x) __oracle_has_trivial_assign(x)
#define __has_trivial_constructor(x) __oracle_has_trivial_constructor(x)
#define __has_trivial_copy(x) __oracle_has_trivial_copy(x)
#define __has_trivial_destructor(x) __oracle_has_trivial_destructor(x)
#define __has_virtual_destructor(x) __oracle_has_virtual_destructor(x)
#define __is_abstract(x) __oracle_is_abstract(x)
#define __is_base_of(x,y) __oracle_is_base_of(x,y)
#define __is_class(x) __oracle_is_class(x)
#define __is_empty(x) __oracle_is_empty(x)
#define __is_enum(x) __oracle_is_enum(x)
#define __is_final(x) __oracle_is_final(x)
#define __is_literal_type(x) __oracle_is_literal_type(x)
#define __is_pod(x) __oracle_is_pod(x)
#define __is_polymorphic(x) __oracle_is_polymorphic(x)
#define __is_standard_layout(x) __oracle_is_standard_layout(x)
#define __is_trivial(x) __oracle_is_trivial(x)
#define __is_union(x) __oracle_is_union(x)
#define __LINE__ 
#define __LP64__ 1
#define __PRAGMA_REDEFINE_EXTNAME 1
#define __STDC__ 0
#define __sun 1
#define __SUN_PREFETCH 1
#define __SunOS 1
#define __SunOS_5_11 1
#define __SUNPRO_CC 0x5130
#define __SUNPRO_CC_COMPAT 5
#define __SVR4 1
#define __TIME__ "20:58:00"
#define __underlying_type(x) __oracle_underlying_type(x)
#define __unix 1
#define __volatile volatile
#define __volatile__ volatile
#define __x86_64 1
#define __x86_64__ 1
#define _BOOL 1
#define _LARGEFILE64_SOURCE 1
#define _LP64 1
#define _SIGNEDCHAR_ 1
#define _TEMPLATE_NO_EXTDEF 1
#define _WCHAR_T 
#define sun 1
#define unix 1

首先,您不想从已编译的二进制文件中删除指令集标志。当您使用 -xarch=NNNN 选项编译时,编译将使用这些指令。如果您尝试在 "lower" 处理器上 运行,但该处理器未实现您在 -xarch 参数中提供的体系结构中的指令,则您的二进制文件很可能无法工作。

来自Solaris Studio 12.4: C User's Guide

1.3 Binary Compatibility Verification

On Solaris systems, beginning with Solaris Studio 11, program binaries compiled with the Oracle Solaris Studio compilers are marked with architecture hardware flags indicating the instruction sets assumed by the compiled binary. At runtime, these marker flags are checked to verify that the binary can run on the hardware it is attempting to execute on.

Running programs that do not contain these architecture hardware flags on platforms that are not enabled with the appropriate features or instruction set extensions could result in segmentation faults or incorrect results occurring without any explicit warning messages.

另请注意功能和指令集的提及。以我使用 Solaris 文档的经验来看,这一点就足以警告人们

可能还有更多内容

我不知道有什么方法可以通过预处理器检测可用的指令集。您可以在 Solaris Studio 的 Oracle 论坛上获得帮助,网址为 https://community.oracle.com/community/server_%26_storage_systems/application_development_in_c__c%2B%2B__and_fortran/developer_studio_c_c%2B%2B_fortran_compilers

我怀疑即使在那里,您也找不到使用预处理器的方法。在 Solaris 上提供特定于平台和指令集的实现的通常方法是通过特定的共享对象。来自 Solaris Linker and Libraries Guide:

Instruction Set Specific Shared Objects

The dynamic token $ISALIST is expanded at runtime to reflect the native instruction sets executable on this platform, as displayed by the utility isalist(1).

Any string name that incorporates the $ISALIST token is effectively duplicated into multiple strings. Each string is assigned one of the available instruction sets. This token is only available for filter or runpath specifications.

...

Or an application with similar dependencies is executed on an MMX configured Pentium Pro:

$ ldd -ls prog
.....
  find object=libbar.so.1; required by ./libfoo.so.1
    search path=/opt/ISV/lib/$ISALIST  (RPATH from file ./libfoo.so.1)
      trying path=/opt/ISV/lib/pentium_pro+mmx/libbar.so.1
      trying path=/opt/ISV/lib/pentium_pro/libbar.so.1
      trying path=/opt/ISV/lib/pentium+mmx/libbar.so.1
      trying path=/opt/ISV/lib/pentium/libbar.so.1
      trying path=/opt/ISV/lib/i486/libbar.so.1
      trying path=/opt/ISV/lib/i386/libbar.so.1
      trying path=/opt/ISV/lib/i86/libbar.so.1

注意库搜索如何从 "highest" 指令集特定库开始,然后移动到 "lower" 库。这允许定位多个指令集特定的共享对象,从 "fastest specific" 到 "slowest generic"。 libc.so 在 Solaris 上这样做是为了提供特定于平台的库函数版本,例如 memcpy().

第二个问题:

如何禁用 -xarch 位以便程序可以 运行 在下层处理器上?

参见链接器和库指南的第 7 章能力处理:

https://docs.oracle.com/cd/E53394_01/html/E54813/index.html

这向您展示了如何交付同一功能的多个实例 用能力位标记。 运行时间链接器将 根据报告的功能解决使用哪个功能。

如果您真的想自己管理功能位, 请参阅第 9 章映射文件,特别是 CAPABILITY 指令部分。 这显示了如何从生成的对象中删除功能。

我相信对于你的特定情况(它的第二部分),唯一简单的方法是做你想做的事:使用显式设置“-xarch=sse4.2”进行编译(这允许编译器扩展SSE4.2 内在函数),然后将 HWCAP 位剥离到最小架构(这使您的程序可以在 SSE4.2 之前的硬件上运行)。

有关剥离 HWCAP 的信息,请参见: https://docs.oracle.com/cd/E23823_01/html/816-5165/elfedit-1.html

(示例 2 删除硬件功能位)