编译和安装LLVM整个流程

发布时间 2023-06-13 03:56:37作者: 吴建明wujianming

编译和安装LLVM整个流程

1.1 LLVM系统入门

LLVM项目包括多个组件。该项目的核心本身被称为LLVM。其中包含所需的所有工具、库和头文件,以便处理中间表达式并将其转换为目标对象文件。LLVM工具包括汇编程序、反汇编程序、位代码分析器和位代码优化器。另外,LLVM还包含基本的回归测试。

类C语言使用Clang前端。该组件使用LLVM将C、C++、Objective C和Objective C++代码编译为LLVM位代码,并将它们编译为目标对象文件。其他组件包括:libc++C++标准库、LLD链接器等。

1.1.1查看LLVM(包括Clang等子项目)

通过以下方式可获取llvm的源码。

    在linux系统上:

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

或者,在windows上:

git clone --config core.autocrlf=false https://github.com/llvm/llvm-project.git

为了节省存储并加快checkout签出时间,可能需要进行浅层克隆clone。例如,要获取LLVM项目的最新版本,请使用

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

1.1.2配置和构建LLVM和Clang

1. 构建系统生成器

通过以下方式构建LLVM。

cd llvm-project

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

一些常见的构建系统生成器包括:

1)Ninja — 用于生成Ninja构建文件。大多数llvm开发人员都使用Ninja。

2)Unix Makefiles — 用于生成与make兼容的并行makefiles生成文件。

3)Visual Studio — 用于生成Visual Studio项目和解决方案。

4)Xcode — 用于生成Xcode项目。

5)有关更全面的list列表,请参阅CMake文档。

2. 一些常见选项

1)-DLLVM_ENABLE_PROJECTS='...' — 要额外构建的LLVM子项目的semicolon-separated分号分隔列表。可以包括以下任何一项:clang, clang-tools-extra, lldb, lld, polly, 或跨项目测试cross-project-tests。

例如, 构建LLVM, Clang和LLD,请使用 -DLLVM_ENABLE_PROJECTS="clang;lld".

2)-DCMAKE_INSTALL_PREFIX=directory — 为directory 目录指定要安装的LLVM工具和库的完整路径名(default默认方式为:/usr/local)。

3)-DCMAKE_BUILD_TYPE=type — 控制生成的优化级别和调试信息。类型的有效选项包括 Debug, Release, RelWithDebInfo和MinSizeRel. 有关更多详细信息,请参阅 CMAKE_BUILD_TYPE

4)-DLLVM_ENABLE_ASSERTIONS=ON —在启用断言检查的情况下编译(对于调试生成,默认为ON,对于所有其他生成类型,默认为OFF)。

5)-DLLVM_USE_LINKER=lld — 与lld链接器链接,假设已经安装在系统上。如果默认链接器速度较慢,这会大大加快链接时间。

6)-DLLVM_PARALLEL_{COMPILE,LINK}_JOBS=N — 限制同时并行运行的编译/链接工作的数量。这对于链接来说尤其重要,因为链接可能会占用大量内存。如果在构建LLVM时遇到内存问题,请尝试将其设置为限制同时运行的编译/链接作业的最大数量。

3. 直接指定的生成系统

cmake --build . [--target <target>] 或上面直接指定的生成系统。

1)默认目标(即cmake--build.或make)将构建所有LLVM。

2)检测所有目标(即ninja check-all)将运行回归测试,以确保一切正常。

3)CMake将为每个工具和库生成构建目标,大多数LLVM子项目都会生成自己的检测check-<project>目标。

4)运行串行生成会很慢。要提高速度,请尝试运行并行构建。这是在Ninja中默认完成的;对于make,使用选项-j NN,其中NN是并行作业的数量,例如,可用CPU的数量。

4. 基本的CMake和构建/测试调用

基本的CMake和构建/测试调用,只构建LLVM,不构建其他子项目:

cmake -S llvm -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug

ninja -C build check-llvm

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

5. 配置参考信息

1)有关CMake选项的更多详细信息,请参见CMake。

2)如果生成或测试失败,请参阅以下内容。

有关配置和编译LLVM的详细信息,请参阅LLVM入门(Getting Started with LLVM)相关内容。转到目录布局Directory Layout,以了解源代码树的布局。

1.2 独立构建

Stand-alone 独立构建能根据系统上已经存在的clang或llvm库的预构建版本,来构建子项目。

可以使用llvm项目标准checkout签出的源代码(如上所述)来进行独立构建,但也可以从稀疏checkout签出或从release发布页面上可用的原始码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 \

      -DLLVM_ROOT=/path/to/llvm/install/prefix

请注意

独立生成需要在一个文件夹中进行,该文件夹不是生成LLVMN的原始文件夹($builddir!=$builddir_subproj)。

LLVM_ROOT应该指向LLVM安装的前缀。例如,如果LLVM安装到/usr/bin和/usr/lib64中,则应该配置-DLLVM_ROOT=/usr/。

