Clang 语言扩展

简介

本文档介绍了 Clang 提供的语言扩展。除了这里列出的语言扩展之外,Clang 旨在支持广泛的 GCC 扩展。有关这些扩展的更多信息,请参阅 GCC 手册

特性检查宏

语言扩展可能非常有用,但前提是您知道可以依赖它们。为了允许对功能进行细粒度检查,我们支持三个内置函数式宏。这样您就可以在代码中直接测试特性,而不必使用 autoconf 或脆弱的“编译器版本检查”。

__has_builtin

此函数式宏采用一个标识符参数,该参数是内置函数的名称、内置伪函数的名称(接受一个或多个类型参数)或内置模板的名称。如果支持内置函数,则它评估为 1;否则评估为 0。它可以像这样使用

#ifndef __has_builtin         // Optional of course.
  #define __has_builtin(x) 0  // Compatibility with non-clang compilers.
#endif

...
#if __has_builtin(__builtin_trap)
  __builtin_trap();
#else
  abort();
#endif
...

注意

在 Clang 10 之前,__has_builtin 无法用于检测大多数内置伪函数。

__has_builtin 不应用于检测对内置宏的支持;请改用 #ifdef

__has_constexpr_builtin

此函数式宏采用一个标识符参数,该参数是内置函数的名称、内置伪函数的名称(接受一个或多个类型参数)或内置模板的名称。如果支持内置函数并且可以进行常量评估,则它评估为 1;否则评估为 0。它可以用于编写条件 constexpr 代码,如下所示

#ifndef __has_constexpr_builtin         // Optional of course.
  #define __has_constexpr_builtin(x) 0  // Compatibility with non-clang compilers.
#endif

...
#if __has_constexpr_builtin(__builtin_fmax)
  constexpr
#endif
  double money_fee(double amount) {
      return __builtin_fmax(amount * 0.03, 10.0);
  }
...

例如,libcxx 在 <cmath> 头文件中使用 __has_constexpr_builtin,以便在 Clang 中支持相应内置函数(例如,std::fmax 调用 __builtin_fmax)的常量评估时,有条件地使函数成为 constexpr。

__has_feature__has_extension

这些函数式宏采用一个标识符参数,该参数是特性的名称。 __has_feature 如果特性在当前语言标准中同时受 Clang 支持和标准化,则评估为 1;否则评估为 0(但请参阅 下面),而 __has_extension 如果特性在当前语言中受 Clang 支持(作为语言扩展或标准语言特性),则评估为 1;否则评估为 0。它们可以像这样使用

#ifndef __has_feature         // Optional of course.
  #define __has_feature(x) 0  // Compatibility with non-clang compilers.
#endif
#ifndef __has_extension
  #define __has_extension __has_feature // Compatibility with pre-3.0 compilers.
#endif

...
#if __has_feature(cxx_rvalue_references)
// This code will only be compiled with the -std=c++11 and -std=gnu++11
// options, because rvalue references are only standardized in C++11.
#endif

#if __has_extension(cxx_rvalue_references)
// This code will be compiled with the -std=c++11, -std=gnu++11, -std=c++98
// and -std=gnu++98 options, because rvalue references are supported as a
// language extension in C++98.
#endif

为了向后兼容性,__has_feature 也可以用于测试对非标准化特性的支持,即没有 c_cxx_objc_ 前缀的特性。

__has_feature 的另一个用途是检查与语言标准无关的编译器特性,例如 AddressSanitizer

如果给出了 -pedantic-errors 选项,则 __has_extension 等同于 __has_feature

特性标记将在下面与语言特性一起描述。

特性名称或扩展名称也可以用一个前导和后缀的 __(双下划线)来指定,以避免与同名的宏发生冲突。例如,可以使用 __cxx_rvalue_references__ 代替 cxx_rvalue_references

__has_cpp_attribute

此函数式宏默认情况下在 C++20 中可用,并且在更早的语言标准中作为扩展提供。它采用一个参数,该参数是双方括号样式属性的名称。参数可以是单个标识符或作用域标识符。如果支持属性,则返回非零值。如果属性是基于标准的属性,则此宏将基于该属性被投票进入工作草案的年份和月份返回非零值。有关基于标准的属性返回的值列表,请参阅 WG21 SD-6。如果当前编译目标不支持属性,则此宏评估为 0。它可以像这样使用

#ifndef __has_cpp_attribute         // For backwards compatibility
  #define __has_cpp_attribute(x) 0
#endif

...
#if __has_cpp_attribute(clang::fallthrough)
#define FALLTHROUGH [[clang::fallthrough]]
#else
#define FALLTHROUGH
#endif
...

属性作用域标记 clang_Clang 是可互换的,属性作用域标记 gnu__gnu__ 也是如此。任何这些命名空间中的属性标记都可以用一个前导和后缀的 __(双下划线)来指定,以避免与同名的宏发生冲突。例如,可以使用 gnu::__const__ 代替 gnu::const

__has_c_attribute

此函数式宏采用一个参数,该参数是 C 模式下使用双方括号语法公开的属性的名称。参数可以是单个标识符或作用域标识符。如果支持属性,则返回非零值。如果当前编译目标不支持属性,则此宏评估为 0。它可以像这样使用

#ifndef __has_c_attribute         // Optional of course.
  #define __has_c_attribute(x) 0  // Compatibility with non-clang compilers.
#endif

...
#if __has_c_attribute(fallthrough)
  #define FALLTHROUGH [[fallthrough]]
#else
  #define FALLTHROUGH
#endif
...

属性作用域标记 clang_Clang 是可互换的,属性作用域标记 gnu__gnu__ 也是如此。任何这些命名空间中的属性标记都可以用一个前导和后缀的 __(双下划线)来指定,以避免与同名的宏发生冲突。例如,可以使用 gnu::__const__ 代替 gnu::const

__has_attribute

此函数式宏采用一个标识符参数,该参数是 GNU 样式属性的名称。如果当前编译目标支持该属性,则它评估为 1;否则评估为 0。它可以像这样使用

#ifndef __has_attribute         // Optional of course.
  #define __has_attribute(x) 0  // Compatibility with non-clang compilers.
#endif

...
#if __has_attribute(always_inline)
#define ALWAYS_INLINE __attribute__((always_inline))
#else
#define ALWAYS_INLINE
#endif
...

属性名称也可以用一个前导和后缀的 __(双下划线)来指定,以避免与同名的宏发生冲突。例如,可以使用 __always_inline__ 代替 always_inline

__has_declspec_attribute

此函数式宏采用一个标识符参数,该参数是作为 Microsoft 样式 __declspec 属性实现的属性的名称。如果当前编译目标支持该属性,则它评估为 1;否则评估为 0。它可以像这样使用

#ifndef __has_declspec_attribute         // Optional of course.
  #define __has_declspec_attribute(x) 0  // Compatibility with non-clang compilers.
#endif

...
#if __has_declspec_attribute(dllexport)
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
...

属性名称也可以用一个前导和后缀的 __(双下划线)来指定,以避免与同名的宏发生冲突。例如,可以使用 __dllexport__ 代替 dllexport

__is_identifier

此函数式宏采用一个标识符参数,该参数可以是保留字或普通标识符。如果参数仅仅是普通标识符,而不是保留字,则它评估为 1,这意味着它可以用作用户定义的函数或变量的名称。否则它评估为 0。它可以像这样使用

...
#ifdef __is_identifier          // Compatibility with non-clang compilers.
  #if __is_identifier(__wchar_t)
    typedef wchar_t __wchar_t;
  #endif
#endif

__wchar_t WideCharacter;
...

包含文件检查宏

并非所有开发系统都拥有相同的包含文件。 __has_include__has_include_next 宏允许您在执行可能失败的 #include 指令之前检查包含文件是否存在。 包含文件检查宏必须用作 #if#elif 预处理指令中的表达式。

__has_include

此类似函数的宏接受一个字符串参数,该参数是包含文件的名称。 如果使用包含路径可以找到该文件,则它评估为 1;否则为 0。

// Note the two possible file name string formats.
#if __has_include("myinclude.h") && __has_include(<stdint.h>)
# include "myinclude.h"
#endif

要测试此功能,请使用 #if defined(__has_include)

// To avoid problem with non-clang compilers not having this macro.
#if defined(__has_include)
#if __has_include("myinclude.h")
# include "myinclude.h"
#endif
#endif

__has_include_next

此类似函数的宏接受一个字符串参数,该参数是包含文件的名称。 它类似于 __has_include,但它查找包含路径中找到的给定文件的第二个实例。 如果使用包含路径可以找到该文件的第二个实例,则它评估为 1;否则为 0。

// Note the two possible file name string formats.
#if __has_include_next("myinclude.h") && __has_include_next(<stdint.h>)
# include_next "myinclude.h"
#endif

// To avoid problem with non-clang compilers not having this macro.
#if defined(__has_include_next)
#if __has_include_next("myinclude.h")
# include_next "myinclude.h"
#endif
#endif

请注意,__has_include_next 与 GNU 扩展 #include_next 指令一样,旨在仅在标头中使用,如果在顶层编译文件中使用,则会发出警告。 如果在文件参数中使用绝对路径,也会发出警告。

__has_warning

此类似函数的宏接受一个字符串文字,该字符串文字表示警告的命令行选项,如果该选项是有效的警告选项,则返回 true。

#if __has_warning("-Wformat")
...
#endif

内置宏

__BASE_FILE__

定义为包含传递给 Clang 的主输入文件名称的字符串。

__FILE_NAME__

Clang 特定的扩展,其功能类似于 __FILE__,但仅渲染最后一个路径组件(文件名),而不是对该文件的调用依赖的完整路径。

__COUNTER__

定义为一个整数值,该值从零开始,每次扩展 __COUNTER__ 宏时都会递增。

__INCLUDE_LEVEL__

定义为一个整数值,表示当前正在翻译的文件的包含深度。 对于主文件,此值为零。

__TIMESTAMP__

定义为当前源文件的最后修改日期和时间。

__clang__

使用 Clang 编译时定义。

__clang_major__

定义为 Clang 的主要市场版本号(例如,2.0.1 中的 2)。 请注意,营销版本号不应用于检查语言特性,因为不同的供应商使用不同的编号方案。 相反,请使用 特性检查宏

__clang_minor__

定义为 Clang 的次要版本号(例如,2.0.1 中的 0)。 请注意,营销版本号不应用于检查语言特性,因为不同的供应商使用不同的编号方案。 相反,请使用 特性检查宏

__clang_patchlevel__

定义为 Clang 的营销补丁级别(例如,2.0.1 中的 1)。

__clang_version__

定义为一个字符串,该字符串捕获 Clang 营销版本,包括 Subversion 标记或修订号,例如“1.5 (trunk 102332)”。

__clang_literal_encoding__

定义为一个窄字符串文字,表示当前窄字符串文字的编码,例如 "hello"。 此宏通常扩展为“UTF-8”(但如果 -fexec-charset="Encoding-Name" 选项在将来实现,则可能会更改。)

__clang_wide_literal_encoding__

定义为一个窄字符串文字,表示当前宽字符串文字的编码,例如 L"hello"。 此宏通常扩展为“UTF-16”或“UTF-32”(但如果 -fwide-exec-charset="Encoding-Name" 选项在将来实现,则可能会更改。)

实现定义的关键字

__datasizeof

__datasizeof 的行为类似于 sizeof,但它返回类型的大小,忽略尾部填充。

向量和扩展向量

支持 GCC、OpenCL、AltiVec、NEON 和 SVE 向量扩展。

OpenCL 向量类型使用 ext_vector_type 属性创建。 它支持 V.xyzw 语法和其他在 OpenCL 中看到的小细节。 例如

typedef float float4 __attribute__((ext_vector_type(4)));
typedef float float2 __attribute__((ext_vector_type(2)));

float4 foo(float2 a, float2 b) {
  float4 c;
  c.xz = a;
  c.yw = b;
  return c;
}

使用 __has_attribute(ext_vector_type) 查询此功能。

向 clang 提供 -maltivec 选项将启用对 AltiVec 向量语法和函数的支持。 例如

vector float foo(vector int a) {
  vector int b;
  b = vec_add(a, a) + a;
  return (vector float)b;
}

NEON 向量类型使用 neon_vector_typeneon_polyvector_type 属性创建。 例如

typedef __attribute__((neon_vector_type(8))) int8_t int8x8_t;
typedef __attribute__((neon_polyvector_type(16))) poly8_t poly8x16_t;

int8x8_t foo(int8x8_t a) {
  int8x8_t v;
  v = a;
  return v;
}

GCC 向量类型使用 vector_size(N) 属性创建。 参数 N 指定为此类型分配的对象的字节数。 大小必须是向量元素类型的尺寸的倍数。 例如

// OK: This declares a vector type with four 'int' elements
typedef int int4 __attribute__((vector_size(4 * sizeof(int))));

// ERROR: '11' is not a multiple of sizeof(int)
typedef int int_impossible __attribute__((vector_size(11)));

int4 foo(int4 a) {
  int4 v;
  v = a;
  return v;
}

布尔向量

Clang 还支持在 C 和 C++ 中具有布尔元素类型的 ext_vector_type 属性。 例如

// legal for Clang, error for GCC:
typedef bool bool4 __attribute__((ext_vector_type(4)));
// Objects of bool4 type hold 8 bits, sizeof(bool4) == 1

bool4 foo(bool4 a) {
  bool4 v;
  v = a;
  return v;
}

布尔向量是 ext 向量类型的 Clang 扩展。 布尔向量旨在映射到向量掩码寄存器(虽然不保证)。 布尔向量类型的 size 参数是向量中的位数。 布尔向量是密集的,布尔向量中的每个位都是一个向量元素。

布尔向量的语义借鉴了 C 位字段,但有以下区别

  • 不同的布尔向量始终是不同的内存对象(没有打包)。

  • 布尔向量上只允许使用 ?:!~|&^ 和比较运算符。

  • 将标量布尔值强制转换为布尔向量类型意味着将标量值广播到所有通道(与一般的 ext_vector_type 相同)。

  • 无法访问或调整布尔向量的元素(与一般的 ext_vector_type 不同)。

大小和对齐都是将位数向上舍入到下一个 2 的幂,但对齐最多是目标的最大向量对齐。

向量文字

向量文字可用于从一组标量或向量创建向量。 可以使用圆括号或大括号形式。 在圆括号形式中,指定的文字值的个数必须为 1(即引用标量值),或者必须与正在创建的向量类型的尺寸匹配。 如果指定单个标量文字值,则标量文字值将复制到向量类型的所有组件。 在方括号形式中,可以指定任意数量的文字。 例如

typedef int v4si __attribute__((__vector_size__(16)));
typedef float float4 __attribute__((ext_vector_type(4)));
typedef float float2 __attribute__((ext_vector_type(2)));

v4si vsi = (v4si){1, 2, 3, 4};
float4 vf = (float4)(1.0f, 2.0f, 3.0f, 4.0f);
vector int vi1 = (vector int)(1);    // vi1 will be (1, 1, 1, 1).
vector int vi2 = (vector int){1};    // vi2 will be (1, 0, 0, 0).
vector int vi3 = (vector int)(1, 2); // error
vector int vi4 = (vector int){1, 2}; // vi4 will be (1, 2, 0, 0).
vector int vi5 = (vector int)(1, 2, 3, 4);
float4 vf = (float4)((float2)(1.0f, 2.0f), (float2)(3.0f, 4.0f));

向量操作

下表显示了每个向量扩展对每个操作的支持情况。 虚线表示根据相应的规范,操作不可接受。

运算符

OpenCL

AltiVec

GCC

NEON

SVE

[]

一元运算符 +、–

++、–

+、–、*、/、%

按位运算符 &、|、^、~

>>,<<

!, &&, ||

==, !=, >, <, >=, <=

=

?: [1]

sizeof

[2]

C 样式强制转换

reinterpret_cast

static_cast

const_cast

地址 &v[i]

[3]

另请参阅 __builtin_shufflevector__builtin_convertvector

向量内置函数

注意:向量内置函数的实现尚未完善,处于开发阶段。

除了上述运算符之外,Clang 还提供一组内置函数来对某些标量和向量类型执行其他操作。

T 为以下类型之一

  • 整数类型(如 C23 6.2.5p22 所述),但不包括枚举类型和 bool

  • 标准浮点类型 float 或 double

  • 半精度浮点类型(如果目标支持)

  • 向量类型。

对于标量类型,请考虑将操作应用于具有单个元素的向量。

向量大小 要确定向量中的元素数量,请使用 __builtin_vectorelements()。 对于固定大小的向量(例如,通过 __attribute__((vector_size(N))) 或 ARM NEON 的向量类型(例如,uint16x8_t)定义的),这将在编译时返回元素的常数数量。 对于可伸缩的向量(例如,SVE 或 RISC-V V),元素数量在编译时未知,并在运行时确定。 此内置函数可用于(例如)在与向量类型无关的循环中递增循环计数器。

逐元素内置函数

每个内置函数返回一个向量,该向量等效于将指定的操作逐元素应用于输入。

除非另有说明,否则 operation(±0) = ±0,operation(±infinity) = ±infinity

名称

操作

支持的元素类型

T __builtin_elementwise_abs(T x)

返回数字 x 的绝对值;最负整数的绝对值仍然是最负整数

有符号整数和浮点类型

T __builtin_elementwise_fma(T x, T y, T z)

融合乘加,(x * y) + z。

浮点类型

T __builtin_elementwise_ceil(T x)

返回大于或等于 x 的最小整数值

浮点类型

T __builtin_elementwise_sin(T x)

返回 x 的正弦值,其中 x 被解释为以弧度为单位的角度

浮点类型

T __builtin_elementwise_cos(T x)

返回 x 的余弦值,其中 x 被解释为以弧度为单位的角度

浮点类型

T __builtin_elementwise_tan(T x)

返回 x 的正切值,其中 x 被解释为以弧度为单位的角度

浮点类型

T __builtin_elementwise_asin(T x)

返回 x 的反正弦值,其中结果被解释为以弧度为单位的角度

浮点类型

T __builtin_elementwise_acos(T x)

返回 x 的反余弦值,其中结果被解释为以弧度为单位的角度

浮点类型

T __builtin_elementwise_atan(T x)

返回 x 的反正切值,其中结果被解释为以弧度为单位的角度

浮点类型

T __builtin_elementwise_atan2(T y, T x)

返回 y/x 的反正切值

浮点类型

T __builtin_elementwise_sinh(T x)

返回以弧度为单位的角度 x 的双曲正弦值

浮点类型

T __builtin_elementwise_cosh(T x)

返回以弧度为单位的角度 x 的双曲余弦值

浮点类型

T __builtin_elementwise_tanh(T x)

返回以弧度为单位的角度 x 的双曲正切值

浮点类型

T __builtin_elementwise_floor(T x)

返回小于或等于 x 的最大整数值

浮点类型

T __builtin_elementwise_log(T x)

返回 x 的自然对数

浮点类型

T __builtin_elementwise_log2(T x)

返回 x 的以 2 为底的对数

浮点类型

T __builtin_elementwise_log10(T x)

返回 x 的以 10 为底的对数

浮点类型

T __builtin_elementwise_popcount(T x)

返回 x 中 1 位的数量

整数类型

T __builtin_elementwise_pow(T x, T y)

返回 x 的 y 次方

浮点类型

T __builtin_elementwise_bitreverse(T x)

返回对 x 的位进行反转后表示的整数

整数类型

T __builtin_elementwise_exp(T x)

返回指定值的以 e 为底的指数,e^x

浮点类型

T __builtin_elementwise_exp2(T x)

返回指定值的以 2 为底的指数,2^x

浮点类型

T __builtin_elementwise_sqrt(T x)

返回浮点数的平方根

浮点类型

T __builtin_elementwise_roundeven(T x)

将 x 四舍五入到最接近的整数浮点数格式,将一半情况四舍五入到偶数(即最接近的偶整数),而与当前舍入方向无关。

浮点类型

T __builtin_elementwise_round(T x)

将 x 四舍五入到最接近的整数浮点数格式,将一半情况四舍五入到远离零的方向,而与当前舍入方向无关。可能会引发浮点异常。

浮点类型

T __builtin_elementwise_trunc(T x)

返回最接近 x 但绝对值不超过 x 的整数值

浮点类型

T __builtin_elementwise_nearbyint(T x)

将 x 四舍五入到最接近的整数浮点数格式,根据当前舍入方向进行舍入。可能不会引发不精确浮点异常。这与 __builtin_elementwise_rint 相同,除非 启用了 FENV_ACCESS

浮点类型

T __builtin_elementwise_rint(T x)

将 x 四舍五入到最接近的整数浮点数格式,根据当前舍入方向进行舍入。可能会引发浮点异常。这与 __builtin_elementwise_nearbyint 相同,除非 启用了 FENV_ACCESS

浮点类型

T __builtin_elementwise_canonicalize(T x)

返回浮点数的平台特定规范编码

浮点类型

T __builtin_elementwise_copysign(T x, T y)

返回 x 的大小,符号与 y 相同。

浮点类型

T __builtin_elementwise_fmod(T x, T y)

返回 (x/y) 的浮点余数,符号与 x 相同。

浮点类型

T __builtin_elementwise_max(T x, T y)

返回 x 或 y,两者中较大的一个

整数和浮点类型

T __builtin_elementwise_min(T x, T y)

返回 x 或 y,两者中较小的一个

整数和浮点类型

T __builtin_elementwise_add_sat(T x, T y)

返回 x 和 y 的总和,并将其限制在有符号/无符号整数类型可表示的范围之内。

整数类型

T __builtin_elementwise_sub_sat(T x, T y)

返回 x 和 y 的差值,并将其限制在有符号/无符号整数类型可表示的范围之内。

整数类型

T __builtin_elementwise_maximum(T x, T y)

返回 x 或 y,两者中较大的一个。遵循 IEEE 754-2019 语义,有关比较的详细信息,请参阅 LangRef

浮点类型

T __builtin_elementwise_minimum(T x, T y)

返回 x 或 y,两者中较小的一个。遵循 IEEE 754-2019 语义,有关比较的详细信息,请参阅 LangRef

浮点类型

归约内置函数

