首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >这是不是gcc优化器的一个bug?

这是不是gcc优化器的一个bug?
EN

Stack Overflow用户
提问于 2016-04-18 23:02:00
回答 1查看 336关注 0票数 16

当我用gcc 6 -O3 -std=c++14编译以下代码时,我得到的main很好,而且是空的

代码语言:javascript
复制
Dump of assembler code for function main():
   0x00000000004003e0 <+0>:     xor    %eax,%eax
   0x00000000004003e2 <+2>:     retq 

但是不注释main中的最后一行“中断”优化:

代码语言:javascript
复制
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 <__stack_chk_fail@plt>

代码

代码语言:javascript
复制
#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可以优化这两个版本。

知道是怎么回事吗?我是不是达到了一些优化极限?如果是,它是可配置的吗?

EN

回答 1

Stack Overflow用户

发布于 2016-04-20 07:00:33

我用-fdump-ipa-inline运行了g++,以获得有关函数为什么内联或不内联的更多信息。

对于带有main()函数和创建的三个对象的测试用例,我得到:

代码语言:javascript
复制
  (...)
  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
  (...)

此错误码设置在gcc/gcc/ipa-inline.c中:

代码语言:javascript
复制
  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++内联这些函数的最小更改是添加一个声明:

代码语言:javascript
复制
int main() __attribute__((hot));

我在代码中找不到为什么int main()不被认为是热门的,但也许这应该留给另一个问题。

更有趣的是我在上面粘贴的条件的第二部分。这样做的目的是在代码增长时不进行内联,并且您生成了一个在完成内联后代码收缩的示例。

我认为这应该在GCC's bugzilla上报告,但我不确定你是否可以称之为错误-内联影响的估计是一种启发式方法,因此预计它在大多数情况下都会正确工作,而不是所有情况下。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36697701

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档