LLVM_ROOT和LLVM_EXTERNAL_LT选项都是为所有子项目执行独立生成所必需的。每个子项目所需的其他选项可在下表中找到。

表1.1中列出的子项目支持check-$subproj和安装构建目标。

Sub-Project

需要Sub-Directories

需要CMake选项

llvm

llvm, cmake, third-party

LLVM_INSTALL_UTILS=ON

clang

clang, cmake

CLANG_INCLUDE_TESTS=ON (只需要check-clang)

lld

lld, cmake

 

表1.1 check-$subproj与安装构建的子项目

构建独立clang的示例:

#!/bin/sh

build_llvm=`pwd`/build-llvm

build_clang=`pwd`/build-clang

installprefix=`pwd`/install

llvm=`pwd`/llvm-project

mkdir -p $build_llvm

mkdir -p $installprefix

cmake -G Ninja -S $llvm/llvm -B $build_llvm \

      -DLLVM_INSTALL_UTILS=ON \

      -DCMAKE_INSTALL_PREFIX=$installprefix \

      -DCMAKE_BUILD_TYPE=Release

ninja -C $build_llvm install

cmake -G Ninja -S $llvm/clang -B $build_clang \

      -DLLVM_EXTERNAL_LIT=$build_llvm/utils/lit \

      -DLLVM_ROOT=$installprefix

ninja -C $build_clang

1.3 软硬件环境要求

在开始使用LLVM系统之前,请查看下面给出的要求。以便提前知道需要什么硬件和软件,从而省去一些麻烦。

1.3.1硬件环境

表1.2 表示支持LLVM运行的平台。

OS

Arch

Compilers

Linux

x861

GCC, Clang

Linux

amd64

GCC, Clang

Linux

ARM

GCC, Clang

Linux

Mips

GCC, Clang

Linux

PowerPC

GCC, Clang

Linux

SystemZ

GCC, Clang

Solaris

V9 (Ultrasparc)

GCC

DragonFlyBSD

amd64

GCC, Clang

FreeBSD

x861

GCC, Clang

FreeBSD

amd64

GCC, Clang

NetBSD

x861

GCC, Clang

NetBSD

amd64

GCC, Clang

OpenBSD

x861

GCC, Clang

OpenBSD

amd64

GCC, Clang

macOS2

PowerPC

GCC

macOS

x86

GCC, Clang

Cygwin/Win32

x861, 3

GCC

Windows

x861

Visual Studio

Windows x64

x86-64

Visual Studio

表1.2 支持LLVM运行的平台

标注:

1)奔腾及以上处理器支持代码生成。

2)仅支持32位ABI代码生成。

3)要在基于Win32的系统上使用LLVM模块,可以使用-DBUILD_SHARED_LIBS=on配置LLVM。

请注意,调试构建需要大量的时间和磁盘空间。仅LLVM的构建将需要大约1-3GB的空间。LLVM和Clang的完整构建将需要大约15-20GB的磁盘空间。确切的空间要求将因系统而异。(它之所以如此之大,是因为所有的调试信息以及库被静态链接到多个工具中的事实)。

如果受空间约束,则只能生成选定的工具或选定的目标。Release版本所需的空间要少得多。

1.3.2 软件环境

编译LLVM需要安装多个软件包。下表列出了所需的软件包。Package列是LLVM所依赖的软件包的常用名称。Version列提供软件包的“已知工作”版本。Notes列描述了LLVM如何使用该包,并提供了其他详细信息。表1.3 表示支持LLVM运行的软件包。

Package

Version

Notes

CMake

>=3.13.4

Makefile/workspace生成器

GCC

>=7.1.0

C/C++ compiler1

python

>=3.6

自动化测试套件2

zlib

>=1.2.3.4

压缩库3

GNU Make

3.79, 3.79.1

Makefile/build 处理器4

表1.3 支持LLVM运行的软件包

数字标注解析:

1)只需要C和C++语言,因此不需要为LLVM的目的构建其他语言。请参阅下面的具体版本信息。

2)只有在llvm/test目录中运行自动化测试套件时才需要。

3)可选项,为选定的LLVM工具添加压缩/解压缩功能。

4)可选项,可以使用CMake支持的任何其他构建工具。

此外,编译主机通常会有过多的Unix实用程序。明确地:

1)ar-档案库生成器。

2)bzip2-bzip2命令用于生成分发。

3)bunzip2-用于分发检查的bunzip2命令。

4)chmod-更改文件的权限。

5)cat-输出串联实用程序。

6)cp-复制文件。

7)date-打印当前日期/时间。

8)echo-打印到标准输出。

9)egrp-扩展的正则表达式搜索实用程序。

10)find-在文件系统中查找文件/目录。

11)grep-正则表达式搜索实用程序。

12)gzip-用于生成分发的gzip命令。

13)gunzip-用于分发检查的gunzip命令。

14)install-安装目录/文件。

15)mkdir-创建一个目录。

16)mv-移动(重命名)文件。

17)ranlib-用于归档库的符号表生成器。

