C编译器如何持续优化无法访问的代码?

时间:2019-07-09 12:51:29

标签: c optimization

假设您拥有(出于此处不重要的原因)以下代码:

 int k = 0;
 ...  /* no change to k can happen here */
 if (k) { 
   do_something();
 }

使用-O2标志,GCC将不会生成任何代码,因为if测试始终为假。

我想知道这是否是编译器中非常普遍的行为,还是我不应该依赖的东西。

有人知道吗?

2 个答案:

答案 0 :(得分:2)

在编译器之间没有非常普遍的行为。但是,有一种方法可以探索不同的编译器如何处理特定的代码部分。 Compiler explorer将帮助您回答有关代码生成的每个问题,但是您当然必须熟悉汇编语言。

答案 1 :(得分:2)

在这种情况下,对于任何现代的优化编译器而言,消除死代码都是微不足道的。我肯定会依靠它,因为已经启用了优化功能,并且您完全可以确定编译器可以在检查时证明该值为零。

但是,您应该意识到,有时您的代码具有比您想象的更多的潜在副作用。


问题的第一个来源是调用非内联函数。每当您调用未内联的函数时(即因为其定义位于另一个转换单元中),编译器就会假定所有全局变量和堆的整个内容在此调用内可能会更改。局部变量是幸运的例外,因为编译器知道间接修改它们是非法的……除非您将局部变量的地址保存在某个地方。例如,在这种情况下,无效代码won't be eliminated

int function_with_unpredictable_side_effects(const int &x);
void doit() {
  int k = 0;
  function_with_unpredictable_side_effects(k);
  if (k)
    printf("Never reached\n");
}

因此,编译器必须做一些工作,甚至可能会因局部变量而失败。顺便说一下,我相信在这种情况下解决的问题称为escape analysis


问题的第二个来源是pointer aliasing:编译器必须考虑到代码中的各种指针和引用可能是相等的,因此通过一个指针更改某些内容可能会更改另一个指针的内容。这是一个示例:

struct MyArray {
  int num;
  int arr[100];
};
void doit(int idx) {
  MyArray x;
  x.num = 0;
  x.arr[idx] = 7;
  if (x.num)
    printf("Never reached\n");
}

Visual C ++编译器does not eliminate the dead code,因为它认为您可以以x.num的身份访问x.arr[-1]。对您来说,这听起来似乎很糟糕,但是此编译器已在gamedev领域中使用了很多年,并且此类hack并不少见,因此编译器始终处于安全状态。另一方面,GCC会删除无效代码。也许与其对strict pointer aliasing规则的利用有关。


P.S。const键盘键从未被优化程序使用,从不使用,仅以C / C ++语言提供,以方便程序员。