可以跨 C 和 C++ 方法优化 gcc 或 clang 的 LTO

Posted

技术标签:

【中文标题】可以跨 C 和 C++ 方法优化 gcc 或 clang 的 LTO【英文标题】:Can LTO for gcc or clang optimize across C and C++ methods 【发布时间】:2017-12-30 03:37:30 【问题描述】:

如果链接时优化 (LTO) 与 gcc 或 clang 一起使用,是否可以跨 C 和 C++ 语言边界优化代码?

例如,可以将 C 函数内联到 C++ 调用程序中吗?

【问题讨论】:

【参考方案1】:

是的!

链接时优化通常适用于“胖”对象文件中存在的中间表示 (IR),其中可以包含用于传统链接的机器代码和用于 LTO 链接的 IR。

在这个阶段,没有更多的高级语言结构,因此链接时间优化与语言无关。


海合会

GCC 的link-time optimization (LTO) 适用于 GIMPLE,它是 GCC 的中间表示之一。 IR 始终与语言无关,因此任何链接时优化都适用于从任何语言生成的代码。

来自GCC Optimization Options 文档:

LTO 的另一个特点是可以对用不同语言编写的文件应用程序间优化:

gcc -c -flto foo.c
g++ -c -flto bar.cc
gfortran -c -flto baz.f90
g++ -o myprog -flto -O3 foo.o bar.o baz.o -lgfortran

请注意,最后一个链接是使用 g++ 完成的以获取 C++ 运行时库,并添加了-lgfortran 以获取 Fortran 运行时库。通常,在 LTO 模式下混合语言时,您应该使用与在常规(非 LTO)编译中混合语言时相同的链接命令选项。


下面是一个示例,向您展示了这项技术的强大功能。我们将定义一个 C 函数并从 C++ 程序中调用它:

func.h

#ifndef FUNC_DOT_H
#define FUNC_DOT_H

#ifdef __cplusplus
extern "C" 
#endif

int func(int a, int b, int c);

#ifdef __cplusplus

#endif

#endif /* FUNC_DOT_H */

func.c

#include "func.h"

int func(int a, int b, int c)

    return 3*a + 2*b + c;

main.cpp

#include "func.h"

int main()

    int a = 1;
    int b = 2;
    int c = 3;

    return func(a, b, c);

编译

gcc -o func.o -c -Wall -Werror -flto -O2 func.c
g++ -o main.o -c -Wall -Werror -flto -O2 main.cpp
g++ -o testlto -flto -O2 main.o func.o

反汇编 (objdump -Mintel -d -R -C testlto)

Disassembly of section .text:

00000000004003d0 <main>:
  4003d0:   b8 0a 00 00 00          mov    eax,0xa   ; 1*3 + 2*2 + 3 = 10
  4003d5:   c3                      ret

你可以看到它不仅将我的 C func() 内联到我的 C++ main() 中,而且把整个事情变成了一个常量表达式!


Clang / LLVM

使用相同的语法,Clang 能够发出带有 LLVM IR 的“胖”目标文件,可以在链接时进行优化。见LLVM Link Time Optimization。

使用与上面相同的测试代码,clang 产生完全相同的结果:

00000000004004b0 <main>:
  4004b0:   b8 0a 00 00 00          mov    eax,0xa
  4004b5:   c3                      ret

【讨论】:

我想关于 LTO 的常见警告仍然适用:AFAIK 不同版本的 gcc 可能会产生不同的 GIMPLE 版本,如果链接器调用的编译器版本不使用嵌入的版本.o 文件?在真正奇怪的情况下,您的 gccg++ 是不同的版本,我想这可能很重要。 对——我的回答是假设一个理智的、正确工作的编译器/链接器安装。 对 - 可能会发生版本不匹配的更现实的方式是使用静态链接,其中 .a 文件是编译器“胖”并包含 IR,但以不同的方式生成机器或与最终可执行文件不同的时间。在这种情况下,如果 '.a' 中的 GIMPLE 与本地编译器不兼容,它可能会被忽略。当然,这只是 LTO 与传统链接的一般“陷阱”,并不特别适用于跨语言优化。 Clang 似乎没有办法生成“胖”目标文件(至少根据 gcc 的术语)。 func.o 只是 LLVM IR 位码,所以它不能用除了 clang 以外的任何东西进行编译(据说反过来应该是可能的,虽然我现在很挣扎):***.com/questions/51259340/…

以上是关于可以跨 C 和 C++ 方法优化 gcc 或 clang 的 LTO的主要内容,如果未能解决你的问题,请参考以下文章

带有链接时代码生成的 MSVC 能否跨 C 和 C++ 进行优化?

有没有办法在 GCC 或 cl.exe 的预处理和编译之间插入一个步骤?

让编译器生成 adc 指令

手把手写C++服务器:编译实操——打开gcc/g++世界

gcc悄无声色将静态函数内联了

gcc/g++ 实战之编译过程