海湾合作委员会似乎错过了简单的优化

eXX*_*XX2 6 c++ optimization assembly gcc

我试图用三元运算符的语义引入泛型函数:E1 ? E2 : E3.我看到编译器能够消除三元运算符之一E2E3取决于E1条件的计算.然而,GCC在ternary函数调用的情况下错过了这种优化(即使E2/ E3没有副作用).

在下面的清单中ternary,写入函数的行为类似于三元运算符.然而,GCC发出可能对函数的大量调用,f这似乎可以消除某些输入值(确切地说它是如何为三元运算符完成的)因为f使用纯属性声明 - 请查看GCC生成的汇编代码的godbolt链接.

它是否可以在GCC(优化空间)中得到改进,或者C++标准是否明确禁止这种优化?

// Very heavy function
int f() __attribute__ ((pure));

inline int ternary(bool cond, int n1, int n2) {
    return cond ? n1 : n2;
}

int foo1(int i) {
    return i == 0 ? f() : 0;
}

int foo2(int i) {
    return ternary(i == 0, f(), 0);
}
Run Code Online (Sandbox Code Playgroud)

装配清单-O3 -std=c++11:

foo1(int):
  test edi, edi
  jne .L2
  jmp f()
.L2:
  xor eax, eax
  ret
foo2(int):
  push rbx
  mov ebx, edi
  call f()
  test ebx, ebx
  mov edx, 0
  pop rbx
  cmovne eax, edx
  ret
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/HfpNzo

Pet*_*des 7

我看到,对于三元运算符,编译器能够根据E1条件(只要E2/E3没有副作用)消除E2或E3中的一个的计算.

编译器不会消除它; 它从来没有在一开始就优化它cmov. C++抽象机器评估三元运算符的未使用侧.

int a, b;
void foo(int sel) {
    sel ? a++ : b++;
}
Run Code Online (Sandbox Code Playgroud)

像这样编译(Godbolt):

foo(int):
    test    edi, edi
    je      .L2                # if(sel==0) goto
    add     DWORD PTR a[rip], 1   # ++a
    ret
.L2:
    add     DWORD PTR b[rip], 1   # ++b
    ret
Run Code Online (Sandbox Code Playgroud)

cmov如果两个输入都没有任何副作用,则三元运算符只能优化为asm .否则他们就不完全等同.


在C++抽象机器(即gcc优化器的输入)中,你foo2总是会调用f(),而你的调用foo1则不然. foo1编译它的方式就不足为奇了.

为了让foo2以这种方式进行编译,它必须优化掉调用f(). 总是调用它来创建一个arg ternary().


这里有一个错过优化,您应该报告GCC的bugzilla(使用missed-optimization关键字作为标记). https://gcc.gnu.org/bugzilla/enter_bug.cgi?product=gcc

呼叫int f() __attribute__ ((pure)); 应该能够被优化掉.它可以读取全局变量,但不能有任何副作用. (https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html)

正如@melpomene在评论中发现的那样,int f() __attribute__ ((const));确实为您提供了所需的优化.一个__attribute__((const))功能甚至无法读取全局变量,只有它ARGS.(因此没有args,它必须始终返回一个常量.)

HVD指出gcc没有任何费用信息f().即使它可能已经优化了调用((pure)) f()以及((const)) f,也许它没有,因为它不知道它比条件分支更昂贵?可能使用配置文件引导优化进行编译会说服gcc做些什么?

但鉴于它调用了((const)) f条件foo2,gcc可能只是不知道它可以优化对((pure))函数的调用?也许它只能CSE它们(如果没有写过全局变量),但是不能完全从基本块中进行优化?或许当前的优化器无法利用.就像我说的,看起来像一个错过选择的错误.