>

LLVM 简介

LLVM 全称是 Low Level Virtual Machine,它是源自 the University of Illinois 的一个研究项目,该项目旨在提供一个现代化的编译机制,使得对任何编程语言既可以做到静态编译也可以动态编译,而且非常高效。后来 LLVM 项目逐渐发展,并孵化了许多子项目,比如 Clang,LLDB, OpenMP 等。

LLVM 核心库(core libraries)提供了一个源和目标相互独立的优化器,也即一个通用的编译优化和代码生成平台,还为对许多主流 CPU 提供了代码生成的支持。LLVM 定义了一个中间语言 LLVM IR,只要前端把代码编译成 LLVM IR,就可以使用 LLVM 丰富的优化模块和代码生成模块。换句话说,LLVM 让“创造一种计算机语言”变成了一个相对容易的事情,这件事情会有深远的影响。

Clang 作为 LLVM 原生的 “ C/C++/Objective-C 编译器前端,是作为编译 C/C++ 代码的除 GCC 工具链之外的另一种选择。其有许多优点(比如像 LLVM 主页 所介绍的 Debug 模式下编译 Object C 速度比 GCC 快3倍,并且提供更加可读的编译过程中的出错信息等等),更多关于 Clang 的介绍可以去官网查看。

从代码上说,Clang 结构更简单。因为 Clang 只需要完成词法和语法分析,代码优化和机器代码的生成工作由 LLVM 完成。所以和全部由自己包下的 GCC 比起来,Clang 可以更专注地做好一件事。

而且,这种结构也使 Clang 可以被单独拿出来用在其他的程序里。比如 Vim 的 clang_complete 插件就是利用 Clang 进行语法分析后给出精确的自动补全和语法错误提示的。而 GCC 就没法很方便地做到这一点。

在实用性方面,除了有更快的编译速度更快和更友好的出错提示外,Clang 还内置有静态分析工具,可以对代码进行静态分析(clang –analyze)。这也是 GCC 做不到的。

另外,有关 Clang 和 GCC 的更多比较,还可以看知乎上这个帖子

简单来说,Clang 和 CompileRT 这两个子项目为 LLVM 提供了 C, C++, Objective C 和 Objective C++ 的前端及动态库支持,使得我们可以使用 Clang 来编译和构建项目。

由于 LLVM + Clang 项目得到了大财主 Apple 公司的大力赞助,最近这些年发展非常快,

本文介绍如何从源码编译安装 LLVM + Clang,截止本文所写日期最新版本是 Clang 3.9.1。

源码获取

要编译构建 Clang,必须先构建 LLVM 核心库。LLVM 核心库可以下载最新的发行包。也可以从远程库拉取代码。
Clang 源码 :http://llvm.org/releases/3.9.1/cfe-3.9.1.src.tar.xz
Compiler 源码 :http://llvm.org/releases/3.9.1/compiler-rt-3.9.1.src.tar.xz

最新的 LLVM 依赖 cmake 3.7.1,同时 还需要确保 libffi-3.2.1 和 Python-2.7.13 已经安装。

获取远程库代码

LLVM core, Clang 和 Compiler-RT 3个项目的源码是在独立的远程库中的,因此我们需要分别把这3个项目都拉取下来,同时还需要按照一定的组织结构放置。我们在本地创建一个 tmp 文件夹作为顶层目录来放置这3个项目的源码,llvm core 放在 tmp 目录下,而 clang 则放在 llvm 目录的 tools/下, complier-rt 放在 llvm 目录的 projects/ 下。

三个项目的源码拉取完成之后,组织结构应该如下所示:

llvm/
    /tools/clang
    /projects/compiler-rt

1.Checkout LLVM:

$ cd tmp
$ svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm

2. Checkout Clang

$ cd llvm/tools
$ svn co http://llvm.org/svn/llvm-project/cfe/trunk clang

3. Checkout Compiler-RT(Optional)

$ cd llvm/projects
$ svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt

编译及安装

LLVM 禁止源码内编译(in-tree build is not supported),因此我们需要在源码目录外新建一个单独的目录来存放编译后的文件,比如,在与 llvm 的同级的目录中新建一个 build 目录。然后进入该 build 目录进行编译。