每个内置函数都返回一个标量,该标量等效于将指定的运算(x, y) 应用于所有向量元素作为递归的偶数-奇数成对归约。 operation(x, y) 针对具有索引 i * 2i * 2 + 1 的每个不重叠的偶数-奇数元素对重复应用,其中 i in [0, Number of elements / 2)。如果元素的数量不是 2 的幂,则向量将使用中性元素进行扩展,以便在最后归约到 2 的下一个幂。

这些归约支持固定大小和可扩展的向量类型。

示例

__builtin_reduce_add([e3, e2, e1, e0]) = __builtin_reduced_add([e3 + e2, e1 + e0])
                                       = (e3 + e2) + (e1 + e0)

VT 为向量类型,ETVT 的元素类型。

名称

操作

支持的元素类型

ET __builtin_reduce_max(VT a)

返回 x 或 y,两者中较大的一个;如果恰好一个参数为 NaN,则返回另一个参数。如果两个参数都是 NaN,则 fmax() 返回 NaN。

整数和浮点类型

ET __builtin_reduce_min(VT a)

返回 x 或 y,两者中较小的一个;如果恰好一个参数为 NaN,则返回另一个参数。如果两个参数都是 NaN,则 fmax() 返回 NaN。

整数和浮点类型

ET __builtin_reduce_add(VT a)

+

整数类型

ET __builtin_reduce_mul(VT a)

*

整数类型

ET __builtin_reduce_and(VT a)

&

整数类型

ET __builtin_reduce_or(VT a)

|

整数类型

ET __builtin_reduce_xor(VT a)

^

整数类型

ET __builtin_reduce_maximum(VT a)

返回向量中最大的元素。遵循 IEEE 754-2019 语义,有关比较的详细信息,请参阅 LangRef

浮点类型

ET __builtin_reduce_minimum(VT a)

返回向量中最小的元素。遵循 IEEE 754-2019 语义,有关比较的详细信息,请参阅 LangRef

浮点类型

矩阵类型

Clang 提供了对矩阵类型的扩展,目前正在实施中。有关详细信息,请参阅 草案规范

例如,以下代码使用矩阵类型扩展来将两个 4x4 浮点矩阵相乘,并将结果添加到第三个 4x4 矩阵中。

typedef float m4x4_t __attribute__((matrix_type(4, 4)));

m4x4_t f(m4x4_t a, m4x4_t b, m4x4_t c) {
  return a + b * c;
}

矩阵类型扩展还支持对矩阵和标量执行操作。

typedef float m4x4_t __attribute__((matrix_type(4, 4)));

m4x4_t f(m4x4_t a) {
  return (a + 23) * 12;
}

矩阵类型扩展支持对矩阵和标量进行除法,但不支持对矩阵和矩阵进行除法。

typedef float m4x4_t __attribute__((matrix_type(4, 4)));

m4x4_t f(m4x4_t a) {
  a = a / 3.0;
  return a;
}

矩阵类型扩展支持对矩阵和标量进行加法、减法和乘法的复合赋值,前提是它们类型一致。

typedef float m4x4_t __attribute__((matrix_type(4, 4)));

m4x4_t f(m4x4_t a, m4x4_t b) {
  a += b;
  a -= b;
  a *= b;
  a += 23;
  a -= 12;
  return a;
}

矩阵类型扩展支持显式强制转换。不允许在矩阵类型之间进行隐式类型转换。

typedef int ix5x5 __attribute__((matrix_type(5, 5)));
typedef float fx5x5 __attribute__((matrix_type(5, 5)));

fx5x5 f1(ix5x5 i, fx5x5 f) {
  return (fx5x5) i;
}


template <typename X>
using matrix_4_4 = X __attribute__((matrix_type(4, 4)));

void f2() {
  matrix_5_5<double> d;
  matrix_5_5<int> i;
  i = (matrix_5_5<int>)d;
  i = static_cast<matrix_5_5<int>>(d);
}

半精度浮点数

Clang 支持三种半精度 (16 位) 浮点类型:__fp16_Float16__bf16。这些类型在所有语言模式下都受支持,但它们在不同目标之间的支持有所不同。如果目标处理器提供了直接对该类型执行基本算术运算的指令,则该目标被认为对该类型具有“本机支持”。在没有本机支持的情况下,如果编译器可以通过提升到 float 来模拟对该类型的算术运算,则该类型仍然可以得到支持;有关此模拟的更多信息,请参见下文。

  • __fp16 在所有目标上都受支持。这种类型的特殊语义意味着不会直接对 __fp16 值执行任何算术运算;请参见下文。

  • _Float16 在以下目标上受支持

    • 32 位 ARM(在某些体系结构版本上原生支持)

    • 64 位 ARM (AArch64)(在 ARMv8.2a 及更高版本上原生支持)

    • AMDGPU(原生支持)

    • NVPTX(原生支持)

    • SPIR(原生支持)

    • X86(如果可用 SSE2;如果也可用 AVX512-FP16,则原生支持)

    • RISC-V(如果可用 Zfh 或 Zhinx,则原生支持)

  • __bf16 在以下目标上受支持(目前从未原生支持)

    • 32 位 ARM

    • 64 位 ARM (AArch64)

    • RISC-V

    • X86(当可用 SSE2 时)

(对于 X86,SSE2 在 64 位和所有最近的 32 位处理器上都可用。)

__fp16_Float16 都使用 IEEE 754-2008 中的 binary16 格式,它提供 5 位指数和 11 位尾数(包括隐式的引导 1)。 __bf16 使用 bfloat16 格式,它提供 8 位指数和 8 位尾数;这与 float 相同的指数范围,只是精度大大降低了。

_Float16__bf16 遵循算术浮点类型的通常规则。最重要的是,这意味着对这些类型的操作数执行的算术运算在形式上是在该类型中执行的,并且会产生该类型的返回值。 __fp16 并不遵循这些规则:大多数操作会立即将 __fp16 类型的操作数提升为 float,因此算术运算被定义为在 float 中执行,因此会产生 float 类型的返回值(除非由于其他操作数而进一步提升)。有关这些类型的具体规范,请参见下文。

在为没有本机支持的目标编译 _Float16__bf16 上的算术运算时,Clang 将在 float 中执行算术运算,并根据需要插入扩展和截断。这可以通过一种完全匹配本机支持的逐操作行为的方式来完成,但这可能需要许多额外的截断和扩展。默认情况下,当使用 float 模拟 _Float16__bf16 算术运算时,Clang 不会将中间操作数截断回其真实类型,除非操作数是显式强制转换或赋值的结果。这通常要快得多,但可能会生成与严格的逐操作模拟不同的结果。通常,结果更精确。C 和 C++ 标准在中间操作数的额外精度的规则下允许这样做;请参阅 C 标准中对评估格式的讨论以及 C++ 标准中的 [expr.pre]。

可以使用 -ffloat16-excess-precision=-fbfloat16-excess-precision= 选项分别控制这两种类型使用过高精度的行为。有效的值包括

  • none:表示严格地逐个操作进行仿真

  • standard:表示允许在标准中描述的规则下使用过高精度,即在显式转换或语句之间不进行过高精度计算

  • fast:表示只要优化器看到有机会避免截断,就允许使用过高精度;目前这除了 standard 之外没有其他效果

_Float16 类型是 ISO/IEC TS 18661-3:2015(“C 语言浮点扩展”)中定义的交换浮点类型。随着目标平台为其定义 ABI,它将在更多目标平台上得到支持。

__bf16 类型是非标准扩展,但它通常遵循 ISO/IEC TS 18661-3:2015 中关于算术交换浮点类型的规则。在 Clang 的早期版本中,它是一个仅用于存储的类型,禁止进行算术运算。随着目标平台为其定义 ABI,它将在更多目标平台上得到支持。

__fp16 类型最初是 ARM 扩展,由 ARM C 语言扩展 指定。Clang 为 __fp16 使用 IEEE 754-2008 中的 binary16 格式,而不是 ARM 的替代格式。期望算术操作数的操作符会立即将 __fp16 操作数提升为 float

建议可移植代码使用 _Float16 而不是 __fp16,因为它已由 C 标准委员会定义,并且其行为更符合大多数程序员的习惯。

由于 __fp16 操作数始终立即提升为 float,因此 __fp16_Float16 的通用实类型(用于通常的算术转换)为 float

字面量可以使用后缀 f16 指定为 _Float16 类型。例如,3.14f16

由于默认参数提升仅适用于标准浮点类型,因此将 _Float16 值作为可变参数或无类型参数传递时,不会提升为 double。因此,在使用某些库设施时,必须谨慎处理 _Float16;例如,没有用于 _Float16printf 格式说明符,并且(与 float 不同)它不会在传递给 printf 时隐式提升为 double,因此程序员必须在使用 %f 或类似说明符之前显式将其转换为 double

关于 deprecatedunavailable 属性的訊息

可以在 deprecatedunavailable 属性中添加可选的字符串消息。例如

void explode(void) __attribute__((deprecated("extremely unsafe, use 'combust' instead!!!")));

如果使用过时的或不可用的声明,消息将被合并到相应的诊断信息中。

harmless.c:4:3: warning: 'explode' is deprecated: extremely unsafe, use 'combust' instead!!!
      [-Wdeprecated-declarations]
  explode();
  ^

可以使用 __has_extension(attribute_deprecated_with_message)__has_extension(attribute_unavailable_with_message) 查询此功能。

枚举器上的属性

Clang 允许在各个枚举器上添加属性。这允许枚举器被标记为过时、不可用等。属性必须出现在枚举器名称之后,任何初始化器之前,如下所示

enum OperationMode {
  OM_Invalid,
  OM_Normal,
  OM_Terrified __attribute__((deprecated)),
  OM_AbortOnError __attribute__((deprecated)) = 4
};

enum 声明上的属性不适用于各个枚举器。

可以使用 __has_extension(enumerator_attributes) 查询此功能。

using-declarations 上的 C++11 属性

Clang 允许在 using-declarations 上添加 C++ 风格的 [[]] 属性。例如

[[clang::using_if_exists]] using foo::bar;
using foo::baz [[clang::using_if_exists]];

可以使用 __has_extension(cxx_attributes_on_using_declarations) 测试对该扩展的支持。

‘用户指定’ 的系统框架

Clang 提供了一种机制,通过该机制可以构建框架,使其始终被视为“系统框架”,即使它们不在系统框架目录中。这对系统框架开发人员来说很有用,他们希望能够使用框架的开发版本测试构建其他应用程序,包括编译器更改系统头文件警告行为的方式。

框架开发人员可以通过在框架的顶层创建一个“.system_framework”文件来选择加入此机制。也就是说,框架应该包含以下内容

.../TestFramework.framework
.../TestFramework.framework/.system_framework
.../TestFramework.framework/Headers
.../TestFramework.framework/Headers/TestFramework.h
...

Clang 会将此文件的出现视为一个指示器,表明无论框架是在框架搜索路径中如何找到的,都应该将其视为系统框架。为了一致性,我们建议在安装版本的框架中永远不要包含此类文件。

标准语言功能检查

可以使用 __has_feature 宏查询是否启用了某些标准语言功能。可以使用 __has_extension 宏查询在为不提供这些功能的标准编译时,语言功能是否作为扩展可用。可以测试的功能列在下面。

从 Clang 3.4 开始,还支持 C++ SD-6 功能测试宏。这些宏的名称形式为 __cpp_<feature_name>,旨在成为一种可移植的方式来查询编译器支持的功能。有关每个 Clang 版本支持的 SD-6 版本以及该修订版本的建议提供的宏,请参阅 C++ 状态页面

C++98

下面列出的功能是 C++98 标准的一部分。在编译 C++ 代码时,默认情况下启用这些功能。

C++ 异常

使用 __has_feature(cxx_exceptions) 确定是否已启用 C++ 异常。例如,使用 -fno-exceptions 选项编译代码会禁用 C++ 异常。

C++ RTTI

使用 __has_feature(cxx_rtti) 确定是否已启用 C++ RTTI。例如,使用 -fno-rtti 选项编译代码会禁用 RTTI 的使用。

C++11

下面列出的功能是 C++11 标准的一部分。因此,在编译 C++ 代码时,使用 -std=c++11-std=gnu++11 选项会启用所有这些功能。

C++11 SFINAE 包括访问控制

使用 __has_feature(cxx_access_control_sfinae)__has_extension(cxx_access_control_sfinae) 确定访问控制错误(例如,调用私有构造函数)是否被视为模板参数推导错误(也称为 SFINAE 错误),如 C++ DR1170 中所述。

C++11 别名模板

使用 __has_feature(cxx_alias_templates)__has_extension(cxx_alias_templates) 确定是否已启用对 C++11 的别名声明和别名模板的支持。

C++11 对齐说明符

使用 __has_feature(cxx_alignas)__has_extension(cxx_alignas) 确定是否已启用对使用 alignas 的对齐说明符的支持。

使用 __has_feature(cxx_alignof)__has_extension(cxx_alignof) 确定是否已启用对 alignof 关键字的支持。

C++11 属性

使用 __has_feature(cxx_attributes)__has_extension(cxx_attributes) 确定是否已启用对使用 C++11 的方括号表示法的属性解析的支持。

C++11 广义常量表达式

使用 __has_feature(cxx_constexpr) 确定是否已启用对广义常量表达式(例如,constexpr)的支持。

C++11 decltype()

使用 __has_feature(cxx_decltype)__has_extension(cxx_decltype) 来确定是否启用了对 decltype() 说明符的支持。C++11 的 decltype 不需要函数调用表达式的类型完整性。使用 __has_feature(cxx_decltype_incomplete_return_types)__has_extension(cxx_decltype_incomplete_return_types) 来确定是否启用了对该功能的支持。

C++11 函数模板中的默认模板参数

使用 __has_feature(cxx_default_function_template_args)__has_extension(cxx_default_function_template_args) 来确定是否启用了对函数模板中默认模板参数的支持。

C++11 defaulted 函数

使用 __has_feature(cxx_defaulted_functions)__has_extension(cxx_defaulted_functions) 来确定是否启用了对默认函数定义(使用 = default)的支持。

C++11 委托构造函数

使用 __has_feature(cxx_delegating_constructors) 来确定是否启用了对委托构造函数的支持。

C++11 deleted 函数

使用 __has_feature(cxx_deleted_functions)__has_extension(cxx_deleted_functions) 来确定是否启用了对删除函数定义(使用 = delete)的支持。

C++11 显式转换函数

使用 __has_feature(cxx_explicit_conversions) 来确定是否启用了对 explicit 转换函数的支持。

C++11 通用初始化器

使用 __has_feature(cxx_generalized_initializers) 来确定是否启用了对通用初始化器(使用大括号列表和 std::initializer_list)的支持。

C++11 隐式移动构造函数/赋值运算符

使用 __has_feature(cxx_implicit_moves) 来确定 Clang 是否会在需要时隐式生成移动构造函数和移动赋值运算符。

C++11 继承构造函数

使用 __has_feature(cxx_inheriting_constructors) 来确定是否启用了对继承构造函数的支持。

C++11 内联命名空间

使用 __has_feature(cxx_inline_namespaces)__has_extension(cxx_inline_namespaces) 来确定是否启用了对内联命名空间的支持。

C++11 lambda 表达式

使用 __has_feature(cxx_lambdas)__has_extension(cxx_lambdas) 来确定是否启用了对 lambda 表达式的支持。

C++11 局部和未命名类型作为模板参数

使用 __has_feature(cxx_local_type_template_args)__has_extension(cxx_local_type_template_args) 来确定是否启用了对局部和未命名类型作为模板参数的支持。

C++11 noexcept

使用 __has_feature(cxx_noexcept)__has_extension(cxx_noexcept) 来确定是否启用了对 noexcept 异常说明的支持。

C++11 类内非静态数据成员初始化

使用 __has_feature(cxx_nonstatic_member_init) 来确定是否启用了类内非静态数据成员的初始化。

C++11 nullptr

使用 __has_feature(cxx_nullptr)__has_extension(cxx_nullptr) 来确定是否启用了对 nullptr 的支持。

C++11 override control

使用 __has_feature(cxx_override_control)__has_extension(cxx_override_control) 来确定是否启用了对重写控制关键字的支持。

C++11 引用限定函数

使用 __has_feature(cxx_reference_qualified_functions)__has_extension(cxx_reference_qualified_functions) 来确定是否启用了对引用限定函数(例如,在 *this 上应用了 &&& 的成员函数)的支持。

C++11 基于范围的 for 循环

使用 __has_feature(cxx_range_for)__has_extension(cxx_range_for) 来确定是否启用了对基于范围的 for 循环的支持。

C++11 原始字符串字面量

使用 __has_feature(cxx_raw_string_literals) 来确定是否启用了对原始字符串字面量(例如,R"x(foo\bar)x")的支持。

C++11 右值引用

使用 __has_feature(cxx_rvalue_references)__has_extension(cxx_rvalue_references) 来确定是否启用了对右值引用的支持。

C++11 static_assert()

使用 __has_feature(cxx_static_assert)__has_extension(cxx_static_assert) 来确定是否启用了使用 static_assert 进行编译时断言的支持。

C++11 thread_local

使用 __has_feature(cxx_thread_local) 来确定是否启用了对 thread_local 变量的支持。

C++11 类型推断

使用 __has_feature(cxx_auto_type)__has_extension(cxx_auto_type) 来确定是否支持使用 auto 说明符的 C++11 类型推断。如果该功能被禁用,则 auto 将变成一个存储类说明符,就像在 C 或 C++98 中一样。

C++11 强类型枚举

使用 __has_feature(cxx_strong_enums)__has_extension(cxx_strong_enums) 来确定是否启用了对强类型、作用域枚举的支持。

C++11 尾随返回类型

使用 __has_feature(cxx_trailing_return)__has_extension(cxx_trailing_return) 来确定是否启用了使用尾随返回类型的备用函数声明语法。

C++11 Unicode 字符串字面量

使用 __has_feature(cxx_unicode_literals) 来确定是否启用了对 Unicode 字符串字面量的支持。

C++11 无限制联合

使用 __has_feature(cxx_unrestricted_unions) 来确定是否启用了对无限制联合的支持。

C++11 用户定义字面量

使用 __has_feature(cxx_user_literals) 来确定是否启用了对用户定义字面量的支持。

C++11 可变参数模板

使用 __has_feature(cxx_variadic_templates)__has_extension(cxx_variadic_templates) 来确定是否启用了对可变参数模板的支持。

C++14

下面列出的功能是 C++14 标准的一部分。因此,在编译 C++ 代码时,使用 -std=C++14-std=gnu++14 选项,所有这些功能都会被启用。

C++14 二进制字面量

使用 __has_feature(cxx_binary_literals)__has_extension(cxx_binary_literals) 来确定是否识别二进制字面量(例如,0b10010)。Clang 在所有语言模式下都支持此功能作为扩展。

C++14 上下文转换

使用 __has_feature(cxx_contextual_conversions)__has_extension(cxx_contextual_conversions) 来确定在执行隐式转换(用于 *new-表达式* 中的数组边界、*delete-表达式* 的操作数、积分常量表达式或 switch 语句中的条件)时是否使用 C++14 规则。

C++14 decltype(auto)

使用 __has_feature(cxx_decltype_auto)__has_extension(cxx_decltype_auto) 来确定是否启用了对 decltype(auto) 占位符类型的支持。

C++14 聚合体的默认初始化程序

使用 __has_feature(cxx_aggregate_nsdmi)__has_extension(cxx_aggregate_nsdmi) 来确定是否启用了对聚合体成员中默认初始化程序的支持。

C++14 数字分隔符

