LLVM TableGen
Table of Contents
1. LLVM TableGen
https://llvm.org/docs/TableGen/index.html
https://llvm.org/docs/TableGen/ProgRef.html
tablegen 是整个 llvm backend 核心的数据结构, llvm backend 的所有组件都需要通过 tablegen 来解释 target description (td) 文件, 拿到自己关注的数据.
要了解 td 文件, 除了 tablegen 本身的语法, 更重要的是 SelectionDAGISel 和其它模块如何使用 tablegen 的数据, 因为这些数据完全是由各个模块负责解释.
target description 和 gcc machine description 基本是类似, 它们都包含:
- instruction selection 相关的 pattern
- target instruction 对应的 assembly
- 和 scheduling 相关的 instruction latency
但 td 是使用一个新的 DSL 对这些信息进行层次化的定义, 而 md 的定义并不是层次化的: 例如 add,sub 指令需要有单独的完整定义, 虽然它们有许多相似的地方.
另外, md 不包含寄存器信息和指令的编码信息, 所以 gcc 需要自己从头实现 as/disass, 而不是像 llvm 可以使用 td 的信息自动生成 as/disass 的部分代码.
1.1. llvm-tblgen
llvm-tblgen 包括两部分功能:
- 解析 td 文件, 生成 records, 这个 records 是 td 展开的结果, 不涉及具体的语义
- 把 records 按命令行参数提供的 action 进一步解析, 生成对应的 inc 文件, 例如 `-gen-dag-isel` 参数会导致 llvm-tblgen 按 isel 的需求去解析 records, 生成 RISCVGenDAGISel.inc
所以 llvm-tblgen 是认识 llvm 相关的 action 的语义的, 例如 `gen-register-info`, `gen-instr-info`, `gen-dag-isel`…, 但通过 `-print-records` 和 `-print-detailed-records`, 可以只看 td 展开的 records, 而不做进一步分析.
1.2. td
- TableGen DSL 类似于 c++
- 支持类似模板的参数
- 支持多重继承
- 通过 class 关键字定义 abstract record
- 通过 def 关键字来定义 concrete record
使用 `llvm-tblgen xxx.td [-print-records, -print-detailed-records]` 可以查找 td 展开后的结果.
1.2.1. example
// 使用 class 来定义一个 abstract record class A<string n> { string Name=n; } class B<int a> { // 定义了四个 field // 支持 string, int, bit, bits<>, list 五种数据类型 int A=a; bit Bit; bits<10> Bits; list<int> List=[a]; // 其它 class 的实例也能做为 field A ClassA; } // 多重继承, 并通过模板参数初始化基类 class AB<string n,int a, A x>:A<n>,B<a> { // bits 可以通过 {a-b} 的形式访问 bit // 通过 let 修改基类的 field let Bits{2-0}=a; let Bit=true; let List=[a,a]; // 声明 AB 类自己的 field, 通过 Name 访问基类的 field // # 用来拼接 string X=Name#Name; let ClassA=x; } // 使用 def 定义 concrete record def a:A<"a">{ } def ab0:AB<"0",2, a> { // def 可以使用通过 let 修改 class field let Bits{4-2}=2; // def 可以定义自己的 field int AA=1; } // let 可以放在 class, def 外面, 通过 let xxx in {} 的形式给多个 record 的 field // 赋值, 这个和 lisp 的 (let ((a 1)) (xxx)) 很类似 let Name="ab12",Bit=false in { def ab1:AB<"0",2,a>; def ab2:AB<"0",2,a>; } // let 可以嵌套 let Name="abc" in { let Name="bcd" in { class ABC:A<"">; } } // multiclass/defm 的作用和 gcc md 的 mode iterator 类似 multiclass ALU { def rr { string type="RR"; } def ri { string type="RI"; } } defm add:ALU; defm sub:ALU; // foreach, 相当于带循环的 let foreach i=[1,2,3] in { def a_#i:A<"">{} } // bang operators, 即预定义的 !xxx 形式的函数 def aaa { string x=!cast<string>(1); int y=!add(1,2); int z=!cond(!lt(y,2):0,true:1); int a=!if(!eq(y,3),0,1); }
llvm-tblgen test.td 展开的结果:
------------- Classes ----------------- class A<string A:n = ?> { string Name = A:n; } class AB<string AB:n = ?, int AB:a = ?, A AB:x = ?> { // A B string Name = AB:n; int A = AB:a; bit Bit = 1; bits<10> Bits = { ?, ?, ?, ?, ?, ?, ?, !cast<bits<3>>(AB:a){2}, !cast<bits<3>>(AB:a){1}, !cast<bits<3>>(AB:a){0} }; list<int> List = [AB:a, AB:a]; A ClassA = AB:x; string X = !strconcat(Name, Name); } class ABC { // A string Name = "bcd"; } class B<int B:a = ?> { int A = B:a; bit Bit = ?; bits<10> Bits = { ?, ?, ?, ?, ?, ?, ?, ?, ?, ? }; list<int> List = [B:a]; A ClassA = ?; } ------------- Defs ----------------- def a { // A string Name = "a"; } def a_1 { // A string Name = ""; } def a_2 { // A string Name = ""; } def a_3 { // A string Name = ""; } def aaa { string x = "1"; int y = 3; int z = 1; int a = 0; } def ab0 { // A B AB string Name = "0"; int A = 2; bit Bit = 1; bits<10> Bits = { ?, ?, ?, ?, ?, 0, 1, 0, 1, 0 }; list<int> List = [2, 2]; A ClassA = a; string X = "00"; int AA = 1; } def ab1 { // A B AB string Name = "ab12"; int A = 2; bit Bit = 0; bits<10> Bits = { ?, ?, ?, ?, ?, ?, ?, 0, 1, 0 }; list<int> List = [2, 2]; A ClassA = a; string X = "ab12ab12"; } def ab2 { // A B AB string Name = "ab12"; int A = 2; bit Bit = 0; bits<10> Bits = { ?, ?, ?, ?, ?, ?, ?, 0, 1, 0 }; list<int> List = [2, 2]; A ClassA = a; string X = "ab12ab12"; } def addri { string type = "RI"; } def addrr { string type = "RR"; } def subri { string type = "RI"; } def subrr { string type = "RR"; }
1.3. gen-dag-isel
和 isel 相关的 class 主要有:
- Instruction, 其中包含 Pattern, Predicates, Constraints, SchedRW, 在 Target.td 中
- Pattern, 包含 PatternToMatch, Predicates, 在 TargetSelectionDAG.td 中
以 FADD_S 为例:
// 通过 SchedRW 定义了 FADD_S Instruction 的 SchedReadWrite, // 这个相当于 gcc md 中的 (set-attr "type" ...). // 后续相应的 SchedMachineModel 会定义 WriteFALU32 在 machine (例如 sifive rocket) // 上的 Latency, ResourceCycles 和 ProcResourceKind // RISCVSchedRocket.td: // let Latency = 4 in { // def : WriteRes<WriteFALU32, [RocketUnitFPALU]>; // } // 导致 Instruction 的 Latency 为 4 let SchedRW = [WriteFALU32, ReadFALU32, ReadFALU32] in { defm FADD_S : FPALU_rr_frm_m<0b0000000, "fadd.s", FINX>; defm FSUB_S : FPALU_rr_frm_m<0b0000100, "fsub.s", FINX>; } // FINX 是一个 ext list, 展开后最终能看到 Instruction 会包含一个 // hasExtF 的 predicate, 但并不包含 Pattern def : PatFprFprDynFrm<any_fadd, FADD_S, FPR32>; // PatFprFprDynFrm 展开为: // Pat<(fadd FPR32:$rs1, FPR32:$rs2), (F_ADD_S $rs1, $rs2, 0b111)>; // 后者再展开成一个 Pattern, 其中 (fadd FPR32:$rs1, FPR32:$rs2) 是 PatternToMatch // (F_ADD_S $rs1, $rs2, 0b111) 是 ResultInstrs, 没有额外的 Predicates
把 RISCV.td 完整整开后关于 FADD_S 的部分为:
$> build/bin/llvm-tblgen -I llvm/lib/Target/RISCV -Ibuild/include \ -I/home/sunway/source/llvm-project/llvm/include -I llvm/lib/Target llvm/lib/Target/RISCV/RISCV.td def FADD_S { // InstructionEncoding Instruction RVInst RVInstRFrm FPALU_rr_frm ... list<Predicate> Predicates = [HasStdExtF]; string AsmString = "fadd.s $rd, $rs1, $rs2, $frm"; list<dag> Pattern = []; int AddedComplexity = 0; list<SchedReadWrite> SchedRW = [WriteFALU32, ReadFALU32, ReadFALU32]; RISCVVConstraint RVVConstraint = NoConstraint; ... } def anonymous_5505 { // Pattern Pat PatFprFprDynFrm dag PatternToMatch = (any_fadd FPR32:$rs1, FPR32:$rs2); list<dag> ResultInstrs = [(FADD_S ?:$rs1, ?:$rs2, { 1, 1, 1 })]; list<Predicate> Predicates = [HasStdExtF]; int AddedComplexity = 0; } def anonymous_45523 { // ProcWriteResources WriteRes list<ProcResourceKind> ProcResources = [RocketUnitFPALU]; list<int> ResourceCycles = []; int Latency = 4; int NumMicroOps = 1; bit BeginGroup = 0; bit EndGroup = 0; bit Unsupported = 0; bit SingleIssue = 0; bit RetireOOO = 0; SchedMachineModel SchedModel = RocketModel; SchedWrite WriteType = WriteFALU32; } def anonymous_45659 { // ProcWriteResources WriteRes list<ProcResourceKind> ProcResources = [SiFive7PipeB]; list<int> ResourceCycles = []; int Latency = 5; int NumMicroOps = 1; bit BeginGroup = 0; bit EndGroup = 0; bit Unsupported = 0; bit SingleIssue = 0; bit RetireOOO = 0; SchedMachineModel SchedModel = SiFive7Model; SchedWrite WriteType = WriteFALU32; }
1.4. gen-instr-info
Backlinks
LLVM Backend (LLVM Backend > TableGen): TableGen