编写LLVM的后端(一)

本文档是指如何编写一个可以把LLVM的中间表示转换成一个特定的机器或者其他语言的后端。对于一个特定机器的代码或者是汇编语言或者是二进制代码。

llvm后端的是一个和目标相关的代码产生器,可以创建不同类型的目标处理器的输出,如x86,powerpc, alpha, sparc。后端也可以用于产生针对Cell处理器的SPU或者GPU来支持计算内核的执行。

文档重点描述了在llvm/lib/Target子目录下存在的一些例子。特别的,文档描述了如何创建一个静态编译器(一个产生文本汇编)。sparc有相对标准的特性,如RISC指令集和一个比较直接的调用约定。

基本步骤:

写一个的编译器后端,把LLVM IR代码转换成一个特定的目标,需要遵循以下步骤:
创建一个Targetmachine类的子类来描述你的目标机器的特性。拷贝已经存在的某些机器的类和头文件,从SparcTargetMachine.cpp和SparcTargetMachine.h开始,然后把这两个文件改为你自己的目标的文件名。

描述你的目标机器的寄存器集合,使用Tablegen为寄存器定义,寄存器别名和来自目标特定的RegisterInfo.td的寄存器类产生代码。你应该为TargetInfo类添加一些额外的代码来表示这个目标机器支持的指令集。

描述LLVMIR的选择和转换,从一个指令的DAG表示到本地目标指令。使用TableGen产生符合模式的代码,基于目标特定的TargetInstrInfo.td的额外信息来用TableGen产生符合模式的代码和选择指令。编写代码文件xxxxISelDAGToDAG.cpp,xxxx是你的目标名称。用来进行模式匹配和DAG到DAG的指令选择。也可以写xxxISelLowering.cpp来代替或者去除SelectionDAG中不支持的数据类型和操作。
编译汇编打印,来进行LLVM到汇编的转换,一个TargetAsmInfo的子类。

可选的,添加对子目标的支持。你可以写对TargetSubtarget类的子类。这样运行用-mcpu和-mattr的命令行选项。

可选的,添加JIT支持,来创建一个机器码的发射器(TargetJITInfo的子类),可以用于直接把二进制代码存储在内存中。

在你写的.cpp和.h文件中,先把这些方法写出来,后面再去实现他们。否则,开始的时候你并不知道哪些私有成员会被这个类需要,那个组件需要被子类化。

Preliminaries 实际创建一个编译器的后端,你需要创建和修改一些文件。这里讨论一个最简化的工作。但实际上使用LLVM中目标依赖的代码产生器,你必须进行在LLVM Target-indepedent Code Generator中的那些步骤。

首先,应该创建一个子目录来存放和你的目标相关的文件。假设你的目标是Dummy,那创建lib/Target/Dummy目录。

在这个新的目录,创建一个Makefile,最简单的方法是拷贝一个已存在的目标的Makefile,然后进行修改,这个Makefile至少要有LEVEL, LIBRARYNAME和TARGET这些变量。然后Include $(LEVEL)/Makefile.common。这个库可以命名为LLVMDummy(参考MIPS目标)。或者你可以把这个库分割为LLVMDummy和LLVMDUmmyAsmPrinter,后者应该实现在一个lib/Target/Dummy的子目录(参考PowerPC目标)。为了能够让你的目标实现做一些实际的事情,你需要实现Targetmachine的子类。这个实现一般位于lib/Targe/DummyTargetMachine.cpp文件。但是在lib/Target目录中的任何文件都会被编译。使用LLVM目标无关的代码生成器,你可以做当前机器后端的工作:创建一个LLVMTargetmachine的子类。

使LLVM编译并链接你的目标机器的代码,你需要把文件添加到TARGETS_TO_BUILD变量。为了完成这一步,你需要修改configure脚本,在传递–enable-targets选项。搜索configure脚本中的TARGETS_TO_BUILD,添加你的目标到列表中。然后重新培训。另外一种方法你也可以修改修改autotools/configure.ac,并且需要运行./autoconf/AutoGen.sh脚本。