我试图引入一个具有三元操作符语义的泛型函数:E1 ? E2 : E3。我看到编译器能够消除E2或E3之一的计算,这取决于三元操作符的E1条件。然而,GCC在ternary函数调用的情况下(即使E2/E3没有副作用)也忽略了这一优化。
在下面的清单中,函数ternary被写成类似于三元操作符的行为。然而,GCC可能会对函数f发出很大的调用,这对于某些输入值(对三元操作符来说是这样做的)来说似乎是可以消除的,因为f是用纯属性声明的--请看安理会生成的汇编代码的龙芯链接。
这是在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);
}使用-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发布于 2018-09-13 20:09:26
我看到编译器能够根据三值操作符的E2或E3条件(只要E2/E3没有副作用)来消除E1或E1的计算。
编译器并没有消除它;它只是从一开始就没有将其优化为cmov。 C++抽象机器不计算三元操作符的未使用侧。
int a, b;
void foo(int sel) {
sel ? a++ : b++;
}像这样编译(哥德波特 )
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如果任何输入都没有任何副作用,则三元操作符只能对asm cmov进行优化.否则它们就不完全等同了。
在抽象机器(即gcc的优化器的输入)中,您的foo2 foo1 总是调用 f()**,,而您的**foo1没有调用。并不奇怪foo1是这样编译的。
要用这种方式编译f()**.**,必须优化对的调用--它总是被调用来为ternary()创建一个arg。
这里有一个遗漏的优化,您应该报告GCC的bugzilla (使用missed-optimization关键字作为标记)。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 (如果没有编写全局代码),但不能完全从基本块中优化?或者,当前的优化器可能只是没有利用。就像我说的,看上去是个漏掉的小虫子。
https://stackoverflow.com/questions/52320679
复制相似问题