其他分享
首页 > 其他分享> > LLVM 后端实践笔记

LLVM 后端实践笔记

作者:互联网

该系列笔记是我对之前学过的 Tutorial LLVM Backend Cpu0 教程的填充完善与版本升级,首发于我的知乎专栏:https://www.zhihu.com/column/c_1250484713606819840
这是本教程的序言章节,其他章节请访问最后一节中链接访问。
本笔记对应的源码文件链接:https://github.com/P2Tree/LLVM_for_cpu0

已经上传大部分内容,剩余章节正在编写中。

0.1 动机

编译器是一个很复杂的软件系统,在这个系统中,包含的计算机理论非常之多,也和很多数学理论有直接关系。在学校学习编译原理课程中,大多只会讲解一些编译器前端的知识,既抽象、又过于理论化,让很多同学学习起来云里雾里,最后也不清楚编译原理是干什么的,对于实际工作中使用编译器、开发编译器或利用编译器原理完成相关工作来说,会感觉书到用时方恨少。

然而,当遇到实践问题时,只拿着编译原理教材,却依然不知道该怎么做,编译原理的复杂性,使理论和实践之间的鸿沟过宽。而不同的编译器工程师可能面对相关性不大的工作内容,有面向前端的、有面向后端的,也有专门做优化的,具体展开又有很多细分。

一部分商用编译器的代码是开源的,比如gcc 和 clang/LLVM,如果拿商用编译器的代码来学习,但没有掌握学习方法,会感觉跳入泥潭,花费很大精力和时间却无法快速上手。商用编译器的文档和教程非常少,对于国内开发者来说,还有英文阅读水平的阻碍,经常需要一边阅读英文文档,一边翻译编译原理教材,正所谓,每句话都能看懂,连起来还是没弄清什么道理,学习难度也很大。

LLVM 是一套编译器架构,它的设计非常灵活和清晰,相比 gcc 来说,学习会更容易一些。另一个原因是,LLVM 的开源协议(BSD)比较友好,通常是指基于 LLVM 来开发的编译器可以闭源自己的代码,而 gcc 是 GPL 协议,要求必须开源,商业化来讲,LLVM 更有优势。还有个优势是,LLVM 的可重用性强,即使不做编译器,也可以把其中一部分拿出来用在自己的软件中,比如静态分析,或自定义的 DSL 等。

我在学习 LLVM 的过程中,也苦于找不到适合初学者的学习材料,硬着头皮把 LLVM 官网上的几篇超级长的文章读完,并翻译了一部分。然而却发现官方文档很多也不全面,这让我意识到,我该写写代码了 (工作中也会改代码,但不会涉及整个模块重写的需求)。偶然中我遇到了一个教程,由台湾的陳鍾樞(https://github.com/Jonathan2251)编写的《Tutorial: Creating an LLVM Backend for the Cpu0 Architecture》,如获至宝。学习完毕后,颇有收获。书中讲解地非常细致,虽然并没有涉及很复杂的功能,但我认为作为入门材料足矣。

之后,我便有一个想法,LLVM 的中文学习材料中,还没有这样一个 Tutorial,能带着不熟悉 LLVM 后端的朋友一步步入门,为什么我不可以写一个呢?他的教程基于 LLVM 3.1 完成,虽然 3.1 是挺经典的一个版本,但现在都已经 10.0 了,我也刚好可以更新一下(请允许我有点私心,因为工作中用的 8.0.0,所以我的这个教程基于 8.0.0 完成)。于是乎,我一边重新读《Tutorial: Creating an LLVM Backend for the Cpu0 Architecture》这个教程,一边把我的中文版写出来,我打算篇章结构和思路基本不变,毕竟他是花很大精力设计出来的,我便也没必要在这些方面多费心思,直接用优秀的设计即可。在此也对他的工作表示真诚感谢。(注:其中部分内容直接翻译自原文,同时也会按更易理解的方式做一些修改,并排掉一些 bugs)

另外,他的个人主页上还有其他文章,有关于完成一个前端的教程,我还没有看过,相信也非常优秀,如果你的英语阅读能力过关,编译原理知识也扎实,也可参考他的文章(我自己时间有限,很可能写不出他那么丰富的知识)。

0.2 目标读者

本文的目标读者是之前没用过 LLVM 做过开发,但已经有一些编译原理基础知识的朋友。如果你看到 LLVM 的代码后,不知道该怎么写一个后端,搞不懂后端这些文件都是干什么的,应该在哪里写代码,这个教程可能会帮到你。

非常熟悉 LLVM 框架和编译器技术的大佬们,也请多多指教,我也在一边学习一边完善,欢迎交流技术心得。

0.3 前言

由于 LLVM 出色的模块化设计,在 LLVM 的基础上新增一个后端,会非常容易。跟着这篇教程完成整个流程,你就能掌握如何把自己的后端部署在 LLVM 上并为它增加一些功能。

本教程使用 Cpu0 作为硬件的例子,来构建能适配它的编译器后端。Cpu0 是一个非常简单的 RISC 架构处理器,通常是作为学 IC 的同学的练手习题,所以,即使你没有接触过 Cpu0,只要了解一点 RISC 架构处理器(或者 Mips 处理器也成,Cpu0 很多设计依据 Mips 完成),也很容易接受这个处理器;如果你很熟悉它,那更好(其实我并不太熟悉,只要了解地足够详细,并不阻碍编译器后端的设计)。后边我会用一小节的内容介绍一下这个处理器。

《Tutorial: Creating an LLVM Backend for the Cpu0 Architecture》教程中的配套代码中,提供了一套 Cpu0 Simulator 的 Verilog 实现代码,我们只需要把这套代码运行起来,从而不需要你真的有一个 Cpu0 的硬件(实际上也不可能有)。我们最终的测试,是通过我们的编译器,将程序翻译成 Cpu0 支持的 HEX 格式文件,并输入到 Simulator 中运行,最后检查运行结果(教程中也会介绍如何生成 bin 文件,即使它不能运行,但机理是一样的)。

前端语言采用 C 语言,《Tutorial: Creating an LLVM Backend for the Cpu0 Architecture》教程中的目标是 C++ 语言,并且其中一章专门介绍如何支持 C++ 特性,我暂时没打算去做,如果将来有时间,我会再更新。

虽然不支持 C++,但 LLVM 是基于 C++ 语言开发的,所以对于 C++ 语言要足够熟悉。另外,面向对象的思想一定要有,除了 LLVM 后端结构中强表现出继承、多态等特性,还因为会涉及到 TableGen(LLVM 后端中很重要的一种面向对象的 DSL),它也可以看做是一种面向对象语言。

我计划主要讲解代码实现,对于编译器的原理和 LLVM 架构比较复杂的设计,我不会过多涉及,你依然需要搭配 LLVM 官方的一些文档来学习。学习 LLVM 的一个建议是,不要拘泥于与自己无关的功能,就像你在 Linux 上写个串口驱动,便不需要了解太多操作系统实现的东西,适当忽略无关细节,可以让学习更加顺畅。

我会把基于 LLVM 8.0.0 下的 Cpu0 的后端实现代码上传到网络(https://github.com/P2Tree/LLVM_for_cpu0),与教程配合食用极佳。每一章我都会生成一份快照,相对路径一致地存到一个单独路径下(我考虑过使用 git tag,但因为提交比较琐碎,文档也会在未来经常更新,所以 tag 并不完全表示某一章节的结束,不过我还是打入了 tag 供参考)。

我的开发机是 Macbook,系统是 Mojave (10.14.6),编译器版本是系统原生的 clang++ 10.0.0。和我的系统不一致,可能会遇到一些问题,但我估摸着问题不会很大,遇到问题可自行 Google 解决。

0.4 学习 LLVM 知识

如果并不了解 LLVM,那便需要先看一下有关于 LLVM 的入门知识,这可能需要花费一下午的时间,但这真的很值。

这一小节我留着,因为它真的很重要。

入门看这篇文章:http://llvm.org/docs/GettingStarted.html#id8,里边的细节不用记,将来还会回头查。

也可以看我之前写的入门教程:https://zhuanlan.zhihu.com/p/140462815

这篇文章要看一下:http://www.aosabook.org/en/llvm.html,有了 Getting Started 的基础后,大概浏览一下,几个重点要看,一个是三段式结构,一个是 LLVM IR,一个是 TableGen

后端流程的东西也要懂一点,LLVM 的后端主要是:

了解后端流程可以详细看一下这篇官方文档:https://llvm.org/docs/CodeGenerator.html,也可以看我写的译文:https://zhuanlan.zhihu.com/p/301653651。推荐分章节多次阅读,而不是一口气读完,虽然前边的内容简单,但后边的内容会突然很深入,并且讲解并不完善,需要配合去翻代码。

在这个教程里边不会展开讨论 LLVM 的基础知识,我会尽可能的不涉及太多不相关的知识,所以,如果依然看不懂,这一节的推荐网址还是要好好读一读。

0.5 教程章节介绍

标签:教程,LLVM,代码,实践,笔记,编译器,Cpu0,https
来源: https://blog.csdn.net/SiberiaBear/article/details/118713332