Does Gcc Inline C++ Functions Without the 'Inline' Keyword

Does GCC inline C++ functions without the 'inline' keyword?

Yes. Any compiler is free to inline any function whenever it thinks it is a good idea. GCC does that as well.

At -O2 optimization level the inlining is done when the compiler thinks it is worth doing (a heuristic is used) and if it will not increase the size of the code. At -O3 it is done whenever the compiler thinks it is worth doing, regardless of whether it will increase the size of the code. Additionally, at all levels of optimization (enabled optimization that is), static functions that are called only once are inlined.

As noted in the comments below, these -Ox are actually compound settings that envelop multiple more specific settings, including inlining-related ones (like -finline-functions and such), so one can also describe the behavior (and control it) in terms of those more specific settings.

Usage of static inline to functions which calls other larger functions in C

It does not matter. The function total will not be probably inlined (normal function call will be emitted). Function res probably will be inlined.

Why probably. Because the inline keyword is only a suggestion. Functions without inline may be inlined as well. The compiler may inline the total function as well if decides that on a certain level of optimization it will result in the best code generation.

Many compilers have special extensions which give you control over inlining. for example:

gcc has __attribute__((noinline)) and __attribute__((always_inline)).

iar #pragma inline=never and pragma inline=force

C, inline function and GCC