18)rm-删除文件和目录。

19)sed-用于转换输出的流编辑器。

20)sh-用于生成构建脚本的Bourne shell。

21)tar—用于分发生成的磁盘归档。

22)test-在文件系统中测试内容。

23)unzip-用于分发检查的unzip命令。

24)zip-用于生成分发的zip-zip命令。

1.3.3 主机C++工具链,包括编译器和标准库

LLVM对主机C++编译器要求很高,因此往往会暴露编译器中的错误。还需要试图合理地密切关注C++语言和库的改进和发展。因此,为了构建LLVM,需要一个现代的主机C++工具链,包括编译器和标准库。

LLVM是使用编码标准中记录的C++子集编写的。为了强制执行此语言版本,在构建系统中,检查最流行的主机工具链中的特定最低版本:

1)Clang 5.0

2)Apple Clang 10.0

3)GCC 7.1

4)Visual Studio 2019 16.7

任何比这些工具链更旧的内容都可以工作,但需要使用特殊选项强制构建系统,而且它并不是真正受支持的主机平台。还要注意,这些编译器的旧版本经常会导致LLVM崩溃或编译错误。

对于不太广泛使用的主机工具链,如ICC或xlC,请注意,可能需要非常新的版本来支持LLVM中使用的所有C++功能。

跟踪某些版本的软件,这些软件在作为主机工具链的一部分使用时会失败。有时甚至包括链接器。

GNU ld 2.16.X。ld链接器的某些2.16.X版本会产生很长的警告消息,警告某些“.GNU.linkonce.t.*”符号是在丢弃的部分中定义的。可以安全地忽略这些消息,因为它们是错误的,并且链接是正确的。若使用ld 2.17,这些警告消息将消失。

GNU binutils 2.17:Binutils 2.17包含一个错误,该错误在构建LLVM时会导致巨大的链接时间(分钟而不是秒)。建议升级到更新的版本(2.17.50.0.4或更高版本)。

GNU Binutils 2.19.1 Gold:此Gold版本包含一个错误,该错误会在使用位置无关代码构建LLVM时导致间歇性故障。症状是关于循环依赖关系的错误。建议升级到新的Gold版本。

1.3.4 获取现代主机C++工具链

主要介绍适用于Linux和较旧BSD环境的工具链。在macOS上,应该有一个足够现代的Xcode,否则,可能需要升级,直到升级完成为止。Windows没有“系统编译器”,所以必须安装Visual Studio 2019(或更高版本),或最新版本的mingw64。FreeBSD 10.0及更新版本有一个现代的Clang作为系统编译器。

然而,一些Linux发行版和一些其他或更旧的BSD有时会有非常旧的GCC版本。这些步骤试图帮助升级编译器,即使在这样的系统上也是如此。但是,如果可能的话,鼓励使用具有满足这些要求的现代系统编译器的最新的发行版本。请注意,安装Clang和libc++的早期版本作为主机编译器是很有吸引力的,但是libc++直到最近才在Linux上经过很好的测试或设置构建。因此,建议只使用libstdc++和现代GCC作为引导程序中的初始主机,然后使用Clang(可能还有libc++)。

第一步是安装最新的GCC工具链。用户在版本要求方面遇到困难的最常见的发行版是Ubuntu Precise,12.04 LTS。对于这个发行版,一个简单的选择是安装测试PPA的工具链,并使用它来安装现代GCC。在ask-ubuntu堆栈交换和带有更新命令的github gist上对此进行了非常好的讨论。然而,并不是所有用户都可以使用PPA,还有许多其他发行版,所以从源代码构建和安装GCC可能是必要的(或者只是有用的,毕竟是在做编译器开发)。现在做这件事也很容易。

安装GCC 7.1.0的简单步骤:

% gcc_version=7.1.0

% wget https://ftp.gnu.org/gnu/gcc/gcc-${gcc_version}/gcc-${gcc_version}.tar.bz2

% wget https://ftp.gnu.org/gnu/gcc/gcc-${gcc_version}/gcc-${gcc_version}.tar.bz2.sig

% wget https://ftp.gnu.org/gnu/gnu-keyring.gpg

% signature_invalid=`gpg --verify --no-default-keyring --keyring ./gnu-keyring.gpg gcc-${gcc_version}.tar.bz2.sig`

% if [ $signature_invalid ]; then echo "Invalid signature" ; exit 1 ; fi

% tar -xvjf gcc-${gcc_version}.tar.bz2

% cd gcc-${gcc_version}

% ./contrib/download_prerequisites

% cd ..

% mkdir gcc-${gcc_version}-build

% cd gcc-${gcc_version}-build

% $PWD/../gcc-${gcc_version}/configure --prefix=$HOME/toolchains --enable-languages=c,c++

% make -j$(nproc)

% make install

有关更多详细信息,请查看优秀的GCC wiki条目,从中可获得大部分信息。