使用 __cpp_digit_separators 来确定是否启用了使用单引号的数字分隔符的支持(例如,10'000)。目前,还没有相应的 __has_feature 名称。

C++14 泛化 lambda 捕获

使用 __has_feature(cxx_init_captures)__has_extension(cxx_init_captures) 来确定是否启用了对带有显式初始化程序的 lambda 捕获的支持(例如,[n(0)] { return ++n; })。

C++14 泛型 lambda

使用 __has_feature(cxx_generic_lambdas)__has_extension(cxx_generic_lambdas) 来确定是否启用了对泛型(多态)lambda 的支持(例如,[] (auto x) { return x + 1; })。

C++14 放宽的 constexpr

使用 __has_feature(cxx_relaxed_constexpr)__has_extension(cxx_relaxed_constexpr) 来确定是否允许在 constexpr 函数中使用变量声明、局部变量修改和控制流结构。

C++14 返回类型推断

使用 __has_feature(cxx_return_type_deduction)__has_extension(cxx_return_type_deduction) 来确定是否启用了对函数返回类型推断的支持(使用 auto 作为返回类型)。

C++14 运行时大小数组

使用 __has_feature(cxx_runtime_array)__has_extension(cxx_runtime_array) 来确定是否启用了对运行时绑定数组(一种限制形式的变长数组)的支持。Clang 对此功能的实现尚未完善。

C++14 变量模板

使用 __has_feature(cxx_variable_templates)__has_extension(cxx_variable_templates) 来确定是否启用了对模板化变量声明的支持。

C11

以下列出的功能是 C11 标准的一部分。因此,在编译 C 代码时,使用 -std=c11-std=gnu11 选项将启用所有这些功能。此外,由于所有这些功能都向后兼容,因此它们在所有语言模式下都可用作为扩展。

C11 对齐说明符

使用 __has_feature(c_alignas)__has_extension(c_alignas) 来确定是否启用了使用 _Alignas 的对齐说明符的支持。

使用 __has_feature(c_alignof)__has_extension(c_alignof) 来确定是否启用了对 _Alignof 关键字的支持。

C11 原子操作

使用 __has_feature(c_atomic)__has_extension(c_atomic) 来确定是否启用了使用 _Atomic 的原子类型的支持。Clang 还提供 一组内置函数,这些内置函数可用于在 _Atomic 类型上实现 <stdatomic.h> 操作。使用 __has_include(<stdatomic.h>) 来确定 C11 的 <stdatomic.h> 标头是否可用。

当系统提供 <stdatomic.h> 标头时,Clang 将使用该标头,否则将使用其自己的标头。当使用其自己的标头时,原子操作的实现将作为宏提供。在 C11 也需要真实函数的情况下,此标头只提供该函数的声明(以及一个阴影宏实现),如果您使用的是该函数而不是宏,则必须链接到一个提供该函数定义的库。

C11 泛型选择

使用 __has_feature(c_generic_selections)__has_extension(c_generic_selections) 来确定是否启用了泛型选择的支持。

作为扩展,C11 泛型选择表达式在 Clang 支持的所有语言中都可用。语法与 C11 标准中给出的相同。

在 C 中,类型兼容性是根据相应标准中给出的规则决定的,但在 C++ 中,由于缺少 C 中使用的类型兼容性规则,因此只有在类型等效的情况下才认为类型兼容。

Clang 还支持 _Generic 的扩展形式,其中使用控制类型而不是控制表达式。与控制表达式不同,控制类型参数不会进行任何转换,因此适用于尝试匹配限定类型、不完整类型或函数类型时使用。变长数组类型缺乏解决其匹配哪个关联所需的编译时信息,因此不允许作为控制类型参数。

使用 __has_extension(c_generic_selection_with_controlling_type) 来确定是否启用了对该扩展的支持。

C11 _Static_assert()

使用 __has_feature(c_static_assert)__has_extension(c_static_assert) 来确定是否启用了使用 _Static_assert 的编译时断言的支持。

C11 _Thread_local

使用 __has_feature(c_thread_local)__has_extension(c_thread_local) 来确定是否启用了对 _Thread_local 变量的支持。

模块

使用 __has_feature(modules) 来确定是否启用了模块。例如,使用 -fmodules 选项编译代码将启用模块的使用。

更多信息可以在 此处 找到。

反向移植到先前标准的语言扩展

功能

功能测试宏

引入于

反向移植到

可变参数模板

__cpp_variadic_templates

C++11

C++03

别名模板

__cpp_alias_templates

C++11

C++03

非静态数据成员初始化程序

__cpp_nsdmi

C++11

C++03

基于范围的 for 循环

__cpp_range_based_for

C++11

C++03

右值引用

__cpp_rvalue_references

C++11

C++03

属性

__cpp_attributes

C++11

C++03

lambda

__cpp_lambdas

C++11

C++03

泛化 lambda 捕获

__cpp_init_captures

C++14

C++03

泛型 lambda 表达式

__cpp_generic_lambdas

C++14

C++03

变量模板

__cpp_variable_templates

C++14

C++03

二进制字面量

__cpp_binary_literals

C++14

C++03

放宽的 constexpr

__cpp_constexpr

C++14

C++11

没有消息的静态断言

__cpp_static_assert >= 201411L

C++17

C++11

泛化 lambda 捕获中的包展开

__cpp_init_captures

C++17

C++03

if constexpr

__cpp_if_constexpr

C++17

C++11

折叠表达式

__cpp_fold_expressions

C++17

C++03

按值捕获 lambda 的 *this

__cpp_capture_star_this

C++17

C++03

枚举上的属性

__cpp_enumerator_attributes

C++17

C++03

保证的复制省略

__cpp_guaranteed_copy_elision

C++17

C++03

十六进制浮点字面量

__cpp_hex_float

C++17

C++03

inline 变量

__cpp_inline_variables

C++17

C++03

命名空间上的属性

__cpp_namespace_attributes

C++17

C++11

结构化绑定

__cpp_structured_bindings

C++17

C++03

模板模板参数

__cpp_template_template_args

C++17

C++03

用于泛型 lambda 的熟悉模板语法

__cpp_generic_lambdas

C++20

C++03

static operator[]

__cpp_multidimensional_subscript

C++20

C++03

指定初始化程序

__cpp_designated_initializers

C++20

C++03

条件 explicit

__cpp_conditional_explicit

C++20

C++03

using enum

__cpp_using_enum

C++20

C++03

if consteval

__cpp_if_consteval

C++23

C++20

static operator()

__cpp_static_call_operator

C++23

C++03

Lambda 表达式上的属性

C++23

C++11

结构化绑定上的属性

__cpp_structured_bindings

C++26

C++03

带有用户生成消息的静态断言

__cpp_static_assert >= 202306L

C++26

C++11

包索引

__cpp_pack_indexing

C++26

C++03

= delete ("should have a reason");

__cpp_deleted_function

C++26

C++03

可变参数朋友

__cpp_variadic_friend

C++26

C++03

指定初始化程序 (N494)

C99

C89

数组 & 元素限定符 (N2607)

C23

C89

属性 (N2335)

C23

C89

#embed (N3017)

C23

C89、C++

内置类型别名

Clang 提供了一些内置别名,以提高某些元编程设施的吞吐量。

__builtin_common_type

template <template <class... Args> class BaseTemplate,
          template <class TypeMember> class HasTypeMember,
          class HasNoTypeMember,
          class... Ts>
using __builtin_common_type = ...;

此别名用于实现 std::common_type。如果 std::common_type 应该包含一个 type 成员,它将成为 HasTypeMember<TheCommonType> 的别名。否则,它将成为 HasNoTypeMember 的别名。 BaseTemplate 通常为 std::common_typeTsstd::common_type 的参数。

__type_pack_element

template <std::size_t Index, class... Ts>
using __type_pack_element = ...;

此别名返回参数包 TsIndex 处的类型。

__make_integer_seq

template <template <class IntSeqT, IntSeqT... Ints> class IntSeq, class T, T N>
using __make_integer_seq = ...;

此别名返回 IntSeq,它使用 IntSeqT = T``and ``Ints 作为包 0, ..., N - 1

类型特征基元

类型特征基元是特殊的内置常量表达式,标准 C++ 库可以使用它们来促进或简化在 <type_traits> 头文件中面向用户的类型特征的实现。

它们不打算在用户代码中直接使用,因为它们是实现定义的,并且可能会发生变化 - 因此,它们与支持的系统头文件集密切相关,目前

  • LLVM 自有的 libc++

  • GNU libstdc++

  • Microsoft 标准 C++ 库

Clang 支持 GNU C++ 类型特征Microsoft Visual C++ 类型特征 的子集,以及几乎所有 Embarcadero C++ 类型特征

以下类型特征基元受 Clang 支持。那些标有 (C++) 的特征为 C++ 标准指定的类型特征提供了实现; __X(...) 与相应的 std::X_t<...>std::X_v<...> 类型特征具有相同的语义和约束。

  • __array_rank(type) (Embarcadero): 返回类型 type 中的数组级别数:如果 type 不是数组类型,则为 0;如果 typeelement 的数组,则为 __array_rank(element) + 1

  • __array_extent(type, dim) (Embarcadero): 类型 type 中的第 dim 个数组边界,如果 dim >= __array_rank(type),则为 0

  • __builtin_is_implicit_lifetime (C++, GNU, Microsoft)

  • __builtin_is_virtual_base_of (C++, GNU, Microsoft)

  • __can_pass_in_regs (C++) 返回一个类在当前 ABI 下是否可以传递到寄存器中。此类型只能应用于未限定的类类型。这不是一个可移植的类型特征。

  • __has_nothrow_assign (GNU, Microsoft, Embarcadero): 已弃用,请改用 __is_nothrow_assignable

  • __has_nothrow_move_assign (GNU, Microsoft): 已弃用,请改用 __is_nothrow_assignable

  • __has_nothrow_copy (GNU, Microsoft): 已弃用,请改用 __is_nothrow_constructible

  • __has_nothrow_constructor (GNU, Microsoft): 已弃用,请改用 __is_nothrow_constructible

  • __has_trivial_assign (GNU, Microsoft, Embarcadero): 已弃用,请改用 __is_trivially_assignable

  • __has_trivial_move_assign (GNU, Microsoft): 已弃用,请改用 __is_trivially_assignable

  • __has_trivial_copy (GNU, Microsoft): 已弃用,请改用 __is_trivially_copyable

  • __has_trivial_constructor (GNU, Microsoft): 已弃用,请改用 __is_trivially_constructible

  • __has_trivial_move_constructor (GNU, Microsoft): 已弃用,请改用 __is_trivially_constructible

  • __has_trivial_destructor (GNU, Microsoft, Embarcadero): 已弃用,请改用 __is_trivially_destructible

  • __has_unique_object_representations (C++, GNU)

  • __has_virtual_destructor (C++, GNU, Microsoft, Embarcadero)

  • __is_abstract (C++, GNU, Microsoft, Embarcadero)

  • __is_aggregate (C++, GNU, Microsoft)

  • __is_arithmetic (C++, Embarcadero)

  • __is_array (C++, Embarcadero)

  • __is_assignable (C++, MSVC 2015)

  • __is_base_of (C++, GNU, Microsoft, Embarcadero)

  • __is_bounded_array (C++, GNU, Microsoft, Embarcadero)

  • __is_class (C++, GNU, Microsoft, Embarcadero)

  • __is_complete_type(type) (Embarcadero): 如果 type 是完整类型,则返回 true。警告:此特征很危险,因为它可能在同一程序的不同位置返回不同的值。

  • __is_compound (C++, Embarcadero)

  • __is_const (C++, Embarcadero)

  • __is_constructible (C++, MSVC 2013)

  • __is_convertible (C++, Embarcadero)

  • __is_nothrow_convertible (C++, GNU)

  • __is_convertible_to (Microsoft): __is_convertible 的同义词。

  • __is_destructible (C++, MSVC 2013)

  • __is_empty (C++, GNU, Microsoft, Embarcadero)

  • __is_enum (C++, GNU, Microsoft, Embarcadero)

  • __is_final (C++, GNU, Microsoft)

  • __is_floating_point (C++, Embarcadero)

  • __is_function (C++, Embarcadero)

  • __is_fundamental (C++, Embarcadero)

  • __is_integral (C++, Embarcadero)

  • __is_interface_class (Microsoft): 返回 false,即使对于使用 __interface 定义的类型也是如此。

  • __is_layout_compatible (C++, GNU, Microsoft)

  • __is_literal (Clang): __is_literal_type 的同义词。

  • __is_literal_type (C++, GNU, Microsoft): 注意,相应的标准特征在 C++17 中被弃用,并在 C++20 中被删除。

  • __is_lvalue_reference (C++, Embarcadero)

  • __is_member_object_pointer (C++, Embarcadero)

  • __is_member_function_pointer (C++, Embarcadero)

  • __is_member_pointer (C++, Embarcadero)

  • __is_nothrow_assignable (C++, MSVC 2013)

  • __is_nothrow_constructible (C++, MSVC 2013)

  • __is_nothrow_destructible (C++, MSVC 2013)

  • __is_object (C++, Embarcadero)

  • __is_pod (C++, GNU, Microsoft, Embarcadero): 注意,相应的标准特征在 C++20 中被弃用。

  • __is_pointer (C++, Embarcadero)

  • __is_pointer_interconvertible_base_of (C++, GNU, Microsoft)

  • __is_polymorphic (C++, GNU, Microsoft, Embarcadero)

  • __is_reference (C++, Embarcadero)

  • __is_referenceable (C++, GNU, Microsoft, Embarcadero): 如果类型是可引用类型,则返回 true,否则返回 false。可引用类型是指对象类型、引用类型或未限定的函数类型。

  • __is_rvalue_reference (C++, Embarcadero)

  • __is_same (C++, Embarcadero)

  • __is_same_as (GCC): __is_same 的同义词。

  • __is_scalar (C++, Embarcadero)

  • __is_scoped_enum (C++, GNU, Microsoft, Embarcadero)

  • __is_sealed (Microsoft): __is_final 的同义词。

  • __is_signed (C++, Embarcadero): 对枚举类型返回 false,对浮点类型返回 true。注意,在 Clang 10 之前,如果底层类型是有符号的,则返回 true,对于浮点类型则返回 false。

  • __is_standard_layout (C++, GNU, Microsoft, Embarcadero)

  • __is_trivial (C++, GNU, Microsoft, Embarcadero)

  • __is_trivially_assignable (C++, GNU, Microsoft)

  • __is_trivially_constructible (C++, GNU, Microsoft)

  • __is_trivially_copyable (C++, GNU, Microsoft)

  • __is_trivially_destructible (C++, MSVC 2013)

  • __is_trivially_relocatable (Clang): 如果移动给定类型的对象,然后销毁源对象,则返回 true,表示该操作在功能上等同于复制底层字节,然后将源对象丢弃。 这对于 trivial 类型和通过 clang::trivial_abi 属性使其成为 trivially relocatable 的类型是正确的。

  • __is_trivially_equality_comparable (Clang): 如果比较给定类型的两个对象,则返回 true,表示该操作在功能上等同于比较其对象表示。 请注意,包含填充字节的类型永远不会是 trivially equality comparable。

  • __is_unbounded_array (C++, GNU, Microsoft, Embarcadero)

  • __is_union (C++, GNU, Microsoft, Embarcadero)

  • __is_unsigned (C++, Embarcadero): 对枚举类型返回 false。 请注意,在 Clang 13 之前,如果底层类型是 unsigned,则对枚举类型返回 true。

  • __is_void (C++, Embarcadero)

  • __is_volatile (C++, Embarcadero)

  • __reference_binds_to_temporary(T, U) (Clang): 判断类型为 T 的引用绑定到类型为 U 的表达式是否会绑定到已实现的临时对象。 如果 T 不是引用类型,则结果为 false。 请注意,当从 U 初始化 T 时形成错误时,此特性也会返回 false。 已弃用,请使用 __reference_constructs_from_temporary

  • __reference_constructs_from_temporary(T, U) (C++) 如果类型为 T 的引用可以直接从非 cv 限定的 U 类型的临时对象初始化,则返回 true。

  • __reference_converts_from_temporary(T, U) (C++)

    如果类型为 T 的引用可以从非 cv 限定的 U 类型的临时对象进行 copy 初始化,则返回 true。

  • __underlying_type (C++, GNU, Microsoft)

此外,还支持以下表达式特性

  • __is_lvalue_expr(e) (Embarcadero): 如果 e 是左值表达式,则返回 true。 已弃用,请改用 __is_lvalue_reference(decltype((e)))

  • __is_rvalue_expr(e) (Embarcadero): 如果 e 是右值表达式,则返回 true。 已弃用,请改用 !__is_reference(decltype((e)))

有多种方法可以检测编译器是否支持类型特性 __X,具体取决于您希望支持的最旧 Clang 版本。

  • 从 Clang 10 开始,可以使用 __has_builtin(__X)

  • 从 Clang 6 开始,可以使用 !__is_identifier(__X)

  • 从 Clang 3 开始,可以使用 __has_feature(X),但只支持以下特性

    • __has_nothrow_assign

    • __has_nothrow_copy

    • __has_nothrow_constructor

    • __has_trivial_assign

    • __has_trivial_copy

    • __has_trivial_constructor

    • __has_trivial_destructor

    • __has_virtual_destructor

    • __is_abstract

    • __is_base_of

    • __is_class

    • __is_constructible

    • __is_convertible_to

    • __is_empty

    • __is_enum

    • __is_final

    • __is_literal

    • __is_standard_layout

    • __is_pod

    • __is_polymorphic

    • __is_sealed

    • __is_trivial

    • __is_trivially_assignable

    • __is_trivially_constructible

    • __is_trivially_copyable

    • __is_union

    • __underlying_type

以下是一个简单的使用示例,可能在标准 C++ 头文件中看到

#if __has_builtin(__is_convertible_to)
template<typename From, typename To>
struct is_convertible_to {
  static const bool value = __is_convertible_to(From, To);
};
#else
// Emulate type trait for compatibility with other compilers.
#endif

语法和高级语言特性描述在 块语言规范 中。 Clang 实现的实现和 ABI 详细信息在 块-ABI-Apple 中。

使用 __has_extension(blocks) 查询此特性。

带输出约束的 ASM Goto

输出可以在 asm goto 的任何分支中使用,无论分支是否被执行。

使用 __has_extension(gnu_asm_goto_with_outputs) 查询此特性。

在 clang-16 之前,只有在间接分支不被执行时,输出才能安全使用。 使用 __has_extension(gnu_asm_goto_with_outputs_full) 查询此差异。

当使用绑定输出(即既是输入又是输出,而不仅仅是输出的输出)和 +r 约束时,会在标签之前创建一个隐藏的输入,因此对操作数的数字引用必须考虑这一点。

int foo(int x) {
    // %0 and %1 both refer to x
    // %l2 refers to err
    asm goto("# %0 %1 %l2" : "+r"(x) : : : err);
    return x;
  err:
    return -1;
}

这在 clang-13 中更改为匹配 GCC;为了更好的可移植性,可以使用符号引用而不是数字引用。

int foo(int x) {
    asm goto("# %[x] %l[err]" : [x]"+r"(x) : : : err);
    return x;
  err:
    return -1;
}

Objective-C 特性

自动引用计数

Clang 在 Objective-C 中支持 自动引用计数,这消除了手动 retain/release/autorelease 消息发送的需要。 有三个与自动引用计数相关的特性宏:__has_feature(objc_arc) 表示自动引用计数的一般可用性,而 __has_feature(objc_arc_weak) 表示自动引用计数还包括对指向 Objective-C 对象的 __weak 指针的支持。 __has_feature(objc_arc_fields) 表示 C 结构体允许具有指向由自动引用计数管理的 Objective-C 对象的指针字段。

弱引用

Clang 即使在非 ARC 模式下也支持 Objective-C 中的 ARC 风格的弱引用和不安全引用。 弱引用必须使用 -fobjc-weak 选项显式启用;使用 __has_feature((objc_arc_weak)) 测试它们是否已启用。 不安全引用无条件启用。 当启用 Objective-C 垃圾回收时,不能使用 ARC 风格的弱引用和不安全引用。

除了以下说明外,__weak__unsafe_unretained 限定符(以及 weakunsafe_unretained 属性)的语言规则与 ARC 规范 中描述的一致。特别是需要注意的是,有些类不支持对其实例形成弱引用,并且在将弱引用存储在初始化和反初始化不在编译器控制范围内的内存中(例如,在 malloc 分配的内存中)时,需要格外小心。

__weak 变量中加载始终会隐式保留加载的值。在非 ARC 模式下,此保留通常由隐式自动释放来平衡。可以通过在 -retain 消息发送的接收者位置执行加载来抑制此自动释放(例如 [weakReference retain]);请注意,这只会执行一次保留(在从弱引用中原始加载时执行的保留)。

在大多数情况下,在非 ARC 模式下,__unsafe_unretained 只是变量的默认行为,因此不需要。但是,它确实会影响块捕获的语义:通常,复制捕获 Objective-C 对象或块指针的块会导致捕获的指针分别被保留或复制,但是当捕获的变量被 __unsafe_unretained 限定时,该行为会被抑制。

请注意,__weak 限定符以前在所有非 ARC 模式下表示 GC 限定符,并且在 GC 模式之外被静默忽略。它现在在所有非 GC 模式下表示 ARC 风格的限定符,如果未通过 -fobjc-arc-fobjc-weak 启用,则不再允许。预计 -fobjc-weak 最终将在所有非 GC Objective-C 模式下默认启用。

具有固定底层类型的枚举

Clang 在 Objective-C 中提供了对具有固定底层类型的 C++11 枚举的支持。例如,可以将枚举类型写为

typedef enum : unsigned char { Red, Green, Blue } Color;

这指定了用于存储枚举值的底层类型是 unsigned char

使用 __has_feature(objc_fixed_enum) 来确定 Objective-C 中是否支持固定底层类型。

与 C++11 lambda 的互操作性

Clang 通过允许将 lambda 隐式转换为具有相应签名的块指针,提供了 C++11 lambda 和基于块的 API 之间的互操作性。例如,考虑一个 API,例如 NSArray 的数组排序方法

- (NSArray *)sortedArrayUsingComparator:(NSComparator)cmptr;

NSComparator 只是块指针 NSComparisonResult (^)(id, id) 的 typedef,并且此类型参数通常使用块文字作为参数提供。但是,只要提供相同的签名(在本例中,接受两个类型为 id 的参数并返回 NSComparisonResult),也可以使用 C++11 lambda

NSArray *array = @[@"string 1", @"string 21", @"string 12", @"String 11",
                   @"String 02"];
const NSStringCompareOptions comparisonOptions
  = NSCaseInsensitiveSearch | NSNumericSearch |
    NSWidthInsensitiveSearch | NSForcedOrderingSearch;
NSLocale *currentLocale = [NSLocale currentLocale];
NSArray *sorted
  = [array sortedArrayUsingComparator:[=](id s1, id s2) -> NSComparisonResult {
             NSRange string1Range = NSMakeRange(0, [s1 length]);
             return [s1 compare:s2 options:comparisonOptions
             range:string1Range locale:currentLocale];
     }];
NSLog(@"sorted: %@", sorted);

此代码依赖于从 lambda 表达式类型(一个名为闭包类型的未命名局部类类型)到相应块指针类型的隐式转换。转换本身由该闭包类型中的转换运算符表示,该运算符生成与 lambda 本身具有相同签名的块指针,例如:

operator NSComparisonResult (^)(id, id)() const;

此转换函数返回一个新块,它只是将两个参数转发到 lambda 对象(它通过复制捕获),然后返回结果。返回的块首先被复制(使用 Block_copy),然后被自动释放。作为优化,如果 lambda 表达式立即转换为块指针(如上面的第一个示例),则块不会被复制和自动释放:相反,它将获得与在程序中该点编写的块文字相同的生存期,这避免了在常见情况下将块复制到堆的开销。

从 lambda 到块指针的转换仅在 Objective-C++ 中可用,而不在带有块的 C++ 中可用,因为它使用 Objective-C 内存管理(自动释放)。

对象文字和下标

Clang 在 Objective-C 中提供了对 对象文字和下标 的支持,这简化了常见的 Objective-C 编程模式,使程序更加简洁,并提高了容器创建的安全。与对象文字和下标相关的几个特征宏:__has_feature(objc_array_literals) 测试数组文字的可用性;__has_feature(objc_dictionary_literals) 测试字典文字的可用性;__has_feature(objc_subscripting) 测试对象下标的可用性。

Objective-C 属性的自动合成

Clang 为声明的属性提供了自动合成支持。使用此功能,clang 提供了对未声明为 @dynamic 且没有用户提供的后备 getter 和 setter 方法的属性的默认合成。__has_feature(objc_default_synthesize_properties) 检查正在使用的 clang 版本中此功能的可用性。

Objective-C 保留行为属性

在 Objective-C 中,通常假设函数和方法遵循 Cocoa 内存管理 约定,用于对象参数和返回值的所有权。但是,存在例外,因此 Clang 提供属性以允许记录这些例外。这些被 ARC 和 静态分析器 使用。一些例外可以通过使用 objc_method_family 属性更好地描述。

用法ns_returns_retainedns_returns_not_retainedns_returns_autoreleasedcf_returns_retainedcf_returns_not_retained 属性可以放在返回 Objective-C 或 CoreFoundation 对象的方法和函数上。它们通常放置在函数原型或方法声明的末尾

id foo() __attribute__((ns_returns_retained));

- (NSString *)bar:(int)x __attribute__((ns_returns_retained));

*_returns_retained 属性指定返回的对象具有 +1 保留计数。*_returns_not_retained 属性指定返回的对象具有 +0 保留计数,即使其选择器的正常约定是 +1。ns_returns_autoreleased 指定返回的对象为 +0,但保证至少在下一个自动释放池刷新之前存活。

用法ns_consumedcf_consumed 属性可以放在参数声明上;它们指定参数预计具有 +1 保留计数,该计数将以某种方式由函数或方法平衡。ns_consumes_self 属性只能放在 Objective-C 方法上;它指定该方法期望其 self 参数具有 +1 保留计数,它将以某种方式平衡它。

void foo(__attribute__((ns_consumed)) NSString *string);

- (void) bar __attribute__((ns_consumes_self));
- (void) baz:(id) __attribute__((ns_consumed)) x;

这些属性的更多示例可以在静态分析器的 分析注释列表 中找到。

使用 __has_attribute(ns_consumed)__has_attribute(ns_returns_retained) 等查询这些功能。

Objective-C @available

可以通过传递 -mmacos-version-min= / -miphoneos-version-min= 来使用最新的 SDK,但仍然构建可以在 macOS 和 iOS 的旧版本上运行的程序。

在 LLVM 5.0 之前,当调用仅在比目标 OS(由最小部署版本确定)更新的 OS 中存在的函数时,程序员必须仔细检查该函数在运行时是否存在,使用弱链接的 C 函数的空检查、+class 用于 Objective-C 类,以及 -respondsToSelector:+instancesRespondToSelector: 用于 Objective-C 方法。如果遗漏了此类检查,程序将编译正常,在新系统上运行正常,但在旧系统上崩溃。

从 LLVM 5.0 开始,-Wunguarded-availability 使用 可用性属性 以及新的 @available() 关键字来协助解决此问题。当调用在比目标 OS 更新的 OS 中引入的方法时,如果该调用没有被保护,则会发出 -Wunguarded-availability 警告

void my_fun(NSSomeClass* var) {
  // If fancyNewMethod was added in e.g. macOS 10.12, but the code is
  // built with -mmacos-version-min=10.11, then this unconditional call
  // will emit a -Wunguarded-availability warning:
  [var fancyNewMethod];
}

要修复警告并避免在 macOS 10.11 上崩溃,请将其包装在 if(@available())

void my_fun(NSSomeClass* var) {
  if (@available(macOS 10.12, *)) {
    [var fancyNewMethod];
  } else {
    // Put fallback behavior for old macOS versions (and for non-mac
    // platforms) here.
  }
}

* 是必需的,这意味着未明确列出的平台将采用 true 分支,并且编译器将根据这些平台的部署目标为未列出的平台发出 -Wunguarded-availability 警告。多个平台可以列在 @available()

void my_fun(NSSomeClass* var) {
  if (@available(macOS 10.12, iOS 10, *)) {
    [var fancyNewMethod];
  }
}

如果 my_fun() 的调用者已经检查了 my_fun() 仅在 10.12 上调用,那么向其添加一个 可用性属性,这也将抑制警告并要求检查对 my_fun() 的调用

API_AVAILABLE(macos(10.12)) void my_fun(NSSomeClass* var) {
  [var fancyNewMethod];  // Now ok.
}

@available() 仅在 Objective-C 代码中可用。要在 C 和 C++ 代码中使用此功能,请使用 __builtin_available() 语法。

如果现有代码使用空检查或 -respondsToSelector:,则应将其更改为使用 @available()(或 __builtin_available)。

-Wunguarded-availability 默认情况下处于禁用状态,但 -Wunguarded-availability-new(仅针对在 macOS >= 10.13、iOS >= 11、watchOS >= 4 和 tvOS >= 11 中引入的 API 发出此警告)默认情况下处于启用状态。

Objective-C++ ABI:参数的协议限定符混淆

从 LLVM 3.4 开始,Clang 为类型为限定-id(例如,id<Foo>)的参数生成新的混淆。此混淆允许将此类参数与具有常规未限定 id 类型的参数区分开来。

这是一个对 ABI 的非向后兼容的混淆更改。此更改允许正确重载,并防止与协议限定类型模板参数的混淆冲突。

使用 __has_feature(objc_protocol_qualifier_mangling) 查询此新混淆的存在。

C 中复数的初始化列表

clang 支持一个扩展,它允许在 C 中执行以下操作

#include <math.h>
#include <complex.h>
complex float x = { 1.0f, INFINITY }; // Init to (1, Inf)

此结构很有用,因为在标准 C 中没有办法单独初始化复数变量的实部和虚部,考虑到 clang 不支持 _Imaginary。(Clang 还支持来自 gcc 的 __real____imag__ 扩展,这在某些情况下有所帮助,但在静态初始化器中不可用。)

请注意,此扩展不允许省略花括号;以下两行的含义不同

complex float x[] = { { 1.0f, 1.0f } }; // [0] = (1, 1)
complex float x[] = { 1.0f, 1.0f }; // [0] = (1, 0), [1] = (1, 0)

此扩展在 C++ 模式下也有效,但它不适用于 C++ std::complex。(在 C++11 中,列表初始化允许对 std::complex 使用相同的语法,含义相同。)

为了与 GCC 兼容,__builtin_complex(re, im) 也可以用于根据给定的实部和虚部构建复数。

OpenCL 功能

Clang 支持下面记录的内部 OpenCL 扩展。

__cl_clang_bitfields

使用此扩展,可以使用 OpenCL 扩展pragma 机制(在 OpenCL 扩展规范,第 1.2 节 中详细说明)在结构体或联合体中启用位字段。

在 OpenCL 内核中使用位字段可能会导致可移植性降低,因为当由不同的编译器编译时,结构体布局不能保证一致。如果将带有位字段的结构体用作内核函数参数,则当主机代码和设备代码之间的布局不同时,可能会导致功能错误。

使用方法示例:

#pragma OPENCL EXTENSION __cl_clang_bitfields : enable
struct with_bitfield {
  unsigned int i : 5; // compiled - no diagnostic generated
};

#pragma OPENCL EXTENSION __cl_clang_bitfields : disable
struct without_bitfield {
  unsigned int i : 5; // error - bitfields are not supported
};

__cl_clang_function_pointers

使用此扩展,可以使用 OpenCL 扩展 pragma 机制(在 OpenCL 扩展规范,第 1.2 节 中详细说明)启用依赖于函数指针的各种语言功能。

在用于 OpenCL 的 C++ 中,这还启用了

  • 成员函数指针的使用;

  • 对函数的引用的无限制使用;

  • 虚成员函数。

此类功能不符合标准,并且不能保证在任何情况下都能正确编译。它可以用于以下情况:

  • 内核源代码不包含对(成员)函数指针或虚函数的调用表达式。例如,此扩展可以在元编程算法中使用,以便能够以通用方式指定/检测类型。

  • 生成的内核二进制文件不包含间接调用,因为它们使用编译器优化(例如反虚拟化)被消除。

  • 选定的目标支持函数指针类功能,例如大多数 CPU 目标。

使用方法示例:

#pragma OPENCL EXTENSION __cl_clang_function_pointers : enable
void foo()
{
  void (*fp)(); // compiled - no diagnostic generated
}

#pragma OPENCL EXTENSION __cl_clang_function_pointers : disable
void bar()
{
  void (*fp)(); // error - pointers to function are not allowed
}

__cl_clang_variadic_functions

使用此扩展,可以使用 OpenCL 扩展 pragma 机制(在 OpenCL 扩展规范,第 1.2 节 中详细说明)在函数中启用可变参数。

这不是符合标准的行为,它只能在具有可变参数原型的函数未在二进制文件中生成时可移植地使用,例如,可变参数原型用于在用于 OpenCL 的 C++ 中的元编程算法中指定具有任意数量参数的函数类型。

当内核代码旨在用于支持可变参数的目标(例如大多数 CPU 目标)时,此扩展也可以使用。

使用方法示例:

#pragma OPENCL EXTENSION __cl_clang_variadic_functions : enable
void foo(int a, ...); // compiled - no diagnostic generated

#pragma OPENCL EXTENSION __cl_clang_variadic_functions : disable
void bar(int a, ...); // error - variadic prototype is not allowed

__cl_clang_non_portable_kernel_param_types

使用此扩展,可以使用在 用于 OpenCL 的 C++ v1.0 s2.4 中指定的内核参数中的一些受限类型。可以使用 OpenCL 扩展 pragma 机制(在 OpenCL 扩展规范,第 1.2 节 中详细说明)来放宽这些限制。

这不是符合标准的行为,它只能在内核参数未在主机端访问或主机和设备之间的数据布局/大小已知兼容时使用。

使用方法示例:

// Plain Old Data type.
struct Pod {
  int a;
  int b;
};

// Not POD type because of the constructor.
// Standard layout type because there is only one access control.
struct OnlySL {
  int a;
  int b;
  OnlySL() : a(0), b(0) {}
};

// Not standard layout type because of two different access controls.
struct NotSL {
  int a;
private:
  int b;
};

#pragma OPENCL EXTENSION __cl_clang_non_portable_kernel_param_types : enable
kernel void kernel_main(
  Pod a,

  OnlySL b,
  global NotSL *c,
  global OnlySL *d
);
#pragma OPENCL EXTENSION __cl_clang_non_portable_kernel_param_types : disable

删除地址空间内置函数

__remove_address_space 允许推导出在用于 OpenCL 的 C++ 中已删除地址空间限定符的类型。此实用程序仅影响地址空间限定符,因此其他类型限定符(如 constvolatile)保持不变。

使用方法示例:

template<typename T>
void foo(T *par){
  T var1; // error - local function variable with global address space
  __private T var2; // error - conflicting address space qualifiers
  __private __remove_address_space<T>::type var3; // var3 is __private int
}

void bar(){
  __global int* ptr;
  foo(ptr);
}

使用通用地址空间的旧版 1.x 原子

Clang 允许在用于 OpenCL 的 C++ 模式中使用 OpenCL 1.x 标准中的原子函数,这些函数具有通用地址空间指针。

这是一个不可移植的功能,可能并非所有目标都支持。

使用方法示例:

void foo(__generic volatile unsigned int* a) {
  atomic_add(a, 1);
}

WebAssembly 功能

Clang 支持下面记录的 WebAssembly 功能。有关内置函数语义的更多信息,请参阅 WebAssembly 规范。在本节中,当我们提到引用类型时,我们指的是 WebAssembly 引用类型,而不是 C++ 引用类型,除非另有说明。

__builtin_wasm_table_set

此内置函数将值存储在 WebAssembly 表中。它接受三个参数。第一个参数是将值存储到的表,第二个参数是要将值存储到的索引,第三个参数是存储在表中的引用类型的值。它不返回任何值。

static __externref_t table[0];
extern __externref_t JSObj;

void store(int index) {
  __builtin_wasm_table_set(table, index, JSObj);
}

__builtin_wasm_table_get

此内置函数是 __builtin_wasm_table_set 的对应函数,并从引用类型值 WebAssembly 表中加载值。它接受 2 个参数。第一个参数是引用类型值的表,第二个参数是要从中加载值的索引。它返回加载的引用类型值。

static __externref_t table[0];

__externref_t load(int index) {
  __externref_t Obj = __builtin_wasm_table_get(table, index);
  return Obj;
}

__builtin_wasm_table_size

此内置函数返回 WebAssembly 表的大小。以表作为参数,并使用当前表大小返回一个无符号整数 (size_t)。

typedef void (*__funcref funcref_t)();
static __funcref table[0];

size_t getSize() {
  return __builtin_wasm_table_size(table);
}

__builtin_wasm_table_grow

此内置函数将 WebAssembly 表增大一定量。目前,由于在 C/C++ 中创建的所有 WebAssembly 表都是零大小的,因此始终需要调用此函数以增大表。

它接受三个参数。第一个参数是要增长的 WebAssembly 表。第二个参数是存储在新的表条目中的引用类型值(初始化值),第三个参数是要增长的表的大小。它返回先前的表大小或 -1。如果无法分配足够的内存,它将返回 -1。

typedef void (*__funcref funcref_t)();
static __funcref table[0];

// grow returns the new table size or -1 on error.
int grow(__funcref fn, int delta) {
  int prevSize = __builtin_wasm_table_grow(table, fn, delta);
  if (prevSize == -1)
    return -1;
  return prevSize + delta;
}

__builtin_wasm_table_fill

此内置函数将 WebAssembly 表的所有条目设置为给定的引用类型值。它接受四个参数。第一个参数是 WebAssembly 表,第二个参数是开始范围的索引,第三个参数是在新条目中设置的值,第四个也是最后一个参数是范围的大小。它不返回任何值。

static __externref_t table[0];

// resets a table by setting all of its entries to a given value.
void reset(__externref_t Obj) {
  int Size = __builtin_wasm_table_size(table);
  __builtin_wasm_table_fill(table, 0, Obj, Size);
}

__builtin_wasm_table_copy

此内置函数将元素从源 WebAssembly 表复制到可能重叠的目标区域。它接受五个参数。第一个参数是目标 WebAssembly 表,第二个参数是源 WebAssembly 表。第三个参数是复制开始的索引,第四个参数是复制开始的索引,第五个也是最后一个参数是要复制的元素数量。它不返回任何值。

static __externref_t tableSrc[0];
static __externref_t tableDst[0];

// Copy nelem elements from [src, src + nelem - 1] in tableSrc to
// [dst, dst + nelem - 1] in tableDst
void copy(int dst, int src, int nelem) {
  __builtin_wasm_table_copy(tableDst, tableSrc, dst, src, nelem);
}

内置函数

Clang 支持许多与 GCC 语法相同的内置库函数,包括 __builtin_nan__builtin_constant_p__builtin_choose_expr__builtin_types_compatible_p__builtin_assume_aligned__sync_fetch_and_add 等。除了 GCC 内置函数外,Clang 还支持许多 GCC 不支持的内置函数,这些函数将在此处列出。

请注意,Clang 不会也不打算支持所有用于向量操作的 GCC 内置函数。您应该使用目标特定头文件(如 <xmmintrin.h>)中定义的函数,而不是使用内置函数。这些函数定义了这些操作的可移植包装器。许多 Clang 版本的这些函数是直接根据 扩展向量支持 实现的,而不是内置函数,以减少我们需要实现的内置函数数量。

__builtin_alloca

__builtin_alloca 用于在堆栈上动态分配内存。内存将在函数终止时自动释放。

语法:

__builtin_alloca(size_t n)

使用方法示例:

void init(float* data, size_t nbelems);
void process(float* data, size_t nbelems);
int foo(size_t n) {
  auto mem = (float*)__builtin_alloca(n * sizeof(float));
  init(mem, n);
  process(mem, n);
  /* mem is automatically freed at this point */
}

描述:

__builtin_alloca 用于在堆栈上分配动态数量的内存。此数量受堆栈分配限制。

使用 __has_builtin(__builtin_alloca) 查询此功能。

__builtin_alloca_with_align

__builtin_alloca_with_align 用于在堆栈上动态分配内存,同时控制其对齐方式。内存将在函数终止时自动释放。

语法:

__builtin_alloca_with_align(size_t n, size_t align)

使用方法示例:

void init(float* data, size_t nbelems);
void process(float* data, size_t nbelems);
int foo(size_t n) {
  auto mem = (float*)__builtin_alloca_with_align(
                      n * sizeof(float),
                      CHAR_BIT * alignof(float));
  init(mem, n);
  process(mem, n);
  /* mem is automatically freed at this point */
}

描述:

__builtin_alloca_with_align 用于在堆栈上分配动态数量的内存。它类似于 __builtin_alloca,但接受第二个参数,该参数的值是对齐约束(以 _位_ 为单位的 2 的幂)。

使用 __has_builtin(__builtin_alloca_with_align) 查询此功能。

__builtin_assume

__builtin_assume 用于为优化器提供一个定义为 true 的布尔不变量。

语法:

__builtin_assume(bool)

使用方法示例:

int foo(int x) {
    __builtin_assume(x != 0);
    // The optimizer may short-circuit this check using the invariant.
    if (x == 0)
          return do_something();
    return do_something_else();
}

描述:

此函数的布尔参数定义为 true。优化器可以分析作为参数提供的表达式的形式,并从该信息中推断出用于优化程序的信息。如果在执行期间违反了条件,则行为未定义。参数本身永远不会被求值,因此表达式中的任何副作用都会被丢弃。

使用 __has_builtin(__builtin_assume) 查询此功能。

__builtin_assume_separate_storage

__builtin_assume_separate_storage 用于为优化器提供其两个参数指向单独分配的对象的信息。

语法:

__builtin_assume_separate_storage(const volatile void *, const volatile void *)

使用方法示例:

int foo(int *x, int *y) {
    __builtin_assume_separate_storage(x, y);
    *x = 0;
    *y = 1;
    // The optimizer may optimize this to return 0 without reloading from *x.
    return *x;
}

描述:

假设此函数的参数指向单独分配的存储空间(不同的变量定义或不同的动态存储分配)。优化器可以使用此事实来帮助别名分析。如果参数指向同一个存储空间,则行为未定义。请注意,这里的“存储空间”定义是指任何特定对象的外部封闭分配(例如,在传递同一结构体中的字段、同一数组的元素等的地址时,永远不可能正确调用此函数)。

使用 __has_builtin(__builtin_assume_separate_storage) 查询此功能。

__builtin_offsetof

__builtin_offsetof 用于实现 offsetof 宏,该宏计算给定类型中给定成员的偏移量(以字节为单位)。

语法:

__builtin_offsetof(type-name, member-designator)

使用方法示例:

struct S {
  char c;
  int i;
  struct T {
    float f[2];
  } t;
};

const int offset_to_i = __builtin_offsetof(struct S, i);
const int ext1 = __builtin_offsetof(struct U { int i; }, i); // C extension
const int offset_to_subobject = __builtin_offsetof(struct S, t.f[1]);

描述:

此内置函数可在整型常量表达式中使用,该表达式返回 size_t 类型的返回值。返回的值是以字节为单位的偏移量,从 type-name 类型的对象的开头到成员指定符指定的子对象的偏移量。Clang 以以下方式扩展了必需的标准功能

  • 在 C 语言模式下,第一个参数可以是新类型的定义。以这种方式声明的任何类型都将作用于包含对内置函数调用的最近作用域。

使用 __has_builtin(__builtin_offsetof) 查询此功能。

__builtin_call_with_static_chain

__builtin_call_with_static_chain 用于执行静态调用,同时设置更新静态链寄存器。

语法:

T __builtin_call_with_static_chain(T expr, void* ptr)

使用方法示例:

auto v = __builtin_call_with_static_chain(foo(3), foo);

描述:

此内置函数在检查 expr 是非成员静态调用表达式后返回 expr。对该表达式的调用是在使用 ptr 作为存储在专用寄存器中的函数指针时进行的,以实现 _静态链_ 调用约定,如某些语言用于实现闭包或嵌套函数。

使用 __has_builtin(__builtin_call_with_static_chain) 查询此功能。

__builtin_readcyclecounter

__builtin_readcyclecounter 用于访问那些支持它的目标上的循环计数器寄存器(或类似的低延迟、高精度时钟)。

语法:

__builtin_readcyclecounter()

使用方法示例:

unsigned long long t0 = __builtin_readcyclecounter();
do_something();
unsigned long long t1 = __builtin_readcyclecounter();
unsigned long long cycles_to_do_something = t1 - t0; // assuming no overflow

描述:

__builtin_readcyclecounter() 内置函数返回循环计数器值,该值可能是全局的,也可能是特定于进程/线程的,具体取决于目标。由于支持计数器通常会快速溢出(大约几秒钟),因此这应该只用于计时很短的时间间隔。当目标不支持时,返回值始终为零。此内置函数不接受任何参数,并产生一个无符号长长整数结果。

使用 __has_builtin(__builtin_readcyclecounter) 查询此功能。请注意,即使存在,其使用也可能取决于运行时权限或其他操作系统控制状态。

__builtin_readsteadycounter

__builtin_readsteadycounter 用于访问那些支持它的目标上的固定频率计数器寄存器(或类似的稳定速率时钟)。该函数类似于上面的 __builtin_readcyclecounter,只是频率是固定的,使其适合测量经过时间。

语法:

__builtin_readsteadycounter()

使用方法示例:

unsigned long long t0 = __builtin_readsteadycounter();
do_something();
unsigned long long t1 = __builtin_readsteadycounter();
unsigned long long secs_to_do_something = (t1 - t0) / tick_rate;

描述:

__builtin_readsteadycounter() 内置函数返回频率计数器值。当目标不支持时,返回值始终为零。此内置函数不接受任何参数,并产生一个无符号长长整数结果。内置函数不保证任何特定的频率,只保证它是稳定的。计数器的真实频率需要由用户提供。

使用 __has_builtin(__builtin_readsteadycounter) 查询此功能。

__builtin_cpu_supports

语法:

int __builtin_cpu_supports(const char *features);

使用方法示例:

if (__builtin_cpu_supports("sve"))
  sve_code();

描述:

__builtin_cpu_supports 函数检测运行时 CPU 是否支持字符串参数中指定的特性。如果所有特性都受支持,则返回一个正整数;否则返回 0。特性名称是特定于目标的。在 AArch64 上,特性使用 + 结合,例如 __builtin_cpu_supports("flagm+sha3+lse+rcpc2+fcma+memtag+bti+sme2")。如果特性名称不受支持,Clang 将发出警告,并将内置函数替换为常量 0。

使用 __has_builtin(__builtin_cpu_supports) 查询此功能。

__builtin_dump_struct

语法:

__builtin_dump_struct(&some_struct, some_printf_func, args...);

示例:

struct S {
  int x, y;
  float f;
  struct T {
    int i;
  } t;
};

void func(struct S *s) {
  __builtin_dump_struct(s, printf);
}

示例输出

struct S {
  int x = 100
  int y = 42
  float f = 3.141593
  struct T t = {
    int i = 1997
  }
}
#include <string>
struct T { int a, b; };
constexpr void constexpr_sprintf(std::string &out, const char *format,
                                 auto ...args) {
  // ...
}
constexpr std::string dump_struct(auto &x) {
  std::string s;
  __builtin_dump_struct(&x, constexpr_sprintf, s);
  return s;
}
static_assert(dump_struct(T{1, 2}) == R"(struct T {
  int a = 1
  int b = 2
}
)");

