Reputation: 147
Code 1:
#include <iostream>
struct Interface
{
virtual void pr_fn() = 0;
virtual void pr_fn2() = 0;
virtual void pr_fn3() = 0;
};
struct Base : Interface
{
void pr_fn2() final
{
std::cout << "Base\n";
}
};
struct Derived : Base
{
void pr_fn() final
{
std::cout << "Derived2\n";
}
void pr_fn3() final
{
pr_fn2(); pr_fn();
}
};
int main()
{
Derived d;
d.pr_fn3();
return 0;
}
Code 2:
#include <iostream>
struct Interface
{
virtual void pr_fn() = 0;
virtual void pr_fn2() = 0;
virtual void pr_fn3() = 0;
};
void Interface::pr_fn3()
{
pr_fn2();
pr_fn();
}
struct Base : Interface
{
void pr_fn2() final
{
std::cout << "Base\n";
}
};
struct Derived : Base
{
void pr_fn() final
{
std::cout << "Derived\n";
}
void pr_fn3() final
{
Interface::pr_fn3();
}
};
int main()
{
Derived d;
d.pr_fn3();
return 0;
}
Code 1 assembly: (Compiler: x86-64 gcc 14.2, flags: -O3)
.LC0:
.string "Base\n"
.LC1:
.string "Derived2\n"
main:
sub rsp, 8
mov edx, 5
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:std::cout
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov edx, 9
mov esi, OFFSET FLAT:.LC1
mov edi, OFFSET FLAT:std::cout
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
xor eax, eax
add rsp, 8
ret
Code 2 assembly: (Compiler: x86-64 gcc 14.2, flags: -O3)
.LC0:
.string "Base\n"
Base::pr_fn2():
mov edx, 5
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:std::cout
jmp std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
.LC1:
.string "Derived\n"
Derived::pr_fn():
mov edx, 8
mov esi, OFFSET FLAT:.LC1
mov edi, OFFSET FLAT:std::cout
jmp std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
Derived::pr_fn3():
sub rsp, 8
mov edx, 5
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:std::cout
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov edx, 8
mov esi, OFFSET FLAT:.LC1
mov edi, OFFSET FLAT:std::cout
add rsp, 8
jmp std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
Interface::pr_fn3():
push rbx
mov rax, QWORD PTR [rdi]
mov rbx, rdi
mov rax, QWORD PTR [rax+8]
cmp rax, OFFSET FLAT:Base::pr_fn2()
jne .L7
mov edx, 5
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:std::cout
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov rax, QWORD PTR [rbx]
mov rax, QWORD PTR [rax]
cmp rax, OFFSET FLAT:Derived::pr_fn()
jne .L9
.L11:
mov edx, 8
mov esi, OFFSET FLAT:.LC1
mov edi, OFFSET FLAT:std::cout
pop rbx
jmp std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
.L7:
call rax
mov rax, QWORD PTR [rbx]
mov rax, QWORD PTR [rax]
cmp rax, OFFSET FLAT:Derived::pr_fn()
je .L11
.L9:
mov rdi, rbx
pop rbx
jmp rax
main:
sub rsp, 8
mov edx, 5
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:std::cout
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov edx, 8
mov esi, OFFSET FLAT:.LC1
mov edi, OFFSET FLAT:std::cout
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
xor eax, eax
add rsp, 8
ret
Observations:
Base::pr_fn2()
, Derived::pr_fn()
and Derived::pr_fn3()
in Code1.main()
in Code1.Interface::pr_fn3()
, Base::pr_fn2()
, Derived::pr_fn()
and Derived::pr_fn3()
in Code2.main()
in Code2.Interface::pr_fn3()
, Base::pr_fn2()
and Derived::pr_fn()
are inlined in the assembly of Derived::pr_fn3()
in Code2.My question: When the program is run and main function is invoked in both cases same assembly instructions are performed. Then why the compiler generates the assembly for other functions in Code2 but not in Code1 ?
Update 1: (Interface::pr_fn3()
is implemented inside class definition)
Interface::pr_fn3(){ ... }
defined outside the struct{}
is not implicitly inline." When its definition is moved inside the struct its definition is not emitted in the assembly. Yet definitions for Base::pr_fn2()
, Derived::pr_fn()
, Derived::pr_fn3()
are emitted in Code2 but not in Code1. Why is this difference occurring? See hereBase::pr_fn2()
and Derived::pr_fn()
in assembly. But why Derived::pr_fn3()
is still emitted? See hereNote: Please correct me if my understanding is wrong anywhere.
I experimented with some virtual methods and observed the assembly. I expected to see no difference in assembly with -O3
optimization, but found some unnecessary assembly is generated. I googled but no luck. So I am asking the question here.
Upvotes: 5
Views: 115