一旦有了GCC工具链,就可以将LLVM的构建配置为使用主机编译器和C++标准库的新工具链。由于新版本的libstdc++不在系统库搜索路径上,因此需要传递额外的链接器标志,以便在链接时(-L)和运行时(-rpath)可以找到。如果使用的是CMake,那么此调用应该会生成可工作的二进制文件:

% mkdir build

% cd build

% CC=$HOME/toolchains/bin/gcc CXX=$HOME/toolchains/bin/g++ \

cmake .. -DCMAKE_CXX_LINK_FLAGS="-Wl,-rpath,$HOME/toolchains/lib64 -L$HOME/toolchains/lib64"

如果未能设置rpath,大多数LLVM二进制文件将在启动时失败,并从类似于libstdc++.so.6的加载程序中发出消息:找不到版本“GLIBCXX_3.4.20”。这意味着需要调整-rpath链接器标志。

此方法将为所有可执行文件的rpath添加一个绝对路径。这对本地开发很好。如果想分发构建的二进制文件,以便可以在较旧的系统上运行,请将libstdc++.so.6复制到lib/目录中。所有LLVM的装载shipping二进制文件都有一个指向$ORIGIN/../lib的路径,所以会在那里找到libstdc++.so.6。非分布式二进制文件没有rpath集,也找不到libstdc++.so.6。将-DLLVM_LOCAL_RPATH=“$HOME/toolchains/lib64”传递给cmake,以便可以添加到libstdc++.so.6的绝对路径,如上所述。由于这些二进制文件不是分布式的,所以如果有一个绝对的本地路径,这就是很好的。

当构建Clang时,需要允许它访问现代C++标准库,以便在引导过程中将其用作新主机。有两种简单的方法可以做到这一点,要么与Clang一起构建(并安装)libc++,然后将其与-stdlib=libc++编译和链接标志一起使用;要么将Clang安装到与GCC相同的前缀(上面的$HOME/toolchains)中。Clang将在自己的前缀中查找libstdc++,如果找到就使用它。还可以为Clang添加一个显式前缀,以便查找带有--GCC toolchain=/opt/my/GCC/prefix标志的GCC工具链,并在使用just-built-Clang构建的Clang进行引导时,将其传递给编译和链接命令。

1.4 LLVM基础入门

其余部分介绍如何使用LLVM,并提供有关LLVM环境的一些基本信息。

后面部分介绍了LLVM源代码树的总体布局,一个使用LLVM工具链的简单示例,以及查找有关LLVM的更多信息或获得帮助的链接。

1.4.1术语和符号

以下名称用于表示特定于本地系统和工作环境的路径。这些不是需要设置的环境变量,只是下面文档其余部分中使用的字符串。在下面的任何示例中,只需将这些名称中的每一个替换为本地系统上的适当路径名即可。所有这些路径都是绝对的。

SRC_ROOT

这是LLVM源代码树的顶层目录。

OBJ_ROOT

这是LLVM目标树的顶层目录(即放置目标文件和编译程序的树,可以与SRC_ROOT相同)。

1.4.2 打开LLVM档案文件

如果有LLVM分发版,则需要先对其进行解压缩,然后才能开始编译。LLVM是作为许多不同的子项目分发的。每一个子项目都有自己的下载,这是一个用gzip程序压缩的TAR档案文件。

文件如下,其中x.y标记了版本号:

llvm-x.y.tar.gz

这是LLVM库和工具的源代码发布版本。

cfe-x.y.tar.gz格式

这是Clang前端的源代码发布版本。

1.4.3 从Git签出LLVM

可以从Git中checkout签出LLVM的源代码。

标注:

在正确调整.gitattribute设置后,将来不需要配置--config core.autocrlf=false。但在写文档时,Windows用户需要配置。

在linux上只需运行:

% git clone https://github.com/llvm/llvm-project.git

或者在Windows上,

% git clone --config core.autocrlf=false https://github.com/llvm/llvm-project.git

       这是在当前目录中创建一个“llvm项目”目录,并用llvm和所有相关子项目的所有源代码,测试目录和文档文件的本地副本完全填充该目录。注意,与tarball原始码不同的是,tarball原始码将每个子项目都包含在一个单独的文件中,git存储库将所有项目一起包含。

       如果想要获得特定的版本(而不是最新的修订版),可以在克隆存储库后签出标记。例如,git checkout llvmorg-6.0.1位于由上述命令创建的llvm项目目录中。使用git tag -l列出所有这些。

1.4.4 本地LLVM配置

签出存储库后,必须在构建LLVM套件源代码之前对其进行配置。此过程使用CMake。取消常规配置脚本的链接,CMake将以请求的任何格式生成构建文件,以及各种*.inc文件和llvm/include/llvm/Config/Config.h.CMake文件。

在命令行上使用格式-D<variable name>=<value>,变量将其传递给cmake。表1.4表示LLVM开发人员使用的一些常见选项。

Variable

Purpose

CMAKE_C_COMPILER

告诉CMAKE要使用哪个C编译器。默认情况下,使用/usr/bin/cc。

