tre*_*eap 28 c++ g++ clang c++11 g++4.8
#include <array>
#include <cassert>
class P {
public:
P() : _value(nullptr) {}
~P() { delete _value; }
private:
char *_value;
};
void foo() {
if(std::array<P, 4>().size() != 4)
assert(false);
}
Run Code Online (Sandbox Code Playgroud)
该函数foo()
创建一个临时数组来检查程序员所期望的大小.使用-O1
或更高的g ++数字表示assert
不会失败,并且__assert_fail
从生成的代码中删除调用.但是g ++仍然会生成代码来首先构造然后破坏现在未使用的数组.
g++ -std=c++11 -O3 [4.8.2]:
0000000000000000 <_Z3foov>:1
0: 55 push %rbp1
1: 66 0f ef c0 pxor %xmm0,%xmm01
5: 53 push %rbx1
6: 48 83 ec 28 sub $0x28,%rsp1
a: 66 0f 7f 04 24 movdqa %xmm0,(%rsp)1
f: 48 8d 5c 24 20 lea 0x20(%rsp),%rbx1
14: 48 89 e5 mov %rsp,%rbp1
17: 66 0f 7f 44 24 10 movdqa %xmm0,0x10(%rsp)1
1d: 0f 1f 00 nopl (%rax)1
20: 48 83 eb 08 sub $0x8,%rbx1
24: 48 8b 3b mov (%rbx),%rdi1
27: e8 00 00 00 00 callq 2c <_Z3foov+0x2c>1
2c: 48 39 eb cmp %rbp,%rbx1
2f: 75 ef jne 20 <_Z3foov+0x20>1
31: 48 83 c4 28 add $0x28,%rsp1
35: 5b pop %rbx1
36: 5d pop %rbp1
37: c3 retq 1
Run Code Online (Sandbox Code Playgroud)
另一方面,clang删除除return语句之外的所有代码.
clang -std=c++11 -O3:
0000000000000000 <_Z3foov>:1
0: c3 retq 1
Run Code Online (Sandbox Code Playgroud)
用g ++运气不好还是有差异的原因?
首先,很好的问题.
编辑:
我第一次没有正确阅读你的代码.您的代码中有一个重要的外部调用.这是在这个指令中e8 00 00 00 00 callq 2c <_Z3foov+0x2c>1
.它不会提前调用地址 - 而是在链接时替换它的目标.这就是链接的工作方式 - 精灵文件会说"这样的指令会在链接时解析为这个目标." 汇编程序尚未完全列出,因此我们不知道此调用的地址.据推测它delete _value
在代码中,但libstdc ++(与delet等)默认是动态链接的.您可以使用我的编译器标志来更改它,或者在gdb中进行列表(即链接后).
回到答案:
这是一个特殊情况,gcc程序员已经做出了不优化的选择.原因是运算符new和delete被标记为"弱符号",链接器将寻找备选方案,选择提供的用户或如果没有找到则退回.
以下是对此背后的基本原理的讨论.这些运营商旨在全球可更换,弱连接是一种解决方案.
静态链接不会改变这一点,因为glibc中的free和malloc仍然可以在链接时更改.
静态链接或使用链接时优化应使用内置删除,但在这种情况下,如果您改为静态链接,则仍有机会丢失.然而,在你的原始例子中,没有这样的机会.
编译:
#include <array>
#include <cassert>
#include <cstdlib>
void * operator new(std::size_t n) throw(std::bad_alloc)
{
return malloc(n);
}
void operator delete(void * p) throw()
{
if(p != nullptr)
free(p);
}
class P {
public:
P() : _value(nullptr) {}
~P() { delete _value; }
private:
char *_value;
};
void foo() {
if(std::array<P, 4>().size() != 4)
assert(false);
}
int main(){
foo();
}
Run Code Online (Sandbox Code Playgroud)
有了这个; g ++ -std = c ++ 11 -O3 -static -Wa,-alh test.cpp -o test
静态地链接到libstdc ++/glibc ,因此它应该知道free和malloc是什么,但是它并没有完全意识到foo是微不足道的.
然而,nm -gC test | grep free
产生弱符号 __free_hook
,我在这里找到了解释.因此,在使用gcc进行编译时,free和malloc(以及operator new和delete)的行为在链接时实际上总是可以更改.这就是阻止优化的原因 - 令人烦恼的是-fno-weak
那些符号留在那里.
上面的段落是正确的,但是错过了优化的结果,而不是避免优化的原因.
通过链接时间优化,可以将gcc哄骗进行此优化,但首先必须使用-flto构建其他所有内容,包括libstdc ++.
这是静态构建示例时gcc为我做的最多:
Dump of assembler code for function _Z3foov:
0x08048ef0 <+0>: push %esi
0x08048ef1 <+1>: push %ebx
0x08048ef2 <+2>: sub $0x24,%esp
0x08048ef5 <+5>: movl $0x0,0x10(%esp)
0x08048efd <+13>: lea 0x20(%esp),%ebx
0x08048f01 <+17>: movl $0x0,0x14(%esp)
0x08048f09 <+25>: lea 0x10(%esp),%esi
0x08048f0d <+29>: movl $0x0,0x18(%esp)
0x08048f15 <+37>: movl $0x0,0x1c(%esp)
0x08048f1d <+45>: lea 0x0(%esi),%esi
0x08048f20 <+48>: sub $0x4,%ebx
0x08048f23 <+51>: mov (%ebx),%eax
0x08048f25 <+53>: test %eax,%eax
0x08048f27 <+55>: je 0x8048f31 <_Z3foov+65>
0x08048f29 <+57>: mov %eax,(%esp)
0x08048f2c <+60>: call 0x804dea0 <free>
0x08048f31 <+65>: cmp %esi,%ebx
0x08048f33 <+67>: jne 0x8048f20 <_Z3foov+48>
0x08048f35 <+69>: add $0x24,%esp
0x08048f38 <+72>: pop %ebx
0x08048f39 <+73>: pop %esi
0x08048f3a <+74>: ret
Run Code Online (Sandbox Code Playgroud)
对免费的呼吁不会随处可见.
如果是问题,请自行优化.模板使这很容易(假设std :: array作为模板参数传递,否则为什么要检查它的size()?).
#include <array>
#include <cassert>
class P {
public:
P() : _value(nullptr) {}
~P() { delete _value; }
private:
char *_value;
};
void foo() {
if(std::tuple_size<std::array<P, 4> >::value != 4)
assert(false);
}
int main(){
foo();
}
Run Code Online (Sandbox Code Playgroud)
如果std::array<P, 4>
是向量或其他东西,代码可以使其无声地失败,并回退到您的默认构造方法.
nm -C test
W operator new(unsigned int, void*)
当我添加时输出#include <new>
,所以至少可以更改放置链接时间(它是一个弱符号) - 其他是特殊的,它们的最终目标位于libstdc ++中(同样,默认情况下它们是动态链接的).
小智 0
因为 的构造函数可能会产生副作用std::array
。但是,由于 g++ 和 clang 不使用相同的标准库(g++ 为 libstdc++,clang 为 libc++)。
对于clang为什么要删除代码的问题,可能是libc++中的std::array
构造函数是内联的,所以优化器可以看到没有副作用,因此需要保留代码。
归档时间: |
|
查看次数: |
601 次 |
最近记录: |