LLVM的介紹和編譯

一、 概述

LLVM是架構(gòu)編譯器的框架系統(tǒng),由C++編寫而成。由于優(yōu)化以任意程序語言編寫的程序的編譯時間(complie-time)、鏈接時間(link-time)、運行時間(run-time)以及空閑時間(idle-time)。對開發(fā)者保持開放,并兼容已有的腳本。

二、LLVM設(shè)計結(jié)構(gòu)

傳統(tǒng)的LLVM設(shè)計結(jié)構(gòu)如下:

編譯器架構(gòu).png

分為 源碼、前端、優(yōu)化器、后端、以及機器識別語言等幾部分。

2.1 前端編譯器(Frontend)

前端編譯器的任務(wù)是解析源代碼。它會進行相應(yīng)的:詞法分析、語法分析、語義分析。檢查源代碼是否存在語法錯誤,然后構(gòu)建抽象語法樹(Abstract Syntax Tree,AST),LLVM的前端編譯器還會生成中間代碼((intermediate representation,IR).

2.2 優(yōu)化器(optimizer)

優(yōu)化器負責(zé)進行各種優(yōu)化,改善代碼的運行時間。例如消除冗余計算等。

2.3后端(backend)

將代碼映射帶目標(biāo)指令集,生成機器語言,并且進行機器相關(guān)的代碼優(yōu)化。

2.4 iOS的編譯架構(gòu)

Objective C、C、C++使用的編譯器前端是clang,Swift 的編譯器前端是swift,后端都是LLVM.


設(shè)計.png

2.5 LLVM的設(shè)計

當(dāng)編譯器決定支持多種語言或多種硬件架構(gòu)時,LLVM的最重要的時刻就來了。其他的編譯器如GCC,它方法非誠成功,但是由于它是作為整體程序設(shè)計的,因此它的用途受到了很大的限制。
LLVM設(shè)計最重要的地方是、使用通過代碼形式(IR)、是用來在編譯器中表示代碼的形式。所以LLVM可以為任何編程語言獨立編寫前端,并且可以為任意硬件架構(gòu)獨立編寫后端。


圖片.png

2.6 Clang

clang是LLVM的項目中的一個子項目,它是基于LLVM架構(gòu)的輕量級編譯器,誕生之初是為咯替換GCC,提供更快的編譯速度,它屬于LLVM的前端編譯器。

三,LLVM的編譯過程

3.1 LLVM的編譯步驟

現(xiàn)在我們新建一個空項目,創(chuàng)建一個命令行的項目,然后寫一些相關(guān)的代碼

#import <stdio.h>

#define C  30
int main(int argc, const char * argv[]) {
    
    int a = 10;
    int b = 20;
    printf("%d \n",a + b + C);
    
    return 0;
}

然后我們在該工程的目錄下創(chuàng)建終端命令:

然后我們看看LLVM進行編譯的過程中要執(zhí)行的相關(guān)步驟命令

clang -ccc-print-phases main.m

執(zhí)行結(jié)果如下:

0: input, "main.m", objective-c
1: preprocessor, {0}, objective-c-cpp-output
2: compiler, {1}, ir
3: backend, {2}, assembler
4: assembler, {3}, object
5: linker, {4}, image
6: bind-arch, "x86_64", {5}, image

  • step1 :輸入文件,找到源文件
  • stem2:預(yù)處理階段,這個過程包括宏定義的替換,頭文件的導(dǎo)入和展開。
  • step3:編譯階段,進行詞法分析、語法分析、檢測語法是否正確生成IR.
  • step4:后端,這里L(fēng)LVM會通過一個一個的Pass去優(yōu)化,每個Pass做一些事情,最終生成匯編代碼。
  • step5 生成目標(biāo)文件,
  • step6 鏈接,鏈接需要的動態(tài)庫和靜態(tài)庫,生成可執(zhí)行文件。
  • step7 通過不同的架構(gòu),生成對應(yīng)的可執(zhí)行文件

3.2 預(yù)處理階段(preprocessor)

我們執(zhí)行以下命令

clang -E main.m -o main2.m

我們會看到和main文件同級目錄下多了一個main2.m的文件,我們打開改文件會發(fā)現(xiàn)和之前的main有所不一樣了,不一樣之處如下圖

預(yù)編譯.png

我們看到預(yù)編譯階段,文件的行數(shù)變得多了很多,所以并且之處的宏定義C已經(jīng)替換為30。這就是預(yù)編譯過程的事情,

3.3 詞法分析

預(yù)處理階段過后就會進行詞法分析,這里會把每個代碼切成一個一個的token,這里我們只需要執(zhí)行以下命令

clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m

這樣 在終端就會生成一行一行的代碼


token.png

這樣,我們程序就被切成一個一個的token 包括空格和標(biāo)點符號,以及具體對應(yīng)的代碼位置都被token標(biāo)注的清清楚楚。

3.4 生成語法樹

由上一步生成的語法樹,我們只需要執(zhí)行命令

clang -fmodules -fsyntax-only -Xclang -ast-dump main.m

語法樹的生成如下


語法樹.png

3.6 生成中間代碼

對于以上生成中間的代碼。我們需要執(zhí)行命令

clang -S -fobjc-arc -emit-llvm main.m

執(zhí)行完成過后我們能看到和main.m文件同級的目錄中多了的main.ll的文件,這就是IR的格式文件
我們用WebStorm打開改文件如圖


圖片.png

相關(guān)的關(guān)鍵之解析

@:全局標(biāo)識
% 局部標(biāo)識
alloca 開辟內(nèi)存空間
align 字節(jié)對齊
store 寫入內(nèi)存load 肚內(nèi)存
call 調(diào)用函數(shù)
ret 返回值
i32 int 4個字節(jié)

3.7 IR的優(yōu)化方式

LLVM的優(yōu)化級別分別是-O0、-O1、-O2、-O3、-Os(第一個字母必須大寫)
執(zhí)行命令

clang -Os -S -fobjc-arc -emit-llvm main.m -o main.ll

3.8 bitCode

通過編譯生成的中間代碼生成bitCode執(zhí)行指令

clang -emit-llvm -c main.ll -o main.bc

3.9 生成匯編代碼

通過生成的bitCode 或者中間代碼生成匯編代碼如下

clang -S -fobjc-arc main.bc -o main.s
clang -S -fobjc-arc main.ll -o main.s

生成的匯編代碼也可以進行優(yōu)化

clang -Os -S -fobjc-arc main.m -o main.s

3.10 生成目標(biāo)文件

生成目標(biāo)文件的執(zhí)行指令

clang -fmodules -c main.s -o main.o

通過nm指令,查看main.o文件

$xcrun nm -nm main.o

3.11 生成可執(zhí)行文件(鏈接)

命令如下

clang main.o -o main

再次可以通過nm命令查看,可以看到完全不一樣的結(jié)果。

四、總結(jié)

以上就是對LLVM的初探的整個過程,雖然有些內(nèi)容自己還沒去驗證,但是大致流程就這樣,這也是先記錄然后自我學(xué)習(xí)的一個過程,希望在每天的學(xué)習(xí)過程中進步一點點吧,有什么不足的地方還請多多指正。覺得有用的就借鑒一下,如果覺得沒用的請越過。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容