CMAKE_CXX_COMPILER

告诉cmake要使用哪个C++编译器。默认情况下,使用/usr/bin/c++。

CMAKE_BUILD_TYPE

告诉CMAKE要使用哪个C++编译器。默认情况下,这将是/usr/bin/c++。

CMAKE_INSTALL_PREFIX

告诉CMAKE要为哪种类型的生成文件。有效的选项有Debug、Release、RelWithDebInfo和MinSizeRel。默认值为“调试”。

CMAKE_INSTALL_PREFIX指定运行生成文件的安装操作时,要作为目标的安装目录。

Python3_EXECUTABLE

通过向Python解释器传递路径,强制CMake使用特定的Python版本。默认情况下,使用PATH中解释器的Python版本。

LLVM_TARGETS_TO_BUILD

一个分号分隔的列表,用于控制将构建哪些目标,并将其链接到LLVM中。默认列表定义为LLVM_ALL_TARGETS,可以设置为包括out-of-tree targets。默认值包括:AArch64、AMDGPU、ARM、AVR、BPF、Hexagon、Lanai、Mips、MSP430、NVPTX、PowerPC、RISCV、Sparc、SystemZ、WebAssembly、X86、XCore。将其设置为“主机”只会编译主机体系结构(例如,相当于在X86主机上指定X86),可以显著加快编译和测试时间。

LLVM_ENABLE_DOXYGEN

用源代码构建基于DOXYGEN的文档。默认情况下,这是禁用的,因为它速度慢,会生成大量输出。

LLVM_ENABLE_PROJECTS

一个分号分隔的列表,用于选择要额外构建的其他LLVM子项目。(仅在使用并列项目布局时有效,例如通过git)。默认列表为空。可以包括:clang、clang tools extra、cross-project-tests、flang、libc、libclc、lld、lldb、mlir、openmp、polly或pstl。

LLVM_ENABLE_RUNTIMES

一个分号分隔的列表,用于选择要构建的运行时。(仅在使用完整的单一布局时有效)。默认列表为空。可以包括:编译器rt、libc、libcxx、libcxxabi、libunfold或openmp。

LLVM_ENABLE_SPHINX

用源代码构建基于sphinx-based的文档。这在默认情况下是禁用的,因为它速度慢并且会生成大量输出。建议使用Sphinx1.5版或更高版本。

LLVM_BUILD_LLVM_DYLIB

生成libLLVM.so。此库包含一组默认的LLVM组件,这些组件可以用LLVM_DYLIB_components重写。默认值包含大部分LLVM,并在tools/LLVM-shlib/CMakelists.txt中定义。此选项在Windows上不可用。

LLVM_OPTIMIZED_TABLEGEN

构建在LLVM构建过程中使用的发布表生成。这可以显著加快调试构建的速度。

表1.4 LLVM开发人员使用的一些常见选项

要配置LLVM,请执行以下步骤:

将目录更改为对象根目录:

% cd OBJ_ROOT

运行cmake:

% cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=<type> -DCMAKE_INSTALL_PREFIX=/install/path

        [other options] SRC_ROOT

1.4.5 编译LLVM套件源代码

与自动工具不同,使用CMake,构建类型是在配置中定义的。如果想更改构建类型,可以通过以下调用重新运行cmake:

%cmake-G“Unix Makefiles”-DCMAKE_BILD_TYPE=<TYPE>SRC_ROOT

在运行之间,CMake会保留为所有选项设置的值。CMake定义了以下构建类型:

1. 调试信息

这些构建是默认的。构建系统将编译未优化的工具和库,并启用调试信息和断言。

2. 释放信息

对于这些构建,构建系统将编译启用了优化的工具和库,而不会生成调试信息。CMakes的默认优化级别为-O3。这可以通过在CMAKE命令行上设置CMAKE_CXX_FLAGS_RERELEASE变量来配置。

3. 关联有关信息

这些生成在调试时很有用。它们生成带有调试信息的优化二进制文件。CMakes的默认优化级别为-O2。这可以通过在CMAKE命令行上设置CMAKE_CXX_FLAGS_RELWITHDEBINFO变量来配置。

配置LLVM后,可以通过输入OBJ_ROOT目录并发出以下命令来构建LLVM:

如果构建失败,看看是否使用了已知不编译LLVM的GCC版本。如果机器中有多个处理器,可能希望使用GNUMake提供的一些并行构建选项。例如,可以使用以下命令:

% make -j2

使用LLVM源代码时,有几个特殊目标非常有用:

       make clean

删除生成的所有文件。这包括目标文件、生成的C/C++文件、库和可执行文件:

make install

在$PREFIX下的层次结构中安装LLVM头文件、库、工具和文档,该层次结构由CMAKE_INSTALL_PREFIX指定,默认为/usr/local。

make docs-llvm-html

如果配置为-DLLVM_ENABLE_SPHINX=On,这将在OBJ_ROOT/docs/html处生成一个目录,其中包含html格式的文档。

1.4.6 交叉编译LLVM

