了解 LLVM 編譯器

LLVM概述

LLVM是架構(gòu)編譯器的框架系統(tǒng),以C++編寫而成,用于優(yōu)化任意程序語(yǔ)言編寫的程序的編譯時(shí)間(compile-time)、鏈接時(shí)間(link-time)、運(yùn)行時(shí)間(run-time)以及空閑時(shí)間(idle-time)。

LLVM的設(shè)計(jì)

LLVM的一大特色就是,有著獨(dú)立的、完善的、嚴(yán)格約束的中間代碼表示。這種中間代碼,就是LLVM的字節(jié)碼,是LLVM抽象的精髓,前端生成這種中間代碼,后端自動(dòng)進(jìn)行各類優(yōu)化分析,讓用LLVM開發(fā)的編譯器,都能用上最先見(jiàn)的后端優(yōu)化技術(shù)。


傳統(tǒng)編譯器設(shè)計(jì)
  • Frontend

    ????編譯器前端的任務(wù)是解析源代碼(編譯階段),它會(huì)進(jìn)行 詞法分析、語(yǔ)法分析、語(yǔ)義分析、檢查源代碼是否存在錯(cuò)誤,然后構(gòu)建抽象語(yǔ)法樹(Abstract Syntax Tree AST),LLVM的前端還會(huì)生成中間代碼(intermediate representation,簡(jiǎn)稱IR),可以理解為llvm是編譯器 + 優(yōu)化器, 接收的是IR中間代碼,輸出的還是IR,給后端,經(jīng)過(guò)后端翻譯成目標(biāo)指令集

  • 優(yōu)化器 Optimizer

    ????優(yōu)化器負(fù)責(zé)進(jìn)行各種優(yōu)化,改善代碼的運(yùn)行時(shí)間,例如消除冗余計(jì)算等

  • 后端 Backend(代碼生成器 Code Generator)

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

LLVM將中間代碼優(yōu)化這個(gè)流程做到了極致,LLVM工具鏈,不但可以生成所支持的各個(gè)后端平臺(tái)的代碼,更可以方便的將各語(yǔ)言的前端編譯后的模塊鏈接到一起,你可以方便的在你的語(yǔ)言中調(diào)用C函數(shù)。

LLVM的設(shè)計(jì)

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

OC 、C、C++ 使用的編譯器前端是 Clang ,后端都是LLVM,如下圖所示

iOS的編譯器

Clang概述

Clang 是一個(gè) C++ 編寫、基于 LLVM、發(fā)布于 LLVM BSD 許可證下的 C/C++/Objective C/Objective C++ 編譯器。

Clang 編譯流程

通過(guò)命令可以打印源碼的編譯階段
clang -ccc-print-phases 源文件路徑

得到結(jié)果如下:

  1. 輸入文件:找到源文件
    input, "main.m", objective-c

  2. 預(yù)處理階段:這個(gè)過(guò)程處理包括宏的替換,頭文件的導(dǎo)入
    preprocessor, {0}, objective-c-cpp-output

  3. 編譯階段:進(jìn)行詞法分析、語(yǔ)法分析、檢測(cè)語(yǔ)法是否正確,最終生成IR
    compiler, {1}, ir

  4. 后端:這里L(fēng)LVM會(huì)通過(guò)一個(gè)一個(gè)的pass去優(yōu)化,每個(gè)pass做一些事情,最終生成匯編代碼
    backend, {2}, assembler

  5. 匯編代碼生成目標(biāo)文件
    assembler, {3}, object

  6. 鏈接:鏈接需要的動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù),生成可執(zhí)行文件
    linker, {4}, image(鏡像文件)

  7. 綁定:通過(guò)不同的架構(gòu),生成對(duì)應(yīng)的可執(zhí)行文件
    bind-arch, "x86_64", {5}, image

預(yù)處理階段

????這個(gè)階段主要是處理包括宏的替換,頭文件的導(dǎo)入,可以執(zhí)行命令 clang -E 源文件路徑,執(zhí)行完畢可以看到頭文件的導(dǎo)入和宏的替換。define則在預(yù)處理階段會(huì)被替換,所以經(jīng)常被是用來(lái)進(jìn)行代碼混淆,目的是為了 app 安全,實(shí)現(xiàn)邏輯是:將 app 中核心類、核心方法等用系統(tǒng)相似的名稱進(jìn)行取別名,然后在預(yù)處理階段就被替換,來(lái)達(dá)到代碼混淆的目的。

clang -E main.m

編譯階段

詞法分析

預(yù)處理完成后就會(huì)進(jìn)行詞法分析,這里會(huì)把代碼切成一個(gè)個(gè) token,比如大小括號(hào)、等于號(hào)、還有字符串等,

clang -fmodules -fsyntax-only -Xclang -dump-tokens 源文件路徑
語(yǔ)法分析