Okay, so after reading through VivienG's link, I think I've understood the exact reasoning behind this error message. It's confusing and misleading (at least to me; it shouldn't happen if you've got just one translation unit), yet it is possible to explain:

  • Assuming the compiler doesn't want to actually inline the code, it has to know where to put that function, especially when it's used in multiple translation units.

  • Classic approach is to create multiple copies, one for each translation unit (or at least for those units where it's used).

  • This may cause problems, e.g. when trying to do some function pointer comparisons (still leaves the question why you'd to that though).

To counter this (and other issues I possibly didn't list here), they've thought of some actually quite neat (although - as mentioned - in my opinion misleading) solution:

You declare the function as inline the way you know, but at the same time you tell the compiler where to put the non-inline version with the extern keyword.

So in your example, you'd keep your function as-is and put it in a header file (so it's known where it's going to be used):

inline int foo(void)
{
return 10 + 3;
}

In addition, to tell the compiler where to place the non-inlined version, you'll have to add one more "forward" declaration in one translation unit:

extern inline int foo(void);

So the whole concept is essentially reversed when compared to classic functions: Put the implementation in the header and then a short declaration in just one file.

As mentioned already, while using the -O3 parameter, all code marked with inline is actually inlined, which won't cause the issue to happen.

What is the use of the `inline` keyword in C?

Note: when I talk about .c files and .h files in this answer, I assume you have laid out your code correctly, i.e. .c files only include .h files. The distinction is that a .h file may be included in multiple translation units.

static inline void f(void) {} has no practical difference with static void f(void) {}.

In ISO C, this is correct. They are identical in behaviour (assuming you don't re-declare them differently in the same TU of course!) the only practical effect may be to cause the compiler to optimize differently.

inline void f(void) {} in C doesn't work as the C++ way. How does it work in C? What actually does extern inline void f(void); do?

This is explained by this answer and also this thread.

In ISO C and C++, you can freely use inline void f(void) {} in header files -- although for different reasons!

In ISO C, it does not provide an external definition at all. In ISO C++ it does provide an external definition; however C++ has an additional rule (which C doesn't), that if there are multiple external definitions of an inline function, then the compiler sorts it out and picks one of them.

extern inline void f(void); in a .c file in ISO C is meant to be paired with the use of inline void f(void) {} in header files. It causes the external definition of the function to be emitted in that translation unit. If you don't do this then there is no external definition, and so you may get a link error (it is unspecified whether any particular call of f links to the external definition or not).

In other words, in ISO C you can manually select where the external definition goes; or suppress external definition entirely by using static inline everywhere; but in ISO C++ the compiler chooses if and where an external definition would go.

In GNU C, things are different (more on this below).

To complicate things further, GNU C++ allows you to write static inline an extern inline in C++ code... I wouldn't like to guess on what that does exactly

I never really found a use of the inline keyword in my C programs, and when I see this keyword in other people's code, it's almost always static inline

Many coders don't know what they're doing and just put together something that appears to work. Another factor here is that the code you're looking at might have been written for GNU C, not ISO C.

In GNU C, plain inline behaves differently to ISO C. It actually emits an externally visible definition, so having a .h file with a plain inline function included from two translation units causes undefined behaviour.

So if the coder wants to supply the inline optimization hint in GNU C, then static inline is required. Since static inline works in both ISO C and GNU C, it's natural that people ended up settling for that and seeing that it appeared to work without giving errors.

, in which I see no difference with just static.

The difference is just in the intent to provide a speed-over-size optimization hint to the compiler. With modern compilers this is superfluous.

How can I tell gcc not to inline a function?

You want the gcc-specific noinline attribute.

This function attribute prevents a
function from being considered for
inlining. If the function does not
have side-effects, there are
optimizations other than inlining that
causes function calls to be optimized
away, although the function call is
live. To keep such calls from being
optimized away, put
asm ("");

Use it like this:

void __attribute__ ((noinline)) foo() 
{
...
}

why didn't gcc decide inline-or-not for me for this function?

According to GCC documentation, GCC has an optimization setting called -finline-functions. This is actually the setting that makes GCC to use its heuristic inlining criteria on all functions, even if they are not declared inline. This setting is enabled at -O3 optimization level. So, it you want to give GCC full freedom to apply its heuristics to all functions, you have to specify -O3 at least (or specify -finline-functions explicitly).

Without -finline-functions GCC does not generally attempt to inline functions that are not declared inline, with some notable exceptions: a number of other inlining options might also lead to non-inline functions getting inlined. However, these options are targeted at very specific cases

  • -finline-functions-called-once is enabled as early as -O1. Static functions called only once are inlined, even if they are not declared inline.

  • -finline-small-functions is enabled at -O2. It triggers inlining if it results in reduction of code size, even if the function is not declared inline.

Your function apparently does not pass these specific inlining filters active at -O2 level: it is relatively large and (apparently) called more than once. For this reason GCC does not consider it for inlining at -O2, unless you explicitly request it with inline keyword. Note, that explicit inline keyword is basically like -finline-functions setting turned on for that specific function only. It will make GCC to consider it for inlining, but does not guarantee inlining.

Again, if you want GCC to completely take over these decisions you need -finline-functions or -O3. The fact that explicit inline keyword triggers inlining at -O2 means that GCC should decide to inline it at -O3 regardless of whether inline is present there.

C++'s inline - how strong a hint is it for GCC and Clang/LLVM?

[Caveat: not a C++/GCC guru] You'll want to read up on inline here.

Also, this, for GCC/C99.

The extent to which
suggestions made by using the inline
function specifier are effective (C99
6.7.4).

  • GCC will not inline any functions if the -fno-inline option is
    used or if -O0 is used. Otherwise, GCC
    may still be unable to inline a
    function for many reasons; the
    -Winline option may be used to determine if a function has not been
    inlined and why not.

So it appears that unless your compiler settings (like -fno-inline or -O0) are used, the compiler takes the hint. I can't comment on Clang/LLVM (or GCC really).'

I recommend using -Winline if this isn't a code-golf question and you need to know what's going on.

gcc fails to inline functions without -O2

Adding the "-O" option to your compiler command. Inlining is turned on only when optimization is enabled.

C99 inline functions

By default, Clang builds C code in GNU C11 mode, so it uses standard C99 semantics for the inline keyword. These semantics are different from those in GNU C89 mode, which is the default mode in versions of GCC prior to 5.0. For example, consider the following code:

inline int add(int i, int j) { return i + j; }

int main() {
int i = add(4, 5);
return i;
}

In C99, inline means that a function's definition is provided only for inlining, and that there is another definition (without inline) somewhere else in the program. That means that this program is incomplete, because if add isn't inlined (for example, when compiling without optimization), then main will have an unresolved reference to that other definition. Therefore we'll get a (correct) link-time error like this:

Undefined symbols:
"_add", referenced from:
_main in cc-y1jXIr.o

By contrast, GNU C89 mode (used by default in older versions of GCC) is the C89 standard plus a lot of extensions. C89 doesn't have an inline keyword, but GCC recognizes it as an extension and just treats it as a hint to the optimizer.

There are several ways to fix this problem:

  1. Change add to a static inline function. This is usually the right solution if only one translation unit needs to use the function. static inline functions are always resolved within the translation unit, so you won't have to add a non-inline definition of the function elsewhere in your program.
  2. Remove the inline keyword from this definition of add. The inline keyword is not required for a function to be inlined, nor does it guarantee that it will be. Some compilers ignore it completely. Clang treats it as a mild suggestion from the programmer.
  3. Provide an external (non-inline) definition of add somewhere else in your program. The two definitions must be equivalent!
  4. Compile in the GNU C89 dialect by adding -std=gnu89 to the set of Clang options. This option is only recommended if the program source cannot be changed or if the program also relies on additional C89-specific behavior that cannot be changed.

All of this only applies to C code; the meaning of inline in C++ is very different from its meaning in either GNU89 or C99.

C99 inline function in .c file

The inline model in C99 is a bit different than most people think, and in particular different from the one used by C++

inline is only a hint such that the compiler doesn't complain about doubly defined symbols. It doesn't guarantee that a function is inlined, nor actually that a symbol is generated, if it is needed. To force the generation of a symbol you'd have to add a sort of instantiation after the inline definition:

int func(int i);

Usually you'd have the inline definition in a header file, that is then included in several .c files (compilation units). And you'd only have the above line in exactly one of the compilation units. You probably only see the problem that you have because you are not using optimization for your compiler run.

So, your use case of having the inline in the .c file doesn't make much sense, better just use static for that, even an additional inline doesn't buy you much.

Clarification over internal linkage of inline functions in C

TL;DR: GCC still defaults to its old semantics of inline, in which an inline function is still compiled as an externally visible entity. Specifying -std=c99 or -std=c11 will cause GCC to implement the standard semantics; however, the IBM compiler does not conform to the standard either. So linking will still fail, but with a different error.


Since C99, a function declaration with no declared linkage does not generate a function object. The inline definition will only be used with inline substitution, and the compiler is not obliged to perform this optimisation. It is expected that an external definition of the function exists in some other translation unit, and such a definition must exist if the function object is used, either by taking its address or by being called in a context where the compiler chooses not to perform the inline substitution.

If the inline function is declared with either static or extern, then a function object is compiled, with the indicated linkage, thereby satisfying the requirement that the function object be defined.

Prior to C99, inline was not part of the C standard, but many compilers -- particularly GCC -- implemented it as an extension. In the case of GCC, however, the semantics of inline differed slightly from the above exposition.

In C99 (and more recent), an inline function with no linkage specification is only an inline definition ("An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit." §6.7.4p7). But in the GCC extension, an inline function with no linkage specification was given external linkage (just like a non-inline function declaration). GCC then special-cased extern inline to mean "do not generate a function object", which is effectively the same as standard C99's handling of an inline function with neither extern nor static modifiers. See the GCC manual, particularly the last section.

This is only still important because GCC still defaults to using its original inline semantics unless you specify that it should conform to some C standard (using, for example, -std=c11) or disable the GNU inline semantics using -fno-gnu89-inline.

The example code, which I understand is taken from the IBM i7.1 compiler documentation, does not correctly reflect any C standard. The two definitions of foo as inline functions do not generate any actual function named foo, so the use of &foo must refer to some externally-defined foo, and there isn't one in the program. GCC will report this issue if you tell it to use C11/C99 semantics:

$ gcc -std=c99 a.c b.c
/tmp/ccUKlp5g.o: In function `g':
a.c:(.text+0xa): undefined reference to `foo'
a.c:(.text+0x13): undefined reference to `foo'
/tmp/cc2hv17O.o: In function `main':
b.c:(.text+0xa): undefined reference to `foo'
b.c:(.text+0x13): undefined reference to `foo'
collect2: error: ld returned 1 exit status

By contrast, if you ask for Gnu inline semantics, both translation units will define foo, and the linker will complain about a duplicate definition:

$ gcc -std=c99 -fgnu89-inline a.c b.c
/tmp/ccAHHqOI.o: In function `foo':
b.c:(.text+0x0): multiple definition of `foo'
/tmp/ccPyQrTO.o:a.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

Also note that GCC does not inline any function by default. You must provide some optimization option in order to enable function inlining. If you do so, and you remove the use of the address operator, you can get the program to compile:

$ cat a2.c
#include <stdio.h>
inline int foo() { return 3; }
void g() {
printf("foo called from g: return value = %d\n", foo());
}
$ cat b2.c
#include <stdio.h>
inline int foo() { return 4; }
void g();
int main() {
printf("foo called from main: return value = %d\n", foo());
g();
return 0;
}

$ # With no optimisation, an external definition is still needed:
$ gcc -std=c11 a2.c b2.c
/tmp/cccJV9J6.o: In function `g':
a2.c:(.text+0xa): undefined reference to `foo'
/tmp/cct5NcjY.o: In function `main':
b2.c:(.text+0xa): undefined reference to `foo'
collect2: error: ld returned 1 exit status

$ # With inlining enabled, the program works as (possibly) expected:
$ gcc -std=c11 -O a2.c b2.c
$ gcc -std=c11 -O1 a2.c b2.c
$ ./a.out
foo called from main: return value = 4
foo called from g: return value = 3

As indicated by the IBM documentation, the rules for C++ are distinct. This program is not valid C++ because the definitions of foo in the two translation units differ, but the compiler is not obliged to detect this error and the usual Undefined Behaviour rules apply (i.e., the standard doesn't define what will be printed). As it happens, GCC seems to show the same results as i7.1:

$ gcc -std=c++14 -x c++ a.c b.c
$ ./a.out
foo called from main: return value = 3, address = 0x55cd03df5670
foo called from g: return value = 3, address = 0x55cd03df5670


Related Topics



Leave a reply



Submit