-O0 处的内联函数导致 link clang 失败

Inline function at -O0 causes link failure in clang

我正在尝试在各种优化级别使用 clang 编译以下代码:

#include <stdio.h>

inline int foo() { return 42; }

int main() {
    printf("%d\n", foo());
}

-O1-O2-O3-Os处编译成功,使用-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)

-O0 处的失败(和变通方法)可以用 Clang's inline compatibility 来解释,但我天真地认为无论优化级别如何都会失败。似乎在 -O1 及以上启用的一些优化正在做一些事情来防止这个 link 错误的发生,但我很好奇它们是哪些优化以及为什么它们似乎具有与使用不同的语义inline 独自在 -O0

-O1 和更大的时候它不调用函数它只是将代码移动到 main,我们可以通过使用 godbolt which shows the asm as follows see it live:

看到这一点
main:                                   # @main
  pushq %rax
  movl  $.L.str, %edi
  movl  , %esi
  xorl  %eax, %eax
  callq printf
  xorl  %eax, %eax
  popq  %rdx
  retq

这是什么 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[...]

这包含在草案 C99 标准部分 6.7.4 函数说明符:

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)

这个网站对语言的解释很好:The tricky inline specifier in C99