【學(xué)習(xí)總結(jié)】05 | 鏈接器:符號是怎么綁定到地址上的?

1、前言

首先,我認(rèn)為學(xué)習(xí)總結(jié),要有所總,所結(jié),就是有歸納后,能用自己的話告訴別人!有所結(jié),就是有所收獲輸出,一般我認(rèn)為是思維導(dǎo)圖,所以,每篇文章前,我都會先給出文章的腦圖:

iOS開發(fā)高手課-05-鏈接器:符號是怎么綁定到地址上的?.png

2、正文

注意,本系列總結(jié)不會引用或提供原課程文章所有的內(nèi)容或代碼,只會作出思維導(dǎo)圖,需要學(xué)習(xí)可購買課程 《iOS開發(fā)高手課 - 極客時間》

鏈接器 對于剛剛接觸程序開發(fā)的同學(xué)可能基本不懂,雖然大學(xué)學(xué)習(xí)編譯原理、計算機(jī)原理等。這些知識比較抽象,為了讓大家明白,我們需要站在巨人的肩膀上!所以,本文需要一個巨人 ----- 編譯器 來幫助大家理解 鏈接器 是什么?為什么?大家有沒有想過,你寫的代碼,最終是怎么樣在計算機(jī)或手機(jī)上運(yùn)行的???那現(xiàn)在就讓我們來了解一下吧!

《程序是怎樣跑起來的》

“計算機(jī)組成原理”圖解趣味版,蹲馬桶就能看懂的編程基礎(chǔ)知識!普及計算機(jī)知識。如何向小學(xué)生講解CPU和二進(jìn)制?如何向中學(xué)生講解內(nèi)存和磁盤?如何向女高中生講解操作系統(tǒng)的原理?如何向老奶奶說明顯示器和電視的不同?

《程序是怎樣跑起來的》作者是日本矢澤久雄,我們需要解析 編譯器,所以需要簡單入門級的計算機(jī)組成原理,這本書就是這樣一本優(yōu)秀的書!大家可能通過微信讀書讀到電子版: 程序是怎樣跑起來的 - 微信讀書,目前2020年微信還有很多無限卡會員的免費閱讀,當(dāng)然如果必要花 19.99 元我認(rèn)為也值得。好了我們就不多說了,下面我主要摘取書中幾個示意圖來解析 編譯器

程序運(yùn)行流程示例

圖1-1-程序運(yùn)行流程示例.jpg

上圖展示程序運(yùn)行流程示例,從我們編寫的高級語言的代碼,到 CPU(Central Processing Unit,中央處理器)所負(fù)責(zé)的就是解釋和運(yùn)行最終轉(zhuǎn)換成機(jī)器語言的程序內(nèi)容,其實是 編譯器 工作的流程。

CPU的四個構(gòu)成部分

圖1-2-CPU的四個構(gòu)成部分.jpg

CPU 的內(nèi)部由寄存器、控制器運(yùn)算器時鐘四個部分構(gòu)成,各部分之間由電流信號相互連通。本文我們不關(guān)注 CPU 的工作原理,本圖主要是后面要解析代碼運(yùn)行在不同 CPU 上,需要 編譯器 對不同的 CPU 做兼容和優(yōu)化。為什么會有不同的 CPU,上圖就說明了,簡單來說寄存器、控制器、運(yùn)算器和時鐘的設(shè)計和數(shù)量不同,那就是不同的 CPU,所以代碼在不同的 CPU 運(yùn)行就可能有不一樣的要求,這是硬件方面。這里就不深入解析了,大家可以看看本書,或自行搜索了解更多。

CPU負(fù)責(zé)解析并運(yùn)行從源代碼編譯而來的本地代碼

圖7-2-CPU負(fù)責(zé)解析并運(yùn)行從源代碼編譯而來的本地代碼.jpg

CPU 只能解釋其自身固有的機(jī)器語言,機(jī)器語言的程序稱為本地代碼(native code)。不同的CPU能解釋的機(jī)器語言的種類也是不同的。例如,CPU有 x86MIPS、SPARC、PowerPCARM等幾種類型,它們各自的機(jī)器語言是完全不同的。

程序員用C語言等編寫的程序,在編寫階段僅僅是文本文件,文本文件(排除文字編碼的問題)在任何環(huán)境下都能顯示和編輯,我們稱之為源代碼。通過對源代碼進(jìn)行編譯,就可以得到本地代碼。到目前,我們了解了代碼編譯的流程啦,那具體的流程又是怎么樣的呢?在講解之前,我們先看看 CPU 的歷史,這樣對我們了解編譯器也起到重要作用啊~

