【问题标题】:Detect -xarch option in the preprocessor?在预处理器中检测 -xarch 选项?
【发布时间】:2016-11-14 01:52:55
【问题描述】:

我在 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 会设置一些位,这些位会导致程序无法在低级处理器(类似于“最低”平台)上执行。

我有两个问题。

  • 如何检测预处理器中的-xarch 选项?
  • 如何禁用-xarch 位以便程序可以在下级处理器上运行?

以下是使用-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

【问题讨论】:

    标签: macros c-preprocessor solaris cpu-architecture sunstudio


    【解决方案1】:

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

    来自Solaris Studio 12.4: C User's Guide

    1.3 二进制兼容性验证

    在 Solaris 系统上,从 Solaris Studio 11 开始,程序二进制文件 使用 Oracle Solaris Studio 编译器编译的标记为 体系结构硬件标志,指示由假定的指令集 编译的二进制文件。在运行时,这些标记标志被检查为 验证二进制文件是否可以在它尝试的硬件上运行 继续执行。

    运行不包含这些架构硬件标志的程序 在未启用相应功能的平台上或 指令集扩展可能导致分段错误或 在没有任何明确警告消息的情况下发生错误结果。

    还要注意功能和指令集的提及。根据我对 Solaris 文档的经验,这一点就足以警告说,可能还有更多内容需要

    我不知道有什么方法可以通过预处理器检测可用的指令集。您可以通过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 Studio 的 Oracle 论坛获得帮助

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

    指令集特定共享对象

    动态令牌$ISALIST 在运行时扩展以反映 在这个平台上可执行的本机指令集,如下所示 实用程序isalist(1)

    任何包含$ISALIST 标记的字符串名称都是有效的 复制成多个字符串。每个字符串被分配一个 可用的指令集。此令牌仅适用于 filterrunpath 规范。

    ...

    或者在一个MMX配置的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
    

    请注意库搜索如何从“最高”指令集特定库开始,然后移动到“较低”库。这允许定位多个特定于指令集的共享对象,从“最快的特定”到“最慢的通用”。 Solaris 上的libc.so 这样做是为了提供特定于平台的库函数版本,例如memcpy()

    【讨论】:

      【解决方案2】:

      第二个问题:

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

      请参阅链接器和库指南的第 7 章功能处理:

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

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

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

      【讨论】:

      • 谢谢约翰。我们在运行时保护我们的代码路径,因此我们通常只需要编译器来使 ASM 或内在函数可用。也就是说,为 SSE2 构建,我们可以处理其余的,例如优化的 SSE3 和 SSE4 实现。例如,请参阅 crc.cppblake2.cpp
      • 我假设您所指的守卫是 HasSSE2() 和 HasSSE4() 之类的函数。如果这些运行时检查硬件功能,那么您只需使用 Studio 添加选项 -Mmapfile 进行编译。 mapfile 将包含 CAPABILITY 条目,例如“HW -= SSE2”。屏蔽在提供回退功能的最低功能系统上运行所需的每个功能。
      • 谢谢约翰。关于 Solaris,我需要学习的东西太多了。多年来,这是一个红头发的继子,只获得了 C/C++ 实现。作为一项学习练习,我想让它成为一等公民,这样它就能获得所有的速度优势。 (Sun Studio 12.1 及更高版本,第一个使用 GCC 内联汇编)。
      • 您能分享您的电子邮件地址,以便我偶尔发送离线电子邮件吗?我的是 noloader,gmail 帐户(如果您不想这样做,我可以理解)。我想听听您对项目交叉点和GCC defines on Solaris 之类的东西的看法。添加 GCC 样式定义是我唯一能想到的。引用的文件是我们的test script from hell。这不是我们的build system
      • 再次感谢约翰。我应该早点回来。
      【解决方案3】:

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

      关于剥离 HWCAP,请参阅: https://docs.oracle.com/cd/E23823_01/html/816-5165/elfedit-1.html

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

      【讨论】:

      • 使用 -xarch=sse4.2 编译允许编译器使用 SSE4.2 内部函数。稍后剥离 SSE4.2 HWCAP 位可能会导致二进制文件使用这些内在函数,然后将无法在不支持 SSE4.2 的硬件上运行。您不会编译 64 位二进制文​​件,修改 ELF 标头以读取为 32 位二进制文​​件,并期望它在 32 位硬件上运行,对吗?
      • the topic-starter 提到他们有运行时保护,可以根据应用程序运行的硬件选择适当的实现。我知道 HWCAP 在一般情况下的用途。但这种情况很特殊。编译器可能会为代码的非内在部分发出非 SSE2 的内容。这需要检查。附言ELF32 和 ELF64 是 ELF 本身的属性(更准确地说是类型),而不是 HWCAP 部分的一些值。所以,你在这里有点过分了。
      • the topic-starter 提到他们有运行时保护,可以根据应用程序运行的硬件选择适当的实现。我不清楚运行 -提供的时间保护示例保证保护使用特定指令集(如 SSE2)的编译代码。从提供的示例中,似乎代码outside防护也可以使用此类指令集的选项进行编译,因此删除 HWCAP 位意味着未实现特定功能的硬件可能仍会遇到指令这需要它。
      猜你喜欢
      • 2014-07-27
      • 2016-09-11
      • 1970-01-01
      • 2020-06-08
      • 2015-10-17
      • 1970-01-01
      • 2010-11-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多