1. 什么是tablegen
tablegen是llvm用於開發和維護編譯器中公共特性的條目(e.g. 指令描述, 寄存器描述)的代碼, 使之靈活的描述與構造的自動化工具. 其本質是一個parser, 將輸入的td文件轉化為特定的數據結構后再輸出為易於閱讀的cpp代碼. 更多介紹可見http://llvm.org/docs/TableGen/index.html或參見docs/TableGen/下文件說明.
2. tablegen的使用方式
首次成功編譯后, 在[llvm install path]/bin/下會生成可執行文件llvm-tblgen. 通常使用方法(在llvm的cmake工程中)它讀入一個td文件, 並將結果輸出至一個inc文件中. 以高通的Hexagon架構為例(具體使用命令可以llvm-tblgen --help查詢)生成指令信息代碼:
1 [12:32:11] hansy@hansy:~/llvm/llvm (master)$ ../llvm_build/bin/llvm-tblgen -gen-instr-info -I ./lib/Target/Hexagon/ -I ./include/ -I ./lib/Target/ ./lib/Target/Hexagon/Hexagon.td -o ~/test.inc
生成的inc文件實質為cpp代碼, llvm工程中會包含這些文件. 當前tablegen生成的代碼主要分為前端clang(target independent code, 在[llvm build path]/tools/clang/include/clang/下)以及后端llvm(target dependent code, 在[llvm build path]/lib/Target/[arch]下).
tablegen代碼包含兩塊: 對td文件的處理, 在lib/TableGen/目錄下, 包含lexer與parser, 負責解析tablegen的語法並轉換為內部數據結構; 輸出cpp代碼, 在utils/TableGen/目錄下, 用於生成我們需要的cpp代碼, 這塊與llvm代碼邏輯強相關, 基本上一個cpp文件對應一類信息.
3. td文件的語法
tablegen的輸入來源於td文件, 如要了解tablegen就先要了解td文件. 與生成的inc文件類似, td文件也包含兩塊內容(在[llvm source path]/對應的目錄下), 后端還包含部分llvm target independent code(主要為IR相關), 暫不贅述.
仍以Hexagon架構為例, lib/Target/Hexagon/Hexagon.td是Hexagon架構td的入口(每個架構對應目錄下都有一個以架構命令的td文件). 在該文件中定義了一些基礎數據結構, 並包含了構建該架構后端所需的所有信息的文件.
先來看看該文件定義的數據結構. 在td中使用兩個關鍵字定義數據結構(llvm中稱為records, 其實質對應的是tablegen中的一個類/類實例), class與def. 其中classes類似於模板, 用於描述一類抽象的records(e.g. Register, RegisterClass, Instruction). 而definitions用於表達一個具體的records(可以理解為一個特定的類). 每個records包含若干數據成員, 這些成員的類型有bit(布爾量), int(整型), string(字符串), code(代碼段, 包含一行或多行的字符串), bits<n>(位段)等類型. 數據成員使用let關鍵字進行賦值, 對於tablegen中解析的成員必須都初始化(為定義的值可以使用?初始化為'未初始化值'), 否則會導致編譯失敗. 若一個definition record包含一個未初始化成員, 其值將從該definition的superclass中獲取. 若tablegen中未解析該成員則不賦值也不會報錯. 以Asmparser為例(defined in include/llvm/Target/Target.td):
1 class AsmParser { 2 string AsmParserClassName = "AsmParser"; 3 string AsmParserInstCleanup = ""; 4 bit ShouldEmitMatchRegisterName = 1; 5 bit ShouldEmitMatchRegisterAltName = 0; 6 bit AllowDuplicateRegisterNames = 0; 7 bit HasMnemonicFirst = 1; 8 bit ReportMultipleNearMisses = 0; 9 } 10 Hexagon架構的Asmarser如下(defined in lib/Target/Hexagon/Hexagon.td): 11 def HexagonAsmParser : AsmParser { 12 let ShouldEmitMatchRegisterAltName = 1; 13 bit HasMnemonicFirst = 0; 14 }
Hexagon架構的Asmarser如下(defined in lib/Target/Hexagon/Hexagon.td):
1 def HexagonAsmParser : AsmParser { 2 let ShouldEmitMatchRegisterAltName = 1; 3 bit HasMnemonicFirst = 0; 4 }
Hexagon未定義parser的名字, 因此使用默認模板的字符串'AsmParser'; 默認的parser不匹配寄存器別名, 而Hexagon下ShouldEmitMatchegisterltName為true, 覆寫了默認值.
4. tablegen選項
一般情況下並不會修改tablegen的代碼, 而是通過修改td文件實現所需的目標. 對任意一個架構而言, 不是所有tablegen選項都有效(例如不支持vliw的架構就沒有DFAPacketizer), 仍以Hexagon為例常見選項如下:
-gen-emitter
output: [arch]##GenMCCodeEmitter.inc
usage: used to generate binary code for given instruction. included in [arch]MCCodeEmitter.cpp
-gen-register-info
output: [arch]##GenRegisterInfo.inc
usage: output register enum and class, mask, etc. included in [arch]##BaseRegisterInfo.* and [arch]##MCTargetDesc.*
-gen-instr-info
output: [arch]##GenInstrInfo.inc
usage: output instruction enum and class, mask, etc. included in [arch]##BaseInstrInfo.* and [arch]##MCTargetDesc.*
-gen-asm-writer
output: [arch]##GenAsmWriter.inc
usage: generate assembly printer. included in [arch]##InstPrinter.cpp
-gen-asm-matcher
output: [arch]##GenAsmMatcher.inc
usage: generate assembly matcher used in assemble parser. included in [arch]##AsmParser.cpp
-gen-disassembler
output: [arch]##GenDisassemblerTables.inc
usage: generate disassembly emitter. included in [arch]##Disassembler.cpp
-gen-callingconv
output: [arch]##GenCallingConv.inc
usage: implement static function used for calling conversion.
-gen-dag-isel
output: [arch]##GenDAGISel.inc
usage: implement a DAG instruction selector. included in [arch]##ISelDAGToDAG.cpp
-gen-dfa-packetizer
output: [arch]##GenDFAPacketizer.inc
usage: generate function to check whether an instruction can be added to a VLIW. included in [arch]##InstrInfo.cpp
-gen-subtarget
output: [arch]##GenSubtargetInfo.inc
usage: generate subtarget enum.