什么是 LLVM?Swift, Rust, Clang 等语言背后的原力

LLVM作为开源项目,为编程语言的创建和优化提供了强大的支持。它不仅被Swift、Rust等现代语言采纳,还被广泛应用于即时编译和代码优化。LLVM通过提供灵活的中间表示语言,简化了跨平台编译的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

点击上方“iOS开发”,选择“置顶公众号”

关键时刻,第一时间送达!


创造新的语言,变着花样的提升现有语言的能力,这在整个编程界正风行。Mozilla 的 RustApple 的 SwiftJetbrains 的 Kotlin,以及许多其它的语言都给开发者在速度、安全性、便利性、可移植性还有能力这些方面提供了新的选择。


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


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


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


LLVM 定义


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


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



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


LLVM:专为可移植性而生


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


相比之下,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 开发者会因为以下的原因而默认选择其中的一个:


  1. LLVM 本身是用 C++ 编写的

  2. LLVM 的 API 以 C 和 C++ 版本提供

  3. 大量的语言开发往往会以 C/C++ 作为一个基础


不过,这两种语言并不是唯一的选择。许多语言都可以原生调用 C 语言库,所以理论上可以用任何这样的语言进行 LLVM 开发。但需要有一个实际的语言库可以很好地封装 LLVM API。幸运的是,许多语言和语言运行时都有这样的库,包括 C#/.Net/Mono, Rust, Haskell, OCAML, Node.js, Go, 和 Python


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


  1. LLVM 项目维护着自己的一套到 LLVM 的 C API 的绑定,但是目前他们没有继续维护。

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

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

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


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


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

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

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


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


LLVM 尚未实现的功能


了解 LLVM 可以实现的功能的同时,有必要知道 LLVM 目前尚未实现的功能。


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


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


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


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


  • 作者: 程序师 

  • 链接:http://www.techug.com/post/llvm-the-power-behind-swift-rust-clang-and-more.html

  • iOS开发整理发布,转载请联系作者授权

【点击成为Java大神】

<think>好的,用户现在问的是在有clang+llvm文件夹的情况下如何安装LLVM。首先,我需要确认用户的具体情况。他们已经有一个clang+llvm的文件夹,这可能意味着他们已经下载了预编译的二进制包或者从源码克隆了仓库。需要明确的是,用户是否已经下载了这些文件但不知道如何安装,或者是否想从现有的文件夹中进行安装。 接下来,用户的真实需求可能是什么?他们可能遇到了安装LLVM的问题,或者在尝试编译或开发需要LLVM的工具。他们可能已经尝试过某些步骤但遇到了问题,比如路径配置不正确,或者没有正确设置环境变量。此外,用户可能是在Linux环境下工作,特别是考虑到之前的问题涉及Ubuntu虚拟机。 需要区分预编译二进制文件和源码安装两种情况。预编译的二进制文件通常只需要解压并添加到PATH中即可,而源码安装则需要使用CMake进行构建和安装。用户提到的“clang+llvm文件夹”可能是指预编译的版本,但需要确认。 可能的步骤包括:检查文件夹内容,确定是预编译的二进制文件还是源码;添加二进制路径到环境变量;安装依赖项(如果是源码);构建并安装(如果是源码)。还需要考虑权限问题,是否需要sudo,以及如何验证安装是否成功。 另外,用户可能遇到的问题包括路径未正确配置,导致系统找不到llvm的命令,或者动态链接库的问题。需要指导用户如何测试安装是否成功,比如运行clang --version或llc --version,以及如何解决可能的共享库缺失问题。 最后,需要给出清晰的步骤,分情况讨论,确保用户根据他们的具体情况选择合适的安装方法。同时,提醒用户注意权限和环境变量的配置,避免常见错误。</think>--- ### 一、明确安装场景 若您已有一个包含预编译好的 **Clang+LLVM 二进制文件**的文件夹(如通过官网下载的`.tar.xz`解压后的目录),或从源码编译生成的`build`目录,安装步骤如下: --- ### 二、预编译二进制直接安装(推荐) 适用于从[LLVM官网](https://releases.llvm.org/)下载的预编译包(例如文件名类似`clang+llvm-17.0.6-x86_64-linux-gnu-ubuntu-22.04.tar.xz`)。 #### 1. **解压并定位目录** ```bash tar -xvf clang+llvm-*.tar.xz # 解压(若未完成) cd clang+llvm-*/bin # 进入二进制目录 ``` #### 2. **验证可执行性** ```bash ./clang --version # 应输出Clang版本 ./llc --version # 应输出LLVM版本 ``` #### 3. **全局安装(两种方式)** - **方式1:添加环境变量(临时生效)** ```bash export PATH=/path/to/clang+llvm/bin:$PATH export LD_LIBRARY_PATH=/path/to/clang+llvm/lib:$LD_LIBRARY_PATH ``` 将此写入`~/.bashrc`或`~/.zshrc`可永久生效。 - **方式2:复制到系统目录(需root权限)** ```bash sudo cp -r /path/to/clang+llvm/* /usr/local/ # 覆盖系统默认LLVM ``` --- ### 三、从源码构建安装(若需自定义配置) 若文件夹是通过`git clone`获取的源码(如`llvm-project`仓库),需编译安装。 #### 1. **安装依赖项** ```bash sudo apt install cmake ninja-build build-essential libz-dev ``` #### 2. **创建构建目录** ```bash mkdir build && cd build ``` #### 3. **配置CMake(关键参数)** ```bash cmake -G Ninja ../llvm \ -DCMAKE_BUILD_TYPE=Release \ -DLLVM_ENABLE_PROJECTS="clang;lld" \ -DCMAKE_INSTALL_PREFIX=/usr/local/llvm-custom ``` - `-DCMAKE_INSTALL_PREFIX`:指定安装路径(避免覆盖系统LLVM) #### 4. **编译与安装** ```bash ninja -j$(nproc) # 多线程编译(根据CPU核心数调整) sudo ninja install # 安装到指定目录 ``` #### 5. **配置环境变量** ```bash echo 'export PATH=/usr/local/llvm-custom/bin:$PATH' >> ~/.bashrc source ~/.bashrc ``` --- ### 四、验证安装结果 ```bash clang --version # 检查Clang llvm-config --version # 检查LLVM核心版本 ``` --- ### 五、常见问题解决 1. **动态库缺失** 若运行时提示`libLLVM-XX.so not found`: ```bash sudo ldconfig /path/to/clang+llvm/lib # 更新动态库缓存 ``` 2. **多版本共存** 通过`update-alternatives`管理多个LLVM版本: ```bash sudo update-alternatives --install /usr/bin/clang clang /usr/local/llvm-custom/bin/clang 100 ``` --- ### 流程图总结 $$ \text{LLVM安装路径} \begin{cases} 1.\ \text{预编译二进制} \begin{cases} \text{添加环境变量} \\ \text{或复制到系统目录} \end{cases} \\ 2.\ \text{源码编译安装} \begin{cases} \text{CMake配置} \\ \text{Ninja编译} \\ \text{指定安装路径} \end{cases} \end{cases} $$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值