EEVblog Electronics Community Forum

Products => Computers => Programming => Topic started by: RoGeorge on December 17, 2021, 08:08:26 pm

Title: GCC inline across C standards - undefined reference for inline functions
Post by: RoGeorge on December 17, 2021, 08:08:26 pm
There is an error like this for each inline function:

Code: [Select]
bare-metal-arm/accel.c:80: undefined reference to `i2c_start'

though, the function is defined in the same file where it is reported as an unknown symbol, like this:

Code: [Select]
inline void i2c_start(I2C_MemMapPtr p)
{
    i2c_set_master(p);
    i2c_set_tx(p);
}
The full project is a bare metal demo for the FRDM-KL25Z devboard, for Linux and without using the  proprietary Freescale CodeWarrior tollchain/IDE, by payne92:
https://github.com/payne92/bare-metal-arm




Searching online, the error seems to be related with how different C/C++ standards define the "inline", "static inline" and "external inline" behaviour, according to https://gcc.gnu.org/onlinedocs/gcc/Inline.html#Inline

So far I've tried adding either "-std=gnu11" or "-finline-functions" to the Makefile, but that didn't fix the undefined symbols errors for the inline function names.

However, making the inline functions "static", fixes the error.  My understanding so far is that adding "static" will also keep the entire function body out of any code optimization.

Is there any better way to avoid the missing symbols errors, while allowing the compiler to do whatever code optimization it might be able to do inside the body of the inline functions?

Title: Re: GCC inline across C standards - undefined reference for inline functions
Post by: ataradov on December 17, 2021, 09:06:29 pm
Why would static keep it out of optimization?

The error is with the linker. With the code as is, disassemble the individual object file and check how the code was generated. There is a chance compiler ignored your inline request. This behaviour is allowed according to the standard, and the only real way to force it to inline things is to use compiler-specific attributes (__attribute__((always_inline)) in case of GCC).
Title: Re: GCC inline across C standards - undefined reference for inline functions
Post by: TheCalligrapher on December 17, 2021, 09:40:18 pm
Code: [Select]
bare-metal-arm/accel.c:80: undefined reference to `i2c_start'

though, the function is defined in the same file where it is reported as an unknown symbol, like this:

Code: [Select]
inline void i2c_start(I2C_MemMapPtr p)
{
    i2c_set_master(p);
    i2c_set_tx(p);
}


If the compiler decides to perform a regular (non-inline) call to your function, it will require a regular (non-inline) definition for your function. This is your responsibility to provide that definition. As opposed to C++, in C the non-inline definition will NOT be generated automatically.

It is your responsibility to choose one (and only one) translation unit and make an `extern inline` declaration of your function in that translation unit. The compiler will then emit a non-inline definition in that translation unit. The linker will pick it up and use it to dispatch all non-inline calls.

Apparently, you forgot to do that, which is why the linker is complaining.

However, making the inline functions "static", fixes the error.  My understanding so far is that adding "static" will also keep the entire function body out of any code optimization.

Um.. Not sure what you mean here. Making a functinon `static` is fully equivalent to making it `inline`, as far as optimizations are concerned. Every time the compiler can see the full definition of your function (the whole body), you get full optimizations.

This actually raises an obvious question: do you want your function to have external linkage? Is it important to you? `inline` is intended for functions with external linkage. If you don't need external linkage, just make it `static` and forget about `inline`. From `static` you will get everything that `inline` would give you.

I see that in your code the `inline` function is defined in a `.c` file (not in a header file), which most likely means that you don't need external linkage for this function. In that case just make it `static`. You can also make it `static inline` if you want, but it will not make any difference over simple `static`.
Title: Re: GCC inline across C standards - undefined reference for inline functions
Post by: RoGeorge on December 17, 2021, 10:12:09 pm
I'm doing software mostly as a part of the electronic hobby, and so far my understanding about "static" was that static means "don't touch this" to the compiler's optimizer, like when it's used to prevent apparently unused variables for a hardware interrupt function to be removed by the compiler.

I've also "auto-assumed" an inline will always enforce embedding the code instead of making a call, didn't know that this might not be always true.

Just for the records, attaching the .lst for the file where the missing symbols used to be when it was compiled without "static".

Thanks, for the clarifications.  So far there is plenty of space, so I'm not concern with code size and optimization, but I'll check the .lst to debug if necessary. 
Title: Re: GCC inline across C standards - undefined reference for inline functions
Post by: TheCalligrapher on December 17, 2021, 10:23:56 pm
I'm doing software mostly as a part of the electronic hobby, and so far my understanding about "static" was that static means "don't touch this" to the compiler's optimizer, like when it's used to prevent apparently unused variables for a hardware interrupt function to be removed by the compiler.

