Clang 有类似#pragma GCC 的目标吗?
Does Clang have something like #pragma GCC target?
我编写了一些代码,这些代码在当前 CPU 上可用时使用了 AVX 内在函数。在 GCC 和 Clang 中,与 Visual C++ 不同,为了使用内部函数,您必须在命令行上启用它们。
GCC 和 Clang 的问题在于,当您启用这些选项时,您就让编译器可以自由支配源文件中的任何地方使用这些指令。当您的头文件包含内联函数或模板函数时,这是非常糟糕的,因为编译器将使用 AVX 指令生成这些函数。
链接时,重复的函数将被丢弃。但是,由于有些源文件是用-mavx
编译的,有些不是,所以inline/template函数的各种编译会有所不同。如果你运气不好,链接器会随机选择有 AVX 指令的版本,导致程序在没有 AVX 的系统上 运行 时崩溃。
GCC 用 #pragma GCC target
解决了这个问题。可以关闭头文件的特殊指令,生成的代码不会使用AVX:
#pragma GCC push_options
#pragma GCC target("no-avx")
#include "MyHeader.h"
#pragma GCC pop_options
Clang有这样的东西吗?它似乎忽略了这些选项并生成了 AVX 代码。
您可能应该使用 static inline
而不是 inline
,因此使用 -mavx
编译的函数版本将仅供来自该翻译单元的调用者使用。
链接器仍将合并实际的重复项,而不是仅按名称选择一个非内联定义。
这还有一个好处,即编译器不会浪费时间为它决定内联到该翻译单元中的每个调用者的函数发出一个独立的定义。
如果您习惯了 gcc/clang 方法并为其设计了代码,那么它是有意义的。请注意,如果您正在编译使用 AVX 的函数,则 MSVC 需要启用 AVX。否则它将混合 VEX 和非 VEX 编码,导致严重的惩罚,而不是在 _mm256_add_ps
循环末尾的水平添加中对诸如 128 位 _mm_add_ps
之类的东西使用 VEX 编码。
所以你基本上遇到了与 MSVC 相同的问题,即编译 _mm_whatever
将生成仅 AVX 的机器代码。
相当于 GCC push_options / GCC target / GCC pop_options
的 Clang 是 clang attribute push / clang attribute pop
pragmas along with the target
attribute:
#pragma clang attribute push (__attribute__((target("pclmul,sse4.1,ssse3"))), apply_to=function)
// ...
#pragma clang attribute pop
这相当于:
#pragma GCC push_options
#pragma GCC target("pclmul", "sse4.1", "ssse3")
// ...
#pragma GCC pop_options
请注意,在 GCC target
pragma 使用 comma-delimited 目标选项列表的情况下,clang target
属性在内部使用单个字符串 comma-delimited。
Clang 支持负目标选项(例如"no-avx"
),但我更喜欢使用正选项来添加到命令行选项选择的功能集中。
我编写了一些代码,这些代码在当前 CPU 上可用时使用了 AVX 内在函数。在 GCC 和 Clang 中,与 Visual C++ 不同,为了使用内部函数,您必须在命令行上启用它们。
GCC 和 Clang 的问题在于,当您启用这些选项时,您就让编译器可以自由支配源文件中的任何地方使用这些指令。当您的头文件包含内联函数或模板函数时,这是非常糟糕的,因为编译器将使用 AVX 指令生成这些函数。
链接时,重复的函数将被丢弃。但是,由于有些源文件是用-mavx
编译的,有些不是,所以inline/template函数的各种编译会有所不同。如果你运气不好,链接器会随机选择有 AVX 指令的版本,导致程序在没有 AVX 的系统上 运行 时崩溃。
GCC 用 #pragma GCC target
解决了这个问题。可以关闭头文件的特殊指令,生成的代码不会使用AVX:
#pragma GCC push_options
#pragma GCC target("no-avx")
#include "MyHeader.h"
#pragma GCC pop_options
Clang有这样的东西吗?它似乎忽略了这些选项并生成了 AVX 代码。
您可能应该使用 static inline
而不是 inline
,因此使用 -mavx
编译的函数版本将仅供来自该翻译单元的调用者使用。
链接器仍将合并实际的重复项,而不是仅按名称选择一个非内联定义。
这还有一个好处,即编译器不会浪费时间为它决定内联到该翻译单元中的每个调用者的函数发出一个独立的定义。
如果您习惯了 gcc/clang 方法并为其设计了代码,那么它是有意义的。请注意,如果您正在编译使用 AVX 的函数,则 MSVC 需要启用 AVX。否则它将混合 VEX 和非 VEX 编码,导致严重的惩罚,而不是在 _mm256_add_ps
循环末尾的水平添加中对诸如 128 位 _mm_add_ps
之类的东西使用 VEX 编码。
所以你基本上遇到了与 MSVC 相同的问题,即编译 _mm_whatever
将生成仅 AVX 的机器代码。
相当于 GCC push_options / GCC target / GCC pop_options
的 Clang 是 clang attribute push / clang attribute pop
pragmas along with the target
attribute:
#pragma clang attribute push (__attribute__((target("pclmul,sse4.1,ssse3"))), apply_to=function)
// ...
#pragma clang attribute pop
这相当于:
#pragma GCC push_options
#pragma GCC target("pclmul", "sse4.1", "ssse3")
// ...
#pragma GCC pop_options
请注意,在 GCC target
pragma 使用 comma-delimited 目标选项列表的情况下,clang target
属性在内部使用单个字符串 comma-delimited。
Clang 支持负目标选项(例如"no-avx"
),但我更喜欢使用正选项来添加到命令行选项选择的功能集中。