這里解析一下各種 CPU :

  • x86:美國 Intel (英特爾)的微處理器,是按照8086、80286、80386、80486、Pentium(奔騰)……這樣的順序不斷升級的。因為這些型號的后面都帶有86,所以總稱為x86。32位處理器 Intel官方文檔里面稱為“IA-32”,64位處理器稱“x86_64”、“x86-64”,又稱x64,即英文詞 64-bit extended,64位拓展的簡寫。這個64位有點故事,下面講?,F(xiàn)在的PC機(jī)都是64位 CPU 啦。
  • MIPS:美國 MIPS 科技公司開發(fā)的CPU,曾出現(xiàn)過面向 MIPS工作站的 Windows,不過現(xiàn)在市面上已經(jīng)不再出售了。
  • SPARC:美國 SUN系統(tǒng)開發(fā)的CPU,很多工作站都采用了該CPU。
  • PowerPC:是美國蘋果、IBM、摩托羅拉共同開發(fā)的CPU,蘋果的Power Mac及IBM的工作站都采用了該CPU,不過現(xiàn)在的Mac采用的是Intel的x86系列CPU。
  • ARM ARM,安謀控股公司(英語:ARM Holdings plc.),又稱ARM公司。ARM的前身為艾康電腦,1978年于英國劍橋創(chuàng)立。2016年7月18日,日本軟銀集團(tuán)斥資3.3萬億日元(約合311億美元)收購了ARM公司。ARM架構(gòu)版本從 ARMv3 到 ARMv7 支持32位,2011年發(fā)布的ARMv8-A 架構(gòu)添加了對64位支持?,F(xiàn)在移動設(shè)備主流就是使用 ARM 。
x86-64 的故事

x84_64x86 32位 CPU 開始邁向64位的時候,有2種選擇:

  1. 向下兼容x86。
  2. 完全重新設(shè)計指令集,不兼容x86。

結(jié)果在 1999 年 AMD 搶跑了!比 Intel 率先制造出了商用的兼容 x86 的CPU,AMD稱之為 AMD64,搶了64位PC的第一桶金,得到了用戶的認(rèn)同。所以 Intel 為了面子就選擇了設(shè)計一種不兼容x86的全新64為指令集,稱之為IA-64(Itanium,安騰),但是比 AMD 晚了一步,因為 AMD64 能有效地把x86架構(gòu)移至64位的環(huán)境,并且能兼容原有的x86應(yīng)用程序。并且 IA-64 也挺慘淡的,因為是全新設(shè)計的CPU,沒有編譯器,也不支持 Windows(微軟把intel給忽悠了,承諾了會出對應(yīng)的 Windows server 版,但是遲遲拿不出東西),處理器本身和軟件移植的成本難以控制,導(dǎo)致 IA-64 最終流產(chǎn)。后來不得不在時機(jī)落后的情況下也開始支持AMD64的指令集,但是換了個名字,叫x86_64,表示是 x86 指令集的64擴(kuò)展,大概是不愿意承認(rèn)這玩意是AMD設(shè)計出來的?現(xiàn)時英特爾稱之為“Intel 64”,在之前曾使用過“Clackamas Technology” (CT)、“IA-32e”及“EM64T”等名字。換湯不換藥,核心與 AMD64 幾乎相同,猶如一對孿生兄弟,其實都是 x86-64 架構(gòu)。當(dāng)年有媒體為 EM64T 起了“iAMD64”別名,諷刺 Intel 在迎擊 AMD 的民用64位技術(shù)上,使用了 AMD 的技術(shù),直接把 AMD64 吸納過來,并以新名重新包裝使用,所以最后,Intel 索性將此技術(shù)正式命名為 Intel 64

所以,CPU 的 32 位還是 64位,一般是用 x86-64 表示64位,而32位現(xiàn)在已經(jīng)沒有了。另外,不同公司對 x86-64 架構(gòu),名字上還是有一些區(qū)別,蘋果公司和RPM包管理員以“x86-64”或“x86_64”稱呼此64位架構(gòu)。甲骨文公司及Microsoft稱之為“x64”,BSD家族及其他Linux發(fā)行版則使用“x64-64”,32位版本則稱為“i386”(或 i486/586/686),Arch Linux用x86_64稱呼此64位架構(gòu)。