No, absolutely not.

`static` means "don't export this to the outside world" or "don't make this linkable from the outside world". `static` means thas this entity (function or variable) is local to this translation unit. `static` means that the compiler can clearly see all uses of this entity within this single translation unit (and no one can uncontrollably break in from the outside). This in turn means that `static` is a major facilitator of optimizations.

Everything that can be made `static` should be made `static`. This might (and will) significantly improve the efficiency of the generated code - because of better optimizations.

I've also "auto-assumed" an inline will always enforce embedding the code instead of making a call, didn't know that this might not be always true.

No, `inline` has nothing to do with embedding the code. All embedding decisions are made by internal heuristic algorithms built into the compiler. These algorithms normally don't care about keyword `inline` at all. All they care about is seeing the full definition of the function. If they can't see the full definition of the function, they can't assume anything about this function. They have to treat it as a "black box". They can't embed it. And they can't optimize its uses in any other way.

`inline` exists solely to help you to make function definitions (full function bodies) visible to the compiler everywhere these functions might be called, thus facilitating optimizations. For `static` functions `inline` is completely unnecessary, since definitions of `static` functions are already fully visible to the compiler. Functions with external linkage are a different story. And this is where `inline` comes into the picture.
Title: Re: GCC inline across C standards - undefined reference for inline functions
Post by: emece67 on December 18, 2021, 11:36:34 am
.
Title: Re: GCC inline across C standards - undefined reference for inline functions
Post by: Siwastaja on December 18, 2021, 12:09:26 pm
It would actually be beneficial if static worked the opposite way, like publish. It does not help that static keyword is used for different purposes in C. Often when a beginner adds "inline" to tell the compiler "hey, consider optimizing this, it's only a small helper only used within this unit", they actually needed "static".

In a nutshell, by default, you want to make all your internal functions static, and leave static out of only those that belong to the interface of your code module, i.e., those that will be called outside of that compilation unit (code module; often one .c file).

It's a typical beginner mistake to just write a lot of functions none of which are static. This is not catastrophic, but it causes unnecessary symbols to be exported, may prevent compiler optimizations (because compiler need to assume the function needs to exist as a stand-alone entity, which can be called from anywhere, so compiler can't perform optimizations across the function call), or bloat the code size (in case the compiler wants to optimize and has to create two copies of the function, one that can be called outside, and one that is only used within the compilation unit, like it had been qualified with static). Chances are the linker is optimizing away the unused functions (at least if you add command line options like -ffunction-sections and --gc-section). The problem is that compiler versions and command line parameters change, but basic C constructs as described in the standard (and good books/tutorials) keep the same. The actual intent (is this function definition public to the whole project, or only for this compilation unit?) is best described using the language itself, and only minor finetuning with compiler options.

There are some special cases where you really must have something inlined, but do not want to use #define macro. For example if you want to increase the predictability of stack frame when doing some low level magic. Or really depend on every clock cycle, so that even the tiny function call overhead is significant and you can't risk the compiler not inlining the function "on it's own". But in such cases, you need to be careful with everything else, too, because C compiler won't do predictable and repeatable job anyway. In such cases, assuming gcc, use static inline __attribute__(("always_inline")). I would also add -Winline (and maybe -Werror) to the command line to bail out if the inline keyword is not working as you expect it to work (i.e., remove function calls). Then just do not use inline without good reason.

Me, I don't either use inline at all, or if I do, I force it with always_inline. This is regarding microcontroller projects. And I have not found much use for inline without static. So for me, functions fall in three categories:

static void func() - internal function of the compilation unit (usually a single .c file)