需要先执行 cmake 命令生成 Makefile 或者 build.ninja 文件,这一步会检查编译所需的各项依赖(也即编译所需的环境要求),这里,我使用 Ninja 作为编译器并生成对应的 build.ninja,并指定 llvm 的安装目录为 /opt/llvm

$ cd build
$ cmake -G Ninja ../ \
  -DCMAKE_INSTALL_PREFIX=/opt/llvm  \
  -DCMAKE_BUILD_TYPE=Release \
  -DLLVM_ENABLE_FFI=ON \
  -DLLVM_BUILD_LLVM_DYLIB=ON \
  -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
  -DLLVM_TARGETS_TO_BUILD="host;AMDGPU" 
  -Wno-dev

如果本地已经安装 libffi 库,但依然提示找不到 libffi 库,
可以通过 -DFFI_INCLUDE_DIR=/usr/include-DFFI_LIBRARY_DIR=/usr/lib64 来手动指定。

NOTE: 编译时最好指定下面两个选项:

  • –enable-optimized 打开优化,默认情况下是关闭的。这样会生成大量 debug 信息,以致于产生的文件可能高达 9.4 G 之多,这可能很快耗尽磁盘空间导致编译失败。
  • –enable-targets=host-only 选择目标平台,默认情况下会生成所有平台的。这里设置成 host-only 只选择本机即可。

成功检测并生成 build.ninja 文件之后,直接执行 ninja 命令进行编译构建。
由于 LLVM + Clang 比较庞大,编译这一步会比较漫长,ninja 会默认使用系统所有 CPU 进行并发编译,这样的好处是编译速度非常快,但是会导致链接过程占用非常庞大的内存空间,因此如果系统的内存不够的话,最好不要使用所有的核来编译你,最好手动指定并发核数(比如执行 ninja -j4 来指定并发数为 4)。这样可以避免由于内存不够而导致的编译出错。

编译成功之后执行 ninja install 来完成安装。

所有安装的二进制程序包括:

bugpoint, c-index-test, clang, clang++ (symlinks to clang-), clang-, clang-check, clang-cl, clang-format, git-clang-format, llc, lli, llvm-ar, llvm-as, llvm-bcanalyzer, llvm-config, llvm-cov, llvm-c-test, llvm-cxxdump, llvm-diff, llvm-dis, llvm-dsymutil, llvm-dwarfdump, llvm-dwp, llvm-extract, llvm-lib (symlink to llvm-ar), llvm-link, llvm-lto, llvm-mc, llvm-mcmarkup, llvm-nm, llvm-objdump, llvm-pdbdump, llvm-profdata, llvm-ranlib (symlink to llvm-ar), llvm-readobj, llvm-rtdyld, llvm-size, llvm-split, llvm-stress, llvm-symbolizer, llvm-tblgen, obj2yaml, opt, sancov, sanstats, scan-build, scan-view, verify-uselistorder, and yaml2obj

安装的库文件包括:
BugpointPasses.so, LLVMHello.so, libLLVM.so, libLLVM.a (57 libraries), libLTO.so, libclang.so and libclang.a (24 libraries)

libc++ 介绍

libc++ 是由 Apple 公司为其 Mac OS X 平台专门开发的一个 C++ 标准库,其是针对 Clang 而开发完成的,包含了对 C++11 的完整支持,可以作为 Clang 的“御用” C++ 库。libc++ 作为另一个 C++ 标准的实现(传统的是 libstdc++),其与 Clang 的关系就像 libstdc++ 和 GCC 之间的关系一样。但是由于 Apple 公司向来喜欢走不兼容的路线,它在最新版的 Max OS X 中没有提供最新版的 libstdc++ 库(支持 C++11),如果需要使用 libstdc++ 库的话我们必须手动编译安装。

需要注意的是,按照上述流程安装完 LLVM + Clang 之后,默认是没有安装 libc++ 的,但 Clang 也可以用 libstdc++,所以执行 clang++ --std=c++11 main.cpp 就可以编译 C++11 的源码。

