LLVM 系統入門

總覽

歡迎來到 LLVM 專案!

LLVM 專案包含多個組件。專案的核心本身就稱為 “LLVM”。它包含處理中介表示碼並將其轉換為物件檔案所需的所有工具、函式庫和標頭檔。工具包括組譯器、反組譯器、位元碼分析器和位元碼最佳化器。它還包含基本的迴歸測試。

類 C 語言使用 Clang 前端。此組件將 C、C++、Objective C 和 Objective C++ 程式碼編譯為 LLVM 位元碼 – 然後使用 LLVM 從位元碼編譯成物件檔案。

其他組件包括:libc++ C++ 標準函式庫LLD 連結器以及更多組件。

取得原始碼並建置 LLVM

  1. 檢出 LLVM(包含 Clang 等子專案)

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

    • 或者,在 Windows 上

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

    • 為了節省儲存空間並加快檢出時間,您可能需要執行淺層複製。例如,若要取得 LLVM 專案的最新修訂版本,請使用

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

    • 您可能對儲存庫中的使用者分支(用於堆疊式提取請求和還原)不感興趣,您可以透過以下設定從 git fetch(或 git pull)中篩選掉它們

git config --add remote.origin.fetch '^refs/heads/users/*'
git config --add remote.origin.fetch '^refs/heads/revert-*'
  1. 設定並建置 LLVM 和 Clang

    • cd llvm-project

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

      一些常見的建置系統產生器如下:

      • Ninja — 用於產生 Ninja 建置檔。大多數 llvm 開發人員都使用 Ninja。

      • Unix Makefiles — 用於產生與 make 相容的平行 makefile。

      • Visual Studio — 用於產生 Visual Studio 專案和方案。

      • Xcode — 用於產生 Xcode 專案。

      • 如需更完整的清單,請參閱 CMake 文件

      一些常見選項

      • -DLLVM_ENABLE_PROJECTS='...' — 以分號分隔的清單,列出您想要額外建置的 LLVM 子專案。可以包含以下任何一個:clang、clang-tools-extra、lldb、lld、polly 或 cross-project-tests。

        例如,若要建置 LLVM、Clang 和 LLD,請使用 -DLLVM_ENABLE_PROJECTS="clang;lld"

      • -DCMAKE_INSTALL_PREFIX=directory — 為directory 指定您要安裝 LLVM 工具和函式庫的完整路徑名稱(預設為 /usr/local)。

      • -DCMAKE_BUILD_TYPE=type — 控制建置的最佳化層級和偵錯資訊。type 的有效選項為 DebugReleaseRelWithDebInfoMinSizeRel。如需更詳細的資訊,請參閱 CMAKE_BUILD_TYPE

      • -DLLVM_ENABLE_ASSERTIONS=ON — 使用啟用的斷言檢查進行編譯(Debug 建置的預設值為 ON,所有其他建置類型為 OFF)。

      • -DLLVM_USE_LINKER=lld — 使用 lld 連結器進行連結,假設您的系統上已安裝它。如果預設連結器速度較慢,這可以大幅加快連結時間。

      • -DLLVM_PARALLEL_{COMPILE,LINK,TABLEGEN}_JOBS=N — 限制同時平行執行的編譯/連結/tablegen 工作數量。這對於連結尤其重要,因為連結可能會使用大量記憶體。如果您在建置 LLVM 時遇到記憶體問題,請嘗試設定此選項以限制同時執行的編譯/連結/tablegen 工作數量上限。

    • cmake --build build [--target <target>] 或直接使用上面指定的建置系統。

      • 預設目標(即 cmake --build buildmake -C build)將建置所有 LLVM。

      • check-all 目標(即 ninja check-all)將執行迴歸測試,以確保一切運作正常。

      • CMake 將為每個工具和函式庫產生建置目標,而大多數 LLVM 子專案都會產生自己的 check-<project> 目標。

      • 執行序列建置會很。若要提高速度,請嘗試執行平行建置。Ninja 中預設會執行平行建置;對於 make,請使用選項 -j NN,其中 NN 是平行工作數量,例如可用 CPU 數量。

    • 一個基本的 CMake 和建置/測試調用,僅建置 LLVM 而不建置其他子專案

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

      ninja -C build check-llvm

      這將設定具有偵錯資訊的 LLVM 建置,然後編譯 LLVM 並執行 LLVM 測試。

    • 如需 CMake 選項的更詳細資訊,請參閱 CMake

    • 如果您遇到建置或測試失敗,請參閱下方