void func() - interface function to be called from outside. Relevant .h file will have void func(); so that others will know about the func by including that .h file (extern keyword can be added, but for a function it does nothing. For a variable that's definitely needed.)

static inline __attribute__(("always_inline")) toggle_clock() - basically just a better replacement for #define macro, do something small and trivial which the compiler would likely inline anyway, but I want to be sure, regardless of optimization level setting (I might toggle between -Os, -O2 and -O3).
Title: Re: GCC inline across C standards - undefined reference for inline functions
Post by: RoGeorge on December 18, 2021, 01:54:47 pm
I'm doing software mostly as a part of the electronic hobby, and so far my understanding about "static" was that static means "don't touch this" to the compiler's optimizer, like when it's used to prevent apparently unused variables for a hardware interrupt function to be removed by the compiler.

I think you are mixing static with volatile here.

Indeed, that was one of the mistakes, didn't touch C in years, and for some weird reason I was reading/writing "static" while I was thinking about "volatile" all the time.  :palm:

I never knew very well what effect will have on functions, so far I used to use "volatile" (for variables) to preserve them against being silently removed during optimization, and "static" (for variables) when I wanted for a variable inside a function to remember/preserve its value between different function calls.  And by "inline" (not sure if I ever used that) I was thinking inserting code, similar with an ASM block.



Thanks to the kind answers here I'm realizing now I was totally off, and that I need to properly learn about function qualifiers (and maybe review the variable qualifiers, too).

So far didn't even bother to lookup those terms, thinking I knew them already.  ;D

Thank you all for taking the time to explain.
Title: Re: GCC inline across C standards - undefined reference for inline functions
Post by: SiliconWizard on December 18, 2021, 06:39:22 pm
I didn't post anything as the points have already been made, and besides, the "inline" keyword in C has been discussed at great length already in several other threads. You can have a look.
Just a couple points replying to Siwastaja:

It would actually be beneficial if static worked the opposite way, like publish. It does not help that static keyword is used for different purposes in C. Often when a beginner adds "inline" to tell the compiler "hey, consider optimizing this, it's only a small helper only used within this unit", they actually needed "static".

You could wish C was not C forever =)
Actually, making all definitions "static" (in the C sense), or private if you prefer, by default, and have a specific keyword for explicitely making some definitions "public" would probably be better. Quite a few other languages do this. C does not, and so it never will.

In a nutshell, by default, you want to make all your internal functions static, and leave static out of only those that belong to the interface of your code module, i.e., those that will be called outside of that compilation unit (code module; often one .c file).

Yep.

It's a typical beginner mistake to just write a lot of functions none of which are static.

Unfortunately, not just beginners. But this is because many C developers are actually beginners that ignore they are, since, as we have often discussed, C is practically never taught properly.
Title: Re: GCC inline across C standards - undefined reference for inline functions
Post by: gnif on December 20, 2021, 02:43:30 am
Unless I am missing something here the understanding of `inline` is wrong. An `inline` function is one that the compiler can chose to inline into another block of code omitting the overheads of a function call & return.

ie:

Code: [Select]
inline void myFunc(void)
{
  doStuff();
  doMoreStuff();
}

int main(int argc, char * argv[])
{
  myFunc();
}

If the compiler decides it's optimal to inline (unless `always_inline` is used) the above would have the following effect:

Code: [Select]
int main(int argc, char * argv[])
{
  doStuff();
  doMoreStuff();
}

The function `myFunc` has been `inlined` improving performance as it avoids the need to CALL/RET, however if the function is called from many places it will bloat the application size. Because the compiler may inline the function, after the object has been compiled the function may not exist at all making it impossible to be linked against, as such inline functions that need to be shared across units are normally defined in the headers.

As for the definition of `static` above, it's absolutely correct. Making a function `static` does not inline it (but the compiler may opt to if it makes sense to), it simply prevents the compiler from generating a symbol for the function that can be linked against, allowing it to make decisions such as auto-inlining at compile time.

Quote
so far I used to use "volatile" (for variables) to preserve them against being silently removed during optimization,
This is not what volatile does and the compiler can still opt to remove them during optimization.

A good usage of inline in projects (espesially mcu projects) is when you have some code that pokes a register, ie:
Code: [Select]
inline void gpio_set(int pin, int on)
{
  SOME_REGISTER |= 1 << pin;
}

This would inline very nicely and makes the code easy to debug unlike macros that can make this a nightmare as they get more and more complex/deep.

Some more usage examples from one of my FOSS projects:

https://github.com/gnif/LookingGlass/blob/master/common/include/common/time.h#L39
Title: Re: GCC inline across C standards - undefined reference for inline functions
Post by: TheCalligrapher on December 20, 2021, 02:58:13 am
Unless I am missing something here the understanding of `inline` is wrong.

[bulk of the description skipped]

Because the compiler may inline the function, after the object has been compiled the function may not exist at all making it impossible to be linked against, as such inline functions that need to be shared across units are normally defined in the headers.

All pretty much correct. However it is not clear why you introduced your comment with "here the understanding of `inline` is wrong". I don't see any contradiction between what you said and what was said here before.

