LLVM简单介绍

LLVM全称为Lower Level Virtual Machine,最初是以C/C++为编译目标,现如今LLVM已经能够为ActionScript、D、Fortran、Haskell、Java、Objective-C、Swift、Python、Ruby、Rust、Scala等众多语言提供了编译支持,有一些新兴的语言就直接使用LLVM作为后端

编译简单来说其实就是把人类可读的高级语言映射到机器执行码。编译简单的说可以分为几个阶段来讲一个就是前端处理、还有就是代码优化和代码生成等。为了使这个复杂的过程简化,LLVM就使用了模块化的思想,这样可以使得每一个编译阶段都被独立出来,LLVM是使用面向对象的C++语言写的

LLVM的代码可以分为3种表示形式,内存当中的IR形式和存于磁盘的bitcode还有就是文本表现形式的用户可读的汇编码

LLVM IR是基于静态单赋值(Static Single Assignment —SSA)的,有很大的灵活性和类型的安全性所以能够清楚的表达绝大多数高级语言

接下来我们去创建一个输入文件是基于LLVM的中间语言IR语法的文件testfile,然后用不同的优化器进行优化,文件内容如下所示

define i32 @test1(i32 %A){
    %B = add i32 %A,0
    ret i32 %B
}
define internal i32 @test(i32 %X,i32 %dead){
    ret i32 %X
}
define i32 @caller(){
    %A = call i32 @test(i32 123,i32 456)
    ret i32 %A
}

然后我们去使用opt -S -instcombine testfile.ll -o output1.ll 去进行指令的合并操作的优化,优化结果如下所示

; ModuleID = 'testfile.ll'
source_filename = "testfile.ll"

define i32 @test1(i32 %A) {
  ret i32 %A
}

define internal i32 @test(i32 %X, i32 %dead) {
  ret i32 %X
}

define i32 @caller() {
  %A = call i32 @test(i32 123, i32 456)
  ret i32 %A
}

这个命令运行了instcombine Pass,会把指令合并,上面就是把%B = add i32 %A,0;ret i32 %B给变成了ret i32 %A这么一句话

现在我们使用另一种方式进行优化我们输入opt -S -deadargelim -o output2.ll

; ModuleID = 'testfile.ll'
source_filename = "testfile.ll"

define i32 @test1(i32 %A) {
  %B = add i32 %A, 0
  ret i32 %B
}

define internal i32 @test(i32 %X) {
  ret i32 %X
}

define i32 @caller() {
  %A = call i32 @test(i32 123)
  ret i32 %A
}

这里做的优化就是将无用的参数进行消除,将第二个函数当中的无用的参数给消除了

其实LLVM优化器会为用户提供不同的优化的Pass,但是整体的编写风格是一致的,对于每个Pass的源码会进行编译,然后会得到一个Object文件,然后再把这些不同的文件给链接得到一个库。Pass之间的耦合度很小,Pass之间的依赖信息由LLVM Pass管理器(Pass Manager)来进行统一的管理,在Pass运行的时候会进行解析,下面看下关于他们的关系图
这里写图片描述

在LLVM当中的代码生成器(code generator)也采用了模块的设计理念,它可以将代码生成问题分解为多个独立的Pass:比如说指令选择、寄存器分配、指令调度、代码布局优化等,以及它也有很多内建的Pass,它们是默认执行的,但是用户可以选择只去执行其中的一部分

猜你喜欢

转载自blog.csdn.net/zcmuczx/article/details/80717656