两个月前,我报告了一个clang++
错误,下面的C++程序在使用clang++ -O2 -fno-strict-enums
编译时将z
设置为4294967295
。
enum e { e1, e2 } e;
long long x, y, z;
char *p;
void f(void) {
e = (enum e) 4294967295;
x = (long long) e;
y = e > e1;
z = &p[e] - p;
}
我的错误报告因无效而关闭,因为程序未定义。我的感觉是,使用选项-fno-strict-enums
定义它。
据我所知,Clang没有名副其实的文档,因为它旨在与GCC在其接受的选项及其含义方面兼容。我阅读了 GCC 关于选项的文档-fno-strict-enums
说程序应该将z
的值设置为-1
:
fstrict-enums
允许编译器使用枚举类型的值只能是枚举值之一的假设进行优化 (如C++标准中所定义;基本上,该值可以是 以表示所有 枚举器)。如果程序使用 强制转换以将任意整数值转换为枚举类型。
请注意,仅记录了选项-fstrict-enums
,但似乎很清楚,-fno-strict-enums
禁用了-fstrict-enums
启用的编译器行为。我不能针对 GCC 的文档提交错误,因为生成一个将z
设置为-1
的二进制文件,我理解-fno-strict-enums
强制要求,这正是g++ -O2 -fno-strict-enums
所做的。
谁能告诉我-fno-strict-enums
在Clang中做了什么(如果我误解了它在GCC中的作用,在GCC中做了什么),以及期权的价值在Clang的任何地方是否有任何影响?
作为参考,我的错误报告在这里,显示我的意思的编译器资源管理器链接在这里。用作参考的版本是针对I32LP64架构的 Clang 10.0.1 和 GCC 10.2。
-fno-strict-enums
的效果是取消-fstrict-enums
。也就是说,不允许编译器使用枚举类型的值只能是枚举值之一的假设进行优化。我想强调的是,选择这个词是"允许的",而不是"必需的"。很难看到不再允许最初没有做的事情的影响。不过,我想我已经找到了一个可以看到的例子。
首先,我想在问题的上下文中澄清"枚举的值"。枚举e
有两个枚举器,值分别为0
和1
。表示这些值所需的最小位数为 1。因此,枚举的值都是可以用 1 位表示的值。在这种情况下,这恰好与枚举器的值一致,但在其他示例中无法保证。
接下来,让我们从问题代码中删除一行。
enum e { e1, e2 } e;
long long x, y, z;
char *p;
void f(void) {
//e = (enum e) 4294967295;
x = (long long) e;
y = e > e1;
z = &p[e] - p;
}
我删除的行会干扰strict-enum
标志。该标志允许编译器做出假设,当编译器确切知道e
的值是什么时,该假设不是必需的。编译器可以合理地选择不假设e
只能保存0
或1
,而很明显它只是被赋予了不同的值。(这种干扰不依赖于4294967295
对于 32 位有符号整数来说太大,而只是取决于4294967295
是编译时值。另一个示例是,将(enum e) 2
分配给e
也会导致这种干扰。
专注于作业y = e > e1
。如果-fno-strict-enums
有效,则唯一可用的优化是将e1
替换为0
。但是,如果我们可以假设e
只能是0
或1
(枚举的值,恰好也是枚举器的值),则可以使用另一种优化。
如果e
0
,则以下值相同:
(long long) (e > e1)
(long long) (0 > 0)
(long long) false
(long long) e
如果e
1
,则以下值相同:
(long long) (e > e1)
(long long) (1 > 0)
(long long) true
(long long) e
在任何一种情况下,我们都可以跳过比较,简单地e
投射到long long
.这反映在 clang 10 为行y = e > e1
生成的程序集中。
与-fstrict-enums
movq %rax, y(%rip)
与-fno-strict-enums
xorl %ecx, %ecx
testl %eax, %eax
setg %cl
movq %rcx, y(%rip)
对-fno-strict-enums
不允许的-fstrict-enums
进行了优化。