在C中,如果我有一个看起来像的函数调用
// main.c
...
do_work_on_object(object, arg1, arg2);
...
// object.c
void do_work_on_object(struct object_t *object, int arg1, int arg2)
{
if(object == NULL)
{
return;
}
// do lots of work
}
Run Code Online (Sandbox Code Playgroud)
那么编译器会在main.o中生成很多东西来保存状态,传递参数(在这种情况下希望在寄存器中),以及恢复状态.
但是,在链接时可以观察到arg1和arg2没有用在快速返回路径中,因此清理和状态恢复可以短路.链接器是否会自动执行此类操作,或者是否需要启用链接时优化(LTO)才能使此类工作正常工作?
(是的,我可以检查反汇编代码,但我对编译器和链接器的行为以及多种体系结构感兴趣,所以希望从别人的经验中学习.)
假设分析显示此函数调用值得优化,我们是否应该期望以下代码明显更快(例如,无需使用LTO)?
// main.c
...
if(object != NULL)
{
do_work_on_object(object, arg1, arg2);
}
...
// object.c
void do_work_on_object(struct object_t *object, int arg1, int arg2)
{
assert(object != NULL) // generates no code in release build
// do lots of work
}
Run Code Online (Sandbox Code Playgroud) 我将binutils-2.25.1安装到/usr/local/binutils-2.25.1,配置了
../configure --prefix=/usr/local/binutils-2.25.1 --enable-plugins --enable-gold --disable-werror
Run Code Online (Sandbox Code Playgroud)
我想构建RPM包 - 使用LTO支持的gcc,它使用来自/usr/local/binutils-2.25.1的链接器ld.
我尝试:
Summary: The GNU Compiler Collection
Name: gcc-custom
Version: 4.9.3
%define full_name gcc-%{version}
%define binutils_path /usr/local/binutils-2.25.1
Release: 0
...
Requires(post): /sbin/ldconfig
Requires(postun): /sbin/ldconfig
%description
%prep
%setup -q -a0 -n %{full_name}
%build
AR=%{binutils_path}/bin/ar NM=%{binutils_path}/bin/nm RANLIB=@%{binutils_path}/bin/ranlib ./configure \
--prefix=/usr/local/%{full_name} \
--disable-multilib \
--enable-languages=c,c++ \
--enable-lto \
--enable-linker-build-id \
--enable-plugin \
--with-ld=%{binutils_path}/bin/ld \
--with-plugin-ld=%{binutils_path}/bin/ld \
--with-as=%{binutils_path}/bin/as
make
%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
%post -p /sbin/ldconfig
%postun -p /sbin/ldconfig
%clean …Run Code Online (Sandbox Code Playgroud) 我正在使用-Wlto-type-mismatch和-Werror设置gcc编译(为了项目的其余部分).我有extern struct一个灵活的阵列,引发lto-type-mismatch警告/错误.
这是从一个例子中提取的代码:
HH:
typedef struct {
int i;
int ints[];
} struct_t;
Run Code Online (Sandbox Code Playgroud)
AC:
#include "h.h"
extern struct_t my_struct;
int main() { // just here to avoid optimizing away the decls
return my_struct.ints[0];
}
Run Code Online (Sandbox Code Playgroud)
公元前:
#include "h.h"
struct_t my_struct = {
20,
{
1,
2,
},
};
Run Code Online (Sandbox Code Playgroud)
编译(在这里使用arm gcc,但它也使用原生gcc失败)
$ arm-none-eabi-gcc -flto -Wlto-type-mismatch -Werror a.c b.c -o foo
a.c:3:17: error: size of 'my_struct' differ from the size of original declaration [-Werror=lto-type-mismatch]
extern …Run Code Online (Sandbox Code Playgroud) Clang 允许使用精简 lto 来加快编译时间,同时仍然保留使用 lto 和选项的大部分优点-flto=thin。gcc 是否有相当于 clang 的瘦 lto 的功能?
我正在 Mac 上编译一些二进制文件,但是使用更新的编译器编译后的大小变得很大(从之前的 ~5MB 增加到 ~20MB)。我认为这与之前未激活的 LTO(链接时间优化)有关。我没有在 linux 上观察到这个文件膨胀。
在玩弄之后strip(实际上没有减少大小,尽管尝试基于 Xcode 的标志-S -x并且也没有标志,以及 GNU libtools strip 由 homebrew binutils 配方提供的标志-s,所有这些似乎都具有相同的效果)我找到了这个工具:https ://github.com/google/bloaty Bloaty McBloated,在我的二进制文件上运行时,它会产生以下输出:
FILE SIZE VM SIZE
-------------- --------------
53.9% 9.72Mi 53.8% 9.72Mi __GNU_LTO,__wrapper_sects
32.5% 5.86Mi 32.4% 5.86Mi __GNU_DWARF_LTO,__debug_info
6.2% 1.11Mi 6.2% 1.11Mi __TEXT,__text
2.2% 403Ki 2.2% 403Ki __TEXT,__eh_frame
1.6% 298Ki 1.6% 298Ki __GNU_LTO,__wrapper_names
1.0% 177Ki 1.0% 177Ki Export Info
0.7% 131Ki 0.7% 131Ki Weak Binding Info
0.4% 77.0Ki 0.4% 77.0Ki __GNU_DWARF_LTO,__debug_str
0.4% …Run Code Online (Sandbox Code Playgroud) 我最近一直在研究 PGO 和 LTO 如何显着优化程序速度(有人说大约 20%)。我目前只是用 C 语言编程,并在 Windows 中使用 GTK+ 构建 GUI(通过 GCC 编译所有内容),并且仅使用 -O2 进行优化。
我一直在阅读并使用标志进行编译
gcc -Wall -g -mwindows -O3 -fprofile-generate -flto example.c -o example.exe `pkg-config --cflags --libs gtk+-3.0`
Run Code Online (Sandbox Code Playgroud)
创建了 .gcno 和 .gcda 文件,然后运行编译后的 .exe 多次,然后再次重新编译相同的 .c 文件,但将“-fprofile-generate”替换为“-fprofile-use”?(同时确保所有这些文件位于同一文件夹/目录中,并为第二次编译的 .exe 使用不同的名称)
gcc -Wall -g -mwindows -O3 -fprofile-use -flto example.c -o program.exe `pkg-config --cflags --libs gtk+-3.0`
Run Code Online (Sandbox Code Playgroud)
或者您是否以某种方式使用 .gcno/.gcda 文件?另请阅读有关使用“基准”的内容。
问题:
在GCC中使用它的步骤是什么?(分步指南会有所帮助:)
我知道 Clang 和 GCC 或多或少是兼容的 C/C++ 编译器,只要处理好架构标志、预定义和链接正确的库之类的事情。使用一个编译器创建库并将它们与另一个编译器创建的对象链接实际上非常简单(至少在 x86 上)。
这是一个小测试项目,正是这样做的: https://gitlab.com/higaski/Interoperability
但是我想知道链接时间优化(LTO)是否可以跨编译器工作?我知道 LTO 需要某种形式的中间表示,例如 LLVM 位码或 GCC GIMPLE,但也许有一个工作流程可以同时利用它们?
我的目标设备是基于 EFM32 Cortex-M3 的设备。我的工具链是官方的 ARM GNU 工具链 gcc-arm-none-eabi-8-2018-q4-major。
在没有 LTO 的情况下一切正常,但为了使 LTO 工作,我必须用-fno-lto. 我想摆脱这种解决方法。
问题是,每个中断处理程序都从最终的二进制文件中删除。(我正在检查arm-none-eabi-nm --print-size --size-sort --radix=d -C -n file.out)这会导致二进制崩溃。
在谷歌搜索类似问题后进行更深入的挖掘:
__attribute__((used)),__attribute((interrupt))但无济于事 - 尽管有这些属性,但中断处理程序正在被删除。(相关防止 GCC LTO 删除功能)来自startup_efm32gg.c定义默认中断处理程序的示例代码如下:
void DMA_IRQHandler(void) __attribute__ ((weak, alias("Default_Handler")));
/* many other interrupts */
void Default_Handler(void) { while (1); }
Run Code Online (Sandbox Code Playgroud)
常规中断处理程序定义也会发生同样的问题(例如,没有别名且不弱)
这可能是相关的,但似乎弱符号在 LTO 模式下也以同样的方式运行不正常。
提前感谢您的任何想法!
编辑:有关完整解决方案,请参阅我对标记答案的回复!
目标:从 ELF 二进制文件中提取完整程序(合并)的 LTO 后位码。
该程序恰好是用 Rust 编写的,但我认为这不是一个关键的细节。
我可以.llvmbc使用以下 Cargo 调用将 Rust 程序编译为 ELF 二进制文件,其中包含一个部分:
RUSTFLAGS="-C linker_plugin_lto -C linker=clang -C link-arg=-fuse-ld=lld -C link-arg=-Wl,--plugin-opt=-lto-embed-bitcode=optimized" \
cargo build --release
Run Code Online (Sandbox Code Playgroud)
然后我就可以用来readelf -S | grep llvmbc验证该部分是否存在。确实如此。高超!
我现在想提取完整程序的 LTO 后位码并反汇编它:
$ objcopy target/release/world --dump-section .llvmbc=llvm.bc
$ llvm-dis llvm.bc
LLVM ERROR: Invalid encoding
PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace.
Stack dump:
0. Program arguments: llvm-dis llvm.bc
#0 0x000055c06f7ae78c llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/home/vext01/research/yorick/llvm-project/inst/bin/llvm-dis+0x1b578c)
#1 0x000055c06f7ac6e4 llvm::sys::RunSignalHandlers() (/home/vext01/research/yorick/llvm-project/inst/bin/llvm-dis+0x1b36e4)
#2 …Run Code Online (Sandbox Code Playgroud)