LLVM是一个很复杂的软件,了解LLVM的工作原理不是很容易,然而,对于刚开始接触LLVM整个框架的工作原理来说,详细而深入,不如广泛而浅显,所以有了这一篇文章。
通过跟随一条指令在LLVM中的各个passes中的状态变化,从源程序开始,到目标代码结束,可以让我们对LLVM的整体框架有个大致的认识。
这篇文章基于Life of an instruction in LLVM,文章大部分内容与参考文章一致,但由于参考文章编辑于2012年11月,当时的LLVM版本是3.2,距现在新的LLVM版本已有一些差异,所以有部分内容我做了调整。
这篇文章不会详细讲解各个passes中的实现,尽量易于理解,尽量紧贴指令的变化过程。
有关于LLVM中的一些基本概念,可以参考:https://blog.csdn.net/SiberiaBear/article/details/103111028。
输入代码
使用的输入代码与参考文章一致,选择一段C语言来开始:
int foo(int aa, int bb, int cc) {
int sum = aa + bb;
return sum / cc;
}
我们focus的指令是除法指令,不关注其他代码。
Clang
Clang是LLVM框架的前端,用来将C、C++、ObjectC的源代码转换为LLVM IR结构,它最复杂的实现是处理C++中的一些特殊语法,对于我们这个简单的C代码来说,处理很简单,按照词法+语法+语义的方式走就可以。
Clang的parser会构建一个AST,并作为它的中间表示,对于我们的除法操作,在AST中会生成一个BinaryOperator
节点,承载一个B0_div
的操作码类型。通过clang自带的ast dump插件,命令为clang -Xclang -ast-dump -fsyntax-only test.c
,可以查看AST的情况(参考文章中需更新)。Clang的代码生成器会将AST转换为一个LLVM IR,这时我们的指令会生成为一个sdiv
的LLVM IR指令,这是一个有符号的除法指令。
LLVM IR
经过Clang处理后,输出的是LLVM的IR表示:
define i32 @foo(i32 %aa, i32 %bb, i32 %cc) nounwind {
entry:
%add = add nsw i32 %aa, %bb
%div = sdiv i32 %add, %cc
ret i32 %div
}
在LLVM IR中,sdiv
是一个BinaryOperator
对象,这是一个带有SDiv
操作数的Instruction
类的子类。就像其他指令一样,LLVM IR可以被LLVM的解析和转换pass来处理,最终会通过LLVM代码生成,进入下一个环节。
**代码生成器(Code Generator)**是LLVM中一个很复杂的部分。它的工作是将高层级的、目标无关的LLVM IR下降为低层级的、目标相关的机器指令,代码生成通常也就认为是LLVM的后端(注意不是LLVM/Clang的后端&#