<返回更多

WebAssembly 火了!背后的 LLVM 功不可没!

2023-08-21  今日头条  高级前端进阶
加入收藏

前面诸多文章中已经重点表达了自己对 WebAssembly 的浓厚兴趣,但是要深入学习 WebAssembly 就不得不说其背后的 LLVM ,这就是本文的主角。话不多说,直接开始!

1.什么是LLVM

LLVM,全称为 Low Level Virtual machine, 是一个用于构建高度优化的编译器(compilers)、优化器(optimizers)和运行时(run-time environments)环境的工具包。

类 C 语言使用 Clang 前端,组件使用 LLVM 将 C、C++、Objective-C 和 Objective-C++ 代码编译成 LLVM 位码(bitcode),最终编译成目标文件。

Clang:是一个C语言、C++、Objective-C语言的轻量级编译器,源代码通过BSD协议发布。Clang支持普通lambda表达式、返回类型的简化处理等诸多特性。

Image from Jacopo Mangiavacchi via Medium

LLVM 的核心是一个用于以编程方式创建机器本机代码的库。 开发人员使用 API 以称为中间表示或 LLVM IR (LLVM Intermediate Representation)的格式生成指令,然后 LLVM 可以将 IR 编译成独立的二进制文件或对代码执行 JIT(Just-in-Time)编译以在另一个程序的上下文中运行,例如:该语言的解释器或运行时。

LLVM 标志是一个双足飞龙、龙具有力量、速度和智慧的内涵。

LLVM 的 API 为开发编程语言中的许多常见结构和模式提供了原语。 例如,几乎每种语言都有函数和全局变量的概念,而且许多语言都有协程和 C 外部函数接口。 LLVM 在其 IR 中将函数和全局变量作为标准元素,并具有创建协程和与 C 库接口的能力。

 

比如上面的示例中是一个LLVM 的中间表示 (IR) 的示例。右边是一个简单的C程序,左边是由 Clang 编译器翻译成 LLVM IR 的相同代码

目前 LLVM 在 Github 上有 18.8K 的 star、7.1k 的 fork、2800+的代码贡献者。

2.LLVM:专为可移植性而设计

要理解 LLVM,可与 C 语言类比。C 被描述为一种可移植的高级汇编语言,因为它具有可以紧密映射到系统硬件的结构,并且几乎已被成功移植到各个系统的架构。 但是 C 作为一种可移植的汇编语言并非为特定目的而设计

相比之下,LLVM 的 IR 从一开始就被设计成一个可移植的组件,即通过提供独立于任何特定机器体系结构的原语

整数类型不限于底层硬件的最大位宽(例如 32 位或 64 位)。 开发者可以根据需要使用尽可能多的位来创建原始整数类型,如128 位整数,而不必担心制作输出以匹配特定处理器的指令集, LLVM 默认实现。

LLVM 的架构设计使其更容易支持现在和未来的各种硬件。 例如,IBM 最近贡献代码以支持其 z/OS、linux on Power、以及用于 LLVM 的 C、C++ 和 Fortran 项目的 AIX 架构。

3.编程语言如何使用 LLVM方式

LLVM 最常见的用例是作为语言的提前编译器(AOT,即Ahead-of-Time)。 例如,Clang 提前将 C 和 C++ 编译为本机二进制文件。

3.1 使用 LLVM 进行即时编译

有些情况需要在运行时动态生成代码,而不是提前编译。 例如,Julia 语言对其代码进行 JIT 编译,因为需要快速运行并通过 REP或交互式命令与用户交互。

Numba 是用于 Python/ target=_blank class=infotextkey>Python 的数学加速包,JIT 将选定的 Python 函数即时编译为机器代码。部分开发者正在尝试将 LLVM 用作 JIT 的新方法,例如:编译 PostgreSQL 查询,从而将性能提高五倍。

 

如上图:Numba 使用 LLVM 即时编译数字代码并加速其执行。 JIT 加速的 sum2d 函数完成其执行速度比常规 Python 代码快约 139 倍。

3.2 使用 LLVM 自动优化代码

LLVM 不只是将 IR(Intermediate Representation) 编译为本机代码。 开发者还可以通过编程方式指示它以高度粒度优化代码,一直到链接过程。 优化可能非常激进,包括内联函数、消除无用代码(包括未使用的类型声明和函数参数)以及展开循环等等。

LLVM的强大之处在于不必开发者关心,LLVM 可以自动处理优化,或者根据开发者指令关闭特定优化等。

3.3 具有 LLVM 的领域特定语言

LLVM 已被用于为许多通用语言生成编译器,也可用于生成高度垂直或专用于问题域的语言。这是 LLVM 最值得称赞的地方,它降低了创建语言的难度,而且表现良好。

Emscripten 项目采用 LLVM IR 代码并将其转换为 JAVAScript,理论上允许任何具有 LLVM 后端的语言导出可在浏览器中运行的代码。

使用 LLVM 的另一种场景是向现有语言添加特定领域的扩展。 Nvidia 使用 LLVM 创建 Nvidia CUDA 编译器,允许语言添加对 CUDA 的本地支持,将其编译为正在生成的本地代码的一部分(更快),而不是通过它附带的库调用(更慢)。

4.使用各种语言的 LLVM

使用 LLVM 的典型方法是使用开发者熟悉的语言编写代码(当然,它支持 LLVM 的库)。

两种常见的语言选择是 C 和 C++。 许多 LLVM 开发人员默认使用这两者之一,原因如下:

尽管如此,这两种语言并不是唯一的选择。 许多语言都可以本地调用 C 库,因此理论上可以使用任何此类语言执行 LLVM 开发。目前许多语言、运行时都有这样的库,包括 C#/.NET/Mono、Rust、Haskell、OCAML、Node.js、Go 和 Python等等。