請參閱LLVM 入門章節,以取得有關設定和編譯 LLVM 的詳細資訊。前往目錄佈局以了解原始碼樹狀結構的佈局。

獨立建置

獨立建置可讓您針對系統上已存在的 clang 或 llvm 函式庫的預先建置版本來建置子專案。

您可以使用 llvm-project 的標準檢出中的原始碼(如上所述)來執行獨立建置,但您也可以從稀疏檢出或從 發行頁面上提供的 tarball 進行建置。

對於獨立建置,您必須安裝 llvm,且 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_ROOTLLVM_EXTERNAL_LIT 選項。下表列出了每個子專案的其他必要選項。

下表列出的子專案支援 check-$subprojinstall 建置目標。

子專案

必要子目錄

必要 CMake 選項

llvm

llvm, cmake, third-party

LLVM_INSTALL_UTILS=ON

clang

clang, cmake

CLANG_INCLUDE_TESTS=ON(僅 check-clang 需要)

lld

lld, cmake

建置獨立 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

需求

在您開始使用 LLVM 系統之前,請檢閱以下給出的需求。預先了解您需要的硬體和軟體可以為您省去一些麻煩。

硬體

已知 LLVM 可在以下主機平台上運作:

作業系統

架構

編譯器

Linux

x861

GCC, Clang

Linux

amd64

GCC, Clang

Linux

ARM

GCC, Clang

Linux

AArch64

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

FreeBSD

AArch64

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

macOS

arm64

Clang

Cygwin/Win32

x861, 3

GCC

Windows

x861

Visual Studio

Windows x64

x86-64

Visual Studio, Clang4

Windows on Arm

ARM64

Visual Studio, Clang4

注意

  1. 程式碼產生支援 Pentium 或更高階處理器

  2. 程式碼產生僅支援 32 位元 ABI

  3. 若要在 Win32 系統上使用 LLVM 模組,您可以將 LLVM 設定為 -DBUILD_SHARED_LIBS=On

  4. Visual Studio 本身可以編譯 LLVM。使用 Clang 時,您也必須安裝 Visual Studio。

請注意,Debug 建置需要大量時間和磁碟空間。僅 LLVM 的建置約需 1-3 GB 的空間。完整建置 LLVM 和 Clang 約需 15-20 GB 的磁碟空間。確切的空間需求會因系統而異。(空間如此大的原因是所有偵錯資訊以及函式庫以靜態方式連結到多個工具中)。

如果您的空間有限,您可以僅建置選定的工具或僅選定的目標。Release 建置所需的空間明顯更少。

LLVM 套件可能可以在其他平台上編譯,但不保證一定能編譯成功。如果編譯成功,LLVM 公用程式應能夠組譯、反組譯、分析和最佳化 LLVM 位元碼。程式碼產生也應能正常運作,儘管產生的原生程式碼可能無法在您的平台上運作。

軟體

編譯 LLVM 需要您安裝數個軟體套件。下表列出了這些必要的套件。「套件」欄是 LLVM 依賴的軟體套件的常用名稱。「版本」欄提供套件「已知可運作」的版本。「注意事項」欄描述 LLVM 如何使用套件,並提供其他詳細資訊。

套件

版本

注意事項

CMake

>=3.20.0

Makefile/工作區產生器

python

>=3.8

自動化測試套件1

zlib

>=1.2.3.4

壓縮函式庫2

GNU Make

3.79, 3.79.1

Makefile/建置處理器3

PyYAML

>=5.1

標頭產生器4

