LLVM程序员手册笔记——Important and useful LLVM APIs

 

Important and useful LLVM APIs

这部分主要介绍了一些需要知道并会经常用到的LLVM APIs。

The isa<>, cast<> and dyn_cast<> templates

Passing strings(the StringRef and Twine classes)

The DEBUG() macro and -debug option

The Statistic class & -stats option

Viewing graphs while debugging code

 

 

The isa<>, cast<> and dyn_cast<> templates

LLVM的源代码基础广泛使用了一种自定义形式的运行时类型信息。这些模板与C++的dynamic_cast<>操作符具有很多相似之处,但是没有它的一些障碍(这些缺点主要由于dynamic_cast<>只能处理具有虚函数表的类)。由于它们被经常使用,所以必须知道它们做了什么以及如何实现的。这些模板都在llvm/Support/Casting.h文件中定义(注:很少需要直接包含该头文件)。

isa<>:

isa<>操作符的作用很像java中的“instanceof”操作符。它根据一个引用或者指针是否指向一个指定类来返回true或者false。这对许多约束检查都非常有用。

cast<>:

cast<>操作符是“checked cast”操作。它将一个基类的指针或引用转换到子类,如果不是指定类型的实例,就会造成一个断言错误。当能确定一个指针或引用确实是某个类型时,才能使用cast<>。

这里有个isa<>和cast<>的例子:

注:不应该在使用isa<>检测后,再使用cast<>,这种情况下应该使用dyn_cast<>操作符。

dyn_cast<>:

dyn_cast<>操作符是“Checking cast”操作。检查一个操作数是否属于一个指定类型,如果是,返回指向该类型的指针(该操作符对引用无效);如果不是,返回空指针。所以,它和C++中的dynamic_cast<>操作符很像,也应该用在同样的情况下。dyn_cast<>的经典用法是用在if语句或者其它的流控制语句中。

上述形式的if语句有效地将isa<>和cast<>的调用组合到一条语句中,非常方便。

注:dyn_cast<>操作,象C++的dynamic_cast<>或者Java的instanceof操作符一样,可能被滥用。在实际中,不应该将大量的if/then/else块串在一起来检查大量类的不同变体。如果确实需要做这样一件事,使用InstVisitor类会更加清晰和有效地直接派遣到指令类型。

cast_or_null<>:

cast_or_null<>操作符的功能类似于cast<>操作符,只是它允许空指针作为参数。这样可以让将几个Null检查合并成一个,有时可能很有用。

dyn_cast_or_null<>:

dyn_cast_or_null<>操作符的功能和dyn_cast<>操作符相似,它也允许空指针作为参数。

无论一个类是否具有虚函数表,上述五个模板的可以在其中使用。为了支持这些模板,只需往想进行转换的类中添加名为classof的静态方法。关于这块的描述需要参看LLVM源代码中的实例。

Passing strings(the StringRef and Twine classes)

尽管LLVM不进行很多的字符串处理,但还是有一些重要的APIs使用了字符串。两个重要的例子:Value类,包含指令、函数的名字等;StringMap类,在LLVM和Clang中广泛使用。

有一些通用类,它们需要接受一些嵌入了空字符的字符串。所以,它们不能简单地使用const char*, 或者使用const std::string&需要程序进行一些不必要的堆分配。取而代之,很多LLVM APIs使用const StringRef&或const Twine&来进行高效地传输字符串。

The StringRef class

StringRef数据类型表示对一个常量字符串的引用(一个字符数组或者一个长度),并支持std::string上可用一般操作,但是不需要堆分配。

可以隐式地使用一个null-terminated的C样式字符串(std::string)来构造,或者显式地使用字符指针和长度。例如,StringRef的find函数声明为:

而程序员可以采用下面的任何一种形式:

类似地,需要返回字符串的APIs会返回一个StringRef的实例。而它可以直接使用或者使用str成员函数转换成std::string.

应该会很少直接使用StringRef类,因为它包含了指向外部内存,而用它来存储对象并不是一直安全的(除非知道外部存储不会被释放)。

The Twine class

Twine类是APIs用来接受串联字符串的有效途径。例如,一个常见的LLVM范例是根据某个指令的名字加上后缀为新指令命名。

Twine类是轻量级并且高效的rope,它指向临时的(在栈上分配的)对象。Twine类的实例可以是作为字符串(C strings, std::string, StringRef)加法结果隐式构造的。Twine类推迟了字符串的实际连接操作,直到连接结果的确需求,而这时可以直接高效地将结果存入字符数组中。这避免了在构建字符串连接临时对象时进行的不必要堆分配。

因为StringRef, Twine对象指向外部内存,这些内存应该从不被直接存储或者提及。它们应该只在意图定义一个能有效地接受连接字符串的函数时才出现。

