我试图用下一个标志编译汇编我的源代码:
1.2 -flto
. -flto -ffat-lto-objects
3.-flto -fno-fat-lto-objects
第三个提供slim了文档中编写的优化LTO代码,但我没有看到输出汇编文件在第一个和第二个之间有任何差异,为什么?
OS:linux
编译器:GCC 4.7
我通过扩展FunctionPass类在LLVM中编写了标准的Analysis传递.一切似乎都有意义.
现在我要做的是编写几个模块间通道,即允许我一次分析多个模块的通道.一个这样的传递的目的是构建整个应用程序的调用图.另一个这样的传递的目的是我有一个关于函数调用及其参数的优化的想法.
我知道LLVM中的过程间通过,通过扩展ModulePass类,但只允许在单个模块中进行分析.
我知道LLVM中的链接时间优化(LTO),但是(a)我不太清楚这是否是我想要的,(b)我没有找到关于如何实际编写 LTO传递的示例或文档.
如何在LLVM中编写一个模块间传递,即一个可以访问应用程序中所有模块的传递?
如果我想编译我的项目-flto是否足以建立gcc --enable-gold或者我还需要构建黄金并用它替换ld?我还需要其他标志吗?即我这样做
gcc -flto one.c two.c
Run Code Online (Sandbox Code Playgroud) 我使用GCC-ARM-Embedded和FreeRTOS.FreeRTOS具有vTaskSwitchContext()仅在某些内联汇编程序代码中使用的函数.
问题是:当我使用LTO时,GCC不考虑内联汇编程序代码并认为该函数未被使用,因此将其删除.然后链接器失败,因为内联汇编程序代码中的函数调用无法解析.
我会申请,__attribute__((used))但我不想触摸FreeRTOS代码(它是由STM32CubeMX生成的).
我尝试将它放在我的代码中,但实际上GCC足够智能,不允许这个工作:
if(false)
vTaskSwitchContext();
Run Code Online (Sandbox Code Playgroud)
有没有办法告诉GCC在不同的源文件或通过参数,不应该删除此功能?
例
// file1.c
void vTaskSwitchContext( void )
{
...
}
// file2.c
void xPortPendSVHandler( void )
{
__asm volatile
(
...
" isb \n"
" bl vTaskSwitchContext \n"
" mov r0, #0 \n"
...
);
}
Run Code Online (Sandbox Code Playgroud) 通常,可以使用GCC 和 Clang 中的标志从源文件中获取 GCC 的优化汇编器输出,-S如下例所示。
gcc -O3 -S -c -o foo.s foo.c
Run Code Online (Sandbox Code Playgroud)
但是假设我编译所有源文件-O3 -flto以启用链接时整个程序优化,并希望查看最终编译器生成的函数优化程序集,和/或查看代码在哪里/如何内联。
编译的结果是一堆.o文件,正如预期的那样,这些文件实际上是伪装成目标文件的 IR 文件。在链接可执行文件或共享库时,它们会被混合在一起,作为一个整体进行优化,然后编译成目标二进制文件。
但是如果我想要这个过程的汇编输出怎么办?也就是说,在链接时优化之后、在将 IR 编译为程序集期间以及在实际程序集和链接到最终可执行文件之前产生的程序集源。
我尝试简单地向-S链接步骤添加一个标志,但这并没有真正起作用。
我知道反汇编可执行文件是可能的,甚至与源代码交错,但有时查看实际编译器生成的程序集会更好,尤其是使用-fverbose-asm.
我试图使用GCC 4.9.2从Linux(x86_64-pc-linux-gnu)for Windows(x86_64-w64-mingw32)交叉编译应用程序.
当构建链接到静态库并且还使用链接时优化的目标时,我从链接器获取目标从库中使用的所有符号的未定义引用错误.
例如,从bar.cpp建立bar.a
int bar (void) {return 42;}
Run Code Online (Sandbox Code Playgroud)
并与foo.cpp链接
extern int bar (void);
int main (int, char**) {bar ();}
Run Code Online (Sandbox Code Playgroud)
使用命令行
x86_64-w64-mingw32-g++ -flto -o foo.o -c foo.cpp
x86_64-w64-mingw32-g++ -flto -o bar.o -c bar.cpp
x86_64-w64-mingw32-gcc-ar rc bar.a bar.o
x86_64-w64-mingw32-gcc-ranlib bar.a
x86_64-w64-mingw32-g++ -flto -fuse-linker-plugin foo.o bar.a -o foo
Run Code Online (Sandbox Code Playgroud)
导致错误
/tmp/ccc3Twsc.lto.o:foo.o:(.text+0x15): undefined reference to `bar()'
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)
从上面:
我尝试过使用-fuse-linker-plugin,gcc-ar vs ar,符号可见性选项,操作等的各种组合,但是如果不关闭LTO,我无法正确链接.
所有目标都在本机编译器(x86_64 Linux)下正确构建.
有什么明显的东西我在这里不见了吗?
我想使用符号版本控制和链接时优化(LTO)编译共享库.但是,只要我打开LTO,一些导出的符号就会消失.这是一个最小的例子:
首先定义函数fun的两个实现:
$ cat fun.c
#include <stdio.h>
int fun1(void);
int fun2(void);
__asm__(".symver fun1,fun@v1");
int fun1() {
printf("fun1 called\n");
return 1;
}
__asm__(".symver fun2,fun@@v2");
int fun2() {
printf("fun2 called\n");
return 2;
}
Run Code Online (Sandbox Code Playgroud)
创建版本脚本以确保仅导出乐趣:
$ cat versionscript
v1 {
global:
fun;
local:
*;
};
v2 {
global:
fun;
} v1;
Run Code Online (Sandbox Code Playgroud)
第一次尝试,没有LTO编译:
$ gcc -o fun.o -Wall -Wextra -O2 -fPIC -c fun.c
$ gcc -o libfun.so.1 -shared -fPIC -Wl,--version-script,versionscript fun.o
$ nm -D --with-symbol-versions libfun.so.1 | grep fun
00000000000006b0 …Run Code Online (Sandbox Code Playgroud) 我试图将大量代码塞进一个相当小的 ARM 微控制器中。我已经做了尺寸优化工作的巨量已经和我到哪里,我需要加倍运算的地步,但是__aeabi_ddiv,__aeabi_dadd与__aeabi_dsub一些最大的功能,整个设备上。
这两个__aeabi_dadd和__aeabi_dsub是尽管基本上做同样的工作〜1700 bytes每个(双打的最高层位为符号位)。两个函数都没有引用另一个。
实际上,我需要做的就是替换__aeabi_dsub为:
double __aeabi_dsub(double a, double b) {
// flip top bit of 64 bit number (the sign bit)
((uint32_t*)&b)[1] ^= 0x80000000; // assume little endian
return a + b;
}
Run Code Online (Sandbox Code Playgroud)
我会节省大约 1700 个字节 - 所以翻转第二个参数的符号,然后使用__aeabi_dadd.
我知道这可能不是 100% 与 IEEE 规范兼容,但在这个平台上,为了节省 > 1% 的可用闪存,我可以接受。
我的问题是,当我添加该函数时,链接器会抱怨undefined reference to __aeabi_dsub- 这似乎很奇怪,因为定义它的行为导致了错误。
这似乎与链接时间优化 ( -flto) 有关 - 将其关闭意味着一切正常,但它会增加 8k 的固件大小,使其不再适合可用的闪存!
那么__aeabi_dsub …
我有一个 CMake 项目,其中有几个子项目,这些子项目创建使用-flto=thin.
该项目有很多与上述库相关的测试。使用 LTO 需要花费大量时间来构建测试,因此我已禁用 LTO 进行使用-fno-lto.
但我注意到,lld即使使用-fno-lto. 如果我运行链接器,--time-trace我可以看到大部分时间都花在了 LTO 上。
我的问题是:
lld只要在它链接的对象中找到 LTO 信息,就会执行 LTO。-fno-lto到编译器似乎不起作用,并且lld没有显式禁用 LTO 的参数。这就是我lto在 CMake 中的处理方式:
# Enable Thin LTO only on non-test targets.
if(ENABLE_LTO)
if (IS_TEST)
target_compile_options(${TARGET} PRIVATE -fno-lto)
# Probably pointless.
target_link_options(${TARGET} PRIVATE -fno-lto)
else()
message(STATUS "ENABLE_LTO on target ${TARGET})")
target_compile_options(${TARGET} PRIVATE -flto=thin)
target_link_options(${TARGET} PRIVATE -flto=thin -Wl,--thinlto-cache-dir=${CMAKE_BINARY_DIR}/lto.cache)
endif()
endif()
Run Code Online (Sandbox Code Playgroud) 我看到 LTO 优化了某个 TU 中的一些全局对象,如果该 TU 中没有明确来自另一个 TU 的函数。
以下摘录尝试描述所涉及的关键类和文件(请注意,这仅用于演示目的,可能并不完全准确):
我有一个单例类,它维护已构造的Registrar所有类型对象的列表。Foo为了避免静态构造顺序失败,我在构造第一个 Foo 类型的对象时动态构造该对象的实例。
// Registrar.hpp
class Registrar
{
public:
static Registrar * sRegistrar;
std::vector<Foo *> objectList;
Registrar() = default;
};
Run Code Online (Sandbox Code Playgroud)
接下来,我们上课了Foo。此类的实例Registrar如上所述进行注册。
// Foo.hpp
class Foo
{
public:
Foo()
{
if (Registrar::sRegistrar == nullptr)
Registrar::sRegistrar = new Registrar();
Registrar::sRegistrar->objectList.push_back(this);
}
};
Run Code Online (Sandbox Code Playgroud)
的实例Foo是可以从多个文件创建的全局变量。在一个这样的文件中,我们碰巧定义了另一个从其他地方调用的函数:
// file1.hpp
void someFunctionThatIsCalledExplicitly()
{
doSomething();
}
namespace
{
__attribute__((used, retain))
Foo f1;
}
Run Code Online (Sandbox Code Playgroud)
但在另一个文件中,我们只是创建了一个实例Foo:
// file2.hpp …Run Code Online (Sandbox Code Playgroud)