本文主要内容是演示如何在Swift中使用LLVM,其包含了如下四个要点:
LLVM的准备
要获取最新版本的LLVM,可以通过中央的SVN repository或官方git镜像进行获取。我建议使用后者,因为速度更快。
对于本文的实例,我会把所有相关文件存放在主目录下的一个目录中:
LLVM使用CMake来生成程序。编译程序前我们在LLVM目录的同个级别下创建一个独立的目录,并生成编译规定:
CMake可以生成不同的输出:makefiles, Xcode羡慕, VisualStudio方案等等。如果没有指定别的生成系统,CMake将使用默认的GNU/Make生成系统。
模块化是LLVM的重要优点之一,它里面含有很多“小型”库,部分库会用在本文的示例程序中。如果你是一个LLVM新手,我建议可以借助llm-config工具来进行学习。
可用如下方式来运行llvm-config:
注意:-j4表示开启多核环境,同时运行4个作业 llvm-config的可用选项还包括了,库路径定位,头文件路径定位,连接标记匹配等等。
也不妨直接使用无选项llvm-config来看看它的运行结果如何。
编译器也是很重要的,我们来看看如何运行它:
Hello LLVM
上述的准备工作做好后,便可以进行程序编写了。
如果要检查整个编译环境,可以尝试创建一个空模块然后释放它:
此外,编译该示例程序还需要向编译器传入标识符,以便指定具体的SDK,指定连接标识,设置头部文件搜索路径等。
事实上,指定正确的连接标识和库路径是很简单的,这得益于llvm-config:
至于头部文件和额外参数的实现则更有技巧。正常情况下,在clang中,我们会使用llvm-config - -cflags,注意这方式是不能用在swift编译器中的。让我们检视下- -clfags:
其输出包含了几个参数,我们可以忽略全部的参数除了头部文件搜索路径(-I …)以及宏定义(-D_...)。问题是swift 编译器不能像clang那样处理宏定义,尽管我们可以使用-Xcc选项来绕过它以clang驱动方式进行:
如果直接运行它,我们将会看到错误警示:连接器会反馈说不能从ncurses和STD C++库里找到符号。不用担心,正确的写法应该是:
现在我们执行程序了:
这看起来未免有点枯燥,但我们有了新的发现:我们可以从Swift里调用LLVM C API!
接下来尝试创建简单的sum加法函数,它对两个输入参数进行现加并返回结果。以下是等价的C程序:
要创建一个函数,我们需要指定所有用到的类型:返回值类型和参数类型。
多个参数的类型必须以数组方式传递。C API在这方面是强项:
生成它:
执行:
原型就绪后,接着就是函数主体的编写。
代码原子是基本区块。函数和循环体,条件分支(if/else,switch/case等)这些都是基本的区块。我们的函数里也有类似的区块:
该区块可以使用builder来完成。第一步是获得对函数参数的访问;第二步是使用temp变量来存储结果。最后一步是return语句。
把该代码放置在sumFunction声明的后面,但是要在LLVMDumpModule之前。完整的示例代码请在文章末端进行查阅。
编译并运行,你将看到如下的函数体:
最后也是最有趣的环节来了—如何运行这些代码?
LLVM C API 提供了一些执行引擎:如MCJIT以及Interpreter,我们使用的是后者。
下面让我们创建一个执行引擎:
最后,让我们执行整个加法运算。这里我们使用与函数类型类似的技巧—使用UnsafeMutablePointer来进行API指向。请记得在最后进行相关的内存销毁。
运算结果应该是:
完整的代码示例:
小结
从Swift中使用LLVM是件很美妙的事情。它给了我们一个非常好的机会—在Swift中借助LLVM来编写语言。C API虽然有一定限制,但是以这为起点也绰绰有余了。
英文出自: lowlevelbits