描述:

__builtin_dump_struct 函数用于打印简单结构体的字段及其值,以进行调试。内置函数的第一个参数应该是指向要转储的完整记录类型的指针。第二个参数 f 应该是某个可调用表达式,可以是函数对象或重载集。内置函数调用 f,传递任何其他参数 args...,然后是与 printf 兼容的格式字符串以及相应的参数。 f 可能被多次调用,fargs 将在每次调用时评估一次。在 C++ 中,f 可以是模板或重载集,并针对每次调用解析为不同的函数。

在格式字符串中,将使用适合内置类型的格式说明符,Clang 知道如何格式化这些类型。这包括标准内置类型,以及聚合结构体、void*(使用 %p 打印)和 const char*(使用 %s 打印)。对于 Clang 不知道如何格式化的字段,将使用 *%p 说明符,并且相应的参数将是指向该字段的指针。这允许 C++ 模板化格式化函数检测这种情况并实现自定义格式化。否则,* 不会在格式说明符之前出现。

此内置函数不返回值。

此内置函数可以在常量表达式中使用。

使用 __has_builtin(__builtin_dump_struct) 查询此功能。

__builtin_shufflevector

__builtin_shufflevector 用于表达通用的向量排列/混排/旋转操作。此内置函数对于实现各种特定于目标的头文件(如 <xmmintrin.h>)也非常重要。此内置函数可以在常量表达式中使用。