語(yǔ)法分析,它的任務(wù)是驗(yàn)證語(yǔ)法是否正確,在詞法分析的基礎(chǔ)上將單詞序列組合成各類此法短語(yǔ),如程序、語(yǔ)句、表達(dá)式 等等,然后將所有節(jié)點(diǎn)組成抽象語(yǔ)法樹(Abstract Syntax Tree AST),語(yǔ)法分析程序判斷程序在結(jié)構(gòu)上是否正確。

clang -fmodules -fsyntax-only -Xclang -ast-dump 源文件路徑
  • FunctionDecl 函數(shù)
  • ParmVarDecl 參數(shù)
  • CallExpr 調(diào)用一個(gè)函數(shù)
  • BinaryOperator 運(yùn)算符

生成中間代碼IR

完成以上步驟后,就開始生成中間代碼 IR 了,代碼生成器(Code Generation)會(huì)將語(yǔ)法樹自頂向下遍歷逐步翻譯成 LLVM IR,可以通過(guò)下面命令可以生成 .ll 的文本文件,查看 IR 代碼。

clang -S -fobjc-arc -emit-llvm 源文件路徑 -o 輸出文件路徑

Objective-C 代碼在這一步會(huì)進(jìn)行 runtime 橋接,property 合成、ARC 處理等。

IR 基本語(yǔ)法
  • @ 全局標(biāo)識(shí)
  • % 局部標(biāo)識(shí)
  • alloca 開辟空間
  • align 內(nèi)存對(duì)齊
  • i32 32bit,4個(gè)字節(jié)
  • store 寫入內(nèi)存
  • load 讀取數(shù)據(jù)
  • call 調(diào)用函數(shù)
  • ret 返回
Optimization Level

當(dāng)然,IR 文件在 Objective-C 中是可以進(jìn)行優(yōu)化的,一般設(shè)置是在 target - Build Setting - Optimization Level(優(yōu)化器等級(jí))中設(shè)置。

Optimization Level 優(yōu)化級(jí)別

LLVM 的優(yōu)化級(jí)別分別是 -O0、 -O1 -O2 -O3 -Os,下面是帶優(yōu)化的生成中間代碼 IR 的命令

clang -Os -S -fobjc-arc -emit-llvm 源文件路徑 -o 輸出文件路徑
BitCode

xcode7 以后開啟 bitcode ,蘋果會(huì)做進(jìn)一步優(yōu)化,生成 .bc 的中間代碼,我們通過(guò)優(yōu)化后的 IR 代碼生成 .bc代碼

clang -emit-llvm -c .ll源文件 -o .bc文件

后端

LLVM 在后端主要是會(huì)通過(guò)一個(gè)個(gè)的 Pass 去優(yōu)化,每個(gè) Pass 做一些事情,最終生成匯編代碼。我們通過(guò)最終的 .bc 或者 .ll 代碼生成匯編代碼:

 clang -S -fobjc-arc .bc 文件 -o .s 文件
 clang -S -fobjc-arc .ll 文件 -o .s 文件

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

clang -Os -S -fobjc-arc .m 文件 -o .s 文件

生成目標(biāo)文件

目標(biāo)文件的生成,是匯編器以匯編代碼作為插入,將匯編代碼轉(zhuǎn)換為機(jī)器代碼,最后輸出目標(biāo)文件(object file)

clang -fmodules -c .s 文件 -o .o 文件

可以通過(guò) nm 命令,查看下 main.o 中的符號(hào)

xcrun nm -nm .o 文件
  • undefined 表示在當(dāng)前文件暫時(shí)找不到符號(hào)

  • external 表示這個(gè)符號(hào)是外部可以訪問(wèn)的

鏈接

鏈接主要是鏈接需要的動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù),生成可執(zhí)行文件,其中靜態(tài)庫(kù)會(huì)和可執(zhí)行文件合并,動(dòng)態(tài)庫(kù)是獨(dú)立的。連接器把編譯生成的 .o 文件和 .dyld、.a 文件鏈接,生成一個(gè) mach-o 文件

clang .o 文件 -o 輸出文件

LLVM 工程編譯

下載源碼

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

安裝 cmake

brew install cmake

通過(guò) xcode 編譯 LLVM

  • cmake 編譯成 Xcode 項(xiàng)目

    cd llvm-project
    mkdir build_xcode
    cd build_xcode
    cmake -G Xcode ../llvm
    
  • 打開 build_xcode/LLVM.xcodeproj 文件

  • 選擇 Automatically Create Schemes (目前 xcode version 13.2.1)

  • 選擇 ALL_BUILD Secheme 進(jìn)行編譯

通過(guò) ninja 編譯 LLVM

  • 安裝 ninja

    brew install ninja
    
  • llvm-project 目錄下創(chuàng)建文件夾 build_ninja 、 llvm-release

    cd llvm-project
    mkdir build_ninja
    mkdir llvm_release
    
  • cmake 編譯

    cd build_ninja
    cmake -G Ninja ../llvm -DCMAKE_INSTALL_PREFIX=../llvm_release
    
  • ninja 運(yùn)行

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

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

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