假设一个API,其中每个函数都返回一个错误代码,如果没有错误则为零,错误值为非零。
让
int foo(...);
int bar(...);
是此API中的函数。设有一个代码片段,其中必须按顺序调用foo
和bar
,并且foo和bar应该始终被调用,而不管以前的错误但是第一个返回的非零错误代码将被传播,即
int foobar(...)
{
int rc = 0, rc_;
/* ... */
rc_ = foo(...); rc = rc ? rc : rc_;
rc_ = bar(...); rc = rc ? rc : rc_;
return rc;
}
写入rc,rc_多路复用是累人且容易出错(无论是否使用三元运算符,if / else或其他东西)。
让传播帮助函数的错误
static inline
int rc_propagate(int r, int p){ return p ? p : r; }
这可以在foobar
中使用
int foobar(...)
{
int rc = 0;
/* ... */
rc = rc_propagate(foo(...), rc);
rc = rc_propagate(bar(...), rc);
return rc;
}
C标准是否允许通过将rc_propagate
的第一个参数(一个静态内联函数)的评估推入三元组来进行优化,以便由于三元运算符评估规则它可能无法执行参数p
是非零的?
答案 0 :(得分:3)
只要程序保持不变,就允许编译器(或者硬件)优化程序 1 ,即你不能证明执行的程序与程序不同你写的。
在这种情况下,您编写的程序将始终调用外部函数,因为它不会出现在三元运算符中,可能无法对其进行求值。该函数可能具有不同的有趣副作用,并且编译器不知道。这意味着程序的优化版本必须在某个时刻调用外部函数(代码可以重新排序)以保留该行为。
如果这不是真的,你可以证明所执行的程序与你编写的程序不同。这样做很容易;将printf(或等效的)语句放入外部函数调用。
<子> 1。抽象程序在执行时存在的概念,而不是生成的机器代码或可执行文件。
使用来自权限的参数,您可以看到没有编译器实际上会优化对foo()和bar()的调用:
gcc版本4.9.2,5.3或6.1 with -O2:
foobar():
pushq %rbx
call foo()
movl %eax, %ebx
call bar()
testl %ebx, %ebx
cmove %eax, %ebx
movl %ebx, %eax
popq %rbx
ret
clang版本3.7.1,或3.8与-O2:
foobar():
pushq %rbx
callq foo()
movl %eax, %ebx
callq bar()
testl %ebx, %ebx
cmovnel %ebx, %eax
popq %rbx