语法:

__builtin_shufflevector(vec1, vec2, index1, index2, ...)

示例:

// identity operation - return 4-element vector v1.
__builtin_shufflevector(v1, v1, 0, 1, 2, 3)

// "Splat" element 0 of V1 into a 4-element result.
__builtin_shufflevector(V1, V1, 0, 0, 0, 0)

// Reverse 4-element vector V1.
__builtin_shufflevector(V1, V1, 3, 2, 1, 0)

// Concatenate every other element of 4-element vectors V1 and V2.
__builtin_shufflevector(V1, V2, 0, 2, 4, 6)

// Concatenate every other element of 8-element vectors V1 and V2.
__builtin_shufflevector(V1, V2, 0, 2, 4, 6, 8, 10, 12, 14)

// Shuffle v1 with some elements being undefined. Not allowed in constexpr.
__builtin_shufflevector(v1, v1, 3, -1, 1, -1)

描述:

__builtin_shufflevector 的前两个参数是元素类型相同的向量。其余参数是一个整数列表,指定应从前两个向量中提取并返回到新向量中的元素索引。这些元素索引从第一个向量开始按顺序编号,继续到第二个向量。因此,如果 vec1 是一个 4 元素向量,则索引 5 将引用 vec2 的第二个元素。索引 -1 可用于指示返回向量中的对应元素是无关紧要的,可以由后端优化。在常量表达式中不支持 -1 的值。

__builtin_shufflevector 的结果是一个向量,其元素类型与 vec1/vec2 相同,但其元素数量等于指定索引的数量。

使用 __has_builtin(__builtin_shufflevector) 查询此功能。

__builtin_convertvector

__builtin_convertvector 用于表达通用的向量类型转换操作。输入向量和输出向量类型必须具有相同数量的元素。此内置函数可以在常量表达式中使用。

语法:

__builtin_convertvector(src_vec, dst_vec_type)

示例:

typedef double vector4double __attribute__((__vector_size__(32)));
typedef float  vector4float  __attribute__((__vector_size__(16)));
typedef short  vector4short  __attribute__((__vector_size__(8)));
vector4float vf; vector4short vs;

// convert from a vector of 4 floats to a vector of 4 doubles.
__builtin_convertvector(vf, vector4double)
// equivalent to:
(vector4double) { (double) vf[0], (double) vf[1], (double) vf[2], (double) vf[3] }

// convert from a vector of 4 shorts to a vector of 4 floats.
__builtin_convertvector(vs, vector4float)
// equivalent to:
(vector4float) { (float) vs[0], (float) vs[1], (float) vs[2], (float) vs[3] }

描述:

__builtin_convertvector 的第一个参数是一个向量,第二个参数是一个向量类型,其元素数量与第一个参数相同。

__builtin_convertvector 的结果是一个向量,其元素类型与第二个参数相同,其值根据对第一个参数的每个元素应用 C 样式强制转换的动作定义。

使用 __has_builtin(__builtin_convertvector) 查询此功能。

__builtin_bitreverse

  • __builtin_bitreverse8

  • __builtin_bitreverse16

  • __builtin_bitreverse32

  • __builtin_bitreverse64

语法:

__builtin_bitreverse32(x)

示例:

uint8_t rev_x = __builtin_bitreverse8(x);
uint16_t rev_x = __builtin_bitreverse16(x);
uint32_t rev_y = __builtin_bitreverse32(y);
uint64_t rev_z = __builtin_bitreverse64(z);

描述:

__builtin_bitreverse” 系列内置函数用于反转整数值的位模式;例如 0b10110110 变为 0b01101101。这些内置函数可以在常量表达式中使用。

__builtin_rotateleft

  • __builtin_rotateleft8

  • __builtin_rotateleft16

  • __builtin_rotateleft32

  • __builtin_rotateleft64

语法:

__builtin_rotateleft32(x, y)

示例:

uint8_t rot_x = __builtin_rotateleft8(x, y);
uint16_t rot_x = __builtin_rotateleft16(x, y);
uint32_t rot_x = __builtin_rotateleft32(x, y);
uint64_t rot_x = __builtin_rotateleft64(x, y);

描述:

__builtin_rotateleft” 系列内置函数用于将第一个参数中的位按第二个参数中的数量旋转。例如,0b10000110 左旋 11 位变为 0b00110100。移位值被视为按参数大小取模的无符号数量。两个参数和结果都具有内置函数名称指定的位宽。这些内置函数可以在常量表达式中使用。

__builtin_rotateright

  • __builtin_rotateright8

  • __builtin_rotateright16

  • __builtin_rotateright32

  • __builtin_rotateright64

语法:

__builtin_rotateright32(x, y)

示例:

uint8_t rot_x = __builtin_rotateright8(x, y);
uint16_t rot_x = __builtin_rotateright16(x, y);
uint32_t rot_x = __builtin_rotateright32(x, y);
uint64_t rot_x = __builtin_rotateright64(x, y);

描述:

__builtin_rotateright” 系列内置函数用于将第一个参数中的位按第二个参数中的数量旋转。例如,0b10000110 右旋 3 位变为 0b11010000。移位值被视为按参数大小取模的无符号数量。两个参数和结果都具有内置函数名称指定的位宽。这些内置函数可以在常量表达式中使用。

__builtin_unreachable

__builtin_unreachable 用于指示程序中的特定点无法到达,即使编译器可能认为它可以到达。这对于改善优化并消除某些警告很有用。例如,在下面的示例中,如果没有 __builtin_unreachable,编译器将假定内联汇编可以贯穿执行,并打印“声明为“noreturn” 的函数不应该返回” 警告。

语法:

__builtin_unreachable()

使用示例:

void myabort(void) __attribute__((noreturn));
void myabort(void) {
  asm("int3");
  __builtin_unreachable();
}

描述:

__builtin_unreachable() 内置函数具有完全未定义的行为。由于它具有未定义的行为,因此它是一个永远不会到达的语句,优化器可以利用它来生成更好的代码。此内置函数不接受任何参数并产生 void 结果。

使用 __has_builtin(__builtin_unreachable) 查询此功能。

__builtin_unpredictable

__builtin_unpredictable 用于指示分支条件无法由硬件机制(如分支预测逻辑)预测。

语法:

__builtin_unpredictable(long long)

使用示例:

if (__builtin_unpredictable(x > 0)) {
   foo();
}

描述:

__builtin_unpredictable() 内置函数应该与控制流条件一起使用,例如在 ifswitch 语句中使用。

使用 __has_builtin(__builtin_unpredictable) 查询此功能。

__builtin_expect

__builtin_expect 用于指示表达式的值预计与静态已知的结果相同。

语法:

long __builtin_expect(long expr, long val)

使用示例:

if (__builtin_expect(x, 0)) {
   bar();
}

描述:

__builtin_expect() 内置函数通常与控制流条件一起使用,例如在 ifswitch 语句中使用,以帮助分支预测。这意味着它的第一个参数 expr 预计会取其第二个参数 val 的值。它始终返回 expr

使用 __has_builtin(__builtin_expect) 查询此功能。

__builtin_expect_with_probability

__builtin_expect_with_probability 类似于 __builtin_expect,但它将概率作为第三个参数。

语法:

long __builtin_expect_with_probability(long expr, long val, double p)

使用示例:

if (__builtin_expect_with_probability(x, 0, .3)) {
   bar();
}

描述:

__builtin_expect_with_probability() 内置函数通常与控制流条件一起使用,例如在 ifswitch 语句中使用,以帮助分支预测。这意味着它的第一个参数 expr 预计会取其第二个参数 val 的值,概率为 pp 必须在 [0.0 ; 1.0] 范围内。此内置函数始终返回 expr 的值。

使用 __has_builtin(__builtin_expect_with_probability) 查询此功能。

__builtin_prefetch

__builtin_prefetch 用于与缓存处理程序通信,以便在数据被使用之前将其引入缓存。

语法:

void __builtin_prefetch(const void *addr, int rw=0, int locality=3)

使用示例:

__builtin_prefetch(a + i);

描述:

__builtin_prefetch(addr, rw, locality) 内置函数应该用于在开发人员了解接下来要使用哪些数据的情况下避免缓存未命中。 addr 是需要引入缓存的地址。 rw 指示预期的访问模式:0 表示读取1 表示写入。在读写访问的情况下,应使用 1locality 指示数据在缓存中的预期持久性,从 0 表示数据可以在下次使用后从缓存中丢弃,到 3 表示数据将在缓存中被大量重用。 12 提供了这两个极端之间的中间行为。

使用 __has_builtin(__builtin_prefetch) 查询此功能。

__sync_swap

__sync_swap 用于以原子方式交换内存中的整数或指针。

语法:

type __sync_swap(type *ptr, type value, ...)

使用方法示例:

int old_value = __sync_swap(&value, new_value);

描述:

__sync_swap() 内置函数扩展了现有的 __sync_*() 原子内在函数系列,以允许代码以原子方式交换当前值与新值。更重要的是,它通过避免围绕 __sync_bool_compare_and_swap() 的昂贵循环或依赖于 __sync_lock_test_and_set() 的特定于平台的实现细节,帮助开发人员编写更高效、更正确的代码。 __sync_swap() 内置函数是完整的屏障。

__builtin_addressof

__builtin_addressof 的功能与内置 & 运算符相同,忽略任何 operator& 重载。这在 C++11 中的常量表达式中非常有用,因为没有其他方法可以获取重载了 operator& 的对象的地址。Clang 会自动将 [[clang::lifetimebound]] 添加到 __builtin_addressof 的参数中。

使用示例:

template<typename T> constexpr T *addressof(T &value) {
  return __builtin_addressof(value);
}

__builtin_function_start

__builtin_function_start 返回函数体的地址。

语法:

void *__builtin_function_start(function)

使用示例:

void a() {}
void *p = __builtin_function_start(a);

class A {
public:
  void a(int n);
  void a();
};

void A::a(int n) {}
void A::a() {}

void *pa1 = __builtin_function_start((void(A::*)(int)) &A::a);
void *pa2 = __builtin_function_start((void(A::*)()) &A::a);

描述:

__builtin_function_start 内置函数接受一个参数,该参数可以被常量求值为一个函数,并返回函数体的地址。此内置函数并非在所有目标上都受支持。

返回的指针可能与通常获取的函数地址不同,并且不安全调用。例如,使用 -fsanitize=cfi 时,获取函数地址会生成一个可调用指针,指向一个 CFI 跳转表,而 __builtin_function_start 返回一个无法通过 cfi-icall 检查的地址。

__builtin_operator_new__builtin_operator_delete

调用 __builtin_operator_new(args) 与调用 ::operator new(args) 完全相同,区别在于它允许某些优化,而 C++ 标准不允许对 ::operator new 的直接函数调用进行优化(特别是,移除 new / delete 对并合并分配),并且该调用必须解析为一个 可替换的全局分配函数

类似地,__builtin_operator_delete 与调用 ::operator delete(args) 完全相同,区别在于它允许优化,并且该调用必须解析为一个 可替换的全局释放函数

这些内置函数旨在用于 std::allocator 和其他类似的分配库的实现中,并且仅在 C++ 中可用。

使用 __has_builtin(__builtin_operator_new)__has_builtin(__builtin_operator_delete) 查询此功能。

  • 如果该值为至少 201802L,则这些内置函数的行为如上所述。

  • 如果该值不为零,则这些内置函数可能不支持调用任意可替换的全局(释放)分配函数,但至少支持调用 ::operator new(size_t)::operator delete(void*)

__builtin_preserve_access_index

__builtin_preserve_access_index 指定了一个代码段,其中数组下标访问和结构体/联合体成员访问在 bpf 一次编译到处运行框架下是可重新定位的。需要调试信息(通常使用 -g),否则编译器将退出并显示错误。内联函数的返回类型与参数的类型相同。

语法:

type __builtin_preserve_access_index(type arg)

使用方法示例:

struct t {
  int i;
  int j;
  union {
    int a;
    int b;
  } c[4];
};
struct t *v = ...;
int *pb =__builtin_preserve_access_index(&v->c[3].b);
__builtin_preserve_access_index(v->j);

__builtin_debugtrap

__builtin_debugtrap 会导致程序停止执行,以便调试器可以捕获它。

语法:

__builtin_debugtrap()

描述

__builtin_debugtrap 会被转换为 ` llvm.debugtrap <https://llvm.net.cn/docs/LangRef.html#llvm-debugtrap-intrinsic>`_ 内置函数。它应该与在调用内置函数的行上设置断点具有相同的效果。

使用 __has_builtin(__builtin_debugtrap) 查询此功能。

__builtin_trap

__builtin_trap 会导致程序异常停止执行。

语法:

__builtin_trap()

描述

__builtin_trap 会被转换为 ` llvm.trap <https://llvm.net.cn/docs/LangRef.html#llvm-trap-intrinsic>`_ 内置函数。

使用 __has_builtin(__builtin_trap) 查询此功能。

__builtin_arm_trap

__builtin_arm_trap__builtin_trap 的 AArch64 扩展,它也接受一个编译时常量值,该值直接编码到陷阱指令中以供以后检查。

语法:

__builtin_arm_trap(const unsigned short payload)

描述

__builtin_arm_trap 会被转换为 llvm.aarch64.break 内置函数,然后转换为 brk #payload

__builtin_verbose_trap

__builtin_verbose_trap 会导致程序异常停止执行,并在连接调试器或在符号化的崩溃日志中显示对终止原因的人类可读描述。

语法:

__builtin_verbose_trap(const char *category, const char *reason)

描述

__builtin_verbose_trap 会被转换为 ` llvm.trap <https://llvm.net.cn/docs/LangRef.html#llvm-trap-intrinsic>`_ 内置函数。此外,clang 会发出调试信息,该信息表示一个人工内联帧,其名称包含传递给内置函数的类别和原因字符串,并以“magic”前缀开头。

例如,考虑以下代码

void foo(int* p) {
  if (p == nullptr)
    __builtin_verbose_trap("check null", "Argument must not be null!");
}

调试信息看起来就像为以下代码生成的:

__attribute__((always_inline))
inline void "__clang_trap_msg$check null$Argument must not be null!"() {
  __builtin_trap();
}

void foo(int* p) {
  if (p == nullptr)
    "__clang_trap_msg$check null$Argument must not be null!"();
}

但是,生成的代码实际上不会包含对人工函数的调用 - 它只存在于调试信息中。

使用 __has_builtin(__builtin_verbose_trap) 查询此功能。请注意,用户需要启用调试信息才能启用此功能。如果未启用调试信息,则调用此内置函数等效于调用 __builtin_trap

优化器可以合并对具有不同消息的陷阱的调用,这会降低调试体验。

__builtin_allow_runtime_check

__builtin_allow_runtime_check 如果当前程序位置的检查应执行,则返回 true。它应该用于实现类似于 assert 的检查,这些检查可以被优化器安全地移除。

语法:

bool __builtin_allow_runtime_check(const char* kind)

使用示例:

if (__builtin_allow_runtime_check("mycheck") && !ExpensiveCheck()) {
   abort();
}

描述

__builtin_allow_runtime_check 会被转换为 llvm.allow.runtime.check 内置函数。

__builtin_allow_runtime_check() 可以用于像 if 这样的控制结构中,以保护昂贵的运行时检查。返回值由以下编译器选项确定,并且可能在每个调用站点上不同

  • -mllvm -lower-allow-check-percentile-cutoff-hot=N: 禁用配置文件摘要标记为热代码的检查,其热度截止范围在 [0, 999999] 之间(较大的 N 会禁用更多检查)。

  • -mllvm -lower-allow-check-random-rate=P: 以概率 P 保持检查,概率是一个范围在 [0.0, 1.0] 之间的浮点数。

  • 如果同时指定了两个选项,则如果满足任一条件,则会禁用检查。

  • 如果未指定任何选项,则所有检查都允许。

参数 kind 当前未使用,是一个字符串文字,用于指定检查类型。未来的编译器版本可能会使用它来允许更细粒度的控制,例如对不同的检查类型应用不同的热度截止值。

使用 __has_builtin(__builtin_allow_runtime_check) 查询此功能。

__builtin_nondeterministic_value

__builtin_nondeterministic_value 返回与提供的参数类型相同的非确定性值的有效值。

语法:

type __builtin_nondeterministic_value(type x)

示例:

int x = __builtin_nondeterministic_value(x);
float y = __builtin_nondeterministic_value(y);
__m256i a = __builtin_nondeterministic_value(a);

描述

每次调用 __builtin_nondeterministic_value 都会返回参数给出的类型的值。

当前支持的类型:整数类型、浮点类型、向量类型。

使用 __has_builtin(__builtin_nondeterministic_value) 查询此功能。

__builtin_sycl_unique_stable_name

__builtin_sycl_unique_stable_name() 是一个内置函数,它接受一个类型并生成一个字符串文字,其中包含该类型的唯一名称,该名称在拆分编译中是稳定的,主要用于支持 SYCL/数据并行 C++ 语言。

在拆分编译需要在边界上共享类型的唯一标记(例如,在卸载的情况下)的情况下,此名称可用于查找目的,例如在 SYCL 集成头文件中。

此内置函数的值完全在编译时计算,因此它可以在常量表达式中使用。此值基于它们在本地声明上下文中出现的稳定编号顺序对 lambda 函数进行编码。一旦此内置函数在 constexpr 上下文中被求值,则在更改其值的实例化中使用它就会出错。

为了生成唯一名称,内置函数的当前实现使用 Itanium 命名修饰,即使主机编译在运行时使用不同的命名修饰方案。命名修饰器标记命名 SYCL 内核所需的全部 lambda 函数,并发出各自 lambda 函数的稳定本地顺序。生成的模式是可取消修饰的。当非 lambda 类型传递给内置函数时,命名修饰器会发出它们的正常模式,而不会进行任何特殊处理。

语法:

// Computes a unique stable name for the given type.
constexpr const char * __builtin_sycl_unique_stable_name( type-id );

__builtin_popcountg

__builtin_popcountg 返回参数中 1 的数量。参数可以是任何无符号整型。

语法:

int __builtin_popcountg(type x)

示例:

unsigned int x = 1;
int x_pop = __builtin_popcountg(x);

unsigned long y = 3;
int y_pop = __builtin_popcountg(y);

unsigned _BitInt(128) z = 7;
int z_pop = __builtin_popcountg(z);

描述:

__builtin_popcountg 旨在成为 __builtin_popcount{,l,ll} 内建函数的类型泛型替代方案,支持其他整数类型,例如 unsigned __int128 和 C23 unsigned _BitInt(N)

__builtin_clzg__builtin_ctzg

__builtin_clzg(分别为 __builtin_ctzg)返回第一个参数中前导(分别为尾随)0 位的个数。第一个参数可以是任何无符号整数类型。

如果第一个参数为 0 且提供可选的第二个参数为 int 类型,则返回第二个参数。如果第一个参数为 0,但只提供一个参数,则行为未定义。

语法:

int __builtin_clzg(type x[, int fallback])
int __builtin_ctzg(type x[, int fallback])

示例:

unsigned int x = 1;
int x_lz = __builtin_clzg(x);
int x_tz = __builtin_ctzg(x);

unsigned long y = 2;
int y_lz = __builtin_clzg(y);
int y_tz = __builtin_ctzg(y);

unsigned _BitInt(128) z = 4;
int z_lz = __builtin_clzg(z);
int z_tz = __builtin_ctzg(z);

描述:

__builtin_clzg(分别为 __builtin_ctzg)旨在成为 __builtin_clz{,l,ll}(分别为 __builtin_ctz{,l,ll})内建函数的类型泛型替代方案,支持其他整数类型,例如 unsigned __int128 和 C23 unsigned _BitInt(N)

多精度算术内建函数

Clang 提供了一组内建函数,以适合 C 的方式公开多精度算术。它们都具有以下形式

unsigned x = ..., y = ..., carryin = ..., carryout;
unsigned sum = __builtin_addc(x, y, carryin, &carryout);

因此,可以通过以下方式形成多精度加法链

unsigned *x, *y, *z, carryin=0, carryout;
z[0] = __builtin_addc(x[0], y[0], carryin, &carryout);
carryin = carryout;
z[1] = __builtin_addc(x[1], y[1], carryin, &carryout);
carryin = carryout;
z[2] = __builtin_addc(x[2], y[2], carryin, &carryout);
carryin = carryout;
z[3] = __builtin_addc(x[3], y[3], carryin, &carryout);

完整的内建函数列表为

