安装LLVM
为了学习如何使用LLVM,最好的开始方式是从源代码编译LLVM。LLVM是一个总体项目,GitHub仓库包含属于LLVM的所有项目的源代码。每个LLVM项目都位于仓库的顶层目录。除了克隆仓库外,您的系统还必须安装构建系统所需的所有工具。在本章中,您将学习以下主题:
- 准备先决条件,将展示如何设置您的构建系统
- 克隆仓库并从源代码构建,将介绍如何获取LLVM源代码,以及如何使用CMake和Ninja编译和安装LLVM核心库和clang
- 自定义构建过程,将讨论影响构建过程的各种可能性
编译LLVM与安装二进制文件
您可以从各种来源安装LLVM二进制文件。如果您使用的是Linux,那么您的发行版包含LLVM库。那么,为什么要自己编译LLVM呢?
首先,并不是所有的安装包都包含开发LLVM所需的所有文件。自己编译和安装LLVM可以避免这个问题。另一个原因源于LLVM高度可定制的事实。通过构建LLVM,您将学习如何定制LLVM,这将使您能够诊断将您的LLVM应用程序带到另一个平台时可能出现的问题。最后,在本书的第三部分,您将扩展LLVM本身,为此,您需要自己构建LLVM的技能。
然而,对于初学者来说,完全可以避免编译LLVM。如果您想走这条路线,那么您只需要安装下一节中描述的先决条件。
注意
许多Linux发行版将LLVM分成了几个包。请确保安装开发包。例如,在Ubuntu上,您需要安装llvm-dev
包。请同时确保安装了LLVM 17。对于其他版本,本书中的示例可能需要更改。
准备先决条件
为了使用LLVM,您的开发系统应该运行常见的操作系统,如Linux、FreeBSD、macOS或Windows。您可以以不同的模式构建LLVM和clang。启用调试符号的构建可能需要多达30GB的空间。所需的磁盘空间在很大程度上取决于选择的构建选项。例如,仅针对一个平台构建LLVM核心库的发布模式大约需要2GB的空闲磁盘空间,这是最低要求。
为了缩短编译时间,快速的CPU(例如2.5GHz时钟速度的四核CPU)和快速的SSD也很有帮助。甚至可以在像树莓派这样的小设备上构建LLVM - 只是需要很长时间。本书中的示例是在一台运行速度为2.7GHz的英特尔四核CPU、拥有40GB RAM和2.5TB SSD磁盘空间的笔记本电脑上开发的。这个系统非常适合开发任务。
您的开发系统必须安装一些必要的软件。让我们回顾一下这些软件包所需的最低版本。
要从GitHub检出源代码,您需要Git (git-scm.com/)。没有特定版本的要求...
LLVM项目使用CMake (cmake.org/)作为构建文件生成器。...
显然,您还需要一个C/C++编译器。LLVM项目是用基于C++17标准的现代C++编写的。需要一个符合标准的编译器和标准库。以下编译器已知适用于LLVM 17:
- gcc 7.1.0或更高版本
- clang 5.0或更高版本
- Apple clang 10.0或更高版本
- Visual Studio 2019 16.7或更高版本
提示
请注意,随着LLVM项目的进一步发展,对编译器的要求很可能会发生变化。一般来说,您应该使用系统上可用的最新编译器版本。
Python (python.org/)在生成构建文件和运行...
虽然本书中没有涵盖,但可能有理由需要使用Make而不是Ninja。在这种情况下,您需要使用GNU Make (www.gnu.org/software/ma...
LLVM还依赖于zlib
库(www.zlib.net/)。您应该至少安装了1...
要安装先决条件软件,最简单的方法是使用操作系统的包管理器。在以下部分中,将显示针对最流行的操作系统安装软件所需的命令。
Ubuntu
Ubuntu 22.04使用apt
包管理器。大多数基本工具已经安装;只缺少开发工具。要一次性安装所有包,您可以输入以下命令:
markup
$ sudo apt -y install gcc g++ git cmake ninja-build zlib1g-dev
Fedora和RedHat
Fedora 37和RedHat Enterprise Linux 9的包管理器称为dnf
。与Ubuntu一样,大多数基本工具已经安装。要一次性安装所有包,您可以输入以下命令:
markup
$ sudo dnf --y install gcc gcc-c++ git cmake ninja-build \
zlib-devel
FreeBSD
在FreeBSD 13或更高版本上,您必须使用pkg
包管理器。与基于Linux的系统不同,FreeBSD已经安装了clang编译器。要一次性安装所有其他包,您可以输入以下命令:
markup
$ sudo pkg install --y git cmake ninja zlib-ng
OS X
在OS X上进行开发,最好从Apple商店安装Xcode 。虽然本书中没有使用Xcode IDE,但它附带了所需的C/C++编译器和支持工具。要安装其他工具,可以使用包管理器Homebrew (brew.sh/)。要一次性安装所有包...
markup
$ brew install git cmake ninja zlib
Windows
与OS X一样,Windows没有自带的包管理器。对于C/C++编译器,您需要下载Visual Studio Community 2022 (visualstudio.microsoft.com/vs/communit... 2022的x64本机工具命令提示符**。要安装所需的包,您可以输入以下命令:
markup
$ scoop install git cmake ninja python gzip bzip2 coreutils
$ scoop bucket add extras
$ scoop install zlib
请仔细观察Scoop的输出。对于Python和zlib
包,它建议添加一些注册表键。其他软件需要这些条目来找到这些包。要添加注册表键,最好复制并粘贴Scoop的输出,如下所示:
markup
$ %HOMEPATH%\scoop\apps\python\current\install-pep-514.reg
$ %HOMEPATH%\scoop\apps\zlib\current\register.reg
每次执行命令后,注册表编辑器的消息窗口将弹出,询问您是否真的想要导入这些注册表键。您需要点击是来完成导入。现在所有先决条件都已安装。
在本书中的所有示例中,您必须使用VS 2022的x64本机工具命令提示符。使用此命令提示符时,编译器会自动添加到搜索路径中。
提示
LLVM代码库非常庞大。为了舒适地浏览源代码,我们建议使用允许您跳转到类的定义并搜索源代码的IDE。我们发现Visual Studio Code (code.visualstudio.com/download),这...
克隆仓库并从源代码构建
准备好构建工具后,您现在可以从GitHub检出所有LLVM项目并构建LLVM。这个过程在所有平台上基本相同:
- 配置Git。
- 克隆仓库。
- 创建构建目录。
- 生成构建系统文件。
- 最后,构建并安装LLVM。
让我们从配置Git开始。
配置Git
LLVM项目使用Git进行版本控制。如果您以前没有使用过Git,那么在继续之前,您应该先进行一些基本的Git配置:设置用户名和电子邮件地址。如果您提交更改,这两项信息都会被使用。
可以使用以下命令检查他们之前是否已经在Git中配置了电子邮件和用户名:
markup
$ git config user.email
$ git config user.name
前面的命令将输出您在使用Git时已经设置的相应电子邮件和用户名。然而,如果您是第一次设置用户名和电子邮件,可以输入以下命令进行首次配置。在以下命令中,您可以用您的名字替换"Jane",用您的电子邮件替换"jane@email.org":
markup
$ git config --global user.email "jane@email.org"
$ git config --global user.name "Jane"
这些命令更改了全局Git配置。在Git仓库内部,您可以通过不指定--global
选项来本地覆盖这些值。
默认情况下,Git使用vi 编辑器编写提交消息。如果您更喜欢其他编辑器,那么可以以类似的方式更改配置。要使用nano编辑器,您可以输入以下内容:
markup
$ git config --global core.editor nano
现在您已准备好从GitHub克隆LLVM仓库。
克隆仓库
在所有平台上克隆仓库的命令基本相同。只有在Windows上,建议关闭自动转换行尾字符的选项。
在所有非Windows平台上,您输入以下命令来克隆仓库:
markup
$ git clone https://github.com/llvm/llvm-project.git
只有在Windows上,添加禁用自动转换行尾字符的选项。在这里,您输入以下内容:
markup
$ git clone --config core.autocrlf=false \
https://github.com/llvm/llvm-project.git
此Git命令将GitHub上的最新源代码克隆到名为llvm-project
的本地目录中。现在使用以下命令将当前目录更改为新的llvm-project
目录:
markup
$ cd llvm-project
在该目录中,所有LLVM项目都有各自的目录。最值得注意的是,LLVM核心库位于llvm
子目录中。LLVM项目使用分支进行后续发布开发("release/17.x")并使用标签("llvmorg-17.0.1")标记某个特定发布。使用前面的克隆命令,您获得了当前的开发状态。本书使用LLVM 17。要将LLVM 17的第一个发布版本检出到名为llvm-17
的分支中,您输入以下内容:
markup
$ git checkout -b llvm-17 llvmorg-17.0.1
通过前面的步骤,您克隆了整个仓库并从标签创建了一个分支。这是最灵活的方法。
Git还允许您只克隆一个分支或标签(包括历史记录)。使用git clone --branch release/17.x https://github.com/llvm/llvm-project
,您只克隆release/17.x
分支及其历史记录。那么您拥有LLVM 17发布分支的最新状态,所以如果您需要精确的发布版本,只需像之前一样从发布标签创建一个分支。使用附加的--depth=1
选项,即所谓的Git浅克隆,您可以阻止克隆历史记录。这节省了时间和空间,但显然限制了您本地可以做的事情,包括基于发布标签检出分支。
创建构建目录
与许多其他项目不同,LLVM不支持内联构建,需要单独的构建目录。最简单的方法是在llvm-project
目录内创建,这是您当前的目录。为了简单起见,让我们将构建目录命名为build
。这里,Unix和Windows系统的命令不同。在类Unix系统上,您使用以下命令:
markup
$ mkdir build
在Windows上,使用以下命令:
markup
$ md build
现在您已准备好在此目录内使用CMake工具创建构建系统文件。
生成构建系统文件
为了生成用于使用Ninja编译LLVM和clang的构建系统文件,您需要运行以下命令:
markup
$ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_PROJECTS=clang -B build -S llvm
-G
选项告诉CMake为哪个系统生成构建文件。这个选项常用的值包括:
Ninja
- 用于Ninja构建系统Unix Makefiles
- 用于GNU MakeVisual Studio 17 VS2022
- 用于Visual Studio和MS BuildXcode
- 用于Xcode项目
使用-B
选项,您告诉CMake构建目录的路径。类似地,使用-S
选项指定源目录。通过使用-D
选项设置各种变量,可以影响生成过程。通常,这些变量以CMAKE_
(如果由CMake定义)或LLVM_
(如果由LLVM定义)为前缀。
如前所述,我们也感兴趣的是同时编译clang和LLVM。通过设置LLVM_ENABLE_PROJECTS=clang
变量,这允许CMake除了LLVM外还为clang生成构建文件。此外,CMAKE_BUILD_TYPE=Release
变量告诉CMake它应该为发布构建生成构建文件。
-G
选项的默认值取决于您的平台,构建类型的默认值取决于工具链。然而,您可以使用环境变量定义自己的首选项。CMAKE_GENERATOR
变量控制生成器,CMAKE_BUILD_TYPE
变量指定构建类型。如果您使用bash或类似的shell,那么您可以使用以下方式设置变量:
markup
$ export CMAKE_GENERATOR=Ninja
$ export CMAKE_BUILD_TYPE=Release
如果您使用的是Windows命令提示符,那么您可以使用以下方式设置变量:
markup
$ set CMAKE_GENERATOR=Ninja
$ set CMAKE_BUILD_TYPE=Release
有了这些设置,创建构建系统文件的命令变成了以下形式,更容易输入:
markup
$ cmake -DLLVM_ENABLE_PROJECTS=clang -B build -S llvm
在自定义构建过程部分中,您将找到更多关于CMake变量的信息。
编译和安装LLVM
在生成构建文件后,可以使用以下命令编译LLVM和clang:
markup
$ cmake --build build
此命令在底层运行Ninja,因为我们在配置步骤中告诉了CMake生成Ninja文件。然而,如果您为支持多个构建配置的系统(如Visual Studio)生成构建文件,那么您需要使用--config
选项来指定要用于构建的配置。根据硬件资源,此命令的运行时间从15分钟(服务器,拥有大量CPU核心、内存和快速存储)到几个小时(双核Windows笔记本,内存有限)不等。
默认情况下,Ninja利用所有可用的CPU核心。这对编译速度有好处,但可能阻止其他任务运行;例如,在Windows笔记本上,当Ninja运行时,几乎不可能上网冲浪。幸运的是,您可以使用--j
选项限制资源使用。
假设您有四个CPU核心可用,但Ninja只应使用两个(因为您有并行任务要运行),那么您可以使用以下命令进行编译:
markup
$ cmake --build build --j2
在编译完成后,最佳实践是运行测试套件,以检查是否一切正常:
markup
$ cmake --build build --target check-all
再次,此命令的运行时间因可用硬件资源而异。check-all
Ninja目标运行所有测试用例。为包含测试用例的每个目录生成目标。使用check-llvm
而不是check-all
运行LLVM测试,而不是clang测试;check-llvm-codegen
只运行LLVM的CodeGen
目录中的测试(即llvm/test/CodeGen
目录)。
您也可以进行快速手动检查。LLVM应用程序之一是llc ,LLVM编译器。如果您使用-version
选项运行它,它会显示LLVM版本、主机CPU和所有支持的架构:
markup
$ build/bin/llc --version
如果您在编译LLVM时遇到麻烦,那么您应该查阅LLVM系统入门 文档的常见问题 部分(releases.llvm.org/17.0.1/docs...
作为最后一步,您可以安装二进制文件:
markup
$ cmake --install build
在类Unix系统上,安装目录是/usr/local
。在Windows上,使用的是C:\Program Files\LLVM
。当然,这可以更改。下一节将解释如何做到。
自定义构建过程
CMake系统使用CMakeLists.txt
文件中的项目描述。顶层文件位于llvm
目录,llvm/CMakeLists.txt
。其他目录也有CMakeLists.txt
文件,在生成过程中会递归包含。
基于项目描述中提供的信息,CMake检查安装的编译器,检测库和符号,并创建构建系统文件,例如build.ninja
或Makefile
(取决于所选择的生成器)。还可以定义可重用模块,例如用于检测是否安装了LLVM的函数。这些脚本放置在特殊的cmake
目录(llvm/cmake
)中,在生成过程中自动搜索。
可以通过定义CMake变量来自定义构建过程。命令行选项-D
用于将变量设置为一个值。CMake脚本中使用这些变量。CMake本身定义的变量几乎总是以CMAKE_
为前缀,这些变量可以在所有项目中使用。LLVM定义的变量以LLVM_
为前缀,但只有在项目定义包含了LLVM的使用时才能使用。
CMake定义的变量
一些变量使用环境变量的值进行初始化。最值得注意的是CC
和CXX
,它们定义了用于构建的C和C++编译器。CMake尝试自动使用当前shell搜索路径定位C和C++编译器。它选择找到的第一个编译器。如果您安装了多个编译器,例如gcc和clang或不同版本的clang,那么这可能不是您想要用来构建LLVM的编译器。
假设您想使用clang17作为C编译器和clang++17作为C++编译器。然后,您可以以以下方式在Unix shell中调用CMake:
markup
$ CC=clang17 CXX=clang++17 cmake --B build --S llvm
这只为cmake
的调用设置了环境变量的值。如有必要,您可以为编译器可执行文件指定绝对路径。
CC
是CMAKE_C_COMPILER
CMake变量的默认值,CXX
是CMAKE_CXX_COMPILER
CMake变量的默认值。您可以直接设置CMake变量,而不是使用环境变量。这与前面的调用等效:
markup
$ cmake -DCMAKE_C_COMPILER=clang17 \
-DCMAKE_CXX_COMPILER=clang++17 --B build --S llvm
CMake定义的其他有用变量包括:
一些有用的CMake变量
变量名 | 用途 |
---|---|
CMAKE_INSTALL_PREFIX |
这是一个路径前缀,安装时会附加到每个路径前。Unix默认为/usr/local ,Windows默认为C:\Program Files\<Project> 。要将LLVM安装在/opt/llvm 目录中,您可以指定-DCMAKE_INSTALL_PREFIX=/opt/llvm 。二进制文件将被复制到/opt/llvm/bin ,库文件到/opt/llvm/lib 等。 |
CMAKE_BUILD_TYPE |
不同类型的构建需要不同的设置。例如,调试构建需要指定生成调试符号的选项,并通常链接到系统库的调试版本。相反,发布构建使用优化标志,并链接到库的生产版本。此变量仅用于只能处理一种构建类型的构建系统,例如Ninja或Make。对于IDE构建系统,生成所有变体,您必须使用IDE的机制在构建类型之间切换。可能的值包括:DEBUG :带调试符号的构建RELEASE :为速度优化的构建RELWITHDEBINFO :带调试符号的发布构建MINSIZEREL :为大小优化的构建。默认构建类型取自CMAKE_BUILD_TYPE 环境变量。如果此变量未设置,则默认值取决于所用的工具链,通常为空。为了生成发布构建的构建文件,您可以指定-DCMAKE_BUILD_TYPE=RELEASE 。 |
CMAKE_C_FLAGS``CMAKE_CXX_FLAGS |
编译C和C++源文件时使用的额外标志。初始值取自CFLAGS 和CXXFLAGS 环境变量,可以作为替代使用。 |
CMAKE_MODULE_PATH |
指定用于搜索CMake模块的额外目录。指定的目录在默认目录之前搜索。值是分号分隔的目录列表。 |
PYTHON_EXECUTABLE |
如果未找到Python解释器,或者如果您安装了多个版本时选择了错误的版本,您可以将此变量设置为Python二进制文件的路径。只有在包含了CMake的Python模块时(LLVM就是这种情况),此变量才会生效。 |
表1.1 - CMake提供的其他有用变量
CMake为变量提供内置帮助。--help-variable var
选项打印var
变量的帮助信息。例如,您可以输入以下内容以获取CMAKE_BUILD_TYPE
的帮助信息:
markup
$ cmake --help-variable CMAKE_BUILD_TYPE
您也可以使用以下命令列出所有变量:
markup
$ cmake --help-variable-list
此列表很长。您可能希望将输出传送到more
或类似程序。
使用LLVM定义的构建配置变量
LLVM定义的构建配置变量的工作方式与CMake定义的变量相同,只是没有内置帮助。最有用的变量在以下表格中,分为对首次安装LLVM的用户有用的变量,以及对更高级LLVM用户有用的变量。
对首次安装LLVM的用户有用的变量
变量名 | 用途 |
---|---|
LLVM_TARGETS_TO_BUILD |
LLVM支持为不同的CPU架构生成代码。默认情况下,所有这些目标都会被构建。使用此变量指定要构建的目标列表,用分号分隔。当前目标包括AArch64 、AMDGPU 、ARM 、AVR 、BPF 、Hexagon 、Lanai 、LoongArch 、Mips 、MSP430 、NVPTX 、PowerPC 、RISCV 、Sparc 、SystemZ 、VE 、WebAssembly 、X86 和XCore 。all 可用作所有目标的缩写。名称区分大小写。要仅启用PowerPC和System Z目标,您可以指定-DLLVM_TARGETS_TO_BUILD="PowerPC;SystemZ" 。 |
LLVM_EXPERIMENTAL_TARGETS_TO_BUILD |
除了官方目标之外,LLVM源代码树还包含实验性目标。这些目标正在开发中,往往还不支持后端的全部功能。当前实验性目标列表包括ARC 、CSKY 、DirectX 、M68k 、SPIRV 和Xtensa 。要构建M68k 目标,您可以指定-D LLVM_EXPERIMENTAL_TARGETS_TO_BUILD=M68k 。 |
LLVM_ENABLE_PROJECTS |
这是一个您要构建的项目列表,用分号分隔。项目的源代码必须位于与llvm 目录同级(并排布局)。当前列表包括bolt 、clang 、clang-tools-extra 、compiler-rt 、cross-project-tests 、libc 、libclc 、lld 、lldb 、mlir 、openmp 、polly 和pstl 。all 可用作此列表中所有项目的缩写。此外,您也可以在此处指定flang 项目。由于一些特殊的构建要求,它尚未成为all 列表的一部分。要与LLVM一起构建clang和bolt,您可以指定-DLLVM_ENABLE_PROJECT="clang;bolt" 。 |
表1.2 - 对首次安装LLVM的用户有用的变量
高级LLVM用户的变量
变量名 | 用途 |
---|---|
LLVM_ENABLE_ASSERTIONS |
如果设置为ON ,则启用断言检查。这些检查有助于发现错误,在开发期间非常有用。对于DEBUG 构建,默认值为ON ,否则为OFF 。要打开断言检查(例如,对于RELEASE 构建),您可以指定--DLLVM_ENABLE_ASSERTIONS=ON 。 |
LLVM_ENABLE_EXPENSIVE_CHECKS |
启用一些耗时的检查,这些检查可能会大大降低编译速度或消耗大量内存。默认值为OFF 。要打开这些检查,您可以指定-DLLVM_ENABLE_EXPENSIVE_CHECKS=ON 。 |
LLVM_APPEND_VC_REV |
如果给出--version 命令行选项,LLVM工具(如llc )会显示它们基于的LLVM版本以及其他信息。这个版本信息基于LLVM_REVISION C宏。默认情况下,不仅包括LLVM版本,还包括当前Git哈希作为版本信息的一部分。这在您跟踪master分支的开发时很方便,因为它明确了工具基于哪个Git提交。如果不需要,可以通过--DLLVM_APPEND_VC_REV=OFF 关闭。 |
LLVM_ENABLE_THREADS |
如果检测到线程库(通常是pthreads 库),LLVM会自动包含线程支持。此外,LLVM在这种情况下假定编译器支持TLS (线程本地存储 )。如果您不需要线程支持或您的编译器不支持TLS,那么可以通过-DLLVM_ENABLE_THREADS=OFF 关闭。 |
LLVM_ENABLE_EH |
LLVM项目默认不使用C++异常处理,因此默认关闭异常支持。这种设置可能与您的项目链接的其他库不兼容。如有需要,可以通过指定--DLLVM_ENABLE_EH=ON 启用异常支持。 |
LLVM_ENABLE_RTTI |
LLVM使用轻量级、自建的运行时类型信息系统。默认情况下关闭了C++ RTTI的生成。与异常处理支持一样,这可能与其他库不兼容。要打开C++ RTTI的生成,您可以指定--DLLVM_ENABLE_RTTI=ON 。 |
LLVM_ENABLE_WARNINGS |
尽可能地,编译LLVM不应生成警告消息。因此,默认情况下打开了打印警告消息的选项。要关闭它,您可以指定--DLLVM_ENABLE_WARNINGS=OFF 。 |
LLVM_ENABLE_PEDANTIC |
LLVM源代码应符合C/C++语言标准,因此默认启用了对源代码的严格检查。如果可能,还禁用了编译器特定的扩展。要反转此设置,您可以指定--DLLVM_ENABLE_PEDANTIC=OFF 。 |
LLVM_ENABLE_WERROR |
如果设置为ON ,则所有警告都被视为错误 - 一旦发现警告,编译就会中止。这有助于在源代码中找到所有剩余的警告。默认情况下,它是关闭的。要打开它,您可以指定--DLLVM_ENABLE_WERROR=ON 。 |
LLVM_OPTIMIZED_TABLEGEN |
通常,tablegen工具与LLVM的其他部分使用相同的选项构建。与此同时,tablegen用于生成代码生成器的大部分代码。因此,在调试构建中,tablegen的速度会慢得多,显著增加编译时间。如果此选项设置为ON ,则即使对于调试构建,tablegen也会开启优化编译,可能会减少编译时间。默认值为OFF 。要打开它,您可以指定--DLLVM_OPTIMIZED_TABLEGEN=ON 。 |
LLVM_USE_SPLIT_DWARF |
如果构建编译器是gcc或clang,那么打开此选项将指示编译器在单独的文件中生成DWARF调试信息。对象文件大小的减少显著减少了调试构建的链接时间。默认值为OFF 。要打开它,您可以指定-LLVM_USE_SPLIT_DWARF=ON 。 |
表1.3 - 对高级LLVM用户有用的变量
注意
LLVM定义了许多其他CMake变量。您可以在LLVM关于CMake的文档中找到完整列表(releases.llvm.org/17.0.1/docs...
总结
在本章中,您为开发机器准备了编译LLVM所需的环境。您克隆了GitHub仓库,并编译了自己的LLVM和clang版本。构建过程可以通过CMake变量进行自定义。您了解了一些有用的变量以及如何更改它们。有了这些知识,您可以根据需要调整LLVM。
在下一部分中,我们将更深入地了解编译器的结构。我们将探索编译器内部的不同组件,以及其中发生的不同类型的分析 - 特别是词法、语法和语义分析。最后,我们还将简要介绍与LLVM后端进行代码生成的接口。