可以交叉编译LLVM本身。也就是说,可以创建LLVM可执行文件和库,将其托管在不同于构建(加拿大交叉构建)的平台上。为了生成用于交叉编译的构建文件,CMake提供了一个变量CMake_TOOLCHAIN_FILE,该变量可以定义编译器标志和CMake测试操作期间使用的变量。

这种生成的结果是无法在生成主机上运行,但可以在目标上执行的可执行文件。例如,以下CMake调用可以生成针对iOS的构建文件。这将适用于带有最新Xcode的

macOS:

% cmake -G "Ninja" -DCMAKE_OSX_ARCHITECTURES="armv7;armv7s;arm64"

  -DCMAKE_TOOLCHAIN_FILE=<PATH_TO_LLVM>/cmake/platforms/iOS.cmake

  -DCMAKE_BUILD_TYPE=Release -DLLVM_BUILD_RUNTIME=Off -DLLVM_INCLUDE_TESTS=Off

  -DLLVM_INCLUDE_EXAMPLES=Off -DLLVM_ENABLE_BACKTRACES=Off [options]

  <PATH_TO_LLVM>

注意:由于iOS SDK的限制,在为iOS构建时需要传递一些额外的标志。

有关交叉编译的更多信息,请查看如何使用Clang/LLVM交叉编译Clang/LLFM,以及关于如何交叉编译的Clang文档。

1.4.7 LLVM目标对象文件的位置

LLVM构建系统能够在多个LLVM构建之间共享单个LLVM源树(source tree)。因此,可以使用相同的源树为几个不同的平台或配置构建LLVM。

1)将目录更改为LLVM目标对象文件的定位:

% cd OBJ_ROOT

2)运行cmake

% cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release SRC_ROOT

LLVM构建将在OBJ_ROOT下创建一个与LLVM源树匹配的结构。在源树中存在源文件的每个级别,OBJ_ROOT中将有一个相应的CMakeFiles目录。在该目录下还有另一个目录,其名称以.dir结尾,可以在其中找到每个源的目标文件。

例如:

% cd llvm_build_dir

% find lib/Support/ -name APFloat*

lib/Support/CMakeFiles/LLVMSupport.dir/APFloat.cpp.o

1.4.8 可选配置项目

如果在支持binfmt_misc模块的Linux系统上运行,并且对该系统具有root访问权限,则可以将系统设置为直接执行LLVM位代码文件。要执行此操作,请使用以下命令(如果已经在使用模块,则可能不需要第一个命令):

% mount -t binfmt_misc none /proc/sys/fs/binfmt_misc

% echo ':llvm:M::BC::/path/to/lli:' > /proc/sys/fs/binfmt_misc/register

% chmod u+x hello.bc   (if needed)

% ./hello.bc

这允许直接执行LLVM位代码文件。在Debian上,也可以使用此命令而不是上面的“echo”命令:

% sudo update-binfmts --install llvm /path/to/lli --magic 'BC'

1.5 目录布局

关于LLVM源库的一个有用信息来源是LLVM doxygen文档,可在https://llvm.org/doxygen/得到。以下是对代码布局的简要介绍:

1. llvm/cmake

生成系统文件。

llvm/cmake/modules

为llvm用户定义的选项构建配置。检查编译器版本和链接器标志。

llvm/cmake/平台

针对MSVC的Android NDK、iOS系统和非Windows主机的工具链配置。

2. llvm/examples

一些简单的例子展示了如何使用LLVM作为自定义语言的编译器,包括降低、优化和代码生成。

Kaleidoscope教程:Kaleidoscope语言教程是为一种非平凡的语言运行了一个较好的小编译器的实现,包括手写的lexer、解析器、AST,以及使用LLVM的代码生成支持-包括静态(预先准备好的)和各种实时(JIT)编译方法。

构建AJIT:构建AJIT教程的示例,该教程展示了LLVM的ORC JIT API如何与LLVM的其他部分交互。它还帮助如何重新组合它们,以构建适合用例的自定义JIT。

3. llvm/include

从LLVM库导出的公共头文件。三个主要子目录:

llvm/include/llvm

所有LLVM特定的头文件,以及LLVM不同部分的子目录:Analysis、CodeGen、Target、Transforms等。

llvm/include/lilvm/support

LLVM提供的通用支持库,但不一定特定于LLVM。例如,一些C++STL实用程序和处理库的命令行选项在此处存储头文件。

llvm/include/llvm/config

cmake配置的头文件。它们包装“标准”UNIX和C头文件。源代码可以包括这些头文件,这些头文件自动处理cmake生成的#includes条件。

4. llvm/lib

大多数源文件都在这里。通过将代码放在库中,LLVM可以轻松地在工具之间共享代码。

构建AJIT:构建AJIT教程的示例,该教程展示了LLVM的ORC JIT API如何与LLVM的其他部分交互。它还教导如何重新组合它们,以构建适合用例的自定义JIT。

llvm/include

从LLVM库导出的公共头文件。三个主要子目录:

