I am in the process of trying to learn C's more advanced aspects and wrote this when experimenting with the __inline__
keyword:
#include <stdio.h>
void f(int);
void g(int);
__inline__ void f(int egg)
{
printf("f %d\n", egg);
g(egg);
}
__inline__ void g(int egg)
{
printf("g %d\n", egg);
f(egg);
}
int main()
{
f(123);
return 0;
}
I went to the GNU manuals and found that the -Winline
compiler option warns when a function marked __inline__
can't be substituted.
I was indeed expecting a warning, but I compiled this program with gcc -ansi -pedantic -Wall -Wextra -Winline -o test test.c
and there were no warnings.
When I ran the program, it printed out the number a whole bunch of times before a segmentation fault, presumably due to the recursion limit being exceeded.
My question is, how does gcc
behave in cases like that? If it does inline the functions, how does it know it hit a recursive call between two functions?
Thank you in advance
https://gcc.gnu.org/onlinedocs/gcc-7.4.0/gcc/Inline.html#Inline
GCC does not inline any functions when not optimizing unless you specify the ‘always_inline’ attribute for the function
Since you are compiling without optimization, gcc does not even try to inline your functions, hence you do not get a warning that it wasn't done.
When I compile your code with -O -Winline
, I get a warning as expected:
inline.c: In function ‘main’:
inline.c:8:17: warning: inlining failed in call to ‘f’: call is unlikely and code size would grow [-Winline]
__inline__ void f(int egg)
^
inline.c:24:5: note: called from here
f(123);
^~~~~~
According to what I see in goldbolt this case the compiler is smart enough to understand that that code is equivalent to a endless loop (see .L2 in the code below). This optimization is possible when recursive functions are tail recursive
This has nothing to do with __inline__
. In fact if you remove the __inline__
keyword you get the same optimization and also the assembly code for g
and f
, which are never call
ed.
.LC0:
.string "f %d\n"
.LC1:
.string "g %d\n"
main:
sub rsp, 8
.L2:
mov esi, 123
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
mov esi, 123
mov edi, OFFSET FLAT:.LC1
xor eax, eax
call printf
jmp .L2
The following compare the assembly generated by gcc
with (on the right) and withtout (on the left) __inline__
keyword for g
and f
.
As you can see the main
contains exactly the same code. The only difference is that you get additional code for g
and f
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With