The DEBUG() macro and -debug option

有时可能需要添加一系列的调试输出以及其他代码到实际的主线工作代码中。当主线代码能正常工作后,需要移除这些调试代码,但是将来可能又需要它们(譬如解决)。

由于上述原因,不会删除调试输出代码,但又不希望它们总是产生冗余信息。一种标准的折衷办法就是注释掉它们,使之可以在将来需要的时候再激活它们。

"llvm/Support/Debug.h"中提供了一个名为DEBUG()的宏,这是一种更好的处理方法。基本上,可以将任意代码添加到DEBUG宏中,这段代码只会在'opt'(或其他程序)在命令行中传递了‘-debug’参数时才会执行。

然后这样使用:

使用DEBUG()宏代替自己酝酿的解决方案,可以为调试输出创建另一个命令行参数选项。

注:1.DEBUG()宏对优化的构建过程不可用,所以它们不会造成任何性能上的影响(也不会有任何副作用)。

      2.可以直接在gdb中进行开关,只需要在程序运行中“set DebugFlag=0”或者“set DebugFlag=1”。如果程序没有运行,可以使用-debug参数开始运行。 

Fine grained Debug info with DEBUG_TYPE and the -debug-only option

有时在打开-debug选项时,会产生过多的信息(譬如在调试代码生成器时)。如果需要对调试信息进行更精细的控制,可以定义DEBUG_TYPE宏和-debug-only选项。

然后这样使用:

注:1.实际使用中,应该只在文件的顶端设置DEBUG_TYPE,以为整个模块指定调试类型(如果在#include "llvm/Support/Debug.h"前设置,就不需要丑陋的#undef's)。

      2.因为没有保证名字不发生冲突的机制,所以应该使用比“foo”和“bar”更形象的名字。如果两个不同的模块中,使用了同样名字,这两个开关都会被开启。 

即使设定了DEBUG_TYPE宏,也可以使用DEBUG_WITH_TYPE,但只对特定语句有效。需要额外的第一个参数,该参数为使用的类型。

The Statistic class & -stats option

在"llvm/ADT/Statistic.h"文件中提供了名为statistic的类,该类用于记录LLVM编译器正在干什么以及各种优化的性能。这能帮助确定哪种优化使实际程序运行更快。

当对大程序进行处理时,会关注进行多少次特定的转换。当然可以人工观察,或者添加ad-hoc方法(指特为此功能实现的方法),但这都是很痛苦的事情,而且对大程序并没有多少用。使用Statistic类可以非常轻松地记录这种信息,并计算这些信息并以和其他执行方案统一的方法输出。

1.如下定义统计宏:

STATISTIC宏定义了一个静态变量,名字由第一个参数指定。而方案名称从DEBUG_TYPE宏而来,描述作为第二个参数。 定义的变量就如同无符号整形一般。

2.然后每进行一次转换,增加计数器:

 
3.使用"-stats"命令行参数输出收集到的统计信息:

4.对一个SPEC基准测试套的统计报告如下:

在这么多优化中,有输出统计信息的框架无疑是件好事。让方案符合这个框架,会使得它更易维护和使用。

Viewing graphs while debugging code

在LLVM中很多重要的数据结构都是图,例如由LLVM BasicBlock构成的控制流图,由MachineBasicBlock构成的控制流图,以及指令选择有向无环图。很多情况下,能即时显示这些图对调试编译器的有很大地帮助。

LLVM提供了几种在debug build下能实现上述功能的callback。例如调用了Function::viewCFG()方法,当前的LLVM工具将会弹出一个包含该函数控制流图的窗口,类似的方法还有Function::viewCFGOnly()(与前者相比,没有指令)、MachineFunction::viewCFG()、MachineFunction::viewCFGOnly()、SelectionDAG::viewGraph()。在GDB中,常用到类似于call DAG.viewGraph()的命令来弹出一个窗口。或者,可以在希望调试的代码位置加入这些调用。

让这个功能正常运转,需要一些配置。在X11的Unix系统上,安装graphviz工具包,并确保'dot'和'gv'在path环境变量中;在Mac OS/X上,下载并安装相应的Graphviz程序,并添加安装路径到环境变量中。这些工作完成后,重新运行LLVM configure脚本并重新build LLVM以激活功能。

SelectionDAG经过扩展,更易于定位复杂大图中感兴趣的结点。在gdb中,如果使用call DAG.setGraphColor(node, "color"),那么之后的call DAG.viewGraph将会以指定颜色高亮显式结点。通过call DAG.SetGraphAttrs(node, "attributes")还可以进行更加复杂的属性设置。还可以用call DAG.clearGraphAttrs()清除重置所有的图属性。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值