llvm/include/llvm

所有LLVM特定的头文件,以及LLVM不同部分的子目录:Analysis、CodeGen、Target、Transforms等。

llvm/include/lilvm/support

LLVM提供的通用支持库,但不一定特定于LLVM。例如,一些C++STL实用程序和处理库的命令行选项在此处存储头文件。

llvm/include/llvm/config

cmake配置的头文件。它们包装“标准”UNIX和C头文件。源代码可以包括这些头文件,这些头文件自动处理cmake生成的条件#includes。

llvm/lib

大多数源文件都在这里。通过将代码放在库中,LLVM可以轻松地在工具之间共享代码。

llvm/lib/IR/

实现Instruction和BasicBlock等核心类的核心LLVM源文件。

llvm/lib/AsmParser/

LLVM汇编语言分析器库的源代码。

llvm/lib/比特码/

用于读取和写入位代码的代码。

llvm/lib/分析/

各种程序分析,如调用图、归纳变量、自然循环识别等。

llvm/lib/转换/

IR到IR程序转换,如攻击性死码消除、稀疏条件常数传播、内联、循环不变码运动、全局死码消除等。

llvm/lib/target/

描述代码生成的目标体系结构的文件。例如,llvm/lib/Target/X86保存X86机器描述。

llvm/lib/CodeGen/

代码生成器的主要部分:指令选择器、指令调度和寄存器分配。

llvm/lib/MC/

库在机器代码级别表示和处理代码。处理程序集和目标文件的发布。

llvm/lib/ExecutionEngine/

用于在解释和JIT编译的场景中在运行时直接执行位代码的库。

llvm/lib/support/

与llvm/include/ADT/和llvm/ininclude/Support/中的头文件相对应的源代码。

5. Llvm/bindings

包含LLVM编译器基础结构的绑定,以允许使用C或C++以外的语言编写的程序使用LLVM基础结构。LLVM项目为OCaml和Python提供了语言绑定。

6. llvm/projects

项目不是LLVM的严格组成部分,但与LLVM一起提供。这也是创建自己的基于LLVM的项目的目录,这些项目使用LLVM构建系统。

7. llvm/test

LLVM基础设施上的特性和回归测试,以及其他健全性检查。这些架构旨在快速运行,覆盖大量区域,而不是详尽无遗。

8. test-suite

LLVM的全面正确性、性能和基准测试套件。这是在一个单独的git存储库<https://github.com/llvm/llvm-test-suite>中提供的,因为它在各种许可证下包含大量的第三方代码。有关详细信息,请参阅测试帮助文档。

9. llvm/tools

由上面的库构建的可执行文件,构成了用户界面的主要部分。总是可以通过键入tool_name-help来获得工具的帮助。以下是对最重要的工具的简要介绍。

bugpoint

bugpoint用于调试优化过程或代码生成后端,方法是将给定的测试用例缩小到仍然会导致问题的过程和/或指令的最小数量,而且不管是崩溃还是编译错误。

llvm ar

归档器生成一个包含给定LLVM位代码文件的归档文件,可以选择使用索引以加快查找速度。

Llvm-as

汇编程序将人类可读的LLVM程序集转换为LLVM位代码。

Llvm-dis

反汇编程序将LLVM位代码转换为人类可读的LLVM汇编代码。

Llvm-link

llvm链接将多个llvm模块链接到一个程序中,这并不奇怪。

lli

lli是LLVM解释器,它可以直接执行LLVM位代码(尽管速度很慢)。对于支持它的体系结构(目前是x86、Sparc和PowerPC),默认情况下,lli将充当实时编译器(如果已经编译了该功能),并且执行代码的速度将比解释器快得多。

llc

llc是LLVM后端编译器,将LLVM位代码转换为本机代码汇编文件。

opt

opt读取LLVM位代码,应用一系列LLVM的转换(在命令行中指定),并输出生成的位代码。“opt-help”是获取LLVM中可用的程序转换列表的好方法。

opt还可以对输入LLVM位代码文件运行特定分析并打印结果。主要用于调试分析,或熟悉分析的作用。

10. llvm/utils

使用LLVM源代码的实用程序;有些是构建过程的一部分,因为它们是基础设施部分的代码生成器。

codegen-diff

codegen diff发现LLC生成的代码和LLI生成的代码之间的差异。如果正在调试其中一个,假设另一个生成正确的输出,那么这将非常有用。有关完整的用户手册,请运行“perldoc-codegen-diff”。

emacs/

用于LLVM程序集文件和TableGen描述文件的Emacs和XEmacs语法高亮显示。

getsrcs.sh

查找并输出所有未生成的源文件,如果希望跨目录进行大量开发,并且不想查找每个文件,这将非常有用。使用它的一种方法是从LLVM源代码树的顶部运行,例如:xemacs `utils/getsources.sh`。

llvmgrep

对LLVM中的每个源文件执行egrep -H -n,并向其传递llvmgrep命令行上提供的正则表达式。这是一种在源库中搜索特定正则表达式的有效方法。

