Compiler devirtualization, not too smart?
Asked Answered
M

1

8

I wrote this short program to see how devirtualization would work. The compiler should be able to deduce 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/.

What turned out is that neither compiler was able to optimize everything - gcc inlines foo() and makes virtual call to bar() while clang makes call to foo() and devirtualizes and inlines call to bar().

Meanwhile, if instead I call obj->bar(); and then obj->foo();, the compilers have no problem in optimizing - clang inlines both calls and gcc makes normal call to bar() instead of virtual one and inlines foo().

Can anyone explain this behavior?

Mure answered 20/12, 2015 at 11:34 Comment(5)
this question is odd. what are we supposed to answer? GCC is worse than Clang? somtimes compilers can figure stuff, sometimes they miss. Clang is newer and was built from scratch to support these kind of optimizations.Zeus
No, I'm just curious if there's something special beyond this worse optimization in the case where non-virtual call is made first. Some optimizations are already performed that disturb the devirtualization?Mure
hubicka.blogspot.de/2014/04/… provides interesting background information for gcc. It's a whole serious of articles about devirtualization from the gcc developer who implemented it. Have you tried to add "-fwhole-program" or "-fsuggest-final-methods" to gcc?Lanctot
It's probably because the compiler thinks that inlining does not help because the cout is too expensive compared to the overhead of the function call. If you replace it with something simpler, e.g. an assigment to a member, it gets inlined.Lanctot
Wow, all those endls; cout is well and truly flushed! (use` '\n'` when all you need is to end a line; endl does a great deal more, none of which is needed ehre)Analyst
L
6

It's probably because the compiler thinks that inlining does not help because the cout is too expensive compared to the overhead of the function call. If you replace it with something simpler, e.g. an assigment to a member, it will get inlined. See below for the output of

#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
Lanctot answered 20/12, 2015 at 12:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.