需要注意的是,一些与 LLVM 语言绑定可能不如其他语言那么完整。 例如,对于 Python,有很多选择,但每个选择的完整性和实用性各不相同。

如果对如何使用 LLVM 库来构建语言有兴趣,LLVM 有一个教程,使用 C++ 或 OCAML,它会引导你创建一种称为 Kaleidoscope 的简单语言。

5.LLVM 不做什么

LLVM 不解析语言的语法, 许多工具已经可以完成这项工作,例如: lex/yacc、flex/bison、Lark 和 ANTLR,所以 LLVM是解析与编译分离的。

有 LLVM 不提供原语的语言的公共部分。 如:LLVM 不提供垃圾收集器机制,但它确实提供了实现垃圾收集的工具,方法是允许用元数据标记代码,从而更容易编写垃圾收集器。

但是,不排除 LLVM 最终可能添加用于实现垃圾收集的本机机制的可能性。

6.和LLVM相关的WebAssembly运行时

Wasmer

Wasmer 是在非浏览器上下文中运行 Wasm 的一种方式,提供基于 WebAssembly 的超轻量级容器。专注于支持在任何平台上运行 WASM 代码,为其他跨平台运行时(例如 Java)提供了潜在的替代方案。

 

Wasmer 在 GitHub 上将自己描述为“一个快速且安全的 WebAssembly 运行时,它使超轻量级容器可以在任何地方运行:从桌面到云、边缘和物联网IOT设备”。

Wasmer使用熟悉的工具和喜欢的语言,将所有内容编译为 WebAssembly。可以在任何操作系统上运行它或将其嵌入到其他语言中。

WasmEdge

WasmEdge 是一个轻量级、高性能和可扩展的 WebAssembly 运行时, 它是当今号称最快的 Wasm VM。WasmEdge的用例包括:现代 Web 应用程序架构(同构和 Jamstack 应用程序)、边缘云上的微服务、无服务器 SaaS API、嵌入式功能、智能合约和智能设备等方方面面。

 

 

WasmEdge 是世界上第一个正式的 CNCF WebAssembly 运行时项目,可以设想它将作为边缘云的关键任务轻量级运行时,在 CNCF 的开源云计算领域发挥重要作用。

WasmEdge 可以运行从 C/C++、Rust、Swift、AssemblyScript 或 Kotlin 源代码编译的标准 WebAssembly 字节码程序。同时,它还受到标准语言和编译器工具链的支持,例如 LLVM、Rustc 和 emscripten。

更多关于Wasmer、WasmEdge的内容可以继续在我的主页阅读,已经单独撰文《全网最火的5+优秀 WebAssembly 运行时!》进行详细介绍。

7.获取源代码并构建 LLVM教程

下载 LLVM

git clone --depth 1 https://github.com/llvm/llvm-project.git

配置和构建 LLVM 和 Clang

cd llvm-project
cmake -S llvm -B build -G <generator> [options]

可用的 generator 包括:Ninja、Unix Makefiles 、Visual Studio、Xcode

执行 build

cmake --build . [--target <target>]
// 默认target(即 cmake --build . 或 make)将构建所有 LLVM。

基本构建

一个基本的 CMake 和构建/测试调用,它只构建 LLVM 而没有其他子项目:

cmake -S llvm -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
ninja -C build check-llvm

上面代码将使用调试信息设置 LLVM 构建,然后编译 LLVM 并运行 LLVM 测试。

独立构建

独立构建针对系统上已存在的 clang 或 llvm 库的预构建版本构建子项目。

可以使用来自 llvm 项目(如上所述)的标准签出的源代码来进行独立构建,但也可以从发布页面上可用的 tarball 中构建。

对于独立构建,必须有一个 llvm 安装,它被正确配置为可以被其他项目的独立构建使用。 这可能是一个发行版提供的 LLVM 安装,或者可以自己构建它,如下所示:

cmake -G Ninja -S path/to/llvm-project/llvm -B $builddir 
      -DLLVM_INSTALL_UTILS=ON 
      -DCMAKE_INSTALL_PREFIX=/path/to/llvm/install/prefix 
      < other options >

ninja -C $builddir install

安装 llvm 后,要独立构建配置项目,如下调用 CMake:

cmake -G Ninja -S path/to/llvm-project/$subproj 
      -B $buildir_subproj 
      -DLLVM_EXTERNAL_LIT=/path/to/lit 

7.本文总结

本文主要带着大家了解 WebAssembly 学习路上遇到的LLVM 。文章从什么是LLVM、LLVM:为可移植性而设计、编程语言如何使用 LLVM、使用各种语言的 LLVM、LLVM 不做什么等方面逐步深入。

因为篇幅有限,文章并没有过多展开,如果有兴趣,可以在我的主页继续阅读,同时文末的参考资料提供了大量优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!

 

参考资料

原文链接:
https://www.infoworld.com/article/3247799/what-is-llvm-the-power-behind-swift-rust-clang-and-more.html

原文作者:Serdar Yegulalp

翻译&修改:头条号高级前端进阶

封面图:
https://hacks.mozilla.org/2017/02/creating-and-working-with-webassembly-modules/

https://www.heavy.ai/technical-glossary/llvm

https://github.com/llvm/llvm-project

https://llvm.org/

https://llvm.org/docs/GettingStarted.html

https://www.toutiao.com/article/7208714402630058531/

https://github.com/WasmEdge/WasmEdge

关键词:WebAssembly      点击(3)
声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多WebAssembly相关>>>