注意

  1. 只有在您想要在 llvm/test 目錄中執行自動化測試套件,或者您計劃使用任何 Python 函式庫、公用程式或綁定時才需要。

  2. 選用,將壓縮/解壓縮功能新增至選定的 LLVM 工具。

  3. 選用,您可以使用 CMake 支援的任何其他建置工具。

  4. 僅在使用 New Headergen 建置 libc 時才需要。主要由 libc 使用。

此外,您的編譯主機預期具備常用的 Unix 公用程式。具體而言:

  • ar — 封存函式庫建置器

  • bzip2 — 用於發行套件產生的 bzip2 命令

  • bunzip2 — 用於發行套件檢查的 bunzip2 命令

  • chmod — 變更檔案的權限

  • cat — 輸出串連公用程式

  • cp — 複製檔案

  • date — 列印目前日期/時間

  • echo — 列印到標準輸出

  • egrep — 擴充正規表示式搜尋公用程式

  • find — 在檔案系統中尋找檔案/目錄

  • grep — 正規表示式搜尋公用程式

  • gzip — 用於發行套件產生的 gzip 命令

  • gunzip — 用於發行套件檢查的 gunzip 命令

  • install — 安裝目錄/檔案

  • mkdir — 建立目錄

  • mv — 移動(重新命名)檔案

  • ranlib — 封存函式庫的符號表建置器

  • rm — 移除(刪除)檔案和目錄

  • sed — 用於轉換輸出的串流編輯器

  • sh — 用於 make 建置腳本的 Bourne shell

  • tar — 用於發行套件產生的磁帶封存

  • test — 測試檔案系統中的事物

  • unzip — 用於發行套件檢查的 unzip 命令

  • zip — 用於發行套件產生的 zip 命令

主機 C++ 工具鏈,包含編譯器與標準函式庫

LLVM 對於主機 C++ 編譯器的要求非常高,因此往往會暴露出編譯器中的錯誤。我們也嘗試密切關注 C++ 語言和函式庫的改進和發展。因此,我們需要現代的主機 C++ 工具鏈(包含編譯器和標準函式庫)才能建置 LLVM。

LLVM 是使用 編碼標準中記載的 C++ 子集編寫的。為了強制執行此語言版本,我們會在我們的建置系統中檢查最常用的主機工具鏈的特定最低版本:

  • Clang 5.0

  • Apple Clang 10.0

  • GCC 7.4

  • Visual Studio 2019 16.8

比這些工具鏈舊的任何版本可能可以運作,但需要使用特殊選項強制執行建置系統,而且實際上並非支援的主機平台。另請注意,這些編譯器的舊版本經常會當機或錯誤編譯 LLVM。

對於不太常用的主機工具鏈(例如 ICC 或 xlC),請注意,可能需要非常新的版本才能支援 LLVM 中使用的所有 C++ 功能。

我們會追蹤某些已知在用作主機工具鏈一部分時會失敗的軟體版本。有時甚至包括連結器。

GNU ld 2.16.X。某些 2.16.X 版本的 ld 連結器會產生非常長的警告訊息,抱怨某些 “.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。

取得現代主機 C++ 工具鏈

本節主要適用於 Linux 和較舊的 BSD。在 macOS 上,您應具有足夠現代的 Xcode,否則您可能需要升級直到達到要求。Windows 沒有「系統編譯器」,因此您必須安裝 Visual Studio 2019(或更新版本)或最新版本的 mingw64。FreeBSD 10.0 和更新版本具有現代的 Clang 作為系統編譯器。

但是,某些 Linux 發行版和某些其他或較舊的 BSD 有時會具有非常舊版本的 GCC。這些步驟嘗試協助您即使在這種系統上也能升級編譯器。但是,如果有可能,我們鼓勵您使用具有符合這些要求的現代系統編譯器的最新版本發行版。請注意,嘗試安裝先前版本的 Clang 和 libc++ 作為主機編譯器是很誘人的,但是 libc++ 在相對較近之前在 Linux 上進行建置的測試或設定並不好。因此,本指南建議僅使用 libstdc++ 和現代 GCC 作為 bootstrap 中的初始主機,然後使用 Clang(以及可能 libc++)。