最后一個問題,x86、x86_64主要的區(qū)別是什么?就是32位和64位的問題,x86 中只有8個32位通用寄存器,分別是 eax,ebx,ecx,edx, ebp, esp, esi, edi。x86_64 把這8個通用寄存器擴(kuò)展成了64位的,并且比x86增加了若干個寄存器(好像增加了8個,變成了總共16個通用寄存器)。譬如四個通用寄存器(RAX, RBX, RCX, RDX)是由32位的(EAX, EBX, ECX, EDX)64位擴(kuò)展而來,相應(yīng)的還有指針寄存器(RIP, RBP, RSP, RSI, RDI),以及增加八個通用寄存器(R8~R15)等等。 這些資源只可在x64處理器的64位模式下使用,在用來支持x86軟件的遺留模式和兼容模式中是不可見的。

以上內(nèi)容和關(guān)于 CPU 更多歷史和資料,可查看來源: x86 - 維基百科 、 x86-64 - 維基百科,自由的百科全書X86、X64和X86_64區(qū)別 。

轉(zhuǎn)換成本地代碼后就變成了同樣的語言

圖8-2-轉(zhuǎn)換成本地代碼后就變成了同樣的語言.jpg

前面提到,CPU 只能解釋其自身固有的機(jī)器語言,而轉(zhuǎn)換成機(jī)器語言的程序就是本地代碼。不管用那種編程語言編寫的源代碼,最后只要能翻譯(編譯)成本地代碼,那么 CPU 就能理解。這個過程,其實就是編譯器的工作內(nèi)容!

同樣的源代碼可以轉(zhuǎn)換成適用于不同處理器的本地代碼

圖8-5-同樣的源代碼可以轉(zhuǎn)換成適用于不同處理器的本地代碼.jpg

根據(jù) CPU 類型的不同,本地代碼的類型也不同。因而,編譯器不僅和編程語言的種類有關(guān),和 CPU 的類型也是相關(guān)的。這就是前面為什么要重點說 CPU 相關(guān)知識,現(xiàn)在是不是有點理解編譯器了!

把C語言等高級編程語言編寫的源代碼轉(zhuǎn)換本地代碼的程序稱為編譯器,這個轉(zhuǎn)換過程經(jīng)過語法解析、句法解析、語義解析等。每種編程語言編寫的源代碼都需要其專用的編譯器,或者是同用類編譯器,比如 C 家族(C/C++)的編輯器。

Windows中的編譯和鏈接機(jī)制

圖8-8-Windows中的編譯和鏈接機(jī)制.jpg

剛才說的高級編程語言的源代碼轉(zhuǎn)換本地代碼,本地文件是無法直接運(yùn)行的,還需要把多個目標(biāo)文件結(jié)合,生成1個可執(zhí)行文件(圖中是EXE文件,針對Windows系統(tǒng)的。),這個處理就是鏈接,運(yùn)行連接的程序就稱為鏈接器(linkage editor,或連結(jié)器)。至此,我們終于說到鏈接器啦?。。‖F(xiàn)在明白鏈接器是在那里使用了吧?

存儲著目標(biāo)文件的實體,并直接和可執(zhí)行文件結(jié)合的庫文件形式稱為靜態(tài)鏈接庫,經(jīng)常簡稱為靜態(tài)庫。與之相反的,就叫動態(tài)鏈接庫(動態(tài)庫),就是參與鏈接過程,但是并不會與可執(zhí)行文件結(jié)合,就是說鏈接時會做標(biāo)記,綁定的地址在加載后再決定。這個不理解,沒關(guān)系,下面還會在詳細(xì)說說,下一章的內(nèi)容就是動態(tài)鏈接庫動態(tài)鏈接器的內(nèi)容,所以,這個需要了解一下。

至此,我們就過編譯器相關(guān)的內(nèi)容,簡單的解說了一下,不知道大家有點理解沒有,下面,我們在深入一點,了解編譯器的具體工作流程。

編譯器

編譯器一般采用 Three-Phase(三階段/三相)架構(gòu)設(shè)計:

  1. 編譯器前端(Front end)
  2. 中間端(Middle end,或叫優(yōu)化器,Optimizer)
  3. 后端(Backend)

編譯器前端(Front end)

  1. Preprocess - 預(yù)處理
  2. Parser - 解析器
  3. Lexical Analysis - 詞法分析
  4. Semantic Analysis - 語義分析
  5. AST(Abstract Syntax Tree,抽象語法樹)
  6. Static Analysis - 靜態(tài)分析
  7. CodeGen - IR 代碼生成

編譯器前端的任務(wù):預(yù)處理、語法分析,語義分析,在這個過程中,也會進(jìn)行類型檢查,如果發(fā)現(xiàn)錯誤或者警告會標(biāo)注出來在哪一行等,最終生成中間代碼 IR(Intermediate Representation,中間端表達(dá)式)。對于 LLVM 來說,前端就是 CLang。

