0

I was thinking about virtual calls and how they work. I understand that virtual calls can be inlined and hardcoded at compile time for value types. If the pointer type is of a class declared as final (like sealed in c#) could this allow the compiler to do the same thing?

Is this done a lot in current compilers or is it just theoretical/too small to worry about?

guitar80
  • 716
  • 6
  • 19
  • 2
    I'm also curious. The term you're looking for is "devirtualization" btw. My guess is a type marked "final" would at the very least make the analysis easier for the compiler. – CÅdahl Mar 10 '15 at 06:54
  • I believe you're right, yet it's better to check the generated code to prove it. It doesn't seem to be too hard to do. – Matt Mar 10 '15 at 07:02
  • 1
    Yes, you should see devirtualization if the type (or virtual function) is marked `final`. – T.C. Mar 10 '15 at 07:09
  • C++ compiler optimizers already "devirtualized" functions if they could tell which function ends up getting invoked. Before C++11. Requirement is that the allocation ends up near the invocation, either explicitly in the code or by getting code inlined. The *final* keyword does not change that. – Hans Passant Mar 10 '15 at 08:21
  • Possible duplicate of [How does the compiler benefit from C++'s new final keyword?](https://stackoverflow.com/questions/7538820/how-does-the-compiler-benefit-from-cs-new-final-keyword) – Raedwald Nov 29 '17 at 16:06

2 Answers2

4

Yes, the calls can be devirtualized if the virtual function or the class is declared final. For example, given

struct B {
  virtual void f() = 0;
};

struct D1 : B {
  virtual void f();
};

struct D2 : B {
  virtual void f() final;
};

struct D3 final : D1 {};

void f1(D1 * d) { d->f(); }
void f2(D2 * d) { d->f(); }
void f3(D3 * d) { d->f(); }

Clang at -O1 or higher generates this assembly:

f1(D1*):                              # @f1(D1*)
    movq    (%rdi), %rax
    jmpq    *(%rax)  # TAILCALL

f2(D2*):                              # @f2(D2*)
    jmp D2::f()              # TAILCALL

f3(D3*):                              # @f3(D3*)
    jmp D1::f()              # TAILCALL

Note that f1 calls through the vtable, while the calls in f2 and f3 are devirtualized.

T.C.
  • 133,968
  • 17
  • 288
  • 421
2

I was thinking about virtual calls and how they work. I understand that virtual calls can be inlined and hardcoded at compile time for value types. If the pointer type is of a class declared as final (like sealed in c#) could this allow the compiler to do the same thing?

Yes, in gcc calls to final members and through final classes will be devirtualised, and here's the code that does it.

Andy Brown
  • 11,766
  • 2
  • 42
  • 61