0.LLVM是什么
LLVM項目是可重用(reusable)、模塊化(modular)的編譯器以及工具鏈(toolchain)技術的集合,有人將其理解為“底層虛擬機(Low Level Virtual Machine)”的簡稱,但是官方原話為:
“The name “LLVM” itself is not an acronym; it is the full name of the project.”
意思是:LLVM不是首字母縮寫,而是這整個項目的全名。
LLVM項目的發展起源於2000年伊利諾伊大學厄巴納-香檳分校維克拉姆·艾夫(Vikram Adve)與克里斯·拉特納(Chris Lattner)的研究,他們想要為所有靜態及動態語言創造出動態的編譯技術。2005年,蘋果計算機雇用了克里斯·拉特納及他的團隊為蘋果計算機開發應用程序系統,LLVM為現今Mac OS X及iOS開發工具的一部分。
1.LLVM&&Clang安裝
官網安裝教程在這里。這里簡單介紹一下。
Linux環境
1.1.下載有關庫
$ sudo apt-get install cmake $ sudo apt-get install git $ sudo apt-get install gcc $ sudo apt-get install g++
注意最新的llvm-project需要>=3.14版本的cmake,apt安裝的不是最新的(我的情況),需要手動編譯安裝
1.2.下載項目源碼
$ git clone https://github.com/llvm/llvm-project.git
上面這個命令非常慢。通過創建一個shallow clone,可以加快速度。Shallow clone可以節約存儲並加速checkout時間,使用這個命令:
$ git clone --depth=1 https://github.com/llvm/llvm-project.git
1.3.構建項目
$ cd llvm-project
創建build目錄
$ mkdir build $ cd build
利用cmake構建
$ cmake -G <generator> [options] ../llvm
常用的generator有:
-
Unix Makefiles
— 生成和 make 兼容的並行的 makefile Ninja
— 生成一個 Ninja 編譯文件,大多數 LLVM 開發者使用 NinjaVisual Studio
— 生成一個 Visual Studio 項目Xcode
— 生成一個 Xcode 項目
個人使用Unix Makefiles
常用的options有:
- DCMAKE_INSTALL_PREFIX=directory ——為目錄指定要在其中安裝LLVM工具和庫的完整路徑名(默認/usr/local)。
- DCMAKE_BUILD_TYPE=type ——type選項有Debug,Release,RelWithDebInfo和MinSizeRel。默認值為Debug。
- DLLVM_ENABLE_ASSERTIONS=On ——啟用斷言檢查進行編譯。
- DLLVM_ENABLE_PROJECTS=”…” ——要另外構建的LLVM子項目的列表,以’;’分隔。例如要構建LLVM,Clang,libcxx和libcxxabi,使用:
DLLVM_INSTALL_PROJECTS="clang;libcxx;libcxxabi"
- DLLVM_TARGETS_TO_BUILD=”…” ——構建針對的平台的部分項目,以’;’分隔。默認面向所有平台編譯(all),指定只編譯自己需要的CPU架構可以節省時間。
官方文檔在這。
由於全部構建真的很耗費資源和時間,我使用的構建clang命令(可供參考):
$ cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86" -DLLVM_ENABLE_PROJECTS=clang -DLLVM_USE_LINKER=gold -G "Unix Makefiles" ../llvm
當然你如果願意(而且設備跑得動)也可以:
$ cmake -G "Unix Makefiles" ../llvm
我2G1核的小水管只構建clang都會 "virtual memory exhausted: Cannot allocate memory"
1.4.編譯
$ make [-j <core>] $ sudo make install
直接make也可以,但LLVM也支持並行編譯,其中core取決於核心數。如:
$ make -j 4
這兩步一般會很久……甚至內存不夠而終止
1.5.測試
在編譯結束后嘗試在命令行中使用clang:
$ clang -v
本人測試結果,,,失敗,因為編譯時內存不夠,apt install clang不想嗎
apt安裝后結果如下:
root@instance-kdpxvah7:~/llvm-project/build# clang -v clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final) Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7 Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0 Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8 Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7 Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.5.0 Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8 Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0 Candidate multilib: .;@m64 Selected multilib: .;@m64
編寫一段C語言代碼試試看(C++也可以):
//helloworld.c #include <stdio.h> int main() { printf("hello world\n"); return 0; }
用clang編譯:
$ clang helloworld.c -o hello.out
$ ./hello.out
如果是C++代碼則:
//helloworld.cpp #include <iostream> using namespace std; int main() { cout << "hello world" << endl; return 0; }
用clang編譯(注意命令是clang++,本人剛開始只寫clang提示編譯錯誤…):
$ clang++ helloworld.cpp -o hello.out $ ./hello.out
大功告成!
2.LLVM簡介
用戶文檔:llvm.org/docs/LangRef.html
LLVM是基於靜態單一分配的表示形式,可提供類型安全性、底層操作、靈活性,並且適配幾乎所有高級語言,具有通用的代碼表示。現在LLVM已經成為多個編譯器和代碼生成相關子項目的母項目。
The LLVM code representation is designed to be used in three different forms: as an in-memory compiler IR, as an on-disk bitcode representation (suitable for fast loading by a Just-In-Time compiler), and as a human readable assembly language representation.
其中,LLVM提供了完整編譯系統的中間層,並將中間語言(Intermediate Repressentation, IR)從編譯器取出並進行最優化,最優化后的IR接着被轉換及鏈接到目標平台的匯編語言。
我們知道,傳統編譯器主要結構為:
Frontend:前端,詞法分析、語法分析、語義分析、生成中間代碼
Optimizer:優化器,進行中間代碼優化
Backend:后端,生成機器碼
就是編譯原理課上學的那一套流程
LLVM主要結構:
也就是說,對於LLVM來說,不同的前后端使用統一的中間代碼LLVM IR。如果需要支持一種新的編程語言/硬件設備,那么只需要實現一個新的前端/后端就可以了(從這里可以看出LLVM的作用),而優化截斷是一個通用的階段,針對統一的LLVM IR,都不需要對於優化階段修改。對比GCC,其前端和后端基本耦合在一起,所以GCC支持一門新的語言或者目標平台會變得很困難。
一個更具體的例子:
Objective-C
與swift
都采用Clang
作為編譯器前端,編譯器前端主要進行語法分析、語義分析、生成中間代碼,在這個過程中,會進行類型檢查,如果發現錯誤或者警告會標注出來在哪一行。
編譯器后端會進行機器無關的代碼優化,生成機器語言,並且進行機器相關的代碼優化,根據不同的系統架構生成不同的機器碼。C++
,Objective-C
都是編譯語言。編譯語言在執行的時候,必須先通過編譯器生成機器碼。
3.clang簡介
Clang是LLVM針對C語言及其家族語言的前端(a C language family frontend for LLVM)。它的主要目標是提供一個GNU編譯器套裝(GCC)的替代品,支持GNU編譯器大多數便已設置以及非官方語言拓展。項目包括Clang前端和Clang靜態分析器。
The Clang project provides a language front-end and tooling infrastructure for languages in the C language family (C, C++, Objective C/C++, OpenCL, CUDA, and RenderScript) for the LLVM project. Both a GCC-compatible compiler driver (clang) and an MSVC-compatible compiler driver (clang-cl.exe) are provided. You can get and build the source today.
Clang項目為LLVM項目中的C語言家族提供了一個語言前端和工具基礎設施。其提供了兼容GCC和MSVC的編譯器驅動程序(clang和clang-cl.exe)。
官方手冊:http://clang.llvm.org/docs/UsersManual.html#basicusage
針對於GCC,Clang的優點有:
- 占用內存小
- 設計清晰簡單,容易理解
- 編譯速度快
- 設計偏向模塊化,易於集成
- 診斷信息可讀性強
3.0.Clang(Clang++)的使用
我們先隨便寫一段以下代碼:
//test.cpp #include <iostream> #include <algorithm> using namespace std; int a[10] = {4,2,7,5,6,1,8,9,3,0}; int main() { for(int i = 0; i < 10; ++i) cout << a[i] << (i == 9?"\n":" "); sort(a,a+10); for(int i = 0; i < 10; ++i) cout << a[i] << (i == 9?"\n":" "); return 0; }
3.1.生成預處理文件
$ clang++ -E test.cpp -o test.i
3.2.生成匯編程序
$ clang++ -S test.i
3.3.生成目標文件
$ clang++ -c test.s
3.4.生成可執行文件
$ clang++ -o test.out test.o
3.5.查看Clang編譯的過程
$ clang -ccc-print-phases A.c
- 0.獲取輸入:A.c文件,C語言, A.c
- 1.預處理器:處理define、include等, A.i
- 2.編譯:生成中間代碼(IR), ir
- 3.后端:生成匯編代碼, A.s
- 4.匯編:生成目標代碼, A.o
- 5.鏈接器:鏈接其他動態庫, A.out
3.6.詞法分析
$ clang -fmodules -E -Xclang -dump-tokens A.c
如圖,寫一個小函數對其進行詞法分析。
3.7.語法分析
$ clang -fmodules -fsyntax-only -Xclang -ast-dump A.c
生成語法樹如下:
有顏色區分還是比較美觀的。
3.8.語義分析
生成LLVM IR。LLVM IR有3種表示形式(本質是等價的)
- (1).text:便於閱讀的文本格式,類似於匯編語言,拓展名.ll
- (2).memory:內存格式
- (3).bitcode:二進制格式,拓展名.bc
生成text格式:
$ clang -S -emit-llvm A.c
在生成LLVM IR的時候會進行優化(雖然各階段都會優化)
clang file.c -S -emit-llvm -o - (print out unoptimized llvm code) clang file.c -S -emit-llvm -o - -O3
LLVM IR的三種形式是等價的:
a.ll和a.bc之間可以通過llvm-as和llvm-dis命令相互轉換。
至於在內存中的那種格式,我們是無法通過文件的形式得到的。
參考鏈接:
1. https://clheveningflow.github.io/2019/09/28/LLVM1/
2. https://juejin.cn/post/6844903748435705864
3. https://blog.csdn.net/softee/article/details/41128667