您不需要了解内联(或其他优化)标准,因为根据定义(假设 optimizing compiler 在这方面没有问题),内联代码应该与非内联代码的行为相同。
你的第一个例子(被调用者无条件返回一个值)在实践中肯定是错误的,因为几个编译器能够内联条件返回。
例如,考虑这个f.c 文件:
static int fact (int n) {
if (n <= 0) return 1;
else
return n * fact (n - 1);
}
int foo () {
return fact (10);
}
用gcc -O3 -fverbose-asm -S f.c编译它;生成的 f.s 程序集文件仅包含 一个 函数 (foo),fact 函数已完全消失,fact(10) 已被内联(递归)和替换(不断折叠)通过 3628800。
使用GCC - 当前版本是 2015 年 7 月的 GCC 5.2,假设您要求它进行优化(例如使用 gcc -O2 或 g++ -O2 或 g++ -O2 或 -O3 编译),内联决策不容易理解。 编译器很可能会做出比你能做的更好的内联决策。有许多内部heuristics 指导它(所以没有简单的几个指导原则,但一些启发式内联,其他避免内联,可能还有一些元启发式可供选择)。了解optimize options (-finline-limit=...)、function attributes。
您可以使用always_inline 和gnu_inline 和noinline(以及noclone)函数属性,但我一般不建议这样做。
您可以使用 noinline 禁用内联,但通常生成的代码会更慢。所以不要那样做...
关键是编译器的优化和内联比你合理的更好,所以相信它能够很好地内联和优化。
优化编译器(另见this)可以(并且确实)内联函数,即使您不知道,例如他们有时会内联未标记为inline 的函数,或者未内联标记为inline 的某些函数。
所以不,你不想“理解这些语义和指导内联扩展的算法”,它们太难了......并且从一个编译器到另一个编译器(甚至一个版本到另一个版本)都不同。如果您真的想了解为什么 GCC 内联(这意味着要花费数月的时间,我相信您不应该为此浪费时间),请使用 -fdump-tree-all 和其他转储标志,使用 MELT 检测编译器 - 我正在开发-,深入研究源代码(因为 GCC 是 free software)。
你需要比你的一生更多的时间,或者至少几十年,来理解所有的 GCC(超过一千万行源代码)以及它是如何优化的。当你理解某些东西时,GCC 社区已经在进行新的优化等工作了......
顺便说一句,如果你编译并链接整个应用程序或库gcc -flto -O3(例如make CC='gcc -flto -O3')GCC编译器将进行链接时优化并内联一些调用accross 翻译单元(例如,在f1.c 中,您调用在f2.c 中定义的foo,而在f1.c 中对foo 的一些调用将被内联)。
编译器优化确实考虑了缓存大小(用于决定内联、展开、寄存器分配和溢出以及其他优化),尤其是在使用 gcc -mtune=native -O3 编译时
除非你强制编译器(例如,通过在 GCC 中使用 noinline 或 alwaysinline 函数属性,这通常是错误的并且会产生更糟糕的代码),否则你将永远无法在实践中猜测给定的代码块肯定会被内联。即使是从事 GCC 中端优化工作的人也无法可靠地猜到这一点!所以在实践中您无法可靠地理解 - 和预测 - 编译器行为,因此甚至不要浪费时间去尝试。
同时查看MILEPOST GCC;通过使用机器学习技术来调整一些 GCC 参数,他们能够有时获得惊人的性能提升,但他们肯定无法解释或理解他们。
如果您在编写一些 C 或 C++ 代码时需要了解您的特定编译器,那么您的代码可能是错误的(例如,可能有一些 undefined behavior)。您应该针对某些语言规范(C11 或 C++14 标准,或特定的 GCC dialect,例如由您的 GCC 编译器记录和实现的 -std=gnu11)进行编码,并相信您的编译器会忠实于 w.r.t。那个规范。