中間端(Middle end,或叫優(yōu)化器,Optimizer)

  1. LLVM Bitcode - 生成字節(jié)碼
  2. Assemble - 生成Target相關(guān)匯編
  3. Assemble - 生成Target相關(guān) Object(Mach-o)
  4. Link 生成 Executable 可執(zhí)行文件

優(yōu)化是非常重要的,很多直接轉(zhuǎn)換來的代碼是不合適且消耗內(nèi)存的,因為是直接轉(zhuǎn)換,所以必然會有這樣的問題,而優(yōu)化放在這一步的好處在于前端不需要考慮任何優(yōu)化過程,減少了前端的開發(fā)工作。中間代碼要經(jīng)過一系列的優(yōu)化,優(yōu)化用的是 Pass,中間代碼的優(yōu)化也可以開發(fā)者自己編寫,可以插入一個 Pass。

后端(Backend)

  • ARM(ARM,安謀控股公司)
  • x86|x86-64(Intel,英特爾)
  • PowerPC(Apple & IBM & Motorola,AIM聯(lián)盟)

編譯器后端會進(jìn)行機(jī)器無關(guān)的代碼優(yōu)化,生成機(jī)器語言,并且進(jìn)行機(jī)器相關(guān)的代碼優(yōu)化。對于 iOS 的編譯過程,后端的處理:LLVM優(yōu)化器會進(jìn)行BitCode的生成,鏈接期優(yōu)化等,LLVM機(jī)器碼生成器會針對不同的架構(gòu),比如arm64等生成不同的機(jī)器碼。

這里的后端,最終就是生成對應(yīng)的 CPU 能執(zhí)行的機(jī)器語言,前面已經(jīng)介紹過 CPU 相關(guān)的。

上面說了編譯器的組成架構(gòu)和簡要的工作流程,詳細(xì)的工作流程這里就不多說了,因為已經(jīng)有很多優(yōu)秀的內(nèi)容了,并且本文的主要目的是要說明編譯器是什么!你知道它是什么后,再了解它是怎么用,有什么優(yōu)缺點,自然不是難事!以上的部分內(nèi)容是針對 iOS 和 LLVM 的,所以,我們接下來就來介紹一下 LLVM 編譯器吧!

要講解 LLVM 前,有必要的說說 GCC(GNU Compiler Collection,GNU編譯器套件) ,因為蘋果最初也是使用 GCC,后來慢慢的替換為 LLVM 的。

GCC(GNU Compiler Collection,GNU編譯器套件)

原名:GNU C語言編譯器(GNU C Compiler),通常是跨平臺軟件的編譯器首選。(在所有平臺上都使用同一個前端處理程序,產(chǎn)生一樣的中間碼)。

但是由于以下問題,導(dǎo)致蘋果轉(zhuǎn)為 LLVM:

  • GCC 的 Objective-C Frontend 不給力
  • GCC 插件、工具、IDE的支持薄弱

GCC的前端不是蘋果提供維護(hù)的,想要添加一些語法提示等功能還得去求GCC的前端去做。 很多編譯器特性沒有,自動補(bǔ)全、代碼提示、warning、靜態(tài)分析等這些流程不是很給力,都是需要IDE調(diào)用底層命令完成的,結(jié)果需要以插件的形式暴露出來,這一塊GCC做的不是很好。

LLVM

早年Apple一直使用 GCC 作為官方的編譯器,但Apple對 GCC 的性能不滿意,再者 Objective-C 在 GCC 中優(yōu)先級低,GCC 對 Objective-C 語言新特性的支持程度也不高。因此Apple一直在尋找compiler(編譯器)的開源替代品,于是他們將目光轉(zhuǎn)移到LLVM身上。

來自維基百科:關(guān)于LLVM這個名字的來源,LLVM的命名最早源自于底層虛擬機(jī)(Low Level Virtual Machine)的首字母縮寫,由于這個項目的范圍并不局限于創(chuàng)建一個虛擬機(jī),這個縮寫導(dǎo)致了廣泛的疑惑。

LLVM 就是 LLVM, 并不是 Low Level Virtual Machine(底層虛擬機(jī))的簡寫!現(xiàn)今LLVM 已單純成為一個品牌!根據(jù)官網(wǎng)說明:

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies. Despite its name, LLVM has little to do with traditional virtual machines. The name "LLVM" itself is not an acronym; it is the full name of the project.
LLVM項目是模塊化和可重用的編譯器及工具鏈技術(shù)的集合。盡管名稱如此,LLVM與傳統(tǒng)虛擬機(jī)關(guān)系不大。名稱“ LLVM”本身不是縮寫。它是項目的全名。