第一步是安裝最新的 GCC 工具鏈。使用者在版本需求方面遇到困難的最常見發行版是 Ubuntu Precise、12.04 LTS。對於此發行版,一個簡單的選項是安裝 toolchain testing PPA 並使用它來安裝現代 GCC。在 ask ubuntu stack exchange 和具有更新命令的 github gist 上有關於此的非常好的討論。但是,並非所有使用者都可以使用 PPA,而且還有許多其他發行版,因此可能有必要(或者只是有用,如果您在這裡,您畢竟是在進行編譯器開發)從原始碼建置和安裝 GCC。現在執行這些操作也很容易。

安裝特定版本 GCC 的簡單步驟

% gcc_version=7.4.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: version `GLIBCXX_3.4.20' not found。這表示您需要調整 -rpath 連結器旗標。

此方法會將絕對路徑新增至所有可執行檔的 rpath。這對於本機開發來說沒問題。如果您想要發布您建置的二進位檔,以便它們可以在較舊的系統上執行,請將 libstdc++.so.6 複製到 lib/ 目錄中。所有 LLVM 的發行二進位檔都有一個 rpath 指向 $ORIGIN/../lib,因此它們將在那裡找到 libstdc++.so.6。未發布的二進位檔未設定 rpath,因此找不到 libstdc++.so.6。將 -DLLVM_LOCAL_RPATH="$HOME/toolchains/lib64" 傳遞給 cmake 以將絕對路徑新增到 libstdc++.so.6,如上所示。由於這些二進位檔未發布,因此對於它們來說,具有絕對本機路徑是可以的。

當您建置 Clang 時,您需要讓能夠存取現代 C++ 標準函式庫,以便在 bootstrap 的一部分中將其用作您的新主機。有兩種簡單的方法可以做到這一點,一種是與 Clang 一起建置(並安裝)libc++,然後將其與 -stdlib=libc++ 編譯和連結旗標一起使用,或者將 Clang 安裝到與 GCC 相同的前綴中(上面的 $HOME/toolchains)。Clang 將在其自己的前綴中尋找 libstdc++,並在找到時使用它。您也可以為 Clang 新增明確的前綴,以使用 --gcc-toolchain=/opt/my/gcc/prefix 旗標在 GCC 工具鏈中尋找,在使用您剛建置的 Clang 進行 bootstrap 時,將其傳遞給編譯和連結命令。

LLVM 入門

本指南的其餘部分旨在協助您開始使用 LLVM,並為您提供有關 LLVM 環境的一些基本資訊。

本指南的後續章節描述 LLVM 原始碼樹狀結構的一般佈局、使用 LLVM 工具鏈的簡單範例,以及連結,以尋找有關 LLVM 的更多資訊或透過電子郵件取得協助。

術語與符號

在本手冊中,以下名稱用於表示特定於本機系統和工作環境的路徑。這些不是您需要設定的環境變數,而只是在本文檔其餘部分中使用的字串。在以下任何範例中,只需將每個名稱替換為您本機系統上的適當路徑名稱即可。所有這些路徑都是絕對路徑

SRC_ROOT

這是 LLVM 原始碼樹狀結構的頂層目錄。

OBJ_ROOT

這是 LLVM 物件樹狀結構的頂層目錄(即物件檔案和編譯後的程式將放置在其中的樹狀結構。它可以與 SRC_ROOT 相同)。

傳送修補程式

請參閱貢獻

二分法查找提交

請參閱 二分法查找 LLVM 程式碼,以了解如何在 LLVM 上使用 git bisect

還原變更

使用 git 還原變更時,預設訊息會顯示「This reverts commit XYZ」。將其保留在提交訊息的末尾,但在其前面新增一些詳細資訊,說明為何要還原提交。簡要說明和/或指向證明問題的機器人的連結就已足夠。

本機 LLVM 設定

簽出儲存庫後,必須先設定 LLVM 套件原始碼,然後才能建置。此過程使用 CMake。與一般的 configure 腳本不同,CMake 會產生您請求的任何格式的建置檔,以及各種 *.inc 檔案和 llvm/include/llvm/Config/config.h.cmake

變數使用格式 -D<variable name>=<value> 在命令列上傳遞給 cmake。以下變數是開發 LLVM 的人員使用的一些常用選項。

  • CMAKE_C_COMPILER

  • CMAKE_CXX_COMPILER

  • CMAKE_BUILD_TYPE

  • CMAKE_INSTALL_PREFIX

  • Python3_EXECUTABLE

  • LLVM_TARGETS_TO_BUILD

  • LLVM_ENABLE_PROJECTS

  • LLVM_ENABLE_RUNTIMES

  • LLVM_ENABLE_DOXYGEN

  • LLVM_ENABLE_SPHINX

  • LLVM_BUILD_LLVM_DYLIB

  • LLVM_LINK_LLVM_DYLIB

  • LLVM_PARALLEL_LINK_JOBS

  • LLVM_OPTIMIZED_TABLEGEN

如需更多資訊,請參閱常用 CMake 變數清單

若要設定 LLVM,請依照下列步驟執行:

  1. 將目錄變更為物件根目錄

    % cd OBJ_ROOT
    
  2. 執行 cmake

    % cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=<type> -DCMAKE_INSTALL_PREFIX=/install/path
      [other options] SRC_ROOT
    

編譯 LLVM 套件原始碼

與 autotools 不同,使用 CMake 時,您的建置類型是在組態設定時定義的。如果您想要變更您的建置類型,您可以重新執行 cmake,並帶有以下調用

% cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=<type> SRC_ROOT

在多次執行之間,CMake 會保留為所有選項設定的值。CMake 定義了以下建置類型

Debug (除錯)

這些建置是預設值。建置系統將會編譯未經最佳化的工具和函式庫,並包含除錯資訊,且啟用斷言。

Release (發佈)

對於這些建置,建置系統將會編譯已啟用最佳化的工具和函式庫,且不產生除錯資訊。CMake 的預設最佳化層級為 -O3。這可以透過在 CMake 命令列上設定 CMAKE_CXX_FLAGS_RELEASE 變數來進行配置。

RelWithDebInfo (發佈版本含除錯資訊)

這些建置在除錯時很有用。它們產生最佳化的二進位檔,並包含除錯資訊。CMake 的預設最佳化層級為 -O2。這可以透過在 CMake 命令列上設定 CMAKE_CXX_FLAGS_RELWITHDEBINFO 變數來進行配置。

一旦您設定好 LLVM,您可以透過進入 OBJ_ROOT 目錄並發出以下命令來建置它

% make

如果建置失敗,請在此處檢查,看看您是否正在使用已知無法編譯 LLVM 的 GCC 版本。

如果您的機器有多個處理器,您可能會希望使用 GNU Make 提供的某些平行建置選項。例如,您可以使用以下命令

% 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 格式的文件。

交叉編譯 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/LLVM關於如何一般交叉編譯的 Clang 文件,以取得更多關於交叉編譯的資訊。

LLVM 物件檔案的位置

LLVM 建置系統能夠在多個 LLVM 建置之間共享單一 LLVM 原始碼樹。因此,可以使用相同的原始碼樹為多個不同的平台或配置建置 LLVM。

  • 變更目錄到 LLVM 物件檔案應該存放的位置

    % cd OBJ_ROOT
    
  • 執行 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

可選的配置項目

如果您在支援 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'

目錄佈局

關於 LLVM 原始碼庫的一個有用的資訊來源是 LLVM doxygen 文件,可在 https://llvm.dev.org.tw/doxygen/ 取得。以下是對程式碼佈局的簡要介紹

llvm/cmake

產生系統建置檔案。

llvm/cmake/modules

用於 llvm 使用者定義選項的建置配置。檢查編譯器版本和連結器旗標。

llvm/cmake/platforms

用於 Android NDK、iOS 系統和非 Windows 主機以鎖定 MSVC 的工具鏈配置。

llvm/examples

  • 一些簡單的範例,展示如何將 LLVM 用作自訂語言的編譯器 - 包括降低、最佳化和程式碼產生。

  • Kaleidoscope 教學:Kaleidoscope 語言教學,引導完成為非平凡語言實作一個精巧的編譯器,包括手寫的詞法分析器、語法分析器、AST,以及使用 LLVM 的程式碼產生支援 - 包括靜態(預先)和各種即時(JIT)編譯方法。給完全初學者的 Kaleidoscope 教學

  • BuildingAJIT:BuildingAJIT 教學的範例,展示了 LLVM 的 ORC JIT API 如何與 LLVM 的其他部分互動。它也教導如何重新組合它們以建置適合您用例的自訂 JIT。

llvm/include

從 LLVM 函式庫匯出的公用標頭檔。三個主要子目錄

llvm/include/llvm

所有 LLVM 特定的標頭檔,以及 LLVM 不同部分的子目錄:AnalysisCodeGenTargetTransforms 等…

llvm/include/llvm/Support

與 LLVM 一起提供,但不一定專屬於 LLVM 的通用支援函式庫。例如,一些 C++ STL 公用程式和命令列選項處理函式庫將標頭檔儲存在此處。

llvm/include/llvm/Config

cmake 配置的標頭檔。它們封裝了「標準」UNIX 和 C 標頭檔。原始碼可以包含這些標頭檔,這些標頭檔會自動處理 cmake 產生的條件式 #includes。

llvm/lib

大多數原始檔都在這裡。透過將程式碼放入函式庫中,LLVM 可以輕鬆地在工具之間共享程式碼。

llvm/lib/IR/

核心 LLVM 原始檔,實作核心類別,如 Instruction 和 BasicBlock。

llvm/lib/AsmParser/

LLVM 組譯語言語法分析器函式庫的原始碼。

llvm/lib/Bitcode/

用於讀取和寫入位元碼的程式碼。

llvm/lib/Analysis/

各種程式分析,例如呼叫圖、歸納變數、自然迴圈識別等。

llvm/lib/Transforms/

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/include/Support/ 中標頭檔的原始碼。

llvm/bindings

包含 LLVM 編譯器基礎架構的綁定,以允許以 C 或 C++ 以外的語言編寫的程式利用 LLVM 基礎架構。LLVM 專案為 OCaml 和 Python 提供語言綁定。

llvm/projects

並非嚴格意義上屬於 LLVM 但隨 LLVM 一起發佈的專案。這也是建立您自己的基於 LLVM 的專案的目錄,這些專案利用 LLVM 建置系統。

llvm/test

LLVM 基礎架構上的功能和迴歸測試以及其他健全性檢查。這些旨在快速執行並涵蓋廣泛的範圍,而無需詳盡無遺。

test-suite

LLVM 的全面正確性、效能和基準測試套件。這包含在一個 獨立的 git 儲存庫 <https://github.com/llvm/llvm-test-suite> 中,因為它包含大量在各種許可證下的第三方程式碼。有關詳細資訊,請參閱測試指南文件。

llvm/tools

由上述函式庫建置的可執行檔,構成使用者介面的主要部分。您可以隨時透過輸入 tool_name -help 來取得工具的說明。以下是對最重要工具的簡要介紹。更詳細的資訊請參閱 命令指南

bugpoint

bugpoint 用於除錯最佳化過程或程式碼產生後端,方法是將給定的測試案例縮小到仍然會導致問題(無論是崩潰還是錯誤編譯)的最少數量的過程和/或指令。有關使用 bugpoint 的更多資訊,請參閱 HowToSubmitABug.html

llvm-ar

封存器產生一個包含給定 LLVM 位元碼檔案的封存檔,可選擇帶有索引以加快查找速度。

llvm-as

組譯器將人類可讀的 LLVM 組譯轉換為 LLVM 位元碼。

llvm-dis

反組譯器將 LLVM 位元碼轉換為人類可讀的 LLVM 組譯。

llvm-link

llvm-link,不出所料,將多個 LLVM 模組連結到單一程式中。

lli

lli 是 LLVM 直譯器,可以直接執行 LLVM 位元碼(雖然非常慢…)。對於支援它的架構(目前為 x86、Sparc 和 PowerPC),預設情況下,lli 將充當即時編譯器(如果該功能已編譯進來),並且將比直譯器快得多地執行程式碼。

llc

llc 是 LLVM 後端編譯器,它將 LLVM 位元碼轉換為原生程式碼組譯檔案。

opt

opt 讀取 LLVM 位元碼,應用一系列 LLVM 到 LLVM 的轉換(在命令列上指定),並輸出結果位元碼。‘opt -help’ 是取得 LLVM 中可用程式轉換列表的好方法。

opt 也可以在輸入的 LLVM 位元碼檔案上執行特定的分析,並印出結果。主要用於除錯分析,或讓您熟悉分析的作用。

llvm/utils

用於處理 LLVM 原始碼的公用程式;有些是建置過程的一部分,因為它們是基礎架構某些部分的程式碼產生器。

codegen-diff

codegen-diff 查找 LLC 產生的程式碼和 LLI 產生的程式碼之間的差異。如果您正在除錯其中一個,假設另一個產生正確的輸出,這將很有用。有關完整的使用者手冊,請執行 `perldoc codegen-diff'

