C99 Standard是否允许编译器转换代码,以便在满足某些推断条件后不再评估相同的表达式?

sha*_*oth 3 c optimization c99 compiler-optimization

我不太了解5.1.2.3/3的以下部分:

实际实现不需要评估表达式的一部分,如果它可以推断出它的值未被使用并且不产生所需的副作用(包括由调用函数或访问易失性对象引起的任何副作用).

假设我有以下代码:

char data[size];
int i;
int found;
/* initialize data to some values in here */
found = 0;
for( i = 0; i < size; i++ ) {
   if( data[i] == 0 ) {
      found = 1;
      /* no break in here */
   }
}
/* i no longer used, do something with "found" here */
Run Code Online (Sandbox Code Playgroud)

请注意,found以&开头0可以保持不变或变为1.它不能变成1然后变成别的东西.因此,以下代码将产生相同的结果(除了i在循环之后未使用的值):

char data[size];
int i;
int found;
/* initialize data to some values in here */
found = 0;
for( i = 0; i < size; i++ ) {
   if( data[i] == 0 ) {
      found = 1;
      break;
   }
}
/* i no longer used, do something with "found" here */
Run Code Online (Sandbox Code Playgroud)

现在是什么标准说的不一定对表达式求值的一部分关于found = 1和循环控制表达式随后在其控制内得到第一次迭代if

显然,如果found在此代码之后的某处使用,编译器必须发出遍历数组的代码并有条件地计算found = 1表达式.

是否需要对found = 1数组中找到的每个零进行一次评估,或者是否可以将其评估为一次,因此在编译第一个代码段时有效地为第二个代码段发出代码?

Eri*_*ert 6

它可以代替评估它一次,并在编译第一个片段时有效地为第二个片段发出代码吗?

是的,编译器有权执行该优化.这似乎是一个非常积极的优化,但它是合法的.

看一个更符合文本精神的例子可能会很有趣:

实际实现不需要评估表达式的一部分,如果它可以推断出它的值未被使用并且不产生所需的副作用(包括由调用函数或访问易失性对象引起的任何副作用).

假设我们有:

int x = pureFunction(y) * otherPureFunction(z);
Run Code Online (Sandbox Code Playgroud)

假设编译器知道两个函数都是int返回"纯"函数; 也就是说,它们没有副作用,它们的结果完全取决于论点.假设编译器也认为这otherPureFunction是一个非常昂贵的操作.编译器可以选择像您编写的那样实现代码:

int temp = pureFunction(y);
int x = temp == 0 ? 0 : temp * otherPureFunction(z);
Run Code Online (Sandbox Code Playgroud)

也就是说,确定在某些条件下不需要计算,otherPureFunction()因为一旦已知左操作数为零,则已知乘法的结果.没有必要的副作用将被省略,因为没有副作用.


Fre*_*Foo 5

是的,它可以执行此优化,因为没有I/O操作,从volatile位置读取或优化代码省略的外部可见写入内存,因此保留了行为.

作为这种优化的一个例子,GCC将编译

void noop(const char *s)
{
    for (size_t i = 0; i < strlen(s); i++) {
    }
}
Run Code Online (Sandbox Code Playgroud)

一个完全空的功能:

noop:
.LFB33:
        .cfi_startproc
        rep ret
        .cfi_endproc
Run Code Online (Sandbox Code Playgroud)

允许这样做是因为标准保证了行为strlen,编译器知道它对s任何其他内存都没有外部可见的影响,并且它可以推断整个函数没有行为.(令人惊讶的是,这种简单的优化将复杂性从二次变为常数.)