2000年,伊利諾伊大學(xué)厄巴納-香檳分校(University of Illinois at Urbana-Champaign 簡稱UIUC)這所享有世界聲望的一流公立研究型大學(xué)的 Chris Lattner(克里斯·拉特納,twitter @clattner_llvm ) 和 Vikram Adve(維克拉姆·艾夫)想要為所有靜態(tài)及動態(tài)語言創(chuàng)造出動態(tài)的編譯技術(shù),而開發(fā)的編譯器開發(fā)工具套件。2005年,蘋果電腦雇用了 Chris Lattner 克里斯·拉特納及他的團(tuán)隊為蘋果電腦開發(fā)應(yīng)用程序系統(tǒng),LLVM 涉及范圍越來越大,可以用于常規(guī)編譯器,JIT編譯器,匯編器,調(diào)試器,靜態(tài)分析工具等一系列跟編程語言相關(guān)的工作。Chris Lattner 后來又開發(fā)了 Clang,使得 LLVM 直接挑戰(zhàn) GCC 的地位。

Chris Lattner 大神簡介

Chris Lattner 生于 1978 年,2005年加入蘋果,將蘋果使用的 GCC 全面轉(zhuǎn)為 LLVM。2010年開始主導(dǎo)開發(fā) Swift 語言。2017年1月,克里斯·拉特納 辭去在蘋果的工作,入職特斯拉汽車,負(fù)責(zé)自動駕駛軟件的開發(fā)。2017年8月14日,克里斯·拉特納 發(fā)表Twitter表示將于一周后加入聚焦于深度學(xué)習(xí)與人工智能研發(fā)的Google Brain團(tuán)隊。

Xcode 歷史

Clang 編譯效率是 GCC 的3倍,編譯器性能好,編譯出的文件小。LLVM 3.0 發(fā)布已完整支持所有 ISO C++ 標(biāo)準(zhǔn),代表著 LLVM 正式走向成熟。所以,也標(biāo)志著 Xcode 的變化。

  • Xcode3:GCC前端-LLVM后端,
  • Xcode4.2:Clang-LLVM 3.0 成為默認(rèn)編譯器,
  • Xcode5: GCC 被廢棄,新的編譯器是 LLVM 5.0

從 GCC前端 到 LLVM后端的編譯器,到 Clang-LLVM 的編譯器,一步步收回對編譯工具鏈的控制,也為 Swift 的出現(xiàn)奠定基礎(chǔ)。所以,Chris Lattner 真的是大神!

LLVM 工具鏈

LLVM工具鏈集合.png

最后用這張圖來表示完整的LLVM工具鏈集合的六大執(zhí)行單元。

  1. 詞法分析: 將源代碼中的所有字符切分成記號(Token)的序列。包括了詞法分析器、記號序列化生成器和掃描器,不過掃描器常常作為詞法分析器的第一階段。
  2. 語法分析: 分析符合一定語法規(guī)則的一串符號,它通常會生成一個抽象語法樹(AST - Abstract Syntax tree),用于表示記號之間的語法關(guān)系。
  3. 語義分析: 通過語法分析的解析后,這個過程將從源代碼中收集必要的語義信息,通常包括類型檢查、在使用之前確保聲明了變量等等。
  4. 中間代碼(IR)生成:代碼在這個階段會轉(zhuǎn)換為中間表示式(IR),這是一種中立的語言,與源語言(前端)和機(jī)器(后端)無關(guān)。
  5. 優(yōu)化中間表達(dá)式:中間代碼常常會有冗余和死代碼的情況出現(xiàn),而優(yōu)化器可以處理這些問題以獲得更優(yōu)異的性能。
  6. 生成目標(biāo)代碼: 最后后端會生成在目標(biāo)機(jī)器上運(yùn)行的機(jī)器碼,我們也將其稱之為目標(biāo)代碼。

鏈接器

  • 為什么要讓鏈接器做符號和地址綁定?不綁定的話,又會有什么問題?
  • 鏈接器為什么還要把項目中的多個 Mach-O 文件合并成一個。

回到本文開始的問題,大家對這2個問題心中有答案了嗎?

鏈接器的作用是將符號綁定到地址上,符號表是內(nèi)存地址與函數(shù)名、文件名、行號的映射表。所以,變量、函數(shù)與地址綁定,CPU 才能理解解析代碼。而為什么要多個 Mach-O 文件合并一個最終的 Mach-O?過去面向過程編程時,可以寫一個文件中,而現(xiàn)在主流是面向?qū)ο?,就有類與對象關(guān)系,所以一般是分類、分模塊、分空間來編寫源代碼,所以,需要把這些類與對象和他們之前調(diào)用關(guān)系綁定一起,合并成一個Mach-O。為了更好理解這個 Mach-O,需要了解 CPU 是怎么執(zhí)行他們的,這里就不展開了,感興趣的同學(xué)可以先自行搜索了解更多。

