Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Behaviour of 2 inline functions calling each other in C

Tags:

c

gcc

inline

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

like image 646
Saucy Goat Avatar asked Mar 04 '23 04:03

Saucy Goat


2 Answers

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);
     ^~~~~~
like image 146
Nate Eldredge Avatar answered Mar 15 '23 15:03

Nate Eldredge


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 called.

     .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.enter image description here

like image 25
Davide Spataro Avatar answered Mar 15 '23 16:03

Davide Spataro