emacs/

Emacs 和 XEmacs 的 LLVM 組譯檔案和 TableGen 描述檔案的語法高亮顯示。請參閱 README 以取得有關使用它們的資訊。

getsrcs.sh

查找並輸出所有非產生的原始檔,如果您希望跨目錄進行大量開發,並且不想查找每個檔案,這會很有用。一種使用方法是執行,例如:從 LLVM 原始碼樹的頂層執行 xemacs `utils/getsources.sh`

llvmgrep

在 LLVM 中的每個原始檔上執行 egrep -H -n,並將在 llvmgrep 命令列上提供的正規表示式傳遞給它。這是搜尋原始碼庫中特定正規表示式的有效方法。

TableGen/

包含用於從通用 TableGen 描述檔案產生暫存器描述、指令集描述甚至組譯器的工具。

vim/

vim 對 LLVM 組譯檔案和 TableGen 描述檔案的語法高亮顯示。請參閱 README 以了解如何使用它們。

使用 LLVM 工具鏈的範例

本節提供一個使用 LLVM 和 Clang 前端的範例。

使用 clang 的範例

  1. 首先,建立一個簡單的 C 檔案,命名為 'hello.c'

    #include <stdio.h>
    
    int main() {
      printf("hello world\n");
      return 0;
    }
    
  2. 接下來,將 C 檔案編譯成原生可執行檔

    % clang hello.c -o hello
    

    注意

    預設情況下,Clang 的運作方式與 GCC 類似。標準的 -S 和 -c 參數照常運作(分別產生原生 .s 或 .o 檔案)。

  3. 接下來,將 C 檔案編譯成 LLVM 位元碼檔案

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

    -emit-llvm 選項可以與 -S 或 -c 選項一起使用,以針對程式碼發出 LLVM .ll.bc 檔案(分別)。這允許您在位元碼檔案上使用標準 LLVM 工具

  4. 以兩種形式執行程式。若要執行程式,請使用

    % ./hello
    

    % lli hello.bc
    

    第二個範例展示如何調用 LLVM JIT,lli

  5. 使用 llvm-dis 公用程式來查看 LLVM 組譯碼

    % llvm-dis < hello.bc | less
    
  6. 使用 LLC 程式碼產生器將程式編譯為原生組譯

    % llc hello.bc -o hello.s
    
  7. 將原生組譯語言檔案組譯成程式

    % /opt/SUNWspro/bin/cc -xarch=v9 hello.s -o hello.native   # On Solaris
    
    % gcc hello.s -o hello.native                              # On others
    
  8. 執行原生碼程式

    % ./hello.native
    

    請注意,當不使用 -emit-llvm 選項時,使用 clang 直接編譯為原生碼(即)會為您執行步驟 6/7/8。