unsigned char      __builtin_addcb (unsigned char x, unsigned char y, unsigned char carryin, unsigned char *carryout);
unsigned short     __builtin_addcs (unsigned short x, unsigned short y, unsigned short carryin, unsigned short *carryout);
unsigned           __builtin_addc  (unsigned x, unsigned y, unsigned carryin, unsigned *carryout);
unsigned long      __builtin_addcl (unsigned long x, unsigned long y, unsigned long carryin, unsigned long *carryout);
unsigned long long __builtin_addcll(unsigned long long x, unsigned long long y, unsigned long long carryin, unsigned long long *carryout);
unsigned char      __builtin_subcb (unsigned char x, unsigned char y, unsigned char carryin, unsigned char *carryout);
unsigned short     __builtin_subcs (unsigned short x, unsigned short y, unsigned short carryin, unsigned short *carryout);
unsigned           __builtin_subc  (unsigned x, unsigned y, unsigned carryin, unsigned *carryout);
unsigned long      __builtin_subcl (unsigned long x, unsigned long y, unsigned long carryin, unsigned long *carryout);
unsigned long long __builtin_subcll(unsigned long long x, unsigned long long y, unsigned long long carryin, unsigned long long *carryout);

已检查算术内建函数

Clang 提供了一组内建函数,以快速且易于在 C 中表达的方式为安全关键应用程序实现已检查算术。作为其用法的示例

errorcode_t security_critical_application(...) {
  unsigned x, y, result;
  ...
  if (__builtin_mul_overflow(x, y, &result))
    return kErrorCodeHackers;
  ...
  use_multiply(result);
  ...
}

Clang 提供以下已检查算术内建函数

bool __builtin_add_overflow   (type1 x, type2 y, type3 *sum);
bool __builtin_sub_overflow   (type1 x, type2 y, type3 *diff);
bool __builtin_mul_overflow   (type1 x, type2 y, type3 *prod);
bool __builtin_uadd_overflow  (unsigned x, unsigned y, unsigned *sum);
bool __builtin_uaddl_overflow (unsigned long x, unsigned long y, unsigned long *sum);
bool __builtin_uaddll_overflow(unsigned long long x, unsigned long long y, unsigned long long *sum);
bool __builtin_usub_overflow  (unsigned x, unsigned y, unsigned *diff);
bool __builtin_usubl_overflow (unsigned long x, unsigned long y, unsigned long *diff);
bool __builtin_usubll_overflow(unsigned long long x, unsigned long long y, unsigned long long *diff);
bool __builtin_umul_overflow  (unsigned x, unsigned y, unsigned *prod);
bool __builtin_umull_overflow (unsigned long x, unsigned long y, unsigned long *prod);
bool __builtin_umulll_overflow(unsigned long long x, unsigned long long y, unsigned long long *prod);
bool __builtin_sadd_overflow  (int x, int y, int *sum);
bool __builtin_saddl_overflow (long x, long y, long *sum);
bool __builtin_saddll_overflow(long long x, long long y, long long *sum);
bool __builtin_ssub_overflow  (int x, int y, int *diff);
bool __builtin_ssubl_overflow (long x, long y, long *diff);
bool __builtin_ssubll_overflow(long long x, long long y, long long *diff);
bool __builtin_smul_overflow  (int x, int y, int *prod);
bool __builtin_smull_overflow (long x, long y, long *prod);
bool __builtin_smulll_overflow(long long x, long long y, long long *prod);

每个内建函数对前两个参数执行指定的数学运算,并将结果存储在第三个参数中。如果可能,结果将等于数学上正确的结果,并且内建函数将返回 0。否则,内建函数将返回 1,结果将等于唯一的值,该值等效于数学上正确的结果模 2 的 k 次方,其中 k 是结果类型中的位数。这些内建函数的行为对所有参数值都是明确定义的。

前三个内建函数对任何整数类型的操作数都有效,包括布尔类型。操作数不必具有与彼此相同的类型,也不必与结果相同。其他内建函数可以在执行运算之前隐式地提升或转换其操作数。

使用 __has_builtin(__builtin_add_overflow) 等查询此功能。

浮点内建函数

__builtin_isfpclass

__builtin_isfpclass 用于测试指定的浮点值是否属于指定的浮点类别之一。

语法:

int __builtin_isfpclass(fp_type expr, int mask)
int_vector __builtin_isfpclass(fp_vector expr, int mask)

使用示例:

if (__builtin_isfpclass(x, 448)) {
   // `x` is positive finite value
       ...
}

描述:

__builtin_isfpclass() 内建函数是对 C 标准定义的函数 isnanisinfisfinite 以及其他一些函数的泛化。它测试由第一个参数指定的浮点值是否属于由第二个参数指定的任何数据类别。后者是整数常量位掩码表达式,其中每个数据类别都使用编码表示为一个位

掩码值

数据类别

0x0001

信号 NaN

__FPCLASS_SNAN

0x0002

静默 NaN

__FPCLASS_QNAN

0x0004

负无穷大

__FPCLASS_NEGINF

0x0008

负正常数

__FPCLASS_NEGNORMAL

0x0010

负次正规数

__FPCLASS_NEGSUBNORMAL

0x0020

负零

__FPCLASS_NEGZERO

0x0040

正零

__FPCLASS_POSZERO

0x0080

正次正规数

__FPCLASS_POSSUBNORMAL

0x0100

正正常数

__FPCLASS_POSNORMAL

0x0200

正无穷大

__FPCLASS_POSINF

为了方便起见,预处理器为这些值定义了宏。如果 expr 属于指定的任何数据类别,则函数返回 1,否则返回 0。

在上面的示例中,掩码值 448 (0x1C0) 包含选择正零、正次正规数和正正常数类别的位。__builtin_isfpclass(x, 448) 仅当 x 属于这些数据类别之一时才返回 true。使用合适的掩码值,该函数可以实现任何标准分类函数,例如,__builtin_isfpclass(x, 3) 等同于 isnan,``__builtin_isfpclass(x, 504)`` - 等同于 isfinite 等等。

如果第一个参数是向量,则该函数等效于对输入逐元素应用的 __builtin_isfpclass 的一组标量调用。

__builtin_isfpclass 的结果是一个布尔值,如果第一个参数是标量,则是一个整数向量,其元素数量与第一个参数相同。此向量中的元素类型具有与第一个参数类型元素相同的位长度。

此函数从不引发浮点异常,并且不规范化其输入。浮点参数不会被提升,其数据类别是根据其在实际语义类型中的表示来确定的。

__builtin_canonicalize

double __builtin_canonicalize(double);
float __builtin_canonicalizef(float);
long double __builtin_canonicalizel(long double);

返回浮点数的平台特定规范编码。此规范化对于实现某些数值原语(如 frexp)很有用。有关语义的更多信息,请参见 LLVM canonicalize 内建函数

__builtin_flt_rounds__builtin_set_flt_rounds

int __builtin_flt_rounds();
void __builtin_set_flt_rounds(int);

返回和设置当前浮点舍入模式。返回的值和输入参数的编码与 C 标准指定的 FLT_ROUNDS 的结果相同: - 0 - 向零舍入 - 1 - 最近舍入,偶数舍入 - 2 - 向正无穷大舍入 - 3 - 向负无穷大舍入 - 4 - 最近舍入,远离零 将其他任何值传递给 __builtin_flt_rounds 的效果是实现定义的。 __builtin_set_flt_rounds 目前仅支持在 x86、x86_64、Arm 和 AArch64 目标上工作。这些内建函数读取和修改浮点环境,这并不总是允许的,并且可能产生意外行为。有关更多信息,请参见有关 访问浮点环境 的部分。

字符串内建函数

Clang 为 C 标准库头文件 <string.h><wchar.h> 中以下函数的内建函数形式提供常量表达式求值支持

  • memchr

  • memcmp(及其已弃用的 BSD/POSIX 别名 bcmp

  • strchr

  • strcmp

  • strlen

  • strncmp

  • wcschr

  • wcscmp

  • wcslen

  • wcsncmp

  • wmemchr

  • wmemcmp

在每种情况下,内建函数形式都以 __builtin_ 为前缀的 C 库函数名称命名。示例

void *p = __builtin_memchr("foobar", 'b', 5);

除了上述之外,还提供了一个内建函数

char *__builtin_char_memchr(const char *haystack, int needle, size_t size);

__builtin_char_memchr(a, b, c) 等同于 (char*)__builtin_memchr(a, b, c),不同之处在于它在 C++11 及更高版本中的常量表达式中是允许的(在一般情况下,不允许从 void*char* 的强制转换)。

仅为 charsigned charunsigned charchar8_t 数组提供 __builtin_mem* 函数的常量表达式求值支持,尽管这些函数接受类型为 const void* 的参数。

可以使用 __has_feature(cxx_constexpr_string_builtins) 检测对上述内建函数的常量表达式求值的支持。

可变参数函数内建函数

Clang 为使用 C 标准库 <stdarg.h> 头文件中的可变参数函数提供了一些内建函数

  • __builtin_va_list

针对特定目标的 va_list 类型的预定义 typedef。通过调用 memcpymemmove 或类似函数产生的此类型的逐字节副本的使用是未定义的行为。有效的显式副本仅通过调用 va_copy__builtin_va_copy 产生。

  • void __builtin_va_start(__builtin_va_list list, <parameter-name>)

针对目标平台的 va_start 函数式宏的内置函数。 parameter-name 参数是函数签名中省略号 (...) 前面的参数的名称。或者,在 C23 模式或更高版本中,如果省略号之前没有参数,则可以是整数字面量 0。 此函数初始化给定的 __builtin_va_list 对象。 对已初始化的 __builtin_va_list 对象调用此函数会导致未定义的行为。

  • void __builtin_va_end(__builtin_va_list list)

针对目标平台的 va_end 函数式宏的内置函数。 此函数完成给定的 __builtin_va_list 对象,使其不再可用,除非使用对 __builtin_va_start__builtin_va_copy 的调用重新初始化。 对未通过 __builtin_va_start__builtin_va_copy 初始化的 list 调用此函数会导致未定义的行为。

  • <type-name> __builtin_va_arg(__builtin_va_list list, <type-name>)

针对目标平台的 va_arg 函数式宏的内置函数。 此函数返回调用中下一个可变参数的值。 当没有下一个可变参数可检索或下一个可变参数的类型与给定的 type-name 不兼容时,调用此内置函数会导致未定义的行为。 函数的返回类型是作为第二个参数给出的 type-name。 对未通过 __builtin_va_start__builtin_va_copy 初始化的 list 调用此函数会导致未定义的行为。

  • void __builtin_va_copy(__builtin_va_list dest, __builtin_va_list src)

针对目标平台的 va_copy 函数式宏的内置函数。 此函数将 dest 初始化为 src 的副本。 对已初始化的 dest 参数调用此函数会导致未定义的行为。

内存内置函数

Clang 为来自 C 标准库头文件 <string.h><wchar.h> 的以下函数的内置形式提供常量表达式求值支持

  • memcpy

  • memmove

  • wmemcpy

  • wmemmove

在每种情况下,内置形式都具有以 __builtin_ 为前缀的 C 库函数的名称。

仅当源和目标是指向具有相同平凡可复制元素类型的数组的指针时,并且给定的大小是元素大小的精确倍数,并且该倍数不大于可通过源和目标操作数访问的元素数量时,才提供常量求值支持。

保证内联复制

void __builtin_memcpy_inline(void *dst, const void *src, size_t size);

__builtin_memcpy_inline 已被设计为高效 memcpy 实现的构建块。 它与 __builtin_memcpy 相同,但也保证不调用任何外部函数。 有关更多信息,请参见 LLVM IR llvm.memcpy.inline 内在函数。

这对于实现 memcpy 的自定义版本、实现 libc memcpy 或解决缺少 libc 的问题很有用。

请注意,size 参数必须是编译时常量。

请注意,此内在函数目前还不能在 constexpr 上下文中调用。

保证内联 memset

void __builtin_memset_inline(void *dst, int value, size_t size);

__builtin_memset_inline 已被设计为高效 memset 实现的构建块。 它与 __builtin_memset 相同,但也保证不调用任何外部函数。 有关更多信息,请参见 LLVM IR llvm.memset.inline 内在函数。

这对于实现 memset 的自定义版本、实现 libc memset 或解决缺少 libc 的问题很有用。

请注意,size 参数必须是编译时常量。

请注意,此内在函数目前还不能在 constexpr 上下文中调用。

__is_bitwise_cloneable

类型特征用于检查类型是否可以安全地通过 memcpy 复制。

语法:

bool __is_bitwise_cloneable(Type)

描述:

位克隆类型对象的位可以通过 memcpy/memmove 复制。 Clang 编译器保证这种行为是定义良好的,并且不会因编译器优化和消毒器而破坏。

对于隐式生命周期类型,新对象的生命周期在复制后隐式开始。 对于其他类型(例如,具有虚方法的类),生命周期不会开始,并且根据 C++ 标准,使用该对象会导致未定义的行为。

此内置函数可以在常量表达式中使用。

带有内存排序的原子 Min/Max 内置函数

有两个原子内置函数,它们在内存中进行最小值/最大值比较和交换。 语法和语义类似于兼容 GCC 的 __atomic_* 内置函数。

  • __atomic_fetch_min

  • __atomic_fetch_max

内置函数可用于带符号和无符号整数,并且需要指定内存排序。 返回值为比较之前存储在内存中的原始值。

示例

unsigned int val = __atomic_fetch_min(unsigned int *pi, unsigned int ui, __ATOMIC_RELAXED);

第三个参数是内存排序说明符之一,例如 __ATOMIC_RELAXED__ATOMIC_CONSUME__ATOMIC_ACQUIRE__ATOMIC_RELEASE__ATOMIC_ACQ_REL__ATOMIC_SEQ_CST,遵循 C++11 内存模型语义。

就获取-释放排序屏障而言,这两个操作始终被视为具有 *加载-存储* 语义的操作,即使在比较后实际上未修改原始值也是如此。

__c11_atomic 内置函数

Clang 提供了一组内置函数,旨在用于实现 C11 的 <stdatomic.h> 头文件。 这些内置函数提供了与 C11 操作的 _explicit 形式相同的语义,并使用 __c11_ 前缀命名。 支持的操作以及与相应 C11 操作的区别如下

  • __c11_atomic_init

  • __c11_atomic_thread_fence

  • __c11_atomic_signal_fence

  • __c11_atomic_is_lock_free(参数是 _Atomic(...) 对象的大小,而不是其地址)

  • __c11_atomic_store

  • __c11_atomic_load

  • __c11_atomic_exchange

  • __c11_atomic_compare_exchange_strong

  • __c11_atomic_compare_exchange_weak

  • __c11_atomic_fetch_add

  • __c11_atomic_fetch_sub

  • __c11_atomic_fetch_and

  • __c11_atomic_fetch_or

  • __c11_atomic_fetch_xor

  • __c11_atomic_fetch_nand(Nand 在 <stdatomic.h> 中没有提供)

  • __c11_atomic_fetch_max

  • __c11_atomic_fetch_min

__ATOMIC_RELAXED__ATOMIC_CONSUME__ATOMIC_ACQUIRE__ATOMIC_RELEASE__ATOMIC_ACQ_REL__ATOMIC_SEQ_CST 已提供,其值对应于 C11 的 memory_order 枚举的枚举器。

(请注意,Clang 还提供了兼容 GCC 的 __atomic_* 内置函数和 OpenCL 2.0 __opencl_atomic_* 内置函数。 OpenCL 2.0 原子内置函数是相应 OpenCL 2.0 内置函数的显式形式,并使用 __opencl_ 前缀命名。 宏 __OPENCL_MEMORY_SCOPE_WORK_ITEM__OPENCL_MEMORY_SCOPE_WORK_GROUP__OPENCL_MEMORY_SCOPE_DEVICE__OPENCL_MEMORY_SCOPE_ALL_SVM_DEVICES__OPENCL_MEMORY_SCOPE_SUB_GROUP 已提供,其值对应于 OpenCL 的 memory_scope 枚举的枚举器。)

__scoped_atomic 内置函数

Clang 提供了一组原子函数,它们接受一个内存范围参数。 这些原子函数与标准 GNU/GCC 原子内置函数相同,但接受额外的内存范围参数。 这些被设计为标准 __opencl_atomic_* 内置函数的通用替代方案,适用于支持原子内存范围的目标。

原子内存范围旨在帮助优化具有多个内存层次结构级别(例如 GPU)的系统。 目前支持以下内存范围

  • __MEMORY_SCOPE_SYSTEM

  • __MEMORY_SCOPE_DEVICE

  • __MEMORY_SCOPE_WRKGRP

  • __MEMORY_SCOPE_WVFRNT

  • __MEMORY_SCOPE_SINGLE

这控制原子操作是否按整个系统、当前设备、OpenCL 工作组、波前或单个线程进行排序。 如果这些在不支持原子范围的目标上使用,那么它们的行为将与标准 GNU 原子内置函数完全相同。

低级 ARM 独占内存内置函数

Clang 提供了重载的内置函数,可以直接访问三个用于实现原子操作的关键 ARM 指令。

T __builtin_arm_ldrex(const volatile T *addr);
T __builtin_arm_ldaex(const volatile T *addr);
int __builtin_arm_strex(T val, volatile T *addr);
int __builtin_arm_stlex(T val, volatile T *addr);
void __builtin_arm_clrex(void);

目前支持的类型 T

  • 宽度不超过 64 位(或 AArch64 上的 128 位)的整数类型。

  • 浮点类型

  • 指针类型。

注意,编译器不保证它不会在 ldrex 类型操作及其配对的 strex 之间插入清除独占监视器的存储。在实践中,这通常只有在额外的存储位于与正在修改的变量相同的缓存行中时才会成为风险,而 Clang 仅会插入它自己的堆栈存储,因此最好不要在具有自动存储时长的变量上使用这些操作。

此外,在 ldrexstrex 之间编写的代码中可能存在隐式加载和存储。Clang 也不一定会减轻这些的影响,因此应谨慎使用。

出于这些原因,应尽可能优先使用更高级别的原子原语。

非临时加载/存储内置函数

Clang 提供重载的内置函数,允许生成非临时内存访问。

T __builtin_nontemporal_load(T *addr);
void __builtin_nontemporal_store(T value, T *addr);

目前支持的类型 T

  • 整数类型。

  • 浮点类型。

  • 向量类型。

注意,编译器不保证会使用非临时加载或存储。

C++ 协程支持内置函数

警告

这仍在开发中。跨 Clang/LLVM 版本的兼容性不受保证。

Clang 提供实验性的内置函数来支持 C++ 协程,如 https://wg21.link/P0057 中所定义。以下四个旨在由标准库使用来实现 std::coroutine_handle 类型。

语法:

void  __builtin_coro_resume(void *addr);
void  __builtin_coro_destroy(void *addr);
bool  __builtin_coro_done(void *addr);
void *__builtin_coro_promise(void *addr, int alignment, bool from_promise)

使用示例:

template <> struct coroutine_handle<void> {
  void resume() const { __builtin_coro_resume(ptr); }
  void destroy() const { __builtin_coro_destroy(ptr); }
  bool done() const { return __builtin_coro_done(ptr); }
  // ...
protected:
  void *ptr;
};

template <typename Promise> struct coroutine_handle : coroutine_handle<> {
  // ...
  Promise &promise() const {
    return *reinterpret_cast<Promise *>(
      __builtin_coro_promise(ptr, alignof(Promise), /*from-promise=*/false));
  }
  static coroutine_handle from_promise(Promise &promise) {
    coroutine_handle p;
    p.ptr = __builtin_coro_promise(&promise, alignof(Promise),
                                                    /*from-promise=*/true);
    return p;
  }
};

其他协程内置函数要么用于内部 Clang 使用,要么用于协程功能开发期间使用。有关其语义的更多信息,请参阅 LLVM 中的协程。注意,与以令牌作为第一个参数的内在函数匹配的内置函数(llvm.coro.begin、llvm.coro.alloc、llvm.coro.free 和 llvm.coro.suspend)省略令牌参数并在发射期间将其填充到适当的值。

语法:

size_t __builtin_coro_size()
void  *__builtin_coro_frame()
void  *__builtin_coro_free(void *coro_frame)

void  *__builtin_coro_id(int align, void *promise, void *fnaddr, void *parts)
bool   __builtin_coro_alloc()
void  *__builtin_coro_begin(void *memory)
void   __builtin_coro_end(void *coro_frame, bool unwind)
char   __builtin_coro_suspend(bool final)

注意,没有与 llvm.coro.save 内在函数匹配的内置函数。如果 llvm.coro.suspend 的第一个参数是令牌 none,LLVM 会自动插入一个。如果用户调用 __builtin_suspend,Clang 会将 token none 作为第一个参数插入到内在函数中。

源位置内置函数

Clang 提供内置函数来支持 C++ 标准库对 std::source_location 的实现,如 C++20 中所指定。除了 __builtin_COLUMN__builtin_FILE_NAME__builtin_FUNCSIG 外,这些内置函数也由 GCC 实现。

语法:

const char *__builtin_FILE();
const char *__builtin_FILE_NAME(); // Clang only
const char *__builtin_FUNCTION();
const char *__builtin_FUNCSIG(); // Microsoft
unsigned    __builtin_LINE();
unsigned    __builtin_COLUMN(); // Clang only
const std::source_location::__impl *__builtin_source_location();

使用示例:

void my_assert(bool pred, int line = __builtin_LINE(), // Captures line of caller
               const char* file = __builtin_FILE(),
               const char* function = __builtin_FUNCTION()) {
  if (pred) return;
  printf("%s:%d assertion failed in function %s\n", file, line, function);
  std::abort();
}

struct MyAggregateType {
  int x;
  int line = __builtin_LINE(); // captures line where aggregate initialization occurs
};
static_assert(MyAggregateType{42}.line == __LINE__);

struct MyClassType {
  int line = __builtin_LINE(); // captures line of the constructor used during initialization
  constexpr MyClassType(int) { assert(line == __LINE__); }
};

描述:

内置函数 __builtin_LINE__builtin_FUNCTION__builtin_FUNCSIG__builtin_FILE__builtin_FILE_NAME 分别在“调用点”返回 __LINE____FUNCTION____FUNCSIG____FILE____FILE_NAME__ 的值。__builtin_COLUMN 类似地返回列,尽管没有相应的宏。这些内置函数是常量表达式。

当内置函数作为默认函数参数的一部分出现时,调用点是调用者的位置。当内置函数作为默认成员初始化器的一部分出现时,调用点是用于创建对象的构造函数或聚合初始化的位置。否则,调用点与内置函数的位置相同。

__builtin_FUNCTION 的调用点不是函数范围时,将返回空字符串。

内置函数 __builtin_source_location 返回指向类型为 std::source_location::__impl 的常量静态数据的指针。此类型必须已经定义,并且必须包含四个字段:const char *_M_file_nameconst char *_M_function_name<any-integral-type> _M_line<any-integral-type> _M_column。这些字段将以与上述四个内置函数相同的方式填充,除了 _M_function_name 使用 __PRETTY_FUNCTION__ 而不是 __FUNCTION__ 填充。

对齐内置函数

Clang 提供内置函数来支持检查和调整指针和整数的对齐方式。这些内置函数可用于避免依赖于从指针派生的整数的实现定义的行为。此外,这些内置函数保留类型信息,并且与按位算术不同,它们可以对对齐值执行语义检查。

语法:

Type __builtin_align_up(Type value, size_t alignment);
Type __builtin_align_down(Type value, size_t alignment);
bool __builtin_is_aligned(Type value, size_t alignment);

使用示例:

char* global_alloc_buffer;
void* my_aligned_allocator(size_t alloc_size, size_t alignment) {
  char* result = __builtin_align_up(global_alloc_buffer, alignment);
  // result now contains the value of global_alloc_buffer rounded up to the
  // next multiple of alignment.
  global_alloc_buffer = result + alloc_size;
  return result;
}

void* get_start_of_page(void* ptr) {
  return __builtin_align_down(ptr, PAGE_SIZE);
}

void example(char* buffer) {
   if (__builtin_is_aligned(buffer, 64)) {
     do_fast_aligned_copy(buffer);
   } else {
     do_unaligned_copy(buffer);
   }
}

// In addition to pointers, the builtins can also be used on integer types
// and are evaluatable inside constant expressions.
static_assert(__builtin_align_up(123, 64) == 128, "");
static_assert(__builtin_align_down(123u, 64) == 64u, "");
static_assert(!__builtin_is_aligned(123, 64), "");

描述:

内置函数 __builtin_align_up__builtin_align_down 返回其第一个参数向上/向下对齐到第二个参数的下一个倍数。如果该值已经足够对齐,则会原样返回。内置函数 __builtin_is_aligned 返回第一个参数是否对齐到第二个参数的倍数。所有这些内置函数都期望对齐方式以字节数表示。

这些内置函数可用于所有整数类型以及(非函数)指针类型。对于指针类型,这些内置函数根据指针的整数地址进行操作,并返回一个新的相同类型(包括限定符,例如 const)的指针,其地址已调整。向上或向下对齐指针时,结果值必须位于同一底层分配内或超过末尾一个(参见 C17 6.5.6p8、C++ [expr.add])。这意味着存储在指针类型变量中的任意整数值不能传递给这些内置函数。对于这些用例,仍然可以使用这些内置函数,但必须在将指针强制转换为 uintptr_t 后执行操作。

如果 Clang 可以确定对齐方式在编译时不是 2 的幂,它将导致编译失败。如果对齐参数在运行时不是 2 的幂,这些内置函数的行为是未定义的。

非标准 C++11 属性

Clang 的非标准 C++11 属性位于 clang 属性命名空间中。

Clang 支持 GCC 的 gnu 属性命名空间。所有使用 __attribute__((foo)) 语法接受的 GCC 属性也作为 [[gnu::foo]] 接受。这仅扩展到由 GCC 指定的属性(参见 GCC 函数属性GCC 变量属性GCC 类型属性 的列表)。与 GCC 实现一样,这些属性必须属于声明中的declarator-id,这意味着它们必须位于声明的开头或声明的名称之后。

例如,这将 GNU unused 属性应用于 af,并将 GNU noreturn 属性应用于 f

示例:.. code-block:: c++

[[gnu::unused]] int a, f [[gnu::noreturn]] ();

特定于目标的扩展

Clang 有条件地支持某些语言功能,具体取决于某些目标。

AMDGPU 语言扩展

__builtin_amdgcn_fence

__builtin_amdgcn_fence 发射栅栏。

  • unsigned 原子排序,例如 __ATOMIC_ACQUIRE

  • const char * 同步范围,例如 workgroup

  • 零个或多个 const char * 地址空间名称。

地址空间参数必须是以下字符串文字之一

  • "local"

  • "global"

如果提供了一个或多个地址空间名称,代码生成器将尝试发射可能更快的指令,这些指令至少对这些地址空间进行排序。发射此类指令可能并不总是可行,编译器可以自由地更积极地进行栅栏。

如果没有提供地址空间名称,则所有地址空间都将进行栅栏。

// Fence all address spaces.
__builtin_amdgcn_fence(__ATOMIC_SEQ_CST, "workgroup");
__builtin_amdgcn_fence(__ATOMIC_ACQUIRE, "agent");

// Fence only requested address spaces.
__builtin_amdgcn_fence(__ATOMIC_SEQ_CST, "workgroup", "local")
__builtin_amdgcn_fence(__ATOMIC_SEQ_CST, "workgroup", "local", "global")

ARM/AArch64 语言扩展

内存屏障内在函数

Clang 在 Arm C 语言扩展 中定义的 __dmb__dsb__isb 内在函数中实现。请注意,这些内在函数实现为运动屏障,它们阻止内存访问和副作用指令的重新排序。其他指令(如简单算术)可能会重新排序到内在函数周围。如果您希望根本没有重新排序,请使用内联汇编代替。

指针验证

参见 指针验证

X86/X86-64 语言扩展

X86 后端具有以下语言扩展

对指定段的内存引用

使用地址空间 #256 对指针进行注释会导致它相对于 X86 GS 段寄存器进行代码生成,地址空间 #257 会导致它相对于 X86 FS 段进行代码生成,而地址空间 #258 会导致它相对于 X86 SS 段进行代码生成。请注意,这是一个非常底层的特性,只有在您知道自己在做什么时才应使用它(例如,在操作系统内核中)。

这是一个例子

#define GS_RELATIVE __attribute__((address_space(256)))
int foo(int GS_RELATIVE *P) {
  return *P;
}

这将编译为(在 X86-32 上)

_foo:
        movl    4(%esp), %eax
        movl    %gs:(%eax), %eax
        ret

您还可以使用 GCC 兼容性宏 __seg_fs__seg_gs 来实现相同目的。预处理器符号 __SEG_FS__SEG_GS 指示它们的支持。

PowerPC 语言扩展

设置浮点舍入模式

PowerPC64/PowerPC64le 支持内建函数 __builtin_setrnd 来设置浮点舍入模式。此函数将使用整数参数的最低两位来设置浮点舍入模式。

double __builtin_setrnd(int mode);

模式的有效值是

  • 0 - 舍入到最接近

  • 1 - 舍入到零

  • 2 - 舍入到 +无穷大

  • 3 - 舍入到 -无穷大

注意,模式参数将对 4 取模,因此如果整数参数大于 3,它将只使用模式的最低两位。也就是说,__builtin_setrnd(102)) 等于 __builtin_setrnd(2)

