Calling a constant function of a pointer to an element that is not inline

Now I know that there are no guarantees for inlining, but ...

Given the following:

struct Base { virtual int f() = 0; }; struct Derived : public Base { virtual int f() final override { return 42; } }; extern Base* b; 

We have:

 int main() { return static_cast<Derived*>(b)->f(); } 

Compiles to:

 main: movl $42, %eax ret 

But...

 int main() { return (static_cast<Derived*>(b)->*(&Derived::f))(); } 

Compiles to:

 main: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $16, %esp movl b, %eax movl (%eax), %edx movl %eax, (%esp) call *(%edx) leave ret 

Which is really sad.

Why is this PMF call not inline? PMF is a constant expression!

+6
source share
3 answers

The problem is that in the first case, type-based devirtualization turns an indirect call into a direct call. When you add a pointer to a member, the type-based devirtualization type cannot be used (since it works with the frontend, passing optimization information about the type and virtual method, which is not trivially known from this case). GCC may be able to constantly collapse the actual access to the virutal table , knowing that B is a class and knowing that its member can only be called after it is created. At the moment, he does not do such an analysis.

I would suggest filling out the GCC bugzilla extension request.

+4
source

It is not always possible to embed a pointer to a function (if the compiler cannot determine what the pointer points to, which is often difficult, so the compiler may “refuse” before you expect it).

EDIT

To expand my answer: The first priority in all compilers is the generation of CORRECT code (although sometimes this also does not happen!). Optimization, such as built-in functions, is what the compiler will only do when it is "safe." The compiler may not quite “understand” that the above expression is indeed a constant expression and, therefore, returns to “let it do a safe thing” (which should invoke virtual functions through the table, and not inline the function). Pointers to virtual member functions are a pretty tricky question in C ++.

+1
source

It is really a little annoying, but it is not surprising.

Most compiler transformations work by pattern: they recognize the pattern and apply the transform. So what is the reason that it cannot be optimized? Well, maybe it's just that there are no transformations on this template.

In particular, it is possible that the problem could be about &Derived::f , there is a certain representation used by both gcc and clang for a pointer to a virtual function: they use a pointer to a trampoline function that performs virtual resolution, so it just might be that this trampoline function is one socket too much for the compiler to see.

0
source

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


All Articles