常見問題

如果您在建置或使用 LLVM 時遇到問題,或者如果您對 LLVM 有任何其他一般問題,請查閱常見問題解答頁面。

如果您在記憶體有限和建置時間方面遇到問題,請嘗試使用 ninja 而不是 make 進行建置。請考慮使用 cmake 配置以下選項

  • -G Ninja

    設定此選項將允許您使用 ninja 而不是 make 進行建置。使用 ninja 建置可以顯著縮短您的建置時間,尤其是在增量建置時,並改善您的記憶體使用量。

  • -DLLVM_USE_LINKER

    將此選項設定為 lld 將顯著減少 LLVM 可執行檔的連結時間,尤其是在 Linux 和 Windows 上。如果您是第一次建置 LLVM 並且 lld 作為二進位套件不可用,那麼您可能希望使用 gold 連結器作為 GNU ld 的更快替代方案。

  • -DCMAKE_BUILD_TYPE

    控制建置的最佳化層級和除錯資訊。此設定可能會影響 RAM 和磁碟使用量,有關更多資訊,請參閱 CMAKE_BUILD_TYPE

  • -DLLVM_ENABLE_ASSERTIONS

    對於 Debug 建置,此選項預設為 ON,對於 Release 建置,則預設為 OFF。如前一個選項中所述,使用 Release 建置類型並啟用斷言可能是使用 Debug 建置類型的一個很好的替代方案。

  • -DLLVM_PARALLEL_LINK_JOBS

    將此設定為您希望同時運行的作業數量。這與 make 使用的 -j 選項類似,但僅適用於連結作業。此選項只能與 ninja 一起使用。您可能希望使用非常少的作業數量,因為這將大大減少建置過程中使用的記憶體量。如果您的記憶體有限,您可能希望將其設定為 1

  • -DLLVM_TARGETS_TO_BUILD

    將此設定為您希望建置的目標。您可能希望將其僅設定為主機架構。例如,如果您使用的是 Intel 或 AMD 機器,則為 X86。您將在 llvm-project/llvm/lib/Target 目錄中找到目標的完整列表。

  • -DLLVM_OPTIMIZED_TABLEGEN

    將其設定為 ON 以在您的建置期間產生完全最佳化的 TableGen 編譯器,即使該建置是 Debug 建置。這將顯著縮短您的建置時間。如果您的目的是除錯 TableGen 編譯器,則不應啟用此選項。

  • -DLLVM_ENABLE_PROJECTS

    將此設定為您希望編譯的專案(例如 clanglld 等)。如果編譯多個專案,請用分號分隔項目。如果您遇到分號問題,請嘗試用單引號將其括起來。

  • -DLLVM_ENABLE_RUNTIMES

    將此設定為您希望編譯的執行時期(例如 libcxxlibcxxabi 等)。如果編譯多個執行時期,請用分號分隔項目。如果您遇到分號問題,請嘗試用單引號將其括起來。

  • -DCLANG_ENABLE_STATIC_ANALYZER

    如果您不需要 clang 靜態分析器,請將此選項設定為 OFF。這應該會稍微縮短您的建置時間。

  • -DLLVM_USE_SPLIT_DWARF

    如果您需要除錯建置,請考慮將其設定為 ON,因為這將減輕連結器上的記憶體壓力。這將使連結速度更快,因為二進位檔將不包含任何除錯資訊。相反,除錯資訊位於單獨的 DWARF 物件檔案中(副檔名為 .dwo)。這僅適用於使用 ELF 的主機平台,例如 Linux。

  • -DBUILD_SHARED_LIBS

    將其設定為 ON 將會建置共享函式庫而不是靜態函式庫。這將減輕連結器上的記憶體壓力。但是,這僅應在開發 llvm 時使用。有關更多資訊,請參閱 BUILD_SHARED_LIBS