PowerPC 缓存内建函数

PowerPC 架构指定了实现缓存操作的指令。Clang 提供了内建函数,使程序员可以直接访问这些缓存指令。

目前,Clang 中实现了以下内建函数

__builtin_dcbf 将修改块的内容从数据缓存复制到主内存,并从数据缓存中清除副本。

语法:

void __dcbf(const void* addr); /* Data Cache Block Flush */

使用方法示例:

int a = 1;
__builtin_dcbf (&a);

静态分析扩展

Clang 支持其他属性,这些属性对于记录程序不变量和静态分析工具的规则很有用,例如 Clang 静态分析器。这些属性在分析器的 源代码级注释列表 中有文档。

动态分析扩展

使用 __has_feature(address_sanitizer) 检查代码是否正在使用 AddressSanitizer 构建。

使用 __has_feature(thread_sanitizer) 检查代码是否正在使用 ThreadSanitizer 构建。

使用 __has_feature(memory_sanitizer) 检查代码是否正在使用 MemorySanitizer 构建。

使用 __has_feature(dataflow_sanitizer) 检查代码是否正在使用 DataFlowSanitizer 构建。

使用 __has_feature(safe_stack) 检查代码是否正在使用 SafeStack 构建。

选择性禁用优化的扩展

Clang 提供了一种机制,可以在函数和方法中选择性地禁用优化。

要禁用单个函数定义中的优化,可以使用 GNU 风格或 C++11 非标准属性 optnone

// The following functions will not be optimized.
// GNU-style attribute
__attribute__((optnone)) int foo() {
  // ... code
}
// C++11 attribute
[[clang::optnone]] int bar() {
  // ... code
}

为了方便禁用一系列函数定义的优化,提供了一个基于范围的pragma。其语法是 #pragma clang optimize 后跟 offon

off 和下一个 on 之间的区域中的所有函数定义都将使用 optnone 属性进行修饰,除非这样做会导致与函数上已经存在的显式属性冲突(例如控制内联的属性)。

#pragma clang optimize off
// This function will be decorated with optnone.
int foo() {
  // ... code
}

// optnone conflicts with always_inline, so bar() will not be decorated.
__attribute__((always_inline)) int bar() {
  // ... code
}
#pragma clang optimize on

如果找不到 on 来关闭 off 区域,则区域的末尾是编译单元的末尾。

请注意,一个独立的 #pragma clang optimize on 在低优化级别进行编译时不会选择性地启用其他优化。此功能只能用于选择性地禁用优化。

pragma 对函数的影响仅在函数定义点生效;对于函数模板,这意味着 pragma 在实例化点的状态并不一定相关。考虑以下示例

template<typename T> T twice(T t) {
  return 2 * t;
}

#pragma clang optimize off
template<typename T> T thrice(T t) {
  return 3 * t;
}

int container(int a, int b) {
  return twice(a) + thrice(b);
}
#pragma clang optimize on

在此示例中,模板函数 twice 的定义在 pragma 区域之外,而 thrice 的定义在区域内。container 函数也在区域内,并且不会被优化,但它会导致使用 int 类型实例化 twicethrice;在这两个实例中,twice 将被优化(因为它的定义在区域之外),而 thrice 不会被优化。

Clang 还实现了 MSVC 的基于范围的 pragma,#pragma optimize("[optimization-list]", on | off)。目前,Clang 仅支持空优化列表,而 MSVC 支持参数 sgty。目前,pragma optimize 的实现与 #pragma clang optimize 的行为相同。在 offon 之间的区域中的所有函数都将使用 optnone 属性进行修饰。

#pragma optimize("", off)
// This function will be decorated with optnone.
void f1() {}

#pragma optimize("", on)
// This function will be optimized with whatever was specified on
// the commandline.
void f2() {}

// This will warn with Clang's current implementation.
#pragma optimize("g", on)
void f3() {}

对于 MSVC,空优化列表和 off 参数将关闭所有优化,sgty。空优化和 on 参数将重置优化为命令行中指定的优化。

参数(Clang 不支持)

参数

优化类型

g

已弃用

s 或 t

短或快的机器代码序列

y

启用帧指针

循环提示优化的扩展

指令 #pragma clang loop 用于为优化后续的 for、while、do-while 或 c++11 基于范围的 for 循环指定提示。该指令提供了向量化、交织、预测、展开和分配的选项。循环提示可以在任何循环之前指定,如果优化不安全应用,将被忽略。

有一些循环提示控制转换(例如向量化、循环展开),还有一些循环提示设置转换选项(例如 vectorize_widthunroll_count)。设置转换选项的pragma 意味着转换已启用,就像它通过相应的转换 pragma 启用一样(例如 vectorize(enable))。如果转换被禁用(例如 vectorize(disable)),它将优先于暗示该转换的转换选项 pragma。

向量化、交织和预测

向量化循环使用向量指令并行执行原始循环的多个迭代。目标处理器的指令集决定了哪些向量指令可用及其向量宽度。这限制了可以向量化的循环类型。向量化器会自动确定循环是否安全且有利于向量化。向量指令成本模型用于选择向量宽度。

交织多个循环迭代允许现代处理器使用高级硬件功能(例如多个执行单元和乱序执行)进一步提高指令级并行性 (ILP)。向量化器使用一个成本模型,该模型取决于寄存器压力和生成的代码大小来选择交织计数。

vectorize(enable) 启用向量化,interleave(enable) 启用交织。这在使用 -Os 编译时,手动启用向量化或交织很有用。

#pragma clang loop vectorize(enable)
#pragma clang loop interleave(enable)
for(...) {
  ...
}

向量宽度由 vectorize_width(_value_[, fixed|scalable]) 指定,其中 _value_ 是一个正整数,向量化类型可以使用可选的第二个参数指定。第二个参数的默认值为 ‘fixed’,表示固定宽度向量化,而 ‘scalable’ 表示编译器应该使用可扩展向量。向量化宽度的另一种用法是 vectorize_width(fixed|scalable),用户可以在不指定确切宽度的条件下提示使用哪种向量化类型。在这两种 pragma 变体中,如果目标不支持可扩展向量,向量化器可能会决定回退到固定宽度向量化。

交织计数由 interleave_count(_value_) 指定,其中 _value_ 是一个正整数。这对于指定应用程序支持的目标体系结构集的最佳宽度/计数很有用。

#pragma clang loop vectorize_width(2)
#pragma clang loop interleave_count(2)
for(...) {
  ...
}

指定 1 的宽度/计数将禁用优化,等效于 vectorize(disable)interleave(disable)

向量预测由 vectorize_predicate(enable) 启用,例如

#pragma clang loop vectorize(enable)
#pragma clang loop vectorize_predicate(enable)
for(...) {
  ...
}

这会对循环中的所有指令进行预测(掩盖),这允许将标量余数循环(尾部)折叠到主要的向量化循环中。当目标平台有效地支持向量预测时,这可能更有效。

循环展开

展开循环会减少循环控制开销,并提供更多 ILP 机会。循环可以完全展开或部分展开。完全展开将消除循环,并用循环迭代的枚举序列替换它。只有在编译时知道循环行程计数时,才能进行完全展开。部分展开在循环内复制循环体并减少行程计数。

如果指定了unroll(enable),则展开器将尝试在编译时知道循环次数的情况下完全展开循环。如果完全展开的代码大小大于内部限制,则循环将被部分展开到该限制。如果在编译时不知道循环次数,则循环将被部分展开,并使用启发式选择的展开因子。

#pragma clang loop unroll(enable)
for(...) {
  ...
}

如果指定了unroll(full),则展开器将尝试在编译时知道循环次数的情况下完全展开循环,与unroll(enable)相同。但是,使用unroll(full),如果在编译时不知道循环次数,则循环将不会被展开。

#pragma clang loop unroll(full)
for(...) {
  ...
}

可以使用unroll_count(_value_)显式指定展开计数,其中_value_是正整数。如果该值大于循环次数,则循环将被完全展开。否则,循环将被部分展开,并受与unroll(enable)相同的代码大小限制。

#pragma clang loop unroll_count(8)
for(...) {
  ...
}

可以通过指定unroll(disable)来阻止循环展开。

循环展开参数可以通过选项-mllvm -unroll-count=n-mllvm -pragma-unroll-threshold=n进行控制。

循环分配

循环分配允许将一个循环拆分为多个循环。例如,当整个循环无法向量化但一些生成的循环可以向量化时,这将很有益。

如果指定了distribute(enable)),并且循环具有阻止向量化的内存依赖关系,则编译器将尝试将有问题的操作隔离到一个新的循环中。默认情况下,此优化不会被启用,只有用 pragma 标记的循环才会被考虑。

#pragma clang loop distribute(enable)
for (i = 0; i < N; ++i) {
  S1: A[i + 1] = A[i] + B[i];
  S2: C[i] = D[i] * E[i];
}

此循环将在语句 S1 和 S2 之间被拆分为两个循环。包含 S2 的第二个循环将被向量化。

循环分配目前在优化器中默认情况下不会被启用,因为它在某些情况下会导致性能下降。例如,指令级并行性可能会因按顺序执行上面的语句 S1 和 S2 而降低。

如果循环分配使用-mllvm -enable-loop-distribution全局启用,则可以使用distribute(disable)在每个循环的基础上禁用它。

附加信息

为了方便起见,可以在一行上指定多个循环提示。

#pragma clang loop vectorize_width(4) interleave_count(8)
for(...) {
  ...
}

如果无法应用优化,则所有适用于它的提示都将被忽略。例如,如果循环未被证明是安全的向量化,则提示vectorize_width(4)将被忽略。要识别和诊断优化问题,请使用-Rpass-Rpass-missed-Rpass-analysis命令行选项。有关详细信息,请参阅用户指南。

指定浮点标志的扩展

#pragma clang fppragma 允许为源代码的一部分指定浮点选项。此 pragma 只能出现在文件作用域或复合语句的开头(不包括注释)。在复合语句中使用时,pragma 在复合语句的作用域内有效。

目前,可以使用此 pragma 控制以下设置

#pragma clang fp reassociate允许控制浮点表达式的重新关联。启用后,此 pragma 允许表达式x + (y + z)重新关联为(x + y) + z。重新关联也可能跨多个语句发生。此 pragma 可用于在使用-fassociative-math标志为翻译单元启用时禁用重新关联。此 pragma 可以接受两个值:onoff

float f(float x, float y, float z)
{
  // Enable floating point reassociation across statements
  #pragma clang fp reassociate(on)
  float t = x + y;
  float v = t + z;
}

#pragma clang fp reciprocal允许控制在浮点表达式中使用倒数近似值。启用后,此 pragma 允许表达式x / y近似为x * (1.0 / y)。此 pragma 可用于在使用-freciprocal-math标志或其他快速数学选项为翻译单元启用时禁用倒数近似值。此 pragma 可以接受两个值:onoff

float f(float x, float y)
{
  // Enable floating point reciprocal approximation
  #pragma clang fp reciprocal(on)
  return x / y;
}

#pragma clang fp contract指定编译器是否应该在目标支持的情况下将乘法和加法(或减法)收缩为融合的 FMA 操作。

此 pragma 可以接受三个值:onfastoffon选项与使用#pragma STDC FP_CONTRACT(ON)相同,它允许像语言标准中指定的那样进行融合。fast选项允许在语言标准不允许的情况下进行融合(例如,在 C 中跨语句)。

for(...) {
  #pragma clang fp contract(fast)
  a = b[i] * c[i];
  d[i] += a;
}

此 pragma 也可以与off一起使用,这将为代码的一部分关闭 FP 收缩。当使用-ffp-contract=fast-honor-pragmas标志为翻译单元启用快速收缩时,这将非常有用。请注意,-ffp-contract=fast将覆盖 pragma 以跨语句融合乘法和加法,而不管任何控制 pragma。

#pragma clang fp exceptions指定浮点异常行为。它可以接受以下值之一:ignoremaytrapstrict。这些值的含义与约束浮点内在函数相同。

{
  // Preserve floating point exceptions
  #pragma clang fp exceptions(strict)
  z = x + y;
  if (fetestexcept(FE_OVERFLOW))
    ...
}

一个#pragma clang fppragma 可以包含任意数量的选项

void func(float *dest, float a, float b) {
  #pragma clang fp exceptions(maytrap) contract(fast) reassociate(on)
  ...
}

#pragma clang fp eval_method允许为源代码的一部分指定浮点行为。此 pragma 可以在文件或命名空间作用域,或复合语句的开头(不包括注释)出现。此 pragma 在复合语句的作用域内有效。

pragma clang fp eval_method(source)启用时,由 pragma 控制的代码部分的行为就像命令行选项-ffp-eval-method=source被启用一样。将中间结果舍入到源定义的精度。

pragma clang fp eval_method(double)启用时,由 pragma 控制的代码部分的行为就像命令行选项-ffp-eval-method=double被启用一样。将中间结果舍入到double精度。

pragma clang fp eval_method(extended)启用时,由 pragma 控制的代码部分的行为就像命令行选项-ffp-eval-method=extended被启用一样。将中间结果舍入到目标相关的long double精度。例如,在 Win32 编程中,long double 数据类型映射到 double,64 位精度数据类型。

此 pragma 支持的完整语法是#pragma clang fp eval_method(source|double|extended)

for(...) {
  // The compiler will use long double as the floating-point evaluation
  // method.
  #pragma clang fp eval_method(extended)
  a = b[i] * c[i] + e;
}

注意:math.h根据包含头文件时的活动评估方法定义类型定义float_tdouble_t,而不是使用类型定义的地方。因此,不建议将这些类型定义与#pragma clang fp eval_method结合使用。为了捕捉明显的错误,Clang 将对在此 pragma 作用域内的任何对这些类型定义的引用发出错误;但是,这不是一个万无一失的保护,程序员必须小心。

#pragma float_controlpragma 允许为源代码的一部分指定精确的浮点语义和浮点异常行为。此 pragma 只能出现在文件或命名空间作用域,语言链接规范内,或复合语句的开头(不包括注释)。在复合语句中使用时,pragma 在复合语句的作用域内有效。此 pragma 模仿了一个具有相同拼写和语法的 Microsoft pragma。对于在文件或命名空间作用域,或语言链接规范中指定的 pragma,支持堆栈,以便可以推送或弹出pragma float_control设置。

pragma float_control(precise, on)启用时,由 pragma 控制的代码部分使用精确的浮点语义,实际上-ffast-math被禁用,而-ffp-contract=on(融合乘法加法)被启用。此 pragma 启用-fmath-errno

pragma float_control(precise, off)启用时,在由 pragma 控制的代码部分中启用了不安全的浮点优化。实际上-ffast-math被启用,而-ffp-contract=fast。此 pragma 禁用-fmath-errno

当启用 pragma float_control(except, on) 时,该 pragma 所控制的代码部分的行为就好像启用了命令行选项 -ffp-exception-behavior=strict 一样;当启用 pragma float_control(except, off) 时,该 pragma 所控制的代码部分的行为就好像启用了命令行选项 -ffp-exception-behavior=ignore 一样。

此 pragma 支持的完整语法是 float_control(except|precise, on|off [, push])float_control(push|pop)pushpop 形式(包括将 push 用作可选的第三个参数)只能出现在文件范围内。