I'd just reiterate, again, that the need "to link against" an inline function might arise from the fact that at some call site the compiler decided NOT to inline the call to an `inline` function with external linkage. In that case the linker will later attempt to "link against" this function from that call site. In C it is your responsibility to make sure that this attempt succeeds. It is your responsibility to provide a regular function body for the linker to "link against".

Since you have no control over compiler's decisions to inline or not to inline each call, you always have to assume the worst: that the linker will always attempt to "link against" an inline function with external linkage. The language provides you with a tool to easily generate the required body (i.e. `extern inline`), but you have to remember to use it explicitly.

This actually means that your above statement is not entirely accurate. In C your are effectively forced to trigger generation of a regular body for each inline function with external linkage. Which means that it will exist in one object file - the one that you yourself selected for `extern inline` declaration.

That's all.

Meanwhile `static inline` is easy. It requires no additional effort. And, as I stated above, it is actually redundant.

Some more usage examples from one of my FOSS projects:

Your sources seem to be using `static inline` exclusively, meaning that they don't have to deal with the linkage issues we are discussing here. I'd even guess that you've never had to deal with C usage model for `inline` functions with external linkage. That's fine because `inline` functions with external linkage is a rather niche feature. In 999 cases out of 1000 `static inline` is what's really needed.

Quote
so far I used to use "volatile" (for variables) to preserve them against being silently removed during optimization,
This is not what volatile does and the compiler can still opt to remove them during optimization.

Not really, outside of degenerate cases. If your program actually accesses (read or writes) a volatile variable, it cannot be removed during optimization. Every access to a volatile variable is part of the observable behavior of the program. This is out of bounds for the optimizer. The optimizer is not permitted to change the observable behavior in any way. The optimizer is not allowed to eliminate volatile accesses. The optimizer is not allowed to reorder volatile accesses.
Title: Re: GCC inline across C standards - undefined reference for inline functions
Post by: gnif on December 20, 2021, 03:25:30 am
Quote
All pretty much correct. However it is not clear why you introduced your comment with "here the understanding of `inline` is wrong". I don't see any contradiction between what you said and what was said here before.

Simple, too tired and misread things, sorry.
Title: Re: GCC inline across C standards - undefined reference for inline functions
Post by: Nominal Animal on December 20, 2021, 09:42:15 pm
Meanwhile `static inline` is easy. It requires no additional effort. And, as I stated above, it is actually redundant.
Yes.

Human programmers often use "static" alone for internal functions, and "static inline" for macro-like accessor functions.
The compiler treats the two exactly the same, but for us humans, the distinction can be useful.  Consider it a "coding style" choice.

As far as I know, the distinction originates from GCC (https://gcc.gnu.org/onlinedocs/gcc-3.0.4/gcc_5.html) from a couple of decades ago, and how its implementation used to differ from ISO C99 – the related topic "An Inline Function is As Fast As a Macro" used to cause a bit of contention then.  Also, when typical/common warnings were enabled, GCC used to emit a warning when a static function was not used, but be silent when a static inline function was not used.  Nowadays, even GCC treats "static" and "static inline" the same, so the difference only matters to (some) human programmers.

I sometimes point this kind of stuff out, because in my opinion, "coding style" is and should not be about how pretty the code looks, but something that reduces the cognitive load of working with the code, so that humans with limited brainpower like myself can spend it on the important things, and not waste on superfluous stuff.  Nowadays, I'm investigating whether using descriptive preprocessor macros (like say ACCESSOR or INTERNAL) instead of the abovementioned static/static inline works better.

Every access to a volatile variable is part of the observable behavior of the program. This is out of bounds for the optimizer. The optimizer is not permitted to change the observable behavior in any way. The optimizer is not allowed to eliminate volatile accesses. The optimizer is not allowed to reorder volatile accesses.
I do believe this description should help those unfamiliar with volatile.

Personally, I might follow up with explaining how the optimizer can eliminate non-volatile variable accesses by "caching" or remembering the value from the previous access, if it can determine that the value is not/will not be modified as part of the program flow.  (Needs better wording, though.)

This stuff is not hard or complicated; it is just that many programmers have an incorrect/partial understanding of what and how the compiler does what it does and per what rules, and volatile is one of the tripping points where it shows.  Better understanding the model and/or rules, like how the optimizer does what it does, immediately leads to better understanding of volatile as well.  So, the underlying problem is definitely not that they misunderstand volatile per se; it is the partial or incorrect understanding of how the compiler (including the optimizer in it) do what they do and based on what rules, and how volatile is hard/impossible to reconcile with such a misunderstanding.  Especially when they've heard claims like "C is just a macro assembler" and let those shape their understanding.