本文為L_Ares個人寫作篇裁,以任何形式轉(zhuǎn)載請表明原文出處菲饼。
內(nèi)容較多,本文盡量不廢話远剩,看起來會有點刻板扣溺,但都是干貨,請各位先仔細
并且充分
的理解以下三個問題 :
- 為什么要了解
LLVM
和Clang
?- 什么是
LLVM
?- 什么是
Clang
?注 : 概念性的資料源于百科(
維基百科
或LLVM官網(wǎng))瓜晤。
前言 : 為什么要了解LLVM?
因為
Objective-C
和Swift
語言都是編譯型語言
锥余,所以需要編譯器對其轉(zhuǎn)換成機器語言
才會被系統(tǒng)識別,而它們的編譯器都是基于LLVM體系
開發(fā)的痢掠。對
編譯器
驱犹、編譯型語言
有所遺忘的童鞋看這里。
一足画、簡述什么是LLVM
前言
第一 :
本節(jié)的重點雖然是LLVM
雄驹,但是主要還是以iOSer
的角度來看的。第二 :
要想深入學(xué)習(xí)LLVM
的話淹辞,本節(jié)的這些內(nèi)容只能算是一個概念的大體簡述医舆,尤其主要是iOS
的視角,所以想了解更多LLVM
的同學(xué)桑涎,還需自行查看LLVM官網(wǎng)彬向。第三 :
如果覺得LLVM官網(wǎng)真的很難懂,又全是英文攻冷,翻譯無法精準的話娃胆,那么可以看一下知乎上的藍色大大對于LLVM
的講解,再配合LLVM官網(wǎng)進行學(xué)習(xí)等曼。
概念
這里的概念全部引自LLVM官網(wǎng)里烦。
LLVM
現(xiàn)在已經(jīng)不是一個名稱的首字母縮寫了(最初還真是),而是一個項目名稱禁谦,是一個模塊化的胁黑、可重用的編譯器
和工具鏈技術(shù)
的集合。簡言之州泊,
LLVM
是架構(gòu)編輯器的框架系統(tǒng)
丧蘸。
所謂工具鏈技術(shù)
蒸走,舉個栗子 :
嵌入式
Linux
就提供了一套完整的工具鏈遭赂,它利用GNU
的gcc
做編譯器,利用gdb
、xgdb
做調(diào)試工具氯庆,可以很方便的實現(xiàn)從操作系統(tǒng)的內(nèi)核態(tài)到用戶態(tài)的應(yīng)用軟件各個級別的調(diào)試庙楚。
所謂框架系統(tǒng)
:
就是說
LLVM
不僅僅是某一樣具體的事物膝擂,而是由很多模塊組合起來的框架亚斋。所謂框架就是你可以基于它提供的功能開發(fā)自己的模塊,并且集成到
LLVM
系統(tǒng)上拂募,增加LLVM
的功能庭猩。或者你也可以利用
LLVM
作為一種底層支持,去完成自己的軟件開發(fā)陈症。
概述
LLVM
曾是Low Level Virtual Machine
(低等級虛擬機)的縮寫蔼水,現(xiàn)在它是一個項目體系、是一套框架系統(tǒng)录肯。
LLVM
項目包括了非常多的子項目徙缴,包括iOS
開發(fā)者常見的Clang
,LLDB
嘁信,libc++
和libc++ ABI
,這都是LLVM
的主要子項目疏叨。
LLVM
雖然包含了這么多編譯器作為子項目潘靖,但是它本身并不是編譯器,也不是編譯器后端蚤蔓。
它提供的是編譯器所需的一系列的庫卦溢。例如程序分析、代碼優(yōu)化秀又、機器代碼生成等单寂,并且提供了調(diào)用這些庫的相關(guān)工具。也為這些庫提供了方便簡單的吐辙、具備類型的宣决、與平臺無關(guān)的統(tǒng)一中間代碼語言——LLVM IR
。
LLVM
以C++
編寫而成昏苏,用于優(yōu)化以任意程序語言編寫的 :
編譯時間 (
compile-time
)鏈接時間 (
link-time
)運行時間 (
run-time
)空閑時間 (
idle-time
)
LLVM
對開發(fā)者保持開放尊沸,并且兼容現(xiàn)有的腳本。
二贤惯、編譯器的設(shè)計
在探索LLVM
的設(shè)計之前洼专,先看一下傳統(tǒng)的編譯器設(shè)計,來比較兩者的區(qū)別孵构,以探索LLVM
的優(yōu)勢屁商。
1. 傳統(tǒng)編譯器的設(shè)計
如圖2.1.0所示
傳統(tǒng)的編譯器整體流程是 :
源碼 --> 編譯器前端 --> 優(yōu)化器 --> 編譯器后端/代碼生成器(CodeGenetator) --> 機器語言
1.1 編譯器前端(Frontend)
傳統(tǒng)的編譯器前端主要任務(wù)就是讀取源代碼,對讀取的源代碼做詞法分析颈墅、語法分析蜡镶、語義分析雾袱,通過這些分析來檢查源代碼是否存在錯誤,然后構(gòu)建
語法樹 (Abstract Syntax Tree, AST)
帽哑。
1.2 優(yōu)化器(Optimizer)
優(yōu)化器的責(zé)任就是各種優(yōu)化谜酒。改善代碼的運行時間,例如消除冗余計算等妻枕。
1.3 編譯器后端(Backend)/代碼生成器(CodeGenerator)
將代碼映射到目標指令集僻族,生成機器語言,并進行機器相關(guān)的代碼優(yōu)化屡谐。
后端或者說代碼生成器述么,最后生成的是對應(yīng)不同架構(gòu)的二進制文件,之所以需要生成不同的二進制文件愕掏,就是因為不同的架構(gòu)有不同的指令集度秘,不同的指令集對相同的二進制文件的指令識別是不同的,所以要將代碼映射到對應(yīng)的指令集饵撑。
2. iOS的編譯器架構(gòu)
所謂iOS
的編譯器架構(gòu)其實就是LLVM
編譯器架構(gòu)中的一部分剑梳。包括Objective-C
、C
滑潘、C++
在內(nèi)垢乙,它們使用的編譯器前端
都是Clang
。而Swift
的編譯器前端
就是Swift
语卤。它們的編譯器后端
則全部都是LLVM Code Generator
追逮。
而Clang
也只是整個LLVM
架構(gòu)中的一個子項目,屬于編譯器前端
粹舵。
來看iOS
的編譯器架構(gòu)圖 :
很明顯钮孵,iOS
的編譯器是通過Clang
或者Swift
來完成詞法分析、語法分析眼滤、語義分析的步驟巴席,來完成對源代碼的錯誤排查,然后生成抽象語法樹(AST)
柠偶。
這里利用的就是LLVM
的前端情妖,和傳統(tǒng)編譯器的不同點是,Clang
不止做完了上述的排查诱担,還會生成一個非常重要的東西——IR
毡证。
什么是IR
?
LLVM
的編譯器前端不僅會完成分析排查和生成抽象語法樹
的工作,還會生成中間代碼蔫仙,這個中間代碼在LLVM
中被稱作IR (intermediate representation)
料睛。
那么用語言來描述iOS
的編譯器架構(gòu)設(shè)計流程就是 :
Clang
/Swift
讀取源代碼,檢查詞法、語法恤煞、語義的分析屎勘,排查錯誤,生成抽象語法樹(AST)
居扒,并生成中間代碼IR
概漱。然后將
IR
給到LLVM
的優(yōu)化器(Optimizer
),改善代碼的運行時間喜喂,消除冗余計算等瓤摧。最后將優(yōu)化過的
IR
再傳給后端或者說代碼生成器,由后端/代碼生成器完成將IR
轉(zhuǎn)換成對應(yīng)不同架構(gòu)的二進制文件玉吁,完成機器代碼的相關(guān)優(yōu)化照弥。
3. LLVM的設(shè)計
LLVM
的設(shè)計核心 : 中間代碼IR
。
LLVM
的設(shè)計重點和優(yōu)勢 : 面對多種源語言(編譯器前端來做)进副,或者多種硬件架構(gòu)(編譯器后端來做)的情況時这揣,對比傳統(tǒng)的編譯器例如GCC
,優(yōu)勢就是LLVM
的IR
設(shè)計影斑。
由于GCC
是以一個整體應(yīng)用程序設(shè)計的给赞,也就是說,如果你的前端源語言或者后端硬件架構(gòu)矫户,任一一個發(fā)生了變化的話塞俱,你需要重新設(shè)計一整套前端--優(yōu)化器--后端
的編譯器。
LLVM
的設(shè)計圖 :
前端讀取源碼吏垮,經(jīng)過傳統(tǒng)步驟后,多增加一步生成中間代碼
IR
罐旗,然后輸出IR
給優(yōu)化器膳汪。優(yōu)化器讀取的是前端傳輸過來的
IR
,優(yōu)化的也是IR
九秀,然后輸出的還是IR
遗嗽。所以優(yōu)化器只需要對傳入過來的IR
做優(yōu)化即可,無論前端是什么樣的源語言類型鼓蜒,都會被轉(zhuǎn)成IR
痹换。后端/代碼生成器處理的則是優(yōu)化器傳來的
IR
,也是只需要針對IR
做處理都弹,不需要管前端到底是什么類型的源語言娇豫。
優(yōu)勢 :
如果前端出現(xiàn)了新的源語言類型,
LLVM
只需要針對新語言適配一個IR
轉(zhuǎn)換器即可畅厢,優(yōu)化器和后端都不需要進行大的改變冯痢,甚至不用改變。如果后端出現(xiàn)了新的硬件架構(gòu),
LLVM
只需要針對新的硬件架構(gòu)適配一個IR
轉(zhuǎn)換器即可浦楣,優(yōu)化器和前端則不需要進行大的改變袖肥,甚至不用改變。
三振劳、關(guān)于Clang
1. 什么是Clang
內(nèi)容引自Clang官網(wǎng)椎组,個人翻譯,如有不準確的地方历恐,還請不吝賜教寸癌。
Clang
是LLVM原生的
針對C
、C++
夹供、Objective-C
的編譯器灵份。它的存在是為了提供一個平臺,
Clang
做到了 :
- 非诚ⅲ快的編譯速度
- 非常有用的
error
信息和warning
警告- 可以構(gòu)建非常優(yōu)秀的源代碼
Clang Static Analyzer
和Clang -tidy
都是可以自動發(fā)現(xiàn)你代碼里bug的工具填渠。它們都是Clang
工具中很好的例子,它們可以將Clang編譯器前端
作為一個庫去解析C
/C++
的代碼鸟辅。
2. Clang的概述
總的來說氛什,
Clang
是LLVM
的子項目,它是基于LLVM
架構(gòu)的輕量級編譯器匪凉,誕生之初是為了替代GCC
枪眉,因為GCC
是傳統(tǒng)型的編譯器,雖然非常好用再层,但是GCC
的源碼太長了贸铜,又晦澀難懂,而且編譯速度還可提升聂受。于是基于LLVM
架構(gòu)的Clang
就逐步的擴大了影響力蒿秦。
3. 一些LLVM或Clang中的名詞解釋
3.1 詞法分析
詞法分析是計算機科學(xué)中將字符序列轉(zhuǎn)換為單詞序列的過程。
詞法分析是整個編譯流程的第一個階段蛋济,是編譯的基礎(chǔ)棍鳖。詞法分析要做的是從左到右一個字符一個字符的讀入源程序,對構(gòu)成源程序的字符進行掃描碗旅,然后根據(jù)構(gòu)詞規(guī)則識別單詞(也稱單詞符號或符號渡处,英文叫做
Token
)。所謂單詞就是一個字符串祟辟,是構(gòu)成源代碼的最小單位医瘫。
詞法分析一般都是經(jīng)過詞法分析的程序或者專門的函數(shù)來完成的,這種程序或者函數(shù)叫做詞法分析器旧困,也叫掃描器(
Scanner
)登下。
3.2 語法分析
語法分析是整個編譯流程中的一個邏輯階段茫孔。語法分析的任務(wù)是在詞法分析的基礎(chǔ)上,將單詞序列的組合成各種語法短語被芳,如程序缰贝、語句、表達式等畔濒。
語法分析是判斷源程序在語法的結(jié)構(gòu)上是否正確剩晴。它和詞法分析分別像英語中的語法和單詞,詞法分析標明了單詞的含義侵状,語法分析則判斷單詞組合成的語句是否符合語法規(guī)則赞弥。
3.3 語義分析
語義分析也是整個編譯流程中的一個邏輯階段。語義分析的任務(wù)是對結(jié)構(gòu)上正確的源程序的上下文進行有關(guān)性質(zhì)的檢查和類型檢查趣兄。
語義分析是檢查源程序有無語義錯誤绽左,為代碼生成階段收集類型信息。
語義分析檢查的是源程序是否符合規(guī)定的語言規(guī)范艇潭。
語義分析是編譯程序最實質(zhì)性的工作拼窥,它是第一次對源代碼做出解釋的階段,引起源程序的實質(zhì)變化蹋凝。
語義和語法的區(qū)別鲁纠,我舉個例子,方便大家理解鳍寂。比如 : 我學(xué)習(xí)編程和編程學(xué)習(xí)我改含。兩者的語法都是沒有問題的,符合語法的規(guī)范——主謂賓迄汛。但是后者并不符合語義規(guī)范捍壤,后者的語言意義是一種空泛的表達。
3.4 抽象語法樹
這是源代碼的語法結(jié)構(gòu)的一種抽象表示鞍爱。所謂抽象語法白群,是指不會表示出所有細節(jié)的語法。
4. iOS中可能遇到的Clang常用指令
這里只說一些iOS中可能常用的Clang
指令硬霍,如果想要全部的Clang
指令集,可以在terminal
終端中輸入clang --help
查看笼裳。
如果認為上面的命令查看不方便的唯卖,這里還有github版本,內(nèi)容就是copy的clang --help
躬柬。
或者可以直接在LLVM
官網(wǎng)的Clang Document查詢拜轨。
Clang命令 | 釋義 |
---|---|
-ccc-print-phases | 打印源碼的編譯階段,得到的打印結(jié)果就是整個源碼到機器代碼的整體流程步驟允青。 |
-rewrite-objc | 將OC源碼編譯成C++源碼 |
-E | 查看預(yù)處理階段詳細步驟(在terminal 中查看橄碾,也可以生成一個新文件 : clang -E main.m >> new_main.m) 。>> 就是文件重定向。 |
-c | 必須在-E 之后才可以使用法牲,例如clang -E main.m -c 史汗。只進行預(yù)處理,編譯拒垃,匯編步驟停撞,不進行鏈接步驟。執(zhí)行完成后生成的是.o 鏈接文件悼瓮。 |
-S | 只進行預(yù)處理和編譯步驟戈毒,不進行匯編,鏈接等后續(xù)步驟横堡。執(zhí)行完成后埋市,生成的是.s 匯編代碼文件。 |
-o <file> | 完成預(yù)編譯-->編譯-->匯編-->鏈接 后命贴,生成可執(zhí)行文件到<file> 文件道宅。 |
-g | 在生成的可執(zhí)行文件中,包含標準的調(diào)試信息套么。 |
-i <路徑>(應(yīng)該是大寫i 最標準) |
在頭文件中的搜索路徑中添加<路徑> 培己。 |
-fmodules | 啟用模塊化語言特性。 |
-fsyntax-only | 編譯器并不生成代碼胚泌,后續(xù)的操作只是語法級別的修改省咨。 |
-Xclang <arg> | 向clang 編譯器傳遞參數(shù)<arg> 。 |
-dump-tokens | 運行預(yù)處理器玷室,將源碼內(nèi)部拆分成各種單詞(Token)零蓉。 |
-ast-dump | 構(gòu)建抽象語法樹AST,然后對其進行拆解和調(diào)試穷缤。 |
-fobjc-arc | 生成Objective-C指針的retain 和release 的調(diào)用敌蜂。 |
-emit-llvm | 對匯編文件和目標文件生成LLVM IR 代碼。 |