[转帖]什么是 LLVM?Swift, Rust, Clang 等语言背后的支持

要了解用于以编程方式生成机器原生代码的编译器框架是如何让新语言的推出以及对现有的语言进行加强比以往更加容易了。html

https://www.oschina.net/translate/what-is-llvm-the-power-behind-swift-rust-clang-and-more?print

debian 好像也在进行 RISC-V 的迁移

可是由于LLVM暂时不知此后 不少包 没迁移完. 

感受东西好多的说.

 

新的语言,还有对现有语言的提高,在整个编程环境中正大行其道。Mozilla 的 RustApple 的 SwiftJetbrains 的 Kotlin,以及许多其它的语言都给开发者在速度、安全性、便利性、可移植性还有能力这些方面提供了新的选择。java

为何如今正当时呢?一个大因素就是那些用来构建语言的新工具,特别是编译器。它们中首当其冲就是 LLVM (底层虚拟机 Low-Level Virtual Machine),这是一个开源项目,最开始做为伊利诺伊州大学的一个研究项目由 Swift 语言的创始人 Chris Lattner 进行开发。node

LLVM 使建立新语言变得更加容易,同时也能够加强现有语言的开发。它提供了一些工具,用于自动执行语言建立任务中最不讨人喜欢的部分:建立一个编译器,将输出的代码移植到多个平台和架构,编写代码来处理常见的语言隐喻,好比异常。它的自由受权意味着它能够自由地做为软件组件重用或做为服务部署。python

使用 LLVM 的语言名册中有许多熟悉的名字。苹果的 Swift 语言使用 LLVM 做为它的编译器框架,而 Rust 则将 LLVM 做为其工具链的核心组件。并且,许多编译器都有一个 LLVM 版本,如 Clang、C/C++ 编译器(这个名称叫作“C-lang”),它自己就是一个与 LLVM 紧密相连的项目。而 Kotlin,名义上是一种 JVM 语言,正在开发一种名为 Kotlin Native 的语言版本,它使用 LLVM 来编译成机器原生代码。git

LLVM 定义

在它的核心,LLVM 是一个以编程方式建立机器原生代码的库。开发人员使用该 API 以一种称为中间代理或 IR 的格式生成指令。而后 LLVM 能够将 IR 编译成一个独立的二进制文件,或者在另外一个程序(如语言解释器)的上下文中执行 JIT (just-in-time) 编译。github

LLVM 的 API 为开发在编程语言中发现的许多常见结构和模式提供了原始的方式。例如,几乎每种语言都有函数和全局变量的概念。LLVM 将函数和全局变量做为其 IR 中的标准元素,所以,你只需在乎 LLVM 的实现,并关注须要注意的语言部分,而不是花费时间和精力从新建立这些特定的轮子。npm

这是一个 LLVM 中间代理(IR)的例子。右边是一个简单的 C 程序;左边是由 Clang 编译器翻译成 LLVM IR 的代码。编程

LLVM:专为可移植性而生

关于 LLVM 的一个说法是它像常提到的 C 编程语言:C 语言有时候被认为是一种便携式、高级的汇编语言,由于它能够紧密地映射到系统硬件的结构,并且它已经被移植到几乎全部的系统架构。可是,C 语言只是做为一种可移植的汇编语言,是其工做方式的另外一种效果;这并非它的设计目标之一。swift

相比之下,LLVM 的 IR 是从一开始就设计为可移植的组件。它实现这种可移植性的一种方法是提供独立于任何特定机器架构的原语。例如,整数类型不局限于底层硬件的最大位宽度(例如 32 或 64 位),您能够根据须要使用尽量多的比特字节来建立基本的整数类型,好比 128 位整数。您也没必要担忧手工输出来匹配特定处理器的指令集;LLVM 也会为你处理这个问题。后端

若是你但愿看到 LLVM IR 的现场示例,请访问 ELLCC 项目网站,并尝试在浏览器中将 C 代码转换为 LLVM IR 的现场演示 Demo

编程语言中如何使用 LLVM

LLVM 最多见的用例是做为一种语言的预先(AOT ahead-of-time)编译器。但 LLVM 也能够用于即时编译。

用 LLVM 进行即时编译

有些状况下须要在运行时动态生成代码,而不是预先编译。例如,Julia 语言就是使用 JIT 编译代码,由于它须要快速运行,并经过 REPL(read-eval-print loop)或交互式提示与用户交互。.Net 和 Mono 能够选择经过 LLVM 后端方式编译为原生代码

Numba 是一个 Python 的数学加速包,JIT 将所选择的 Python 函数编译成机器码。它也能够预先编译使用 Numba 装饰器装饰的代码,可是(好比 Julia)Python 做为一种快速发展的解释性语言,使用 JIT 编译来产生这样的代码更好地补充了 Python 的交互式工做流,比 Python 的预先编译方式更好。

其余人正在尝试以非正统方式使用 LLVM 做为 JIT 编译方式,例如编译 PostgreSQL 查询,听说性能提升了五倍。


Numba 使用 LLVM 进行即时编译数字代码并加速其执行。JIT 加速过的 sum2d 函数的执行速度比常规 Python 代码快 139 倍。

使用 LLVM 进行自动代码优化

LLVM 不只将 IR 编译为原生机器码。你也能够直接以编程的方式在整个连接过程当中高度精细地优化代码。优化方式是至关积极主动的,可以实现包括内联函数在内,消除死代码(包括未使用的类型声明和函数参数)和展开循环这些事情。

这里再一次强调,LLVM 的力量让你没必要本身实现全部这一切。LLVM 能够为您处理它们,您也能够根据须要直接禁用。例如,若是你想要一些更小的二进制代码,那么你可让你的编译器告诉 LLVM 禁用循环展开。

使用 LLVM 的领域特定语言

LLVM 已被用于生成多种通用语言的编译器,但它也可用于生成高度垂直或排他性问题域的语言。从某种意义上说,这就是 LLVM 最闪光的地方,由于它在创造这样一类语言方面消除了诸多苦差事,并使其表现良好。

例如,Emscripten 项目采用 LLVM IR 代码并将其转换为 JavaScript,理论上支持使用 LLVM 做为后端的任何语言导出可在浏览器中运行的代码。长期规划是支持基于 LLVM 的后端并可以生成 WebAssembly 代码,Emscripten 是 LLVM 灵活性的一个很好的例子。

LLVM 能够被使用的另外一种方法是将特定领域的扩展添加到现有语言。Nvidia 使用 LLVM 建立了 Nvidia CUDA 编译器,该编译器容许语言为 CUDA 添加原生支持,它是做为你生成的原生代码的一部分编译的,而不是经过附带的库进行调用的。

在不一样语言中使用 LLVM

使用 LLVM 的典型方式是经过你所熟悉的语言来编写代码(固然也要有支持 LLVM 的库)。

两种常见的可选语言是 C 和 C++。许多 LLVM 开发者会由于如下的缘由而默认选择其中的一个:

  • LLVM 自己是用 C++ 编写的

  • LLVM 的 API 以 C 和 C++ 版本提供

  • 大量的语言开发每每会以 C/C++ 做为一个基础

不过,这两种语言并非惟一的选择。许多语言均可以原生调用 C 语言库,因此理论上能够用任何这样的语言进行 LLVM 开发。但须要有一个实际的语言库能够很好地封装 LLVM API。幸运的是,许多语言和语言运行时都有这样的库,包括 C#/.Net/MonoRustHaskellOCAMLNode.jsGo, 和 Python

须要注意的是,一些与 LLVM 的语言绑定可能不完整。以 Python 为例,有不少种绑定选择,但每一个选项的完整性和实用性各不相同:

  • LLVM 项目维护着本身的一套到 LLVM 的 C API 的绑定,可是目前他们没有继续维护。

  • llvmpy 在 2015 年后就没有进行维护了 —— 这对于任何软件项目都是不利的,在使用 LLVM 时更是如此,由于每一个版本的 LLVM 都有一些变化。

  • 由建立 Numba 的团队开发的 llvmlite 已经成为当前在 Python 中的 LLVM 的竞争者。它只实现了 LLVM 功能的一个子集,正如 Numba 项目的需求所规定的那样。可是这个子集知足了绝大多数 LLVM 用户所需。

  • llvmcpy 旨在为 C 库带来最新的 Python 绑定,它以自动化的方式保持更新,并使用 Python 的习惯用法来访问它们。llvmcpy 还处于早期阶段,可是已经能够用 LLVM API 作一些基本的工做。

若是你对如何使用 LLVM 库构建语言感兴趣,不妨看看 LLVM 的建立者撰写的使用 C++ 或 OCAML 语言的教程,它将一步步指导你建立一种简单的名为 Kaleidoscope 的语言。它还被移植到其余语言之上:

  • Haskell:参考原始教程的直接移植。

  • Python在此网站的教程和原始版本很是相近,而另外一个版本则是用交互式命令行进行更为雄心勃勃的重写。这两种版本都使用 llvmlite 做为到 LLVM 的绑定。

  • Rust 和 Swift:不可避免地,咱们不得不将该教程移植到这两种语言之上,它们都是由 LLVM 自身帮助使其诞生的。

最后,这个教程也有其余国家语言版本的。这里有中文版,分别是使用原始的 C++ 和 Python 版本。

LLVM 还没有实现的功能

了解 LLVM 能够实现的功能的同时,有必要知道 LLVM 目前还没有实现的功能。

例如,LLVM 不解析语言的语法。由于目前已经有许多工具实现这个功能,好比 lex/yaccflex/bison,以及ANTLR。解析语法就意味着必须从编译中解耦出来,难怪 LLVM 并无涉及这个领域。

LLVM 也不会直接干涉到开发语言的软件文化,好比安装编译器的二进制文件、如何在安装中管理软件包、升级工具链 —— 这些都须要开发者本身去实现。

最后也是最重要的一点是,LLVM尚未对部分通用语言成分给出原语。许多语言都具备某种垃圾回收的内存管理方式,或者是做为管理内存的主要方式,或者做为对 RAII ( C++ 底层实现的自动垃圾回收,表面使用 Rust 语法)等策略的附属方式。LLVM 并不会给你一个垃圾回收机制,可是它提供了实现垃圾回收的工具,它容许在代码中使用元数据标记,让编写垃圾回收器变得更加容易。

尽管如此,可是 LLVM 将来还有有可能添加原生的机制来实现垃圾回收机制。LLVM 正在快速发展中,大概 6 个月就会有一次大版本的更新。因为当前的许多语言都使用 LLVM 做为开发的核心,所以 LLVM 的迭代速度只会更快而不会放慢。

相关文章
相关标签/搜索