__is_constexpr(x)
怎么办Linux内核的宏工作?它的目的是什么?什么时候介绍的?为什么要介绍?
/*
* This returns a constant expression while determining if an argument is
* a constant expression, most importantly without evaluating the argument.
* Glory to Martin Uecker <Martin.Uecker@med.uni-goettingen.de>
*/
#define __is_constexpr(x) \
(sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
对于 不同方法的讨论 要解决同样的问题,请参阅:Detecting Integer Constant Expressions in Macros
最佳答案
Linux内核的__is_constexpr
宏
简介__is_constexpr(x)
宏可以在 Linux Kernel 的 include/kernel/kernel.h 中找到:
/*
* This returns a constant expression while determining if an argument is
* a constant expression, most importantly without evaluating the argument.
* Glory to Martin Uecker <Martin.Uecker@med.uni-goettingen.de>
*/
#define __is_constexpr(x) \
(sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
它是在 Linux Kernel v4.17 的合并窗口中引入的,commit 3c8ba0d61d04 2018-04-05;尽管围绕它的讨论是在一个月前开始的。
该宏以利用 C 标准的微妙细节而著称:用于确定其返回类型的条件运算符规则 (6.5.15.6) 和空指针常量的定义 (6.3.2.3.3)。
另外,它依赖于
sizeof(void)
被允许(与 sizeof(int)
不同),这是一个 GNU C extension .它是如何工作的?
宏的主体是:
(sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
让我们专注于这一部分:
((void *)((long)(x) * 0l))
注:
(long)(x)
Actor 是 intended允许 x
具有指针类型并避免在 u64
上出现警告32 位平台上的类型。但是,这个细节对于理解宏的关键点并不重要。如
x
是 一个整数常量表达式 (6.6.6),那么它遵循 ((long)(x) * 0l)
是值 0
的整数常量表达式.因此,(void *)((long)(x) * 0l)
是一个空指针常量 (6.3.2.3.3):An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant
如
x
是 不是 一个整数常量表达式,然后 (void *)((long)(x) * 0l)
不是空指针常量,不管它的值(value) .知道了这一点,我们可以看到之后会发生什么:
8 ? ((void *)((long)(x) * 0l)) : (int *)8
注:第二个
8
文字是 intended以避免编译器发出有关创建指向未对齐地址的指针的警告。第一8
文字可以简单地是 1
.但是,这些细节对于理解宏的关键点并不重要。这里的关键是条件运算符返回 不同类型 取决于操作数之一是否为空指针常量 (6.5.15.6):
[...] if one operand is a null pointer constant, the result has the type of the other operand; otherwise, one operand is a pointer to void or a qualified version of void, in which case the result type is a pointer to an appropriately qualified version of void.
所以,如果
x
是 一个整数常量表达式,那么第二个操作数是一个空指针常量,因此表达式的类型是第三个操作数的类型,它是指向 int
的指针.否则 ,第二个操作数是指向
void
的指针因此表达式的类型是指向 void
的指针.因此,我们最终有两种可能:
sizeof(int) == sizeof(*((int *) (NULL))) // if `x` was an integer constant expression
sizeof(int) == sizeof(*((void *)(....))) // otherwise
根据GNU C extension ,
sizeof(void) == 1
.因此,如果 x
是一个整数常量表达式,宏的结果是 1
;否则,0
.此外,由于我们只是比较两个
sizeof
是否相等表达式,结果本身就是另一个整数常量表达式 (6.6.3, 6.6.6):Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated.
An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof operator.
因此,综上所述,
__is_constexpr(x)
宏返回值 1
的整数常量表达式如果参数是整数常量表达式。否则,它返回值 0
的整数常量表达式。 .为什么要介绍?
宏来了during the effort删除所有 Variable Length Arrays (VLAs)来自 Linux 内核。
为了方便起见,希望启用 GCC's
-Wvla
warning内核范围内;以便编译器标记所有 VLA 实例。启用警告后,事实证明 GCC 报告了许多阵列是 VLA 的情况,而这并非有意为之。例如在 fs/btrfs/tree-checker.c :
#define BTRFS_NAME_LEN 255
#define XATTR_NAME_MAX 255
char namebuf[max(BTRFS_NAME_LEN, XATTR_NAME_MAX)];
开发人员可能期望
max(BTRFS_NAME_LEN, XATTR_NAME_MAX)
已解析为 255
因此它应该被视为标准数组(即非 VLA)。但是,这取决于max(x, y)
宏扩展为。关键问题是,如果数组的大小不是 C 标准定义的(整数)常量表达式,则 GCC 会生成 VLA 代码。例如:
#define not_really_constexpr ((void)0, 100)
int a[not_really_constexpr];
根据C90标准,
((void)0, 100)
是 不是 常量表达式 (6.6),由于使用了逗号运算符 (6.6.3)。在这种情况下,GCC 选择发布 VLA 代码,even when it knows the size is a compile-time constant .相比之下,Clang 则不然。自
max(x, y)
内核中的宏不是一个常量表达式,GCC 触发了警告并生成了内核开发人员不想要的 VLA 代码。因此,一些内核开发人员试图开发
max
的替代版本。和其他宏以避免警告和 VLA 代码。一些尝试试图利用 GCC's __builtin_constant_p
builtin ,但没有一种方法适用于内核当时支持的所有 GCC 版本( gcc >= 4.4
)。在某个时候,Martin Uecker proposed一种不使用内置函数的特别聪明的方法( taking inspiration 来自 glibc's tgmath.h ):
#define ICE_P(x) (sizeof(int) == sizeof(*(1 ? ((void*)((x) * 0l)) : (int*)1)))
虽然该方法使用 GCC 扩展,it was nevertheless well-received并被用作
__is_constexpr(x)
背后的关键思想在与其他开发人员进行几次迭代后出现在内核中的宏。然后使用宏来实现 max
宏和其他需要为常量表达式的宏,以避免 GCC 生成 VLA 代码。
关于c - Linux 内核的 __is_constexpr 宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49481217/