tablegen/

包含用于从通用TableGen描述文件生成寄存器描述、指令集描述,甚至汇编程序的工具。

vim/

LLVM程序集文件和TableGen描述文件的vim语法高亮显示。

1.6 使用LLVM工具链的示例

本节给出了一个将LLVM与Clang前端一起使用的示例。

Clang示例

首先,创建一个简单的C文件,将其命名为“hello.C”:

#include <stdio.h>

int main() {

  printf("hello world\n");

  return 0;

}

接下来,将C文件编译为本机可执行文件:

% clang hello.c -o hello

Note

默认情况下,Clang与GCC的工作方式相同。标准的-S和-c参数照常工作(分别生成本机.S或.o文件)。

接下来,将C文件编译为LLVM位代码文件:

% clang -O3 -emit-llvm hello.c -c -o hello.bc

-emit-llvm选项可以与-S或-c选项一起使用,为代码分别输出llvm.ll或.bc文件。这可以对位代码文件使用标准LLVM工具。

以两种形式运行程序。要运行程序,请使用

% ./hello

% lli hello.bc

第二个示例显示了如何调用LLVM JIT与LLI。

使用llvm-dis实用程序查看llvm程序集代码:

% llvm-dis < hello.bc | less

使用LLC代码生成器将程序编译为本机程序集:

% llc hello.bc -o hello.s

将本机汇编语言文件汇编到程序中:

% /opt/SUNWspro/bin/cc -xarch=v9 hello.s -o hello.native   # On Solaris

% gcc hello.s -o hello.native                              # On others

执行本机代码程序:

% ./hello.native

请注意,使用clang直接编译为本机代码(即,当-eemit-llvm选项不存在时)可以执行步骤6/7/8。

1.7 LLVM常用问题

如果在构建或使用LLVM时遇到问题,或者对LLVM有任何其他一般性问题,请参阅常见问题页面。

如果遇到内存和构建时间有限的问题,请尝试使用ninja而不是make进行构建。请考虑使用cmake配置以下选项:

1)-G Ninja

设置此选项,将允许使用Ninja而不是cmake进行构建。使用ninja构建可以显著缩短构建时间,尤其是增量构建,并提高内存使用率。

2)-DLLVM_USE_LINKER

将此选项设置为lld,将显著减少基于ELF的平台(如Linux)上LLVM可执行文件的链接时间。如果是第一次构建LLVM,并且lld不能作为二进制包提供,那么可能希望使用gold链接器作为GNU ld的更快替代方案。

3)-DCMAKE_BILD_TYPE

控制生成的优化级别和调试信息。此设置可能会影响RAM和磁盘的使用,有关详细信息,请参阅CMAKE_BUILD_TYPE。

4)-DLLVM_ENABLE_ASSERTIONS

此选项对于调试版本默认为ON,对于发布版本默认为OFF。如前一个选项中所述,使用Release构建类型并启用断言,可能是使用Debug构建类型的一个很好的替代方案。

5)-DLLVM_PARALEL_LINK_JOBS

将其设置为希望同时运行的作业数。这与make中使用的-j选项类似,但仅适用于链接作业。此选项只能与Ninja一起使用。可能希望使用数量非常少的作业,因为这将大大减少构建过程中使用的内存量。如果内存有限,可能希望将其设置为1。

6)-DLLVM_TARGETS_TO_BUILD

将其设置为希望生成的目标。可能希望将其设置为X86;但是,将在llvm项目/llvm/lib/Target目录中找到完整的目标列表。

7)-DLLVM_OPTIMIZED_TABLEGEN

将此选项设置为ON,以在构建过程中生成完全优化的表生成。这将显著改善构建时间。只有在使用“调试”生成类型时,这才有用。

8)-DLLVM_ENABLE_PROJECTS

将其设置为与要编译的项目(例如clang、lld等)相等。如果编译多个项目,请用分号分隔项目。如果遇到分号的问题,请尝试用单引号将其括起来。

9)-DLLVM_ENABLE_RUNTIMES

将其设置为要编译的运行时(例如libcxx、libcxxabi等)。如果编译多个运行时,请用分号分隔这些项。如果遇到分号的问题,请尝试用单引号将其括起来。

-如果不需要clang静态分析器,请将此选项设置为OFF。这应该会稍微改善构建时间。

10)-DLLVM_USE_SPLIT_DWARF

如果需要调试构建,请考虑将其设置为ON,因为这将减轻链接器上的内存压力。这将使链接速度更快,因为二进制文件将不包含任何调试信息;然而,这将以DWARF目标文件(扩展名.dwo)的形式生成调试信息。这只适用于使用ELF的主机平台,如Linux。

1.8 LLVM链接

本章只是介绍如何使用LLVM做一些简单的事情,还有很多更有趣和复杂的事情可以做,但这里没有记录。有关LLVM的更多信息,请查看:

1)LLVM主页

2)LLVM Doxygen Tree

3)启动使用LLVM的项目