Optimization clang/LLVM项目级优化
因此,我定期尝试LLVM,因为我有这个理论,它应该比GNU更好。可悲的是,事实并非如此 该理论的一部分与它将模块/对象链接在一起然后进行优化的能力有关,而优化通常是基于每个文件/对象进行的 我看到了如何为特定的默认目标构建,而不是使用通用目标Optimization clang/LLVM项目级优化,optimization,llvm,compiler-optimization,llvm-clang,Optimization,Llvm,Compiler Optimization,Llvm Clang,因此,我定期尝试LLVM,因为我有这个理论,它应该比GNU更好。可悲的是,事实并非如此 该理论的一部分与它将模块/对象链接在一起然后进行优化的能力有关,而优化通常是基于每个文件/对象进行的 我看到了如何为特定的默认目标构建,而不是使用通用目标 rm -rf llvm-project git clone https://github.com/llvm/llvm-project.git cd llvm-project git checkout llvmorg-10.0.0 mkdir build c
rm -rf llvm-project
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
git checkout llvmorg-10.0.0
mkdir build
cd build
cmake -DLLVM_ENABLE_PROJECTS='clang;lld' -DCMAKE_CROSSCOMPILING=True -DCMAKE_INSTALL_PREFIX=/opt/llvm/llvm10armv6m -DLLVM_DEFAULT_TARGET_TRIPLE=armv6m-none-eabi -DLLVM_TARGET_ARCH=ARM -DLLVM_TARGETS_TO_BUILD=ARM -G "Unix Makefiles" ../llvm
make -j 8
make -j 4
make
sudo make install
还有测试文件
测试c
unsigned int one ( void )
{
return(1);
}
unsigned int two ( void );
unsigned int testone ( void )
{
return(one());
}
unsigned int testtwo ( void )
{
return(two());
}
二、c
基本运行
clang -O2 -fomit-frame-pointer -c test.c -o test.o
llvm-objdump -D test.o
00000000 one:
0: 01 20 movs r0, #1
2: 70 47 bx lr
00000004 testone:
4: 01 20 movs r0, #1
6: 70 47 bx lr
00000008 testtwo:
8: 80 b5 push {r7, lr}
a: ff f7 fe ff bl #-4
e: 80 bd pop {r7, pc}
正如所料,one()已内联到testone()中
我们还希望将testwo()内联
clang -fomit-frame-pointer -c -emit-llvm test.c -o test.bc
clang -fomit-frame-pointer -c -emit-llvm two.c -o two.bc
llvm-link test.bc two.bc -o both.bc
llc both.bc -o both.s
cat both.s
opt -O2 both.bc -o both.opt.bc
llc both.opt.bc -o both.opt.s
cat both.opt.s
给予
及
这更糟
opt -std-link-opts both.bc -o both.opt.bc
同样的,没有更好的
现在这是有效的
clang -O2 -fomit-frame-pointer -c -emit-llvm test.c -o test.bc
clang -O2 -fomit-frame-pointer -c -emit-llvm two.c -o two.bc
llvm-link test.bc two.bc -o both.bc
opt -O2 both.bc -o both.opt.bc
llc both.opt.bc -o both.opt.s
cat both.opt.s
testone:
.fnstart
@ %bb.0: @ %entry
movs r0, #1
bx lr
testtwo:
.fnstart
@ %bb.0: @ %entry
movs r0, #2
bx lr
有人会认为,不优化部分会为优化整体提供更多的肉供咀嚼。对尽管这表明情况并非如此
clang -fomit-frame-pointer -c -emit-llvm test.c -o test.bc
clang -fomit-frame-pointer -c -emit-llvm two.c -o two.bc
llvm-link test.bc two.bc -o both.bc
opt -O3 both.bc -o both.opt.bc
llc both.opt.bc -o both.opt.s
cat both.opt.s
testone:
.fnstart
@ %bb.0: @ %entry
.save {r7, lr}
push {r7, lr}
bl one
movs r0, #1
pop {r7, pc}
testtwo:
.fnstart
@ %bb.0: @ %entry
.save {r7, lr}
push {r7, lr}
bl two
movs r0, #2
pop {r7, pc}
-O3也没有帮助,这个输出非常糟糕,它调用函数并将其内联。那里发生了什么
llvm-dis both.opt.bc
cat both.opt.ll
; ModuleID = 'both.opt.bc'
source_filename = "llvm-link"
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "thumbv6m-none-unknown-eabi"
; Function Attrs: noinline nounwind optnone
define dso_local i32 @one() local_unnamed_addr #0 {
entry:
ret i32 1
}
; Function Attrs: noinline nounwind optnone
define dso_local i32 @testone() local_unnamed_addr #0 {
entry:
%call = call i32 @one()
ret i32 1
}
; Function Attrs: noinline nounwind optnone
define dso_local i32 @testtwo() local_unnamed_addr #0 {
entry:
%call = call i32 @two()
ret i32 2
}
; Function Attrs: noinline nounwind optnone
define dso_local i32 @two() local_unnamed_addr #0 {
entry:
ret i32 2
}
如何撤销
clang -O2 -fomit-frame-pointer -c -emit-llvm test.c -o test.bc
clang -O2 -fomit-frame-pointer -c -emit-llvm two.c -o two.bc
llvm-link test.bc two.bc -o both.bc
llvm-dis both.bc
cat both.ll
opt -O3 both.bc -o both.opt.bc
llvm-dis both.opt.bc
cat both.opt.ll
给予
及
因此,为了使项目级别得到优化,您必须在文件/对象级别的任何地方应用优化,这是正确的吗
还有一个问题是尾部调用或叶等优化,如果没有其他的测试二:即使在第一种情况下
clang -O2 -fomit-frame-pointer -c test.c -o test.o
可以简单地分支到two()而不设置堆栈帧,但不执行任何操作。或者这是一个拇指的东西?b够不着
one:
0: b8 01 00 00 00 movl $1, %eax
5: c3 retq
testone:
10: b8 01 00 00 00 movl $1, %eax
15: c3 retq
testtwo:
20: e9 00 00 00 00 jmp 0 <testtwo+5>
我想问题是,如果要使用llvm链接和opt进行项目级优化,是否需要对每个项目进行优化,或者是否缺少命令行选项。对源代码本身中特定于编译器的属性不感兴趣,希望代码既不包含gcc也不包含llvm细节
在gcc 5.x.x之后,代码变得更加臃肿,我希望llvm会有机会,但每当我尝试这一点(在项目上,而不仅仅是在10行代码上)时,gcc最终会执行更少的指令,和/或更少的内存访问,等等。对于上述简单的演示函数,除某些例外情况外,它们产生相同/等效的输出
为了更好地利用clang/llvm,我是否缺少了一些工具或命令行选项
这是不是一个太琐碎的例子,工具无法发挥作用
根据答案编辑
clang -c start.s -o start.o
clang -O2 -flto=thin -fomit-frame-pointer -c test.c
clang -O2 -flto=thin -fomit-frame-pointer -c two.c
ld.lld start.o test.o two.o -o test.elf
llvm-objdump -D test.elf
000110fc testtwo:
110fc: 02 20 movs r0, #2
110fe: 70 47 bx lr
00011100 two:
11100: 02 20 movs r0, #2
11102: 70 47 bx lr
因此,去掉-emit llvm并使用lto基本上可以得到期望的结果
看看bc分解
clang -O2 -flto=thin -fomit-frame-pointer -c test.c
llvm-dis test.o
cat test.o.ll
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @one() local_unnamed_addr #0 {
entry:
ret i32 1
}
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @testone() local_unnamed_addr #0 {
entry:
ret i32 1
}
; Function Attrs: nounwind
define dso_local i32 @testtwo() local_unnamed_addr #1 {
entry:
%call = tail call i32 @two() #3
ret i32 %call
}
启用/添加尾部调用。我真的不喜欢使用编译器/shell作为链接器(对于有自己的引导和链接器脚本的嵌入式项目),llvm ldd的用法不容易理解或者基本上无法理解,但是ld.lld也支持tlo的东西,这就解决了。答案其实很简单:人们永远不应该想使用llc/opt/llvm链接来执行“最终用户”项目级的优化。这些是具有不同默认值、阈值等的开发人员端工具。基本上,它们只是各种LLVM工具箱的简单命令行前端 为了执行适当的链接时间优化,您需要使用用于此任务的管道。基本上,使用“clang-flto”编译所有内容,然后通过“clang-flto”再次链接所有内容都可以。使用像lld这样的LTO感知链接器也是一个先决条件
关于ThinLTO的更多信息也可以在这里找到:而且这似乎不是LLVM开发人员经常谈论或关注的事情。在某种意义上来说,你所拥有的是小的,优化是毫无意义的——无论有没有这种改变,程序都将是小而快的。您假设LLVM在有帮助的情况下不内联,因为您看到它在这种情况下不内联,这是。。。你可能是对的,但我不会打赌。相反,尝试一些非常大的事情,选择一个可能有帮助或有害的优化,并评估LLVM对哪些站点接收优化的选择。如果它不能消除这里的呼叫,那么我想知道它将如何处理更复杂的事情。但是它确实消除了对命令行选项组合的调用有一个可以内联的类,源代码中包含一条注释,说明“哪些调用有益于内联的决定在其他地方实现。”该类在许多地方使用。我没有费心去看可亵渎性评估逻辑。您的呼叫似乎无利可图-一个最低成本呼叫,仅执行一次,内联不可能实现其他优化。感谢您根据我对LTO的发现编辑了答案,基本上是叮当声-flto加上LTO感知链接器工作。
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @one() local_unnamed_addr #0 {
entry:
ret i32 1
}
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @testone() local_unnamed_addr #0 {
entry:
ret i32 1
}
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @testtwo() local_unnamed_addr #0 {
entry:
ret i32 2
}
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @two() local_unnamed_addr #0 {
entry:
ret i32 2
}
clang -O2 -fomit-frame-pointer -c test.c -o test.o
one:
0: b8 01 00 00 00 movl $1, %eax
5: c3 retq
testone:
10: b8 01 00 00 00 movl $1, %eax
15: c3 retq
testtwo:
20: e9 00 00 00 00 jmp 0 <testtwo+5>
arm-none-eabi-gcc -c -O2 -mcpu=cortex-m0 test.c -o test.o
arm-none-eabi-objdump -D test.o
00000000 <one>:
0: 2001 movs r0, #1
2: 4770 bx lr
00000004 <testone>:
4: 2001 movs r0, #1
6: 4770 bx lr
00000008 <testtwo>:
8: b510 push {r4, lr}
a: f7ff fffe bl 0 <two>
e: bd10 pop {r4, pc}
clang --version
clang version 10.0.0 (https://github.com/llvm/llvm-project.git d32170dbd5b0d54436537b6b75beaf44324e0c28)
Target: armv6m-none-unknown-eabi
Thread model: posix
InstalledDir: /opt/llvm/llvm10armv6m/bin
arm-none-eabi-gcc --version
arm-none-eabi-gcc (GCC) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
clang -c start.s -o start.o
clang -O2 -flto=thin -fomit-frame-pointer -c test.c
clang -O2 -flto=thin -fomit-frame-pointer -c two.c
ld.lld start.o test.o two.o -o test.elf
llvm-objdump -D test.elf
000110fc testtwo:
110fc: 02 20 movs r0, #2
110fe: 70 47 bx lr
00011100 two:
11100: 02 20 movs r0, #2
11102: 70 47 bx lr
clang -O2 -flto=thin -fomit-frame-pointer -c test.c
llvm-dis test.o
cat test.o.ll
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @one() local_unnamed_addr #0 {
entry:
ret i32 1
}
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @testone() local_unnamed_addr #0 {
entry:
ret i32 1
}
; Function Attrs: nounwind
define dso_local i32 @testtwo() local_unnamed_addr #1 {
entry:
%call = tail call i32 @two() #3
ret i32 %call
}