In various programming languages, it is possible to inline code. For example, with C++, one can use design patterns or keywords like extern, static, or inline to suggest to the compiler that code should be inlined. Non-inlined code can generate extra, unnecessary calls to the global offset table or procedure linkage table.
Consider the following non-inlined code:
int g;
int foo() {
return g;
}
int bar() {
g = 1;
foo();
return g;
}
If we compile the code like so with gcc
-O3 -Wall -fPIC -m32,
we can observe in the assembly that indeed, this code was not inlined. There are extra calls to the PLT.
foo():
call __x86.get_pc_thunk.ax
add eax, OFFSET FLAT:_GLOBAL_OFFSET_TABLE_
mov eax, DWORD PTR g@GOT[eax]
mov eax, DWORD PTR [eax]
ret
bar():
push esi
push ebx
call __x86.get_pc_thunk.bx
add ebx, OFFSET FLAT:_GLOBAL_OFFSET_TABLE_
sub esp, 4
mov esi, DWORD PTR g@GOT[ebx]
mov DWORD PTR [esi], 1
call foo()@PLT
mov eax, DWORD PTR [esi]
add esp, 4
pop ebx
pop esi
ret
g:
.zero 4
__x86.get_pc_thunk.ax:
mov eax, DWORD PTR [esp]
ret
__x86.get_pc_thunk.bx:
mov ebx, DWORD PTR [esp]
ret
Now consider the use of the inline
keyword.
int g;
inline int foo() {
return g;
}
int bar() {
g = 1;
foo();
return g;
}
The aforementioned code vastly reduces the amount of instructions generated by GCC—the function foo
has essentially been merged into bar
. If this were a small program that made repeated calls to these functions, this optimization could reduce overhead and increase performance.
bar():
call __x86.get_pc_thunk.ax
add eax, OFFSET FLAT:_GLOBAL_OFFSET_TABLE_
mov eax, DWORD PTR g@GOT[eax]
mov DWORD PTR [eax], 1
mov eax, 1
ret
g:
.zero 4
__x86.get_pc_thunk.ax:
mov eax, DWORD PTR [esp]
ret
The inline
keyword is a hint to the compiler. The compiler may not always follow such suggestions—it is context dependent. Other functions may be inlined and optimized by default even without such hints. But if you're nudging the compiler with inline suggestions, caveats may apply[1][2][3].
No comments:
Post a Comment