You can tell it "I won't do that" though with additional flags, like Clang's -fwhole-program-vtables, and even then it's not that simple. There was an effort in Clang to better support whole program devirtualization, but I haven't been following what kind of progress has been made: https://groups.google.com/g/llvm-dev/c/6LfIiAo9g68?pli=1
Maybe I can set this option at work. Though it's scary because I'd have to be certain.
Lots of code gets slower if it might need to be called from something not currently in the compiler's scope. That's essentially what ABI overhead is. If there isn't already, there should be a compiler flag that says "this is the whole program, have at it" which implies the vtables option.
* It is possible with `dlopen()` to load code objects that violate the assumptions made during compilation.
* The presence of runtime configuration mechanisms and application input can make it impossible to anticipate things like the choice of implementations of an interface.
One can always strive to reduce such situations, but it might simply not be necessary if a JIT is present.
$ cat animal.h cat.cpp main.cpp
// animal.h
#pragma once
class animal {
public:
virtual ~animal() {}
virtual void speak() = 0;
};
animal& get_mystery_animal();
// cat.cpp
#include "animal.h"
#include <cstdio>
class cat final : public animal {
public:
~cat() override{}
void speak() override{
puts("meow");
}
};
static cat garfield{};
animal& get_mystery_animal() {
return garfield;
}
// main.cpp
#include "animal.h"
int main() {
animal& a = get_mystery_animal();
a.speak();
}
$ make clean && CXX=clang++ make -j && objdump --disassemble=main -C lto_test
rm -f *.o lto_test
clang++ -c -flto -O3 -g cat.cpp -o cat.o
clang++ -c -flto -O3 -g main.cpp -o main.o
clang++ -flto -O3 -g cat.o main.o -o lto_test
lto_test: file format elf64-x86-64
Disassembly of section .init:
Disassembly of section .plt:
Disassembly of section .plt.got:
Disassembly of section .text:
00000000000011b0 <main>:
11b0: 50 push %rax
11b1: 48 8b 05 58 2e 00 00 mov 0x2e58(%rip),%rax # 4010 <garfield>
11b8: 48 8d 3d 51 2e 00 00 lea 0x2e51(%rip),%rdi # 4010 <garfield>
11bf: ff 50 10 call *0x10(%rax)
11c2: 31 c0 xor %eax,%eax
11c4: 59 pop %rcx
11c5: c3 ret
Disassembly of section .fini:
$ make clean && CXX=g++ make -j && objdump --disassemble=main -C lto_test|sed -e 's,^, ,'
rm -f *.o lto_test
g++ -c -flto -O3 -g cat.cpp -o cat.o
g++ -c -flto -O3 -g main.cpp -o main.o
g++ -flto -O3 -g cat.o main.o -o lto_test
lto_test: file format elf64-x86-64
Disassembly of section .init:
Disassembly of section .plt:
Disassembly of section .plt.got:
Disassembly of section .text:
0000000000001090 <main>:
1090: 48 83 ec 08 sub $0x8,%rsp
1094: 48 8d 3d 75 2f 00 00 lea 0x2f75(%rip),%rdi # 4010 <garfield>
109b: e8 50 01 00 00 call 11f0 <cat::speak()>
10a0: 31 c0 xor %eax,%eax
10a2: 48 83 c4 08 add $0x8,%rsp
10a6: c3 ret
Disassembly of section .fini: