TableGen 语言语法介绍

有关于TableGen语言语法的文章,LLVM官方发布有两篇,第一篇是:TableGen Language Introduction,第二篇是:TableGen Language Reference(version llvm 10.0.0)。文章开头声明说,第一篇不是规范的参考文档,第二篇是规范的参考文档,并且两篇都有点年久失修。我把两篇都看了一下,确实感觉第二篇更规范一些,尤其是语法描述的章节,特别严谨。但是,我这里还是选择以第一篇的内容作为参考文档,主要是因为从易读性的角度来说,第一篇更容易理解(读者友好型),当然,第二篇作为参考查阅更便捷(活学活用Ctrl-f),关键在于能通过学习,掌握TableGen语言语法。

需要说明:本文中提到的记录是指官方文章中的record,即通过defdefm等语法定义的实例化的数据对象。

TableGen语法的特点

在我看来,TableGen的语法使用起来特别的灵活,但它还确实是一个强类型的语法,由于TableGen的作用只是用来描述信息,所以几乎没有控制流的语法规范,它也不关心语言本身的意义是否正确(由TableGen后端关心),它只关心语言本身的语法是否合法。它具有一些不同数据类型的定义,也支持一定程度的类型转换,数据类型范围特别宽泛。

以下介绍一下主要的语法。

TableGen语法介绍

注释

使用C++的注释风格://,不过也支持C风格的嵌套注释:/* */

数据类型

由于它是一个强类型语言,所以我们需要在编写td文件时考虑到数据格式规范。另外,它的覆盖范围特别广,小到位类型,大到dag类型,还支持类参数类型的扩展和列表的扩展,这使得TableGen非常的灵活和易于使用。

主要类型有:

  • bit:一位就是表示一个布尔值,比如0或者1;
  • int:表示一个32位的整形值,比如5;
  • string:表示一个有序的固定长度的字符序列,比如“add”;
  • code:表示一个代码片段,可以是单行或多行,不过其实和string本质一样,只是展示的意义不同而已;
  • bits<n>bit的扩展,可以指定n个位同时赋值,比如当n=3,可以是010,指定位模式时特别常用;
  • list<ty>:很灵活的一个类型,可以保存指定ty类型的数据的列表,ty类型也可以是另一个list<ty>,可以理解是C++中的List模板类;
  • class:指定一些类型数据的集合表示,必须用def或defm来使用这个类定义记录之后,内部数据才被分配。用来声明多个记录的共有信息,支持继承和重载等特性;
  • dag:表示可嵌套的有向无环图元素;

原文中指出:当前这些数据类型已经足够使用,如果日后还添加其他数据类型,再另行发布(我也不知道会不会更新这篇文章,以官方为准)

值和表达式

也非常灵活,有些时候,def的参数列表搞的非常长,就是因为这一部分,阅读代码会比较累,也没办法。

  • ?:未定义;

  • 0b1001001:位值,注意它的长度是固定的,它不会自动扩展或截断;

  • 7:十进制数;

  • 0x7F:十六进制数;

  • “foo”:单行的字符串值,可以直接赋给stringcode

  • [{ … }]:代码片段,通常用于赋给code,但其实就是多行字符串值;

  • [ X, Y, Z ]<type>:列表,type是指定列表中元素类型,一般情况下可省略,TableGen前端能够推测类型,极少数特殊情况下需要明确指定;

  • { a, b, 0b10 }:初始化bits<n>这样的类型,第一位是a变量的值,第二位是b变量的值,第三位和第四位是’0b1’ 和 ‘0b0’;

  • value:值的引用,比如上边出现的XYZab

  • value{17}:值的引用并截取一位;

  • value{15-17}:值的引用并截取一部分位,-两边必须要连续

  • DEF:记录的引用;

  • CLASS<val list>:匿名定义的引用,<val list>是模版参数,这里是这个意思,对于一个带模版参数的类,通过指定模版参数,可以直接定义一个匿名的记录,这里就是引用这个匿名记录;

  • X.Y:引用一个值的子域,常用在记录上;

  • list[4-7,17,2-3]:列表片段,比如这个例子中,引用了列表list的第4、5、6、7、17、2、3这几位;

  • foreach <var> = [ <list> ] in { <body> }:一种循环结构,依次将list中的值赋给var,并执行body体,类似于C++11中的foreach,body中仅可以包含def和defm;

  • foreach <var> = [ <list> ] in <def>:同上,不同是循环体只有一条语句,不需要用{}

  • foreach <var> = 0-15 in …:同上,不同的是循环的是整数;

  • foreach <var> = {0-15,32-47} in …:同上,不同的是循环的是几个整数片段;

  • (DEF a, b, …):这是dag类型的表示,第一个参数DEF是一个记录的定义,剩下的参数可以是其他类型值,当然也包括嵌套的dag类型值,除第一个参数外,其他参数可省略;

  • !con(a, b, …):将两个或多个dag类型节点连接,它们的操作码必须相同;

    例子:!con((op a1:$name1, a2:$name2), (op b1:$name3)),等效于(op a1:$name1, a2:$name2, b1:$name3)。其中 a1,a2,b1 均表示接后边值的类型。

  • !dag(op, children, names):生成一个dag节点,children和names必须有相同的长度的列表或者是?,names必须是list<string>类型,children必须是常见类型的列表,children列表中的类型必须是相同的或者它们的祖先类是相同的,不支持混合的dag和non-dag;

    例子:!dag(op, [a1, a2, ?], ["name1", "name2", "name3"]),等效于(op a1:$name1, a2:$name2, ?:name3)

  • !listconcat(a, b, …):将两个或多个列表合并成一个列表,这些子列表必须具有相同的子项类型;

  • !listsplat(a, size):将指定a列表中的子项重复包含size次;

    例子:!listsplat(0, 2),等效于[0, 0]

  • !strconcat(a, b, …):将两个或多个字符串合并成一个字符串;

  • !str1#str2:将两个字符串合并成一个字符串,是!strconcat(a, b)的简化用法,如果其中有不是字符串的类型,TableGen前端会隐式调用!cast<string>操作转换为字符串类型。

  • !cast<type>(a):强制类型转换。(注:这段我方了,没有完全理解,后边解释不用看,感兴趣的同学回去翻原文吧)如果a是字符串,而type是记录类型,那么将会通过完全检查a和所有记录之间的匹配,并确保匹配记录已经完整声明。这个完整声明的意思是,如果这个记录是在含参模版类中,那么这个记录在定义时,必须要求该类和其内部可能有的其他含参类都已指明类参数值;而如果还没有指定完整的类参数值,那么会在指定完整类参数值之后再执行这次转换,而如果没有匹配到任何记录,则会报错。若type只是简单的值类型,比如bit或int之间,或记录之间。对于记录之间的情况,cast会首先将记录转换为子类,如果类型不匹配,则不会做常量折叠。!cast 是一个特殊的情况,他的参数 a 可以是一个 int 或记录类型,如果满足记录类型的转换,则会返回一个类型名;

  • !isa<type>(a):返回布尔值,如果a是type类型,返回1,否则返回0;

  • !subst(a, b, c):如果a和b是字符串类型或者是引用类型,将c中的b替换为a,类似于GNU make中的$(subst)语法;

  • !foreach(a, b, c):对于b中的每一个值,将c中的b替换为a,类似于GNU make中的$(foreach)语法(不是 C++ 中的那个 foreach);

  • !foldl(start, lst, a, b, expr):使用给定的start,对lst做left-fold操作。a和b是变量名,将在expr中被替换,如果expr视为函数f(a,b),则left-fold将做这样的操作:f((...f(f(start, lst[0]), lst[1]), ...), lst[n-1]),循环次数取决于lst的长度n。a与start相同类型,b与lst中元素相同类型,expr和start相同类型;

  • !head(a):取列表a的第一个元素;

  • !tail(a):取列表a的最后一个元素(原文中是:`The 2nd-N elements of list ‘a’,是同一个意思吗);

  • !empty(a):返回布尔值,列表a是否为空;

  • !size(a):返回一个整数,表示列表a的元素个数;

  • !if(a, b, c):类似C中的三元操作符:? :,如果a为非0,返回b,否则返回c;

  • !cond(condition_1 : val1, condition_2: val2, …, condition_n : valn):是!if(a, b, c)的扩展,避免多次的if嵌套。如果condition_1满足,返回val1,否则如果condition_2满足,返回val2,以此类推,如果condition_n仍然不满足,返回错误;

    例子:!cond(!lt(x, 0) : "negative", !eq(x, 0) : "zero", 1 : "positive"),注意到最后一个条件是恒成立,避免了可能的报错。

  • !eq(a, b):返回布尔值,如果a和b相等,返回1,否则返回0,

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值