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
