2016-04-18 10 views
16

私はgcc 6 -O3 -std=c++14と、次のコードをコンパイルするとき、私はいいとmain空の取得:これはgcc optimizerのバグですか?

Dump of assembler code for function main(): 
    0x00000000004003e0 <+0>:  xor %eax,%eax 
    0x00000000004003e2 <+2>:  retq 

しかし、主な "休憩" の最適化の最後の行のコメントを解除:

Dump of assembler code for function main(): 
    0x00000000004005f0 <+0>:  sub $0x78,%rsp 
    0x00000000004005f4 <+4>:  lea 0x40(%rsp),%rdi 
    0x00000000004005f9 <+9>:  movq $0x400838,0x10(%rsp) 
    0x0000000000400602 <+18>: movb $0x0,0x18(%rsp) 
    0x0000000000400607 <+23>: mov %fs:0x28,%rax 
    0x0000000000400610 <+32>: mov %rax,0x68(%rsp) 
    0x0000000000400615 <+37>: xor %eax,%eax 
    0x0000000000400617 <+39>: movl $0x0,(%rsp) 
    0x000000000040061e <+46>: movq $0x400838,0x30(%rsp) 
    0x0000000000400627 <+55>: movb $0x0,0x38(%rsp) 
    0x000000000040062c <+60>: movl $0x0,0x20(%rsp) 
    0x0000000000400634 <+68>: movq $0x400838,0x50(%rsp) 
    0x000000000040063d <+77>: movb $0x0,0x58(%rsp) 
    0x0000000000400642 <+82>: movl $0x0,0x40(%rsp) 
    0x000000000040064a <+90>: callq 0x400790 <ErasedObject::~ErasedObject()> 
    0x000000000040064f <+95>: lea 0x20(%rsp),%rdi 
    0x0000000000400654 <+100>: callq 0x400790 <ErasedObject::~ErasedObject()> 
    0x0000000000400659 <+105>: mov %rsp,%rdi 
    0x000000000040065c <+108>: callq 0x400790 <ErasedObject::~ErasedObject()> 
    0x0000000000400661 <+113>: mov 0x68(%rsp),%rdx 
    0x0000000000400666 <+118>: xor %fs:0x28,%rdx 
    0x000000000040066f <+127>: jne 0x400678 <main()+136> 
    0x0000000000400671 <+129>: xor %eax,%eax 
    0x0000000000400673 <+131>: add $0x78,%rsp 
    0x0000000000400677 <+135>: retq 
    0x0000000000400678 <+136>: callq 0x4005c0 <[email protected]> 

コード

#include <type_traits> 
#include <new> 

namespace 
{ 
struct ErasedTypeVTable 
{ 
    using destructor_t = void (*)(void *obj); 

    destructor_t dtor; 
}; 

template <typename T> 
void dtor(void *obj) 
{ 
    return static_cast<T *>(obj)->~T(); 
} 

template <typename T> 
static const ErasedTypeVTable erasedTypeVTable = { 
    &dtor<T> 
}; 
} 

struct ErasedObject 
{ 
    std::aligned_storage<sizeof(void *)>::type storage; 
    const ErasedTypeVTable& vtbl; 
    bool flag = false; 

    template <typename T, typename S = typename std::decay<T>::type> 
    ErasedObject(T&& obj) 
    : vtbl(erasedTypeVTable<S>) 
    { 
     static_assert(sizeof(T) <= sizeof(storage) && alignof(T) <= alignof(decltype(storage)), ""); 
     new (object()) S(std::forward<T>(obj)); 
    } 

    ErasedObject(ErasedObject&& other) = default; 

    ~ErasedObject() 
    { 
     if (flag) 
     { 
     ::operator delete(object()); 
     } 
     else 
     { 
     vtbl.dtor(object()); 
     } 
    } 

    void *object() 
    { 
     return reinterpret_cast<char *>(&storage); 
    } 
}; 

struct myType 
{ 
    int a; 
}; 

int main() 
{ 
    ErasedObject c1(myType{}); 
    ErasedObject c2(myType{}); 
    //ErasedObject c3(myType{}); 
} 

clangは両方のバージョンを最適化できます。

何が起こっているのですか?最適化の限界にぶつかっていますか?もしそうなら、それは設定可能ですか?

+0

私のバグのように見えます。 –

+0

GCC(Ubuntu 5.3.1-14ubuntu2)で再現可能5.3.1 20160413 – Zeta

+0

誤ったコードやあまり最適化されていないコードが生成されない限り、それをバグと呼んでいません。 –

答えて

5

-fdump-ipa-inlineg++を実行して、機能がインライン化されているかどうかの詳細を確認しました。このエラーコードはGCC/GCC/IPA-inline.cに設定されている

(...) 
    150 Deciding on inlining of small functions. Starting with size 35. 
    151 Enqueueing calls in void {anonymous}::dtor(void*) [with T = myType]/40. 
    152 Enqueueing calls in int main()/35. 
    153 not inlinable: int main()/35 -> ErasedObject::~ErasedObject()/33, call is unlikely and code size would grow 
    154 not inlinable: int main()/35 -> ErasedObject::~ErasedObject()/33, call is unlikely and code size would grow 
    155 not inlinable: int main()/35 -> ErasedObject::~ErasedObject()/33, call is unlikely and code size would grow 
    (...) 

::私が得た作成main()関数でのテストケースと3つのオブジェクトのために

else if (!e->maybe_hot_p() 
     && (growth >= MAX_INLINE_INSNS_SINGLE 
     || growth_likely_positive (callee, growth))) 
{ 
     e->inline_failed = CIF_UNLIKELY_CALL; 
     want_inline = false; 
} 

その後g ++でこれらの関数をインライン化するための最小の変更点は、宣言を追加することです。

私はそうではありませんでした。なぜint main()が熱く見なされないのかをコードで見つけることができますが、おそらくこれは別の質問のために残すべきです。

さらに興味深いのは、上に貼り付けた条件付きの2番目の部分です。インライン展開後にコードが縮小されるときに、コードが大きくなり、サンプルを作成するという意図がインラインではありませんでした。

これはGCC's bugzillaに報告する価値があると思いますが、バグと呼ぶことができるかどうかはわかりません - インラインインパクトの評価はヒューリスティックなので、ほとんどの場合正しく動作するはずですそのうちの。

+0

[creduce](https://embed.cs.utah .edu/creduce /)を使用して、インライン化に失敗した最小の例を取得します。 –

+0

私は、 'main()'が明示的にまたはポインタを介して呼び出されるための未定義の動作であるため、それが熱くないと推測します(C++ 11 3.6.1/3: "関数mainはプログラム")。したがって、gccは 'main()'は一度しか呼び出せないことを知っています。 –

+0

@MichaelBurrあなたは未定義の振る舞いについてはまったく正しいですが、GCCソースで 'main()'が寒くなるのを見つけることができないので、それはまだ私には分かりません:)私は ' maybe_hot_p'なので、おそらくhot/coldヒューリスティックそのものから来るでしょう。 –

関連する問題