Nagefire
Nagefire

Reputation:

Return in an encapsulated inline function

I'm cleaning up some code in a driver (Netgear A6210) written in C, and I've run into a helper function, VIRTUAL_IF_DOWN(), which is forcibly inlined (e.g. __inline instead of inline) and contains a seemingly arbitrary return statement at the end.

__inline void VIRTUAL_IF_DOWN(void *pAd)
{
    /* Some code here */
    return ;
}

However, this helper function is called in the body of two other functions, before control is given back to the rest of the program, so my question is, does this return statement get inlined with the rest of the function, thus breaking out of the larger function or just do nothing? My general rule of thumb for inlined functions is that I should always treat them as separate functions and not assume that they'll be inlined as-is, anyway, I've give one of the encapsulating functions as an example:

static void rtusb_disconnect(struct usb_interface *intf)
{
    /* Some code here and then an ugly looking preprocessor branch */       
#ifdef IFUP_IN_PROBE
    VIRTUAL_IF_DOWN(pAd); // Function is used here
#endif
    /* Other code here */
}

I apologize for the messy boilerplate code, but even if the return statement just gets inlined it seems to be obfuscating the code. It seems like bad practice to hide a statement that can affect the flow behind an inlined function. What would be a better solution?

Another part of my question would be, is inlining determined at the preprocessor stage of compilation or later, such as in the assembler or linker stage?

Upvotes: 0

Views: 145

Answers (2)

Mickey695
Mickey695

Reputation: 108

Inlining is more then just copy pasting code in contrast to macro pre-processing.

When a compiler encounters an inlining directive of any sort, it evaluates what the inlined functions returns or does.

You can use sites such as godbolt.org to see what assembly is generated for C code. For example, the following functions evaluate to the same assembly code:

#include <stdio.h>

inline void test1(int number){
    printf("%d", number);
    return;
}

inline int test2(){
    return 1+1;
}

void doSomething() {
    test1(test2());
}

void doSomethingElse() {
    printf("%d", 2);
}

and the assembly:

.LC0:
        .string "%d"
_Z11doSomethingv:
        sub     rsp, 8
        mov     esi, 2
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        add     rsp, 8
        ret
_Z15doSomethingElsev:
        sub     rsp, 8
        mov     esi, 2
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        add     rsp, 8
        ret

You can see this example at https://godbolt.org/z/rBULdo

You should also note that inlining is a compiler optimization. Different compilation flags could result in different results from inlining.

It also depends on how much information is available at compile time versus run time. If the compiler knows more information at compile time it could optimize better then what it could if everything was only known at run time.

See link for GCC's behaviour when encountering the inline attribute

Upvotes: 2

melpomene
melpomene

Reputation: 85757

return; at the end of a void function is redundant.

Inlining preserves semantics: If return returns from the non-inlined call, then return in an inlined function returns from the inlined "call", i.e. it jumps to the end of the inlined body.

Inlining happens during compilation, i.e. after preprocessing and parsing, but before code generation and assembling.

Upvotes: 1

Related Questions