clang / LLVM项目级别优化

老旧的计时器

因此,我定期尝试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
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

和测试文件

测试

unsigned int one ( void )
{
    return(1);
}
unsigned int two ( void );
unsigned int testone ( void )
{
    return(one());
}
unsigned int testtwo ( void )
{
    return(two());
}

两个

unsigned int two ( void )
{
    return(2);
}

基本运行

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

testone:
    .fnstart
@ %bb.0:                                @ %entry
    .save   {r7, lr}
    push    {r7, lr}
    bl  one
    pop {r7, pc}


testtwo:
    .fnstart
@ %bb.0:                                @ %entry
    .save   {r7, lr}
    push    {r7, lr}
    bl  two
    pop {r7, pc}

testone:
    .fnstart
@ %bb.0:                                @ %entry
    .save   {r7, lr}
    push    {r7, lr}
    bl  one
    pop {r7, pc}

testtwo:
    .fnstart
@ %bb.0:                                @ %entry
    .save   {r7, lr}
    push    {r7, lr}
    bl  two
    pop {r7, pc}

那更糟。

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也不起作用,并且此输出非常糟糕,它调用函数AND对其进行内联。那里发生了什么?!

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

; 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() #2
  ret i32 %call
}

; Function Attrs: norecurse nounwind readnone
define dso_local i32 @two() local_unnamed_addr #0 {
entry:
  ret i32 2
}

; 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
}

因此,为了在项目/项目级别进行优化,必须在文件/对象级别上将优化应用于所有位置是否正确?

然后还有尾部调用或叶子等优化的问题,如果没有其他问题,请执行testtwo:即使在第一种情况下

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>

在gnu中,链接器修补蹦床的任何分支到达或模式问题

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.

我想的问题是,是否要使用llvm-link和opt进行项目级优化,是否需要对每个单独的项目进行优化,或者是否缺少命令行选项。对源代码本身中包含的特定于编译器的属性不感兴趣,希望代码不受gcc或llvm的感染。

在gcc 5.xx之后,代码变得更加肿,希望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
}

启用/添加尾呼叫。我真的不喜欢将编译器/外壳程序用作链接器(对于具有自己的引导程序和链接器脚本的嵌入式项目),很难确定llvm-ldd的用法,或者根本无法确定,但ld.lld也支持tlo的东西,所以解决了。

安东·科罗贝尼科夫(Anton Korobeynikov)

答案实际上很简单:人们永远都不想使用llc / opt / llvm-link来执行“最终用户”项目级别的优化。这些是具有不同默认值,阈值等的开发人员工具。基本上,它们只是LLVM工具箱各部分的简单命令行前端。

为了执行正确的链接时间优化,您需要使用用于此类任务的管道。基本上,使用“ clang -flto”编译所有内容,然后通过“ clang -flto”再次链接所有内容都可以。使用lld之类的支持LTO的链接器也是一个先决条件。

关于ThinLTO的一些其他信息也可以在这里找到:https ://clang.llvm.org/docs/ThinLTO.htmlhttp://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto .html

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

修改由Clang / LLVM编译器执行的优化

来自分类Dev

Clang,LLVM和g ++

来自分类Dev

Clang,LLVM和g ++

来自分类Dev

了解clang循环优化

来自分类Dev

使用clang生成LLVM IR

来自分类Dev

llvm / clang编译错误,内存耗尽

来自分类Dev

如何为iOS交叉编译clang / llvm?

来自分类Dev

Clang和LLVM-版本与调试版本

来自分类Dev

强制函数在Clang / LLVM中内联

来自分类Dev

配置文件LLVM通过Clang

来自分类Dev

测量使用Clang / LLVM生成的函数的大小?

来自分类Dev

LLVM(Clang)是否曾经使用GCC?

来自分类Dev

Clang或LLVM中的-Wa-divide选项

来自分类Dev

如何为 Android 编译 LLVM/Clang?

来自分类Dev

使用Clang编译器通过Make构建LLVM项目时出现怪异标志

来自分类Dev

clang如何引导C / C ++代码优化?

来自分类Dev

Clang为什么要优化此代码?

来自分类Dev

用错误的方式对clang优化的代码

来自分类Dev

它是g ++和clang ++优化的错误吗?

来自分类Dev

用clang优化的简单循环,如何?

来自分类Dev

GCC和Clang:关闭纯优化

来自分类Dev

反序列化时的gcc / clang优化

来自分类Dev

空体功能会被 clang 优化吗?

来自分类Dev

clang插件编译错误:未定义符号llvm :: Registry <clang :: PluginASTAction,llvm :: RegistryTraits <clang :: PluginASTAction>>

来自分类Dev

为什么在Visual Studio中使用LLVM + Clang时未定义__clang__?

来自分类Dev

clang(和LLVM)和gcc / g ++有什么区别?

来自分类Dev

使用Clang构建V8并发出LLVM IR

来自分类Dev

使LLVM ModulePass在clang命令行上可用

来自分类Dev

将OpenMP与llvm-clang一起使用