for(...) {
  // This block will be compiled with -fno-fast-math and -ffp-contract=on
  #pragma float_control(precise, on)
  a = b[i] * c[i] + e;
}

为多个声明指定属性 (#pragma clang attribute)

可以使用 #pragma clang attribute 指令将属性应用于多个声明。指令的 #pragma clang attribute push 变体将一个新的 #pragma clang attribute “范围”推入堆栈,属性可以添加到该范围中。 #pragma clang attribute (...) 变体将属性添加到该范围中,而 #pragma clang attribute pop 变体将该范围弹出堆栈。您还可以使用 #pragma clang attribute push (...),这是当您想将一个属性添加到一个新范围时的简写形式。多个 push 指令可以相互嵌套。

#pragma clang attribute 指令中使用的属性可以使用 GNU 风格的语法编写

#pragma clang attribute push (__attribute__((annotate("custom"))), apply_to = function)

void function(); // The function now has the annotate("custom") attribute

#pragma clang attribute pop

属性也可以使用 C++11 风格的语法编写

#pragma clang attribute push ([[noreturn]], apply_to = function)

void function(); // The function now has the [[noreturn]] attribute

#pragma clang attribute pop

还支持 __declspec 风格的语法

#pragma clang attribute push (__declspec(dllexport), apply_to = function)

void function(); // The function now has the __declspec(dllexport) attribute

#pragma clang attribute pop

单个 push 指令可以包含多个属性,但是,单个指令中只能使用一种语法风格

#pragma clang attribute push ([[noreturn, noinline]], apply_to = function)

void function1(); // The function now has the [[noreturn]] and [[noinline]] attributes

#pragma clang attribute pop

#pragma clang attribute push (__attribute((noreturn, noinline)), apply_to = function)

void function2(); // The function now has the __attribute((noreturn)) and __attribute((noinline)) attributes

#pragma clang attribute pop

由于多个 push 指令可以嵌套,如果您正在编写一个扩展为 _Pragma("clang attribute") 的宏,那么最好(虽然不是必需的)为 push/pop 指令添加一个命名空间。带有命名空间的 pop 指令将弹出具有相同命名空间的最内层 push。这将确保另一个宏的 pop 不会意外地弹出您的属性。请注意,没有命名空间的 pop 将弹出最内层没有命名空间的 push。带有命名空间的 push 只能被具有相同命名空间的 pop 弹出。例如

#define ASSUME_NORETURN_BEGIN _Pragma("clang attribute AssumeNoreturn.push ([[noreturn]], apply_to = function)")
#define ASSUME_NORETURN_END   _Pragma("clang attribute AssumeNoreturn.pop")

#define ASSUME_UNAVAILABLE_BEGIN _Pragma("clang attribute Unavailable.push (__attribute__((unavailable)), apply_to=function)")
#define ASSUME_UNAVAILABLE_END   _Pragma("clang attribute Unavailable.pop")


ASSUME_NORETURN_BEGIN
ASSUME_UNAVAILABLE_BEGIN
void function(); // function has [[noreturn]] and __attribute__((unavailable))
ASSUME_NORETURN_END
void other_function(); // function has __attribute__((unavailable))
ASSUME_UNAVAILABLE_END

如果没有宏上的命名空间, other_function 将被注释为 [[noreturn]] 而不是 __attribute__((unavailable))。这看起来可能是一个人为的例子,但如果 pragma 分布在一个大型文件中,这种情况下很可能出现在实际代码中。您可以使用 __has_extension(pragma_clang_attribute_namespaces) 测试您的 clang 版本是否支持 #pragma clang attribute 上的命名空间。

主题匹配规则

从属性堆栈接收单个属性的声明集取决于在 pragma 中指定的主题匹配规则。主题匹配规则在属性之后指定。编译器期望一个标识符,该标识符对应于主题集说明符。 apply_to 说明符是当前唯一支持的主题集说明符。它允许您指定形成属性允许的主题集的子集的匹配规则,即编译器不需要所有属性的主题。例如,像 [[nodiscard]] 这样的属性,其主题集包括 enumrecordhasType(functionType),要求在 apply_to 之后至少存在其中一个规则

#pragma clang attribute push([[nodiscard]], apply_to = enum)

enum Enum1 { A1, B1 }; // The enum will receive [[nodiscard]]

struct Record1 { }; // The struct will *not* receive [[nodiscard]]

#pragma clang attribute pop

#pragma clang attribute push([[nodiscard]], apply_to = any(record, enum))

enum Enum2 { A2, B2 }; // The enum will receive [[nodiscard]]

struct Record2 { }; // The struct *will* receive [[nodiscard]]

#pragma clang attribute pop

// This is an error, since [[nodiscard]] can't be applied to namespaces:
#pragma clang attribute push([[nodiscard]], apply_to = any(record, namespace))

#pragma clang attribute pop

可以使用 any 匹配规则指定多个匹配规则,如上面的示例所示。 any 规则将属性应用于至少与 any 中的一个规则匹配的所有声明。它不嵌套,不能在其他匹配规则中使用。 any 中不应使用冗余匹配规则或相互冲突的规则。如果未在 any 规则中指定规则,则会导致错误。

Clang 支持以下匹配规则

  • function:可用于将属性应用于函数。这包括 C++ 成员函数、静态函数、运算符和构造函数/析构函数。

  • function(is_member):可用于将属性应用于 C++ 成员函数。这包括像静态函数、运算符和构造函数/析构函数这样的成员。

  • hasType(functionType):可用于将属性应用于函数、C++ 成员函数以及类型为函数指针的变量/字段。它不将属性应用于 Objective-C 方法或代码块。

  • type_alias:可用于将属性应用于 typedef 声明和 C++11 类型别名。

  • record:可用于将属性应用于 structclassunion 声明。

  • record(unless(is_union)):可用于将属性仅应用于 structclass 声明。

  • enum:可用于将属性应用于枚举声明。

  • enum_constant:可用于将属性应用于枚举器。

  • variable:可用于将属性应用于变量,包括局部变量、参数、全局变量和静态成员变量。它不将属性应用于实例成员变量或 Objective-C ivars。

  • variable(is_thread_local):可用于将属性应用于仅限线程的变量。

  • variable(is_global):可用于将属性应用于仅限全局变量。

  • variable(is_local):可用于将属性应用于仅限局部变量。

  • variable(is_parameter):可用于将属性应用于仅限参数。

  • variable(unless(is_parameter)):可用于将属性应用于所有不是参数的变量。

  • field:可用于将属性应用于记录中非静态成员变量。这包括 Objective-C ivars。

  • namespace:可用于将属性应用于 namespace 声明。

  • objc_interface:可用于将属性应用于 @interface 声明。

  • objc_protocol:可用于将属性应用于 @protocol 声明。

  • objc_category:可用于将属性应用于类别声明,包括类扩展。

  • objc_method:可用于将属性应用于 Objective-C 方法,包括实例方法和类方法。隐式方法(如隐式属性 getter 和 setter)不会接收属性。

  • objc_method(is_instance):可用于将属性应用于 Objective-C 实例方法。

  • objc_property:可用于将属性应用于 @property 声明。

  • block:可用于将属性应用于代码块声明。这并不包括代码块指针类型的变量/字段。

在匹配规则中使用 unless 目前仅限于支持的属性使用的严格子规则集。这意味着即使 variable(unless(is_parameter)) 是有效的匹配规则, variable(unless(is_thread_local)) 也不行。

支持的属性

并非所有属性都可以在 #pragma clang attribute 指令中使用。值得注意的是,语句属性(如 [[fallthrough]])或类型属性(如 address_space)不受此指令支持。您可以通过参考 该属性的单独文档 来确定属性是否受 pragma 支持。

属性将分别应用于所有匹配的声明,即使属性在语义上不正确。没有应用于任何声明的属性不会在语义上进行验证。

为全局对象指定节名称 (#pragma clang section)

#pragma clang section 指令提供了一种为全局变量、函数和静态变量分配节名称的方法。

节名称可以指定为

#pragma clang section bss="myBSS" data="myData" rodata="myRodata" relro="myRelro" text="myText"

通过向节类型提供空字符串,可以将节名称恢复为默认名称,例如

#pragma clang section bss="" data="" text="" rodata="" relro=""

#pragma clang section 指令遵循以下规则

  • 此 pragma 适用于从 pragma 到翻译单元末尾的所有全局变量、静态变量和函数声明。

  • pragma clang section 自动启用,无需任何标志。

  • 此功能仅定义为对 ELF 和 Mach-O 目标正常工作。

  • 如果节名称通过 _attribute_((section(“myname”))) 指定,则属性名称优先。

  • 初始化为零的全局变量将被放置在命名的 bss 节中(如果存在)。

  • #pragma clang section 指令不会尝试从名称推断节类型。例如,将节命名为“.bss.mySec”并不意味着它将成为 bss 节名称。

  • 关于哪个节类型适用于每个全局变量的决定是在后端做出的。一旦知道节类型,就会将用户使用 #pragma clang section 指令指定的适当节名称应用于该全局变量。

在 ELF 目标上指定链接器选项

#pragma comment(lib, ...) 指令在所有 ELF 目标上受支持。第二个参数是库名称(没有传统的 Unix 前缀 lib)。这使您可以提供对依赖库的隐式链接。

评估对象大小

Clang 支持内建函数 __builtin_object_size__builtin_dynamic_object_size。语义与 GCC 中相同名称的内建函数兼容,但细节略有不同。

size_t __builtin_[dynamic_]object_size(const void *ptr, int type)

返回过去 ptr 的可访问字节数 n。返回的值取决于 type,它需要是 0 到 3 之间的整数常量

  • 如果 type & 2 == 0,则返回最小的 n,这样访问 (const char*)ptr + n 及其之后的访问将被认为是越界的。如果不知道更好的边界,则为 (size_t)-1

  • 如果 type & 2 == 2,则返回最大的 n,这样访问 (const char*)ptr + i 将被认为是在边界内,对于 0 <= i < n。如果不知道更好的边界,则为 (size_t)0

char small[10], large[100];
bool cond;
// Returns 100: writes of more than 100 bytes are known to be out of bounds.
int n100 = __builtin_object_size(cond ? small : large, 0);
// Returns 10: writes of 10 or fewer bytes are known to be in bounds.
int n10 = __builtin_object_size(cond ? small : large, 2);
  • 如果 type & 1 == 0,则如果指针指向与 ptr 相同的存储位置(即相同的堆栈对象、全局变量或堆分配),则认为指针在边界内。

  • 如果 type & 1 == 1,则如果指针指向与 ptr 相同的子对象,则认为指针在边界内。如果 ptr 指向数组元素,则认为同一数组的其他元素(但不包括包含数组)在边界内。

struct X { char a, b, c; } x;
static_assert(__builtin_object_size(&x, 0) == 3);
static_assert(__builtin_object_size(&x.b, 0) == 2);
static_assert(__builtin_object_size(&x.b, 1) == 1);
char a[10][10][10];
static_assert(__builtin_object_size(&a, 1) == 1000);
static_assert(__builtin_object_size(&a[1], 1) == 900);
static_assert(__builtin_object_size(&a[1][1], 1) == 90);
static_assert(__builtin_object_size(&a[1][1][1], 1) == 9);

此内建函数返回的值是对正确答案的尽力而为的保守近似值。当 type & 2 == 0 时,真实值小于或等于内建函数返回的值,当 type & 2 == 1 时,真实值大于或等于内建函数返回的值。

对于 __builtin_object_size,该值完全在编译时确定。启用优化后,将产生更好的结果,尤其是在对 __builtin_object_size 的调用与指针的形成位于不同的函数中时。与 GCC 不同,在 Clang 中启用优化不会允许确定有关子对象的更多信息,因此当在函数调用边界之间使用时,type & 1 == 1 情况通常会给出不精确的结果,即使在启用优化的情况下。

pass_object_size 和 pass_dynamic_object_size 属性 可用于在函数调用中将指针参数的对象大小与指针一起隐式传递。这允许在没有优化的情况下构建以及在 type & 1 == 1 情况下确定更精确的对象大小。

对于 __builtin_dynamic_object_size,结果不限于编译时常量。相反,允许进行少量的运行时评估来确定对象的大小,以便提供更精确的结果。__builtin_dynamic_object_size 旨在用作支持它的库中 __builtin_object_size 的直接替换。例如,以下程序 __builtin_dynamic_object_size 将使其更安全

void copy_into_buffer(size_t size) {
  char* buffer = malloc(size);
  strlcpy(buffer, "some string", strlen("some string"));
  // Previous line preprocesses to:
  // __builtin___strlcpy_chk(buffer, "some string", strlen("some string"), __builtin_object_size(buffer, 0))
}

由于 buffer 的大小在编译时无法知道,因此 Clang 会将 __builtin_object_size(buffer, 0) 折叠为 -1。但是,如果将其写为 __builtin_dynamic_object_size(buffer, 0),Clang 会将其折叠为 size,提供一些额外的运行时安全性。

弃用宏

Clang 支持 pragma #pragma clang deprecated,它可用于为宏使用提供弃用警告。例如

#define MIN(x, y) x < y ? x : y
#pragma clang deprecated(MIN, "use std::min instead")

int min(int a, int b) {
  return MIN(a, b); // warning: MIN is deprecated: use std::min instead
}

应该优先使用 #pragma clang deprecated 来代替 #pragma GCC warning,因为可以使用 -Wdeprecated 来控制警告。

受限展开宏

Clang 支持 pragma #pragma clang restrict_expansion,它可用于限制头文件中的宏展开。这在提供具有 ABI 稳定性要求的头文件时可能很有价值。在 #pragma 注释后,预处理器处理的任何注释宏的展开都将记录警告。重新定义宏或取消定义宏不会被诊断,宏在主源文件中的展开也不会被诊断。例如

#define TARGET_ARM 1
#pragma clang restrict_expansion(TARGET_ARM, "<reason>")

/// Foo.h
struct Foo {
#if TARGET_ARM // warning: TARGET_ARM is marked unsafe in headers: <reason>
  uint32_t X;
#else
  uint64_t X;
#endif
};

/// main.c
#include "foo.h"
#if TARGET_ARM // No warning in main source file
X_TYPE uint32_t
#else
X_TYPE uint64_t
#endif

此警告由 -Wpedantic-macros 控制。

最终宏

Clang 支持 pragma #pragma clang final,它可用于将宏标记为最终,这意味着它们不能被取消定义或重新定义。例如

#define FINAL_MACRO 1
#pragma clang final(FINAL_MACRO)

#define FINAL_MACRO // warning: FINAL_MACRO is marked final and should not be redefined
#undef FINAL_MACRO  // warning: FINAL_MACRO is marked final and should not be undefined

这对于强制执行系统提供的宏很有用,这些宏不应该在用户头文件或代码中被更改。这由 -Wpedantic-macros 控制。最终宏在重新定义时始终会发出警告,包括具有相同主体的情况以及在系统头文件中的情况。

行控制

Clang 支持源行控制的扩展,它采用以无符号整数常量开头的预处理器指令的形式。除了标准的 #line 指令外,此形式还允许控制包含堆栈和头文件类型,这在发出诊断信息时使用。这些行在预处理的输出中发出。

# <line:number> <filename:string> <header-type:numbers>

文件名是可选的,如果未指定,则表示源文件名没有改变。头类型是一个可选的、空格分隔的、以下魔术数字的序列。

  • 1: 将当前源文件名推送到包含堆栈并进入新文件。

  • 2: 弹出包含堆栈并返回指定的文件。如果文件名是 "",则使用从包含堆栈中弹出的名称。否则,没有要求指定的文件名与最初推送到包含堆栈时的当前源文件名匹配。

  • 3: 进入系统头区域。系统头通常包含特定于实现的源代码,这些源代码通常会发出诊断信息。

  • 4: 进入隐式 extern "C" 区域。在现代系统上,不需要这样做,因为系统头是 C++ 意识的。

最多只能出现一个 12,并且值必须按升序排列。

示例如下

# 57 // Advance (or return) to line 57 of the current source file
# 57 "frob" // Set to line 57 of "frob"
# 1 "foo.h" 1 // Enter "foo.h" at line 1
# 59 "main.c" 2 // Leave current include and return to "main.c"
# 1 "/usr/include/stdio.h" 1 3 // Enter a system header
# 60 "" 2 // return to "main.c"
# 1 "/usr/ancient/header.h" 1 4 // Enter an implicit extern "C" header

扩展整数类型

Clang 在较旧的 C 模式和 C++ 中支持 C23 _BitInt(N) 特性作为扩展。这种类型以前在 Clang 中使用相同的语义实现,但拼写为 _ExtInt(N)。这种拼写已被弃用,改为标准类型。

注意:_BitInt(N) 的 ABI 仍在稳定过程中,因此此类型不应在需要 ABI 稳定的接口中使用。

常量表达式中的内建函数支持

以下内置函数可以在常量表达式中使用

  • __builtin_addcb

  • __builtin_addcs

  • __builtin_addc

  • __builtin_addcl

  • __builtin_addcll

  • __builtin_bitreverse8

  • __builtin_bitreverse16

  • __builtin_bitreverse32

  • __builtin_bitreverse64

  • __builtin_bswap16

  • __builtin_bswap32

  • __builtin_bswap64

  • __builtin_clrsb

  • __builtin_clrsbl

  • __builtin_clrsbll

  • __builtin_clz

  • __builtin_clzl

  • __builtin_clzll

  • __builtin_clzs

  • __builtin_clzg

  • __builtin_ctz

  • __builtin_ctzl

  • __builtin_ctzll

  • __builtin_ctzs

  • __builtin_ctzg

  • __builtin_ffs

  • __builtin_ffsl

  • __builtin_ffsll

  • __builtin_fmax

  • __builtin_fmin

  • __builtin_fpclassify

  • __builtin_inf

  • __builtin_isinf

  • __builtin_isinf_sign

  • __builtin_isfinite

  • __builtin_isnan

  • __builtin_isnormal

  • __builtin_nan

  • __builtin_nans

  • __builtin_parity

  • __builtin_parityl

  • __builtin_parityll

  • __builtin_popcount

  • __builtin_popcountl

  • __builtin_popcountll

  • __builtin_popcountg

  • __builtin_rotateleft8

  • __builtin_rotateleft16

  • __builtin_rotateleft32

  • __builtin_rotateleft64

  • __builtin_rotateright8

  • __builtin_rotateright16

  • __builtin_rotateright32

  • __builtin_rotateright64

  • __builtin_subcb

  • __builtin_subcs

  • __builtin_subc

  • __builtin_subcl

  • __builtin_subcll

以下 x86 特定的内建函数可以在常量表达式中使用

  • _addcarry_u32

  • _addcarry_u64

  • _bit_scan_forward

  • _bit_scan_reverse

  • __bsfd

  • __bsfq

  • __bsrd

  • __bsrq

  • __bswap

  • __bswapd

  • __bswap64

  • __bswapq

  • _castf32_u32

  • _castf64_u64

  • _castu32_f32

  • _castu64_f64

  • __lzcnt16

  • __lzcnt

  • __lzcnt64

  • _mm_popcnt_u32

  • _mm_popcnt_u64

  • _popcnt32

  • _popcnt64

  • __popcntd

  • __popcntq

  • __popcnt16

  • __popcnt

  • __popcnt64

  • __rolb

  • __rolw

  • __rold

  • __rolq

  • __rorb

  • __rorw

  • __rord

  • __rorq

  • _rotl

  • _rotr

  • _rotwl

  • _rotwr

  • _lrotl

  • _lrotr

  • _subborrow_u32

  • _subborrow_u64

调试编译器

Clang 支持一些有助于调试编译器本身的预处理指令。语法如下:#pragma clang __debug <command> <arguments>。注意,所有调试预处理指令都可能发生变化。

dump

接受单个标识符或表达式。当传递单个标识符时,标识符的查找结果将打印到 stderr。当传递表达式时,表达式的 AST 将打印到 stderr。表达式是一个未评估的操作数,因此会执行诸如重载解析和模板实例化之类的操作,但表达式没有运行时效果。类型和值相关的表达式尚不支持。

此功能旨在帮助测试名称查找机制。

预定义宏

__GCC_DESTRUCTIVE_SIZE__GCC_CONSTRUCTIVE_SIZE

分别指定两个对象之间避免虚假共享的最小偏移量和促进真实共享的连续内存的最大大小。这些宏在所有 C 和 C++ 语言模式中都预定义,但可以在命令行中使用 -D 重新定义以根据需要指定不同的值,或者可以在命令行中使用 -U 取消定义以禁用对该功能的支持。

注意:宏展开到的值不能保证是稳定的。它们受架构和 CPU 调整标志的影响,可能会在 Clang 版本之间发生变化,并且不会与其他编译器(如 GCC)定义的值匹配。

使用不同的编译器、宏定义或架构标志编译不同的 TU(包括使用 std::hardware_constructive_interferencestd::hardware_destructive_interference)会导致 ODR 冲突,应避免这种情况。

#embed 参数

clang::offset

clang::offset 内嵌参数可以出现在内嵌参数序列中零次或一次。它的预处理参数子句应存在且具有以下形式

..code-block: text

( constant-expression )

并且应为整数常量表达式。整数常量表达式不应计算为小于 0 的值。令牌 defined 不应出现在常量表达式中。

在读取嵌入资源的内容时,偏移量将用于指定开始嵌入的起始偏移量。如果指定的偏移量大于资源中的字节数,则资源将被视为为空。偏移量将在应用任何 limit 参数之前应用。

C 中的联合和聚合初始化

在 C23 (N2900) 中,当对象从初始化器 = {} 初始化时,数组的所有元素、结构的所有成员和联合的第一个成员都会递归地进行空初始化。此外,所有填充位都被初始化为零。

Clang 保证以下行为

  • 1: Clang 在所有 C 标准中支持上面提到的初始化器 = {}

  • 2: 当联合从初始化器 = {} 初始化时,联合第一个成员之外的字节也会被初始化为零。

  • 3: 当联合、结构体和数组从初始化器 = { initializer-list } 初始化时,初始化列表中未显式初始化的所有成员都会递归地进行空初始化。此外,所有填充位都被初始化为零。

目前,上述扩展只适用于 C 源代码,不适用于 C++。