觸類旁通

Clang/Swift-LLVM編譯器架構(gòu).png

讓我們再次回顧一下編譯器的工作流程,以 LLVM 為例,從 iOS 開發(fā)視角來看這個問題。這張圖片來源:從Swift橋接文件到Clang-LLVM - 掘金。詳細(xì)的編譯器過程大家可以參考文章,這里就不過多重復(fù)。

Clang-LLVM編譯過程.png

另外,原文還提到很多的概念,這里也不一一提出了,具體可以看看本文最前面的思維導(dǎo)圖。比如:

  • AST(Abstract Syntax Tree,抽象語法樹)
  • Mach-O:是 Mach Object 文件格式的縮寫,它是一種用于可執(zhí)行文件,目標(biāo)代碼,動態(tài)庫,內(nèi)核轉(zhuǎn)儲的文件格式。作為a.out格式的替代,Mach-O提供了更強(qiáng)的擴(kuò)展性,并提升了符號表中信息的訪問速度。
  • 符號表:是內(nèi)存地址與函數(shù)名、文件名、行號的映射表。格式如:<起始地址> <結(jié)束地址> <函數(shù)> [<文件名:行號>]
  • dyld(the dynamic link editor):當(dāng)加載 Mach-O 文件時,動態(tài)鏈接器會先檢查是否有共享緩存。每個進(jìn)程都會在自己的地址空間映射這些共享緩存,這樣做可以起到優(yōu)化App啟動速度的作用。
  • LLDB(LLVM項目的調(diào)試器組件)
  • ASLR (Address space layout randomization,地址空間配置隨機(jī)加載)
  • 解釋器:不需要經(jīng)過編譯的過程,而是在執(zhí)行的時候通過一個中間的解釋器將代碼解釋為CPU可以執(zhí)行的代碼。JavaScript、Python、PHP等都是直譯式語言。
  • Bitcode:字節(jié)碼
  • SIL(Swift Intermediate Language):AST 和LLVM IR之間的另一種中間代碼表示形式。主要是用來彌補(bǔ)一些 Clang編譯器的缺陷,如無法執(zhí)行一些高級分析,可靠的診斷和優(yōu)化等。

中間代碼 LLVM IR

中間端表達(dá)式 IR(Intermediate Representation),有3種表示形式,但本質(zhì)是等價的,就好比水可以有氣體,液體,固體3種形式。

  1. text:便于閱讀的文本格式,類似于匯編語言,拓展名.II。生成命令:$ clang -S -emit-llvm main.m
  2. memory:內(nèi)存格式,開發(fā)時操作 LLVM IR。
  3. bitcode:二進(jìn)制格式,拓展名.bc。生成命令:$ clang -c -emit-llvm main.m

廣義與狹義 LLVM 與 Clang 的關(guān)系

廣義 LLVM:整個 LLVM 架構(gòu)。

狹義 LLVM:LLVM 后端(代碼優(yōu)化,目標(biāo)文件生成等。)

Clang(C Lange Family Frontend for LLVM),發(fā)音為/?kl??/,是C、C++、Objective-C、Objective-C++ 編程語言的編譯器前端。

所以,非特指,LLVM 就是廣義 LLVM,指整個LLVM項目,包括Clang前端。另外,本文主要講解了 LLVM 在 Apple 平臺上的使用,LLVM 現(xiàn)在被作為實現(xiàn)各種靜態(tài)和運(yùn)行時編譯語言的通用基礎(chǔ)結(jié)構(gòu),可以使用 LLVM 來編譯Ruby,Python,Haskell,Java,D,PHP,Pure,Lua和許多其他語言。LLVM 的主要優(yōu)勢在于其多功能性,靈活性和可重用性,這就是它被用于各種不同任務(wù)的原因:從輕量級JIT編譯嵌入式語言(如Lua)到編譯Fortran代碼(用于大型超級)電腦。

相比較GCC,Clang具有哪些優(yōu)點

  • Clang是:
  • LLVM項目的一個子項目
  • 基于LLVM架構(gòu)的 C、C++、Objective-C、Objective-C++ 編譯器前端
  • Clang優(yōu)點:
  • 編譯速度快:在某些平臺上,Clang的編譯速度顯著的快過GCC
  • 占用內(nèi)存?。篊lang生成的AST(語法樹)所占用的內(nèi)存是GCC的五分之一左右
  • 模塊化設(shè)計:Clang采用基于庫的模塊化設(shè)計,易于IDE集成及其他用途的重用
  • 診斷信息可讀性強(qiáng):在編譯過程中,Clang創(chuàng)建并保存了大量詳細(xì)的元數(shù)據(jù),有利于調(diào)試
  • 設(shè)計清晰簡單,容易理解,易于擴(kuò)展增強(qiáng)

