5

In C, if I call a static function from an inline function, I typically get a compiler warning like

warning: static function <name> is used in an inline function with external linkage [-Wstatic-in-inline]

According to the C23 standard 6.7.5.3:

An inline definition of a function with external linkage shall not contain, anywhere in the tokens making up the function definition, a definition of a modifiable object with static or thread storage duration, and shall not contain, anywhere in the tokens making up the function definition, a reference to an identifier with internal linkage

Is it not allowed to call a static function from an inline function? If so, why? Is it because a static function can use a non-const static variable, and if the definitions are included into several translation units, the different inline definitions will access different copies of the static variable via the static function which may be undesirable as demonstrated below?

inlinedef.h

static double a = 0;

static double f(void) {
  a += 1;
  return a;
}

inline double g(void) {
  return f();
}

TU0.c

#include "inlinedef.h"

// External definition of inline function
double g(void);

TU1.c

#include "inlinedef.h"
#include <stdio.h>

void h1(void) {
  printf("%f\n",g());
}

TU2.c

#include "inlinedef.h"
#include <stdio.h>

void h2(void) {
  printf("%f\n",g());
}

Here, it is unspecified whether h1() and h2() are incrementing the same static variable or not because the compiler is free to choose whether to inline or use the external definition in TU0.c.

Let's say the static function does not use any static variables neither directly nor indirectly. Is there a way to call the static function from the inline function while adhearing to the C standard?

Motivation of question

There are some functions I need to inline into GPU kernels to make it compile and work, but I am not necessarily interested in inlining them when compiling for CPU. I don't think I want to make them static inline since it will force inlining or copying into each TU which results in unnecessary bloat if the functions are large.

5
  • 1
    Define your inline function as static too: static inline double g(void) ... Commented Nov 22 at 16:57
  • 1
    @Wiimm This is the practical solution. In fact, static is normally enough by itself for the compiler to inline the function, unless it's very large and called from a lot of places. And even the inline keyword is officially just a suggestion. Commented Nov 23 at 1:35
  • Don't similar problems exist with static inline functions containing static, modifiable variables? But those are not prohibited. Commented Nov 24 at 12:31
  • @Ian Abbott Don't think so. If you use static static inline instead of inline, then the static variable will be duplicated into the different translation units, and the behaviour is uniquely specified as opposed to when the compiler can choose to use an external definition. Commented Nov 24 at 17:54
  • @Rasmus I was under the impression that inline calls to the static inline function could instantiate their own set of statically defined objects, but on further reflection, but I now think that is incorrect, and that all inline calls to the same static inline function (within the same translation unit) would share a common set of statically defined objects. Commented Nov 25 at 10:29

1 Answer 1

7

Is it not allowed to call a static function from an inline function?

Correct. The definition of g in inlinedef.h is an inline definition of the function when included in both TU1.c and TU2.c. Such definitions have external linkage because they are not declared with the static keyword, and as stated above an inline function with external linkage may not access static resources.

This means that the above constitutes a constraint violation as per 6.7.5p3.

The reason for this comes from 6.7.5p7:

An inline definition does not provide an external definition for the function and does not forbid an external definition in another translation unit. Inline definitions provide an alternative to external definitions, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition.

Which states that a call to an inline function could use any one of multiple definitions.

For example, when I tested this on gcc 4.8.5 with -std=c11 and the following main file:

#include <stdio.h>

void h1(void);
void h2(void);

int main()
{
    h1();
    h2();
    return 0;
}

Compiling with -O0 gave me this:

1.000000
2.000000

While compiling with -O1 gave me this:

1.000000
1.000000

If you're concerned about inlining in some compilations and not others, you could use a macro to enable/disable it:

#ifdef GPU_COMPILE
#define INLINE inline
#else
#define INLINE
#endif

INLINE double g(void) {
  return f();
}
Sign up to request clarification or add additional context in comments.

13 Comments

Exactly. But is there a way around this restriction when static resources are not used or if the static resources are const that allows the compiler to choose a single external definition when not inlining instead of copying the function into each TU?
You might be able to get around it by passing around a pointer to the static function.
@Rasmus, you can make the inline function (also) static, so that it has internal linkage instead of external. That has the additional advantage that you are not obligated to provide a non-inline definition in addition to the inline definitions.
But, what if the inline function is big and I only want to inline it where I have to, like in GPU kernels that gets compiled to some other form of binary?
I don't think it is a good idea to make big functions static and include it into many TUs. So far I only see two solutions: 1) Don't care. gcc, clang and icx seem to compile it correctly. 2) Make the static function inline, and it gets an external definition even though it is meant to be private.
What if it is, @Rasmus? Your question was "is there a way around this?", and giving the inline function internal linkage is such a way. So, for that matter, is making it be a non-inline function with external linkage. Note also that whether the compiler actually inlines calls to the function is largely a separate issue from whether it is declared inline. I suspect you're overthinking this. How about waiting to tune your code until you have an actual problem to solve, and data to guide your tuning?
Some of the functions contain 6-7000 lines of polynomial coefficients...
So you have 7000 lines of heavy code and you want to save function call? It makes no sense\
in the motivation of the question I wrote I need to call functions from GPU kernels. The easiest way to do it is to do it is to inline them, and hope that the GPU compiler reuses and not copies the coefficients into different GPU kernels. Another, but more restrictive way, is to decorate the functions, if the GPU API impl. allows it, and compile them in their own TU.
A third possibility is not use the inline keyword and separate the function g() into one meant for inlining with a different name g_inline() that is defined in a header file inlinedef.h and in TU0.c one defines g() by calling g_inline(). Then one can manually choose where g() gets inlined and where it the external definition gets called.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.