我正试图通过GCC和Clang编译以下带有-O3优化标志的C代码,在查看生成的汇编代码后,我发现这两个编译器都没有实现C标准中提到的短路评估&操作人员
您可以参考下面的汇编代码以获得更多信息,foo函数的前五行代码将按顺序运行,它将比较&;实际违反标准的操作员。那么,这里有什么误解吗?
C代码:
#include <stdio.h>
#include <stdbool.h>
void foo(int x, int y) {
bool logical = x && y;
printf("%d", logical);
}
int main(void) {
foo(1, 3);
return 0;
}
生成的装配代码:
foo: # @foo
test edi, edi
setne al
test esi, esi
setne cl
and cl, al
movzx esi, cl
mov edi, offset .L.str
xor eax, eax
jmp printf # TAILCALL
main: # @main
push rax
mov edi, offset .L.str
mov esi, 1
xor eax, eax
call printf
xor eax, eax
pop rcx
ret
.L.str:
.asciz "%d"
首先,您的示例有错误。
给定x
=1和y
=3,计算x && y
需要计算两个操作数,因为只有当两个操作都为真时,&&
才为真。
第二,C 2018 5.1.2.36说:
对一致性实施的最低要求是:
--对易失性对象的访问严格按照抽象机器的规则进行评估。
--在程序终止时,写入文件的所有数据应与根据抽象语义执行程序所产生的结果相同。
--交互式设备的输入和输出动态应按照7.21.3的规定进行。这些要求的目的是尽快出现无缓冲或行缓冲的输出,以确保在程序等待输入之前实际出现提示消息。
这是程序的可观察行为。
这意味着编译器只负责确保上述行为按指定发生。当结果可以从左操作数推导时,不需要生成不计算&&
或||
的右操作数的汇编语言,只要该计算不会改变可观察的行为。编译器没有义务生成您所寻求的汇编语言,只是为了确保可观察的行为符合规定。
当标准将表达式描述为正在评估或未评估时,它是在"抽象机器"(C 2018 5.1.2.31(的上下文中描述它们,而不是描述生成的程序在汇编语言级别上实际必须做什么。其思想是,C标准描述了程序在抽象机器中生成的内容,然后编译器可以生成任何与抽象机器中的程序具有相同可观察行为的程序,即使它以与抽象机器上的程序完全不同的方式获得结果。
确实如此。但编译器会根据要求选择最快的方式。
main
-编译器知道结果编译时间,根本不调用函数foo
foo
-分支是相当昂贵的操作,因为它们需要刷新管道、潜在的缓存未命中、从慢速内存中读取等。因此在这种情况下分支毫无意义。你的例子太琐碎了
考虑一个不那么琐碎的例子:
bool bar(void);
void foo(int x) {
bool logical = x && bar();
printf("%d", logical);
}
int main(void) {
foo(1);
return 0;
}
foo:
test edi, edi
jne .L12
mov esi, edi
xor eax, eax
mov edi, OFFSET FLAT:.LC0
jmp printf
.L12:
sub rsp, 8
call bar
mov edi, OFFSET FLAT:.LC0
add rsp, 8
movzx esi, al
xor eax, eax
jmp printf
main:
sub rsp, 8
mov edi, 1
call foo
xor eax, eax
add rsp, 8
ret
来自C标准6.5.13:
不同于逐位二进制&运算符,&;运营商担保从左到右评价;如果计算第二个操作数,则存在第一次和第二次评估之间的序列点操作数。如果第一个操作数比较等于0,则第二个操作数未评估。