LLVM 1:Clang入門


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 開發者使用 Ninja
  • Visual 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-Cswift都采用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 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM