偶然看到有人用 LLVM 配套工具生成的 CFG 图,就想看看怎么做出来的。但是吧,LLVM 就在最近一年左右的时间内修改了生成方法,导致网络上国内外很多人记录的方法无法使用,所以写本文记录一下。

假设这里有一个test.c源代码文件,内容是计算矩阵(代码就不贴了,你随便找个程序就行)。

获取 LLVM IR

首先需要使用clang和选项-emit-llvm生成所需的 LLVM IR 文件,需要注意的是,不同优化等级生成的图像是不一样的,所以要注意是否使用-O3等选项(最后会放出对比)

使用-S选项

这里推荐使用-S选项使得编译流程停止在汇编器之前,因为并不需要走完整个编译流程。

clang -emit-llvm -S test.c

这时候会产生一个test.ll的文件,存放的是 LLVM IR。

使用-c选项

有些人使用的是-c(操作到对象文件这一步),

clang -emit-llvm -c test.c

这时候产生的文件是test.bcbc是 bitcode(位码)的意思。

.ll.bc文件有什么区别?

我在《clang到底是什么?gcc和clang到底有什么区别?》中提到过,LLVM 代码和位码是等价的(LLVM代码就是编译器的中间表达 LLVM IR):

请添加图片描述

所以这二者本质上没有区别,都可以使用。

至于有些人写的是同时使用了-S-c,这实在是多此一举了。

关于编译流程和 LLVM IR 的详细内容还请看我的另外两篇博客:
clang到底是什么?gcc和clang到底有什么区别?
使用gcc展示完整的编译过程(gcc预处理模式、编译模式、汇编模式、连接模式)

将 LLVM IR 转换成图像描述语言

然后使用 LLVM 配套工具opt将前面获取的 LLVM IR 转换成你需要的某一种图的图像描述语言。

这里演示的是生成 CFG,方法如下:

#如果是.ll
opt -passes=dot-cfg test.ll
#如果是.bc
opt -passes=dot-cfg test.bc

由于产生的文件是隐藏的,使用ls -a可以看到一个名为.main.dot的文件,这里面存放的就是图像描述语言(转换时,终端输出信息中会显示Writing '.main.dot'...,你的可能会不一样):

请添加图片描述

如果你想生成其他种类的图,那么可以在LLVM’s Analysis and Transform Passes中找到支持的图(调用图、依赖图等等),然后将上面命令中的dot-cfg替换成对应的字符串即可。

请添加图片描述

错误:The opt -passname syntax for the new pass manager is not supported

这里需要注意一点,老的一些记录中,这部分命令为opt -dot-cfg test.bc,但是如果你现在使用这个命令,则会出现以下提示:

% opt -dot-cfg test.bc   
The `opt -passname` syntax for the new pass manager is not supported, please use `opt -passes=<pipeline>` (or the `-p` alias for a more concise version).
See https://llvm.org/docs/NewPassManager.html#invoking-opt for more details on the pass pipeline syntax.

这个方法已经不支持了,我们需要使用opt -passes=<pipeline>这种格式来进行转换,而这里的<pipeline>可以在https://llvm.org/docs/Passes.html中看到。比如说这里现在为opt -passes=dot-cfg,而不是opt -dot-cfg

将图像描述语言转换成PNG等格式的图像

为了方便,大部分人都需要将其转换成 PNG 等格式方便阅读(支持很多格式,但是就不支持 JPEG 格式),方法也很简单:

dot -Tpng -o main.png .main.dot

然后就可以看到这样一张图了:

请添加图片描述

如果使用-O3进行优化,那么这里的图像如下:

请添加图片描述

希望能帮到有需要的人~

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