编译安装 libc++

1. Checkout libc++:

1
2
cd llvm/projects
svn co http://llvm.org/svn/llvm-project/libcxx/trunk libcxx

2. Checkout libc++abi:

1
2
cd llvm/projects
svn co http://llvm.org/svn/llvm-project/libcxxabi/trunk libcxxabi

3. Configure and build libc++ with libc++abi:
注意: LLVM 只支持使用 CMake 来构建编译配置
推荐使用 Clang 来编译构建 libc++。

1
2
3
4
5
cd build
cmake -G Makefile -DLLVM_PATH=path/to/llvm \
-DLIBCXX_CXX_ABI=libcxxabi \
-DLIBCXX_CXX_ABI_INCLUDE_PATHS=path/to/libcxxabi/include \
path/to/libcxx

注意:如果本机已经有一个 libc++ 了,那么不要随意覆盖它,可能会导致由其编译而生成的库不再工作。可以使用 Cmake 的 CMAKE_INSTALL_PREFIX 选项去指定一个安全的安装位置。

配置完成之后,执行下列命令进行构建:

1
2
make cxx                                   — will build libc++ and libc++abi.
make check-cxx check-cxxabi — will run the test suites.

编译完成之后执行下列命令安装

1
make install
下面是 LLVM + Clang 所安装的程序简介:

bugpoint      the automatic test case reduction tool.
clang       the Clang C, C++, and Objective-C compiler.
clang-format   a tool to format C/C++/Java/JavaScript/Objective-C/Protobuf code.
llc        the LLVM static compiler.
lli        used to directly execute programs from LLVM bitcode.
llvm-ar      the LLVM archiver.
llvm-as      the LLVM assembler.
llvm-bcanalyzer  the LLVM bitcode analyzer.
llvm-config    Prints LLVM compilation options.
llvm-cov     used to emit coverage information.
llvm-c-test    bytecode disassembler.
llvm-cxxdump   used as a C++ ABI Data Dumper.
llvm-diff     the LLVM structural ‘diff’.
llvm-dis     the LLVM disassembler.
llvm-dsymutil   a tool used to manipulate archived DWARF debug symbol files,
          compatible with the Darwin command dsymutil.
llvm-dwarfdump   prints the content of DWARF sections in object files.
llvm-dwp      merges split DWARF files.
llvm-extract    used to extract a function from an LLVM module.
llvm-link      the LLVM linker.
llvm-lto      the LLVM LTO (link time optimization) linker.
llvm-mc      a standalone machine code assembler/disassembler.
llvm-nm      used to list LLVM bitcode and object file’s symbol table.
llvm-objdump   an LLVM object file dumper.
llvm-pdbdump   used as PDB Dumper.
llvm-profdata   a small tool to manipulate and print profile data files.
llvm-ranlib    used to generate an index for a LLVM archive.
llvm-readobj   displays low-level format-specific information about object files.
llvm-rtdyld    the LLVM MC-JIT tool.
llvm-size     the LLVM object size dumper.
llvm-split     the LLVM module splitter.
llvm-stress    used to generate random .ll files.
llvm-symbolizer  converts adresses into source code locations.
llvm-tblgen    the LLVM Target Description To C++ Code Generator.
obj2yaml     takes an object file,and produces a YAML representation of it.
opt        the LLVM optimizer.
sancov      the sanitizer coverage processing tool.
sanstats     the sanitizer statistics processing tool.
scan-build    a Perl script that invokes the Clang static analyzer.
scan-view     a viewer for Clang static analyzer results.
verify-uselistorder the LLVM tool to verify use-list order.
yaml2obj     takes a YAML representation of an object file and converts it to an binary file.

参考

http://clang.llvm.org/get_started.html
https://libcxx.llvm.org/
http://libcxx.llvm.org/docs/BuildingLibcxx.html
https://github.com/maidsafe-archive/MaidSafe/wiki/Hacking-with-Clang-llvm-abi-and-llvm-libc


如果你对我的文章感兴趣,欢迎留言或者关注我的专栏。

微信公众号:“知辉”

搜索“deliverit”或

扫描二维码