4

I'm trying to compile the following code with clang at various optimization levels:

#include <stdio.h> inline int foo() { return 42; } int main() { printf("%d\n", foo()); } 

At -O1, -O2, -O3, and -Os, it compiles successfully, but it fails when using -O0:

$ clang -O0 -o main main.c Undefined symbols for architecture x86_64: "_foo", referenced from: _main in main-8b9319.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) 

The failure at -O0 (and workarounds) can be explained by Clang's inline compatibility, but then I'd naively expect this to fail regardless of optimization level. It appears that some optimizations enabled at -O1 and above are doing something to prevent this link error from happening, but I'm curious as to which optimizations they are and why they seem to have different semantics than using inline alone at -O0.

2
  • try add static. E.g static inline int foo() { return 42; } Commented May 28, 2015 at 20:26
  • Yes, adding static fixes the problem at -O0, but I'm asking why it does compile at all at higher optimization levels. Commented May 28, 2015 at 20:29

1 Answer 1

5

At -O1 and greater it is not calling the function it just moves the code into main, we can see this by using godbolt which shows the asm as follows see it live:

main: # @main pushq %rax movl $.L.str, %edi movl $42, %esi xorl %eax, %eax callq printf xorl %eax, %eax popq %rdx retq 

Which is what the reference says:

[...]because if add isn't inlined (for example, when compiling without optimization), then main will have an unresolved reference to that other definition[...]

this is covered in draft C99 standard section 6.7.4 Function specifiers:

Any function with internal linkage can be an inline function. For a function with external linkage, the following restrictions apply: If a function is declared with an inline function specifier, then it shall also be defined in the same translation unit. If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern, then the definition in that translation unit is 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. An inline definition provides an alternative to an external definition, 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.122)

this site has a good explanation of the language: The tricky inline specifier in C99

Sign up to request clarification or add additional context in comments.

5 Comments

That's exactly what to expect with an inline function. As noted, it does not get inlined with -O0 (and also not compiled). See that one live
@Jongware yes, I spend so much time in C++ land I needed to reread this part of C99 carefully. Ok, TIL something new.
I think solution #2 in your "The tricky inline specifier" link is wrong: having extern inline void foo() { body } in a header would cause multiple external definitions if the header is used in two different TUs. The correct setup is to have inline void foo() { body } in the header, and extern inline void foo(); in exactly one TU.
@MattMcNabb I think the recommendation is meant to mean something closer to what the clang docs says in bullet 3 but it is not clear.
@ShafikYaghmour agree that it's unclear. That bullet point should also mention that extern inline int add(); is sufficient, it's not necessary or even desirable to repeat the function body (or the argument list) elsewhere.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.