Let's say there is such an abstract class:
class Base {
public:
virtual void f() = 0;
virtual ~Base() = default;
};
And some function:
void function (Base& x, bool flag1, bool flag2, bool flag3) {
if(flag1)
x.f();
if(flag2)
x.f();
if(flag3)
x.f();
}
In the main()
function I load an instance of derived from this class from the shared library:
int main() {
Base* x = /* load from shared lib*/;
bool flag1 = getchar() == '1';
bool flag2 = getchar() == '2';
bool flag3 = getchar() == '3';
function(*x, flag1, flag2, flag3);
return 0;
}
Question: can I expect that within one call to the function void function (Base& x, bool flag1, bool flag2, bool flag3)
the virtual function table will only be accessed once, even if all three flags are true
?
That is, can the compiler find the function in the table only once and use its address the other two times?
P.s. Loading an instance from a shared library is just an example, to rule out the possibility of inlining a function.
Even doing this:
void function (Base& x, bool flag1, bool flag2, bool flag3) {
if(flag1 || flag2 || flag3) {
if(flag1)
x.f();
if(flag2)
x.f();
if(flag3)
x.f();
}
}
with GCC -O3 loads the vtable pointer (mov rax, QWORD PTR [rbx]
) for every call:
function(Base&, bool, bool, bool):
push r12
mov r12d, ecx
push rbp
mov ebp, edx
push rbx
mov rbx, rdi
test sil, sil
jne .L2
test dl, dl
jne .L2
.L6:
test r12b, r12b
jne .L12
.L9:
pop rbx
pop rbp
pop r12
ret
.L2:
mov rax, QWORD PTR [rbx]
mov rdx, QWORD PTR [rax]
test sil, sil
je .L7
mov rdi, rbx
call rdx
test bpl, bpl
je .L6
mov rax, QWORD PTR [rbx]
.L7:
mov rdi, rbx
call [QWORD PTR [rax]]
test r12b, r12b
je .L9
.L12:
mov rax, QWORD PTR [rbx]
mov rdi, rbx
pop rbx
pop rbp
pop r12
mov rax, QWORD PTR [rax]
jmp rax