Swift 如何橋接 Objtive-C 文件到 Clang-LLVM ?

在 Swift 的項目中,Objtive-C 代碼可以生成 IR(Intermediate Representation,中間端表達(dá)式),從而與 Swift 生成的 IR 聯(lián)通。這個也是分段(層)的好處,編譯器中間端(Middle end,或叫優(yōu)化器,Optimizer),作為中間者通過前端獲取 IR ,從而不用關(guān)心前端的編程語言是什么,這些設(shè)計特點,帶來了非常大的好處。

假如有N種語言(C、OC、C++、Swift...)的前端,同時也有M個架構(gòu)(模擬器、arm64、x86...)的Target,是否就需要 N × M 個編譯器?
三相架構(gòu)的價值就體現(xiàn)出來了,通過共享優(yōu)化器的中轉(zhuǎn),很好的解決了這個問題。
假如你需要增加一種語言,只需要增加一種前端;假如你需要增加一種處理器架構(gòu),也只需要增加一種后端,而其他的地方都不需要改動。這復(fù)用思想很牛逼吧。

來自:淺談iOS編譯過程 - 簡書

需要注意的是 Clang 是基于LLVM架構(gòu)的 C、C++、Objective-C、Objective-C++ 編譯器前端,不包括 Swift!蘋果針對 Swift 做了單獨的前端,與 Clang 是非常相似的,特點就是增加了 SILSwift Intermediate Language)。詳細(xì)具體可以查看 WWDC 視頻:What's New in LLVM - WWDC 2016 - Videos - Apple Developer 、Behind the Scenes of the Xcode Build Process - WWDC 2018 - Videos - Apple Developer

方舟編譯器

方舟編譯器架構(gòu)示意圖.png

當(dāng)前方舟編譯器支持Java/Kotlin程序字節(jié)碼的前端輸入,其它編程語言的支持(如 C/C++/JS 等)還在規(guī)劃中,方舟編譯器的中間表示(IR)轉(zhuǎn)換器將前端輸入轉(zhuǎn)換成方舟IR,并輸送給后端的優(yōu)化器,最終生成二進(jìn)制文件,二進(jìn)制文件與編譯器運(yùn)行時庫文件鏈接生成可執(zhí)行文件,在方舟的運(yùn)行環(huán)境中就可執(zhí)行該文件。

方舟編譯器IR是支持程序編譯和運(yùn)行的中間程序表示。程序源代碼中的任何信息對于程序分析和優(yōu)化都是有幫助的,所以方舟IR的目標(biāo)是盡可能完整詳細(xì)地提供源程序的信息。

方舟編譯器開源范圍示意圖.png

首次開源范圍是編譯器 IR( Intermediate Representation)、RC(Reference Counting)和多語言設(shè)計思想等,用于與業(yè)界、學(xué)術(shù)界溝通交流。后續(xù)將陸續(xù)開源編譯器前端、后端,支持其它語言(如 JavaScript)的編譯等,當(dāng)前部分Java語言特性和JVM虛擬機(jī)特性的支持未包括在本次開源代碼中,包括:annotation、lambda表達(dá)式、泛型等。目前仍有很多地方不完善,會在社區(qū)陸續(xù)迭代,遇到問題請在社區(qū)提交 issue,歡迎在社區(qū)繼續(xù)討論設(shè)計和代碼共建。

怎么樣!現(xiàn)在你至少看的懂這個方舟編譯器架構(gòu)的大體流程了吧,是不是發(fā)現(xiàn)其實并沒有那么難理解!那就對了,萬事開頭難,慢慢的一切都會簡單起來~ 至少去知x上看看這些評論都看懂了啊,如何看待方舟編譯器于 2019 年 8 月 31 日開源? - 知乎,是不是很開心~

詳細(xì)可見官方網(wǎng)頁:方舟編譯器 OpenArkCompiler - FAQ

龍書、虎書、鯨書 --- 編譯原理三大圣書

  1. 龍書:《編譯原理》(編譯原理 技術(shù)與工具)(Compilers: Principles,Techniques, and Tools)
  2. 虎書:《現(xiàn)代編譯原理 — C語言描 》(Modern Compiler Implement in C)
  3. 鯨書:《高級編譯器設(shè)計與實現(xiàn)》(Advanced Compiler Design and Implementation)

龍書:《編譯原理》是編譯領(lǐng)域無可替代的經(jīng)典著作,被廣大計算機(jī)專業(yè)人士譽(yù)為“龍書”。
虎書:增加了數(shù)據(jù)流分析、循環(huán)優(yōu)化、內(nèi)存管理等內(nèi)容。
鯨書:包含了一些更比較高級的編譯器的設(shè)計和實現(xiàn)。

大家是不是瞬間感覺自己就要買這幾本書呢?。?!我到底能不能看的懂呢?可以先自測一下自己是不是適配現(xiàn)在就學(xué)習(xí)編譯原理:

學(xué)習(xí)編譯原理前自測
  • a. 編譯型語言和解釋型語言的區(qū)別是什么?
  • b. 編譯經(jīng)歷了哪幾個基本過程? 每個過程主要干了什么事情?
  • c. 詳細(xì)描述一下你最熟悉的語言中,賦值號的左邊可以由哪些部分組成? 右邊可以由哪些部分組成?
  • d. 什么是遞歸? 如何終止一個遞歸行為?
  • e. 什么是貪心過程?
  • f. 聽說過 KMP 嗎? 介紹一下它的基本思想.
  • g. 生成樹是什么?
  • h. 你覺得編程語言和自然語言最重要的區(qū)別有哪些?
  • i. 什么是有限狀態(tài)自動機(jī)? 你知道幾種不同的自動機(jī)類型? 它們之間的區(qū)別和聯(lián)系?
  • j. 內(nèi)存被人為的劃分成了哪幾種不同的類型, 它們之間的區(qū)別和聯(lián)系?
  • k. 什么是中斷? CPU 在響應(yīng)中斷時會做什么事情?
  • l. 你最熟悉的語言有垃圾回收機(jī)制嗎? 若有描述它進(jìn)行垃圾回收的原理, 若沒有則描述你在編寫程序時是如何管理內(nèi)存的?

本節(jié)內(nèi)容來自:如何學(xué)習(xí)編譯原理? - 歐長坤的回答 - 知乎

3、總結(jié)

終于到總結(jié)部分啦!這個文章,之前給我們組內(nèi)同學(xué)分享時,在回顧都忘記了一大部分,另外在寫成文字時,發(fā)現(xiàn)有很多東西需要考證,每篇文章花了很多心思,正好2020年新型肺炎冠狀病毒都在家里,正好有這時間。而 維基百科 就是一個很棒的網(wǎng)站,關(guān)于大多數(shù)知識和歷史都能查到,真的很有趣!

關(guān)于編譯器的知識有太多了,深入的還可以自己寫一個編譯器!這里就達(dá)不到這樣的層次,所以就提一下,有需要的同學(xué)可以自行搜索。關(guān)于LLVM的知識也還有很多沒有講到,比如抽象語法樹(AST),前端 Clang 是重中之器,Clang 插件開發(fā)等,還有中間端的 Pass,后端的Mach-O 和 CPU 執(zhí)行 Mach-O 的過程等。

最后,本文還是有很多細(xì)節(jié)無法用文字記錄下來,大家可以閱讀下面的參考鏈接,這樣收獲會更大。關(guān)于編譯器鏈接器就到這里先,對于大家理解這個概念應(yīng)該是沒有問題啦!具體的實現(xiàn)細(xì)節(jié),需要大家多學(xué)習(xí),如果有能力,可以看看龍書!虎書!鯨書! 想想也是一件快樂的事件!

2020年,手機(jī)只是小小一部分,智慧家居大屏、穿戴、車機(jī)、音響、手表、PC等等各種各樣設(shè)備,作為開發(fā)者,我們不要只關(guān)注自己開發(fā)的App,應(yīng)該去理解這個世界發(fā)生什么變化,為什么變化,怎么變化,抓住變化!當(dāng)然我承認(rèn)世界是掌握于小數(shù)人,但能先抓住自己!這就是好的開始~


注:更多關(guān)于 iOS 開發(fā)和程序開發(fā)相關(guān)的內(nèi)容,可以查看系列,目前還在連載中 【學(xué)習(xí)總結(jié)】iOS開發(fā)高手課 -- (連載中) | iHTCboy's blog,以上,希望對你有用!

參考

WWDC

Article


  • 如有侵權(quán),聯(lián)系必刪!
  • 如有不正確的地方,歡迎指導(dǎo)!
  • 如有疑問,歡迎在評論區(qū)一起討論!


注:本文首發(fā)于 iHTCboy's blog,如若轉(zhuǎn)載,請注來源

最后編輯于
?著作權(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)容