Reputation:
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
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
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