Devirtualization compiler, not too smart?

I wrote this short program to see how devirtualization will work. The compiler should be able to output the correct type:

#include <iostream> using std::cout; using std::endl; class Base { public: void foo() { cout << "Base::foo" << endl; } virtual void bar() { cout << "Base::bar" << endl; } virtual ~Base() = default; }; class Child : public Base { public: void foo() { cout << "Child::foo" << endl; } void bar() { cout << "Child::bar" << endl; } }; int main() { Base* obj = new Child; obj->foo(); obj->bar(); delete obj; } 

Compiled with -O2 -std=c++11 using gcc 5.3 and clang 3.7 via https://gcc.godbolt.org/ .

It turned out that no compiler could optimize everything - gcc inlines foo() and makes a virtual call to bar() , while clang calls foo() , while devirtualizes and inlines calls bar() .

Meanwhile, if instead I call obj->bar(); and then obj->foo(); , compilers have no problems in optimization - clang inlines and calls, and gcc makes a normal call to bar() instead of virtual and inlines foo() .

Can anyone explain this behavior?

+5
source share
1 answer

Probably because the compiler thinks that inlining does not help, because cout too expensive compared to overhead function calls. If you replace it with something simpler, for example. so that he joins the member, he will be included. See below for a conclusion.

 #include <iostream> using std::cout; using std::endl; class Base { public: void foo() { i = 1; } virtual void bar() { i = 2; } virtual ~Base() = default; int i = 0; }; class Child : public Base { public: void foo() { i = 3; } void bar() { i = 4; } }; int main() { Base* obj = new Child; obj->foo(); obj->bar(); std::cout << obj->i << std::endl; //delete obj; } 

Assembly:

 Base::bar(): movl $2, 8(%rdi) ret Child::bar(): movl $4, 8(%rdi) ret Base::~Base(): ret Child::~Child(): ret Child::~Child(): jmp operator delete(void*) Base::~Base(): jmp operator delete(void*) main: subq $8, %rsp movl $16, %edi call operator new(unsigned long) movl $4, %esi movl std::cout, %edi call std::basic_ostream<char, std::char_traits<char> >::operator<<(int) movq %rax, %rdi call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&) xorl %eax, %eax addq $8, %rsp ret subq $8, %rsp movl std::__ioinit, %edi call std::ios_base::Init::Init() movl $__dso_handle, %edx movl std::__ioinit, %esi movl std::ios_base::Init::~Init(), %edi addq $8, %rsp jmp __cxa_atexit 
+5
source

Source: https://habr.com/ru/post/1238757/


All Articles