iOS - LLVM編譯全解

引言

維基百科:

編譯語言(英語:Compiled language)是一種以編譯器來實(shí)現(xiàn)的編程語言。它不像解釋型語言一樣妈橄,由解釋器將代碼一句一句運(yùn)行蔬啡,而是以編譯器拣帽,先將代碼編譯為機(jī)器碼洽腺,再加以運(yùn)行婚瓜。理論上赛糟,任何編程語言都可以是編譯式鹃祖,或直譯式的宙搬。它們之間的區(qū)別葫掉,僅與程序的應(yīng)用有關(guān)裆针。

一般可以將編程語言分為兩種刨摩,編譯語言直譯式語言

前言

iOS開發(fā)使用Object-C和Swift編譯語言世吨,兩者都需要通過編譯器(Clang + LLVM)把代碼編譯器生成機(jī)器碼澡刹,機(jī)器碼可以直接在CPU上執(zhí)行。接下來詳細(xì)介紹一下這兩種語言的優(yōu)缺點(diǎn)耘婚,可以更深入的理解為什么移動(dòng)端開發(fā)會采用編譯語言罢浇。

編譯語言直譯式語言兩種編程語言的優(yōu)缺點(diǎn):

編譯語言/直譯式語言優(yōu)缺點(diǎn)比較:

編譯/直譯式優(yōu)點(diǎn)缺點(diǎn)

編譯語言運(yùn)行速度快(被預(yù)先編譯成機(jī)器碼,可以直接運(yùn)行)開發(fā)、調(diào)試比較長(程序開發(fā)速度嚷闭,以及除錯(cuò)時(shí)間時(shí)間較長)

直譯式語言開發(fā)攒岛、調(diào)試比較短(程序開發(fā)速度,以及除錯(cuò)時(shí)間時(shí)間較長胞锰。應(yīng)用程序不能脫離其解釋器灾锯,但這種方式比較靈活,可以動(dòng)態(tài)地調(diào)整嗅榕、修改應(yīng)用程序)運(yùn)行速度慢(源代碼一邊由相應(yīng)語言的解釋器翻譯”成目標(biāo)代碼(機(jī)器語言)顺饮,一邊執(zhí)行)

編譯語言: 像C++、Objective-C(Swift)凌那、C兼雄、Java等都是編譯語言,必須通過編譯器生成機(jī)器碼帽蝶,機(jī)器碼可以直接在CPU上執(zhí)行君旦,所以你執(zhí)行效率較高。而每次運(yùn)行都需要編譯器生成機(jī)器碼再運(yùn)行嘲碱,故編寫金砍、調(diào)試、排錯(cuò)比較繁瑣麦锯。

直譯式語言:像JavaScript恕稠、Python、PHP等都是直譯式語言扶欣,不需要經(jīng)過編譯的過程鹅巍,而是在執(zhí)行的時(shí)候通過一個(gè)中間的解釋器將代碼解釋為CPU可以執(zhí)行的代碼。所以料祠,較編譯語言來說骆捧,直譯式語言效率低一些,但是編寫比較靈活髓绽。

總之敛苇,由于移動(dòng)端(iOS、Android)設(shè)備性能限制的情況下顺呕,采用編譯語言進(jìn)行開發(fā)是一種比較好的方式

一枫攀、iOS編譯器

在講解編譯過程之前,需要了解蘋果公司采用的編譯器株茶,編譯器采用哪種方式進(jìn)行編譯来涨,才能更深入的理解編譯底層的整個(gè)過程。

1.Xcode編譯器發(fā)展史

Xcode3 以前: GCC启盛;

Xcode3: 增加LLVM蹦掐,GCC(前端) + LLVM(后端)技羔;

Xcode4.2: 出現(xiàn)Clang - LLVM 3.0成為默認(rèn)編譯器;

Xcode4.6: LLVM 升級到4.2版本卧抗;

Xcode5: GCC被廢棄堕阔,新的編譯器是LLVM 5.0,從GCC過渡到Clang-LLVM的時(shí)代正式完成

1.1.為什么蘋果的Xcode會使用Clang+LLVM取代GCC颗味?

GCC最初是作為CNU(GNU是“GNU is Not Unix”)操作系統(tǒng)的編譯器編寫的,是一套由 GNU 開發(fā)的編程語言編譯器牺弹,不屬于蘋果維護(hù)也不能完全控制開發(fā)進(jìn)程浦马,Apple為Objective-C增加許多新特性,但是GCC開發(fā)者對這些支持卻不友好张漂;Apple需要做模塊化晶默,GCC開發(fā)者卻拖著遲遲不實(shí)現(xiàn)。

1.2.GCC被取代的歷史必然

隨著Apple對其IDE(也就是Xcode)性能的要求越來越高,蘋果公司需要找到一個(gè)可以控制的編譯器航攒。而在在科技的歷史長河中磺陡,LLVM項(xiàng)目于2000年在伊利諾伊大學(xué)厄巴納 - 香檳分校開始,由Vikram Adve和Chris Lattner領(lǐng)導(dǎo)漠畜。 LLVM最初是作為研究基礎(chǔ)設(shè)施開發(fā)的币他,用于研究靜態(tài)和動(dòng)態(tài)編程語言的動(dòng)態(tài)編譯技術(shù)。 LLVM是根據(jù)伊利諾伊大學(xué)/ NCSA開源許可證發(fā)布的憔狞,是一個(gè)許可的免費(fèi)軟件許可證蝴悉。

1.3.GCC被取代

2005年,Apple Inc.聘請了Lattner并組建了一個(gè)團(tuán)隊(duì)瘾敢,致力于LLVM系統(tǒng)拍冠,以便在Apple的開發(fā)系統(tǒng)中實(shí)現(xiàn)各種用途。 LLVM是Apple最新的macOS和iOS開發(fā)工具中不可或缺的一部分簇抵。

2.為什么選擇LLVM編譯器

2.1編譯器

2.1.1經(jīng)典編譯器設(shè)計(jì)簡介

傳統(tǒng)靜態(tài)編譯器(如大多數(shù)C編譯器)最流行的設(shè)計(jì)是三階段設(shè)計(jì)庆杜,其主要組件是前端,優(yōu)化器和后端碟摆。 前端解析源代碼晃财,檢查它是否有錯(cuò)誤,并構(gòu)建一個(gè)特定于語言的抽象語法樹(AST)來表示輸入代碼典蜕。 AST可選地轉(zhuǎn)換為新的表示以進(jìn)行優(yōu)化拓劝,優(yōu)化器和后端在代碼上運(yùn)行。

1111.png

2.1.2GCC編譯器設(shè)計(jì)簡介(同樣采用三相設(shè)計(jì))

GCC編譯器的三相設(shè)計(jì)中最成功的實(shí)現(xiàn)是對Java嘉裤、 .Net語言的編譯解析郑临。

222.png

2.1.3 LLVM編譯器設(shè)計(jì)簡介(實(shí)現(xiàn)三相設(shè)計(jì))

在基于LLVM的編譯器中,前端負(fù)責(zé)解析屑宠,驗(yàn)證和診斷輸入代碼中的錯(cuò)誤厢洞,然后將解析的代碼轉(zhuǎn)換為LLVM IR(通常情況。但是也有例外,通過構(gòu)建AST然后將AST轉(zhuǎn)換為LLVM IR)躺翻。該IR可選地通過一系列改進(jìn)代碼的分析和優(yōu)化過程提供丧叽,然后被發(fā)送到代碼生成器以生成本機(jī)機(jī)器代碼,如圖下圖所示公你。

3333.png

為什么要使用三相設(shè)計(jì)踊淳?優(yōu)勢在哪?

首先解決了一個(gè)很大的問題:假如有N種語言(C陕靠、OC迂尝、C++、Swift...)的前端剪芥,同時(shí)也有M個(gè)架構(gòu)(模擬器垄开、arm64、x86...)的Target税肪,是否就需要 N × M 個(gè)編譯器溉躲?

三相架構(gòu)的價(jià)值就體現(xiàn)出來了,通過共享優(yōu)化器的中轉(zhuǎn)益兄,很好的解決了這個(gè)問題锻梳。

假如你需要增加一種語言,只需要增加一種前端净捅;假如你需要增加一種處理器架構(gòu)唱蒸,也只需要增加一種后端,而其他的地方都不需要改動(dòng)灸叼。這復(fù)用思想很牛逼吧神汹。

2.2 LLVM編譯器的組成

LLVM項(xiàng)目是模塊化和可重用的編譯器和工具鏈技術(shù)的集合。LLVM主要的子項(xiàng)目有一下幾個(gè):

1.LLVM核心庫:

LLVM提供一個(gè)獨(dú)立的鏈接代碼優(yōu)化器為許多流行CPU(以及一些不太常見的CPU)的代碼生成支持古今。這些庫是圍繞一個(gè)指定良好的代碼表示構(gòu)建的屁魏,稱為LLVM中間表示(“LLVM IR”)。LLVM還可以充當(dāng)JIT編譯器 - 它支持x86 / x86_64和PPC / PPC64程序集生成捉腥,并具有針對編譯速度的快速代碼優(yōu)化氓拼。。

2.LLVM IR 生成器Clang:

Clang是一個(gè)“LLVM原生”C / C ++ / Objective-C編譯器抵碟,旨在提供驚人的快速編譯(例如桃漾,在調(diào)試配置中編譯Objective-C代碼時(shí)比GCC快3倍),非常有用的錯(cuò)誤和警告消息以及提供構(gòu)建優(yōu)秀源代碼工具的平臺拟逮。

3.LLDB項(xiàng)目:

LLDB項(xiàng)目以LLVM和Clang提供的庫為基礎(chǔ)撬统,提供了一個(gè)出色的本機(jī)調(diào)試器。它使用Clang AST和表達(dá)式解析器敦迄,LLVM JIT恋追,LLVM反匯編程序等凭迹,以便提供“正常工作”的體驗(yàn)。在加載符號時(shí)苦囱,它也比GDB快速且內(nèi)存效率更高嗅绸。

4.libc ++和libc++:

libc ++和libc++ ABI項(xiàng)目提供了C ++標(biāo)準(zhǔn)庫的標(biāo)準(zhǔn)符合性和高性能實(shí)現(xiàn),包括對C ++ 11的完全支持撕彤。

5.lld項(xiàng)目:

lld項(xiàng)目旨在成為clang / llvm的內(nèi)置鏈接器鱼鸠。目前,clang必須調(diào)用系統(tǒng)鏈接器來生成可執(zhí)行文件羹铅。

其他的就不再詳細(xì)介紹了,詳情可以參考(LLVMClang)

總之蚀狰,LLVM是Apple主導(dǎo)的開源框架,并提供一套使用于Apple平臺的LLVM編譯器睦裳,同時(shí)提供優(yōu)秀的性能,所以Apple采用LLVM的方式進(jìn)行編譯

3. Clang + LLVM 的編譯簡單過程

3.1.Clang + LLVM 編譯過程

LLVM采用三相設(shè)計(jì)撼唾,前端Clang負(fù)責(zé)解析廉邑,驗(yàn)證和診斷輸入代碼中的錯(cuò)誤,然后將解析的代碼轉(zhuǎn)換為LLVM IR倒谷,后端LLVM編譯把IR通過一系列改進(jìn)代碼的分析和優(yōu)化過程提供蛛蒙,然后被發(fā)送到代碼生成器以生成本機(jī)機(jī)器代碼。

簡單的流程如下圖:

444.png

3.2 Clang 編譯前端

編譯器前端的任務(wù)是進(jìn)行:語法分析渤愁,語義分析牵祟,生成中間代碼(intermediate representation )。在這個(gè)過程中抖格,會進(jìn)行類型檢查诺苹,如果發(fā)現(xiàn)錯(cuò)誤或者警告會標(biāo)注出來在哪一行。

555.png

3.3 LLVM 編譯后端

編譯器后端會進(jìn)行機(jī)器無關(guān)的代碼優(yōu)化雹拄,生成機(jī)器語言收奔,并且進(jìn)行機(jī)器相關(guān)的代碼優(yōu)化。iOS的編譯過程滓玖,后端的處理如下:

LVVM優(yōu)化器會進(jìn)行BitCode的生成坪哄,鏈接期優(yōu)化等等。

666.png

LLVM機(jī)器碼生成器會針對不同的架構(gòu)势篡,比如arm64等生成不同的機(jī)器碼翩肌。

777.png

二、LLVM 編譯詳細(xì)過程原理

1.簡述LLVM的使用場景

通過上方的講解禁悠,可能對LLVM有一個(gè)簡單的了解念祭,但是感覺LLVM是底層編譯器的內(nèi)容,和開發(fā)沒有太大的關(guān)系碍侦,在使用過程中基本上用不到棒卷,感覺遙不可及顾孽,其實(shí)LLVM在項(xiàng)目的使用過程中一直都在使用,只是沒有發(fā)現(xiàn)比规。

Clang是LLVM的一個(gè)前端若厚,在Xcode編譯iOS項(xiàng)目的時(shí)候,都是使用的LLVM蜒什,其實(shí)在編寫代碼以及調(diào)試的時(shí)候都在接觸LLVM提供的功能测秸,例如:代碼的亮度(Clang)、實(shí)時(shí)代碼檢查(Clang)灾常、代碼提示(Clang)霎冯、debug斷點(diǎn)調(diào)試(LLDB)。

2.項(xiàng)目編譯過程簡介

下面來簡單的講講整個(gè) iOS 項(xiàng)目的編譯過程,其中可能會有一些疑問钞瀑,先保留著沈撞,后面會詳細(xì)解釋

我們的項(xiàng)目是一個(gè) target,一個(gè)編譯目標(biāo)雕什,它擁有自己的文件和編譯規(guī)則缠俺,在我們的項(xiàng)目中可以存在多個(gè)子項(xiàng)目,這在編譯的時(shí)候就導(dǎo)致了使用了 Cocoapods 或者擁有多個(gè) target 的項(xiàng)目會先編譯依賴庫贷岸。這些庫都和我們的項(xiàng)目編譯流程一致壹士。Cocoapods 的原理解釋將在文章后面一部分進(jìn)行解釋。

iOS 項(xiàng)目的編譯過程

1.寫入輔助文件:將項(xiàng)目的文件結(jié)構(gòu)對應(yīng)表偿警、將要執(zhí)行的腳本躏救、項(xiàng)目依賴庫的文件結(jié)構(gòu)對應(yīng)表寫成文件,方便后面使用螟蒸;并且創(chuàng)建一個(gè).app包盒使,后面編譯后的文件都會被放入包中;2.運(yùn)行預(yù)設(shè)腳本:Cocoapods會預(yù)設(shè)一些腳本七嫌,當(dāng)然你也可以自己預(yù)設(shè)一些腳本來運(yùn)行忠怖。這些腳本都在BuildPhases中可以看到;3.編譯文件:針對每一個(gè)文件進(jìn)行編譯抄瑟,生成可執(zhí)行文件Mach-O凡泣,這過程LLVM的完整流程,前端皮假、優(yōu)化器鞋拟、后端;4.鏈接文件:將項(xiàng)目中的多個(gè)可執(zhí)行文件合并成一個(gè)文件惹资;5.拷貝資源文件:將項(xiàng)目中的資源文件拷貝到目標(biāo)包贺纲;6.編譯storyboard文件:storyboard文件也是會被編譯的;7.鏈接storyboard文件:將編譯后的storyboard文件鏈接成一個(gè)文件褪测;8.編譯Asset文件:我們的圖片如果使用Assets.xcassets來管理圖片猴誊,那么這些圖片將會被編譯成機(jī)器碼潦刃,除了icon和launchImage;9.運(yùn)行Cocoapods腳本:將在編譯項(xiàng)目之前已經(jīng)編譯好的依賴庫和相關(guān)資源拷貝到包中懈叹。10.生成.app包11.將Swift標(biāo)準(zhǔn)庫拷貝到包中12.對包進(jìn)行簽名13.完成打包

簡單的Demo的Build過程圖和App 內(nèi)部的結(jié)構(gòu)

在上述流程中:2 - 9 步驟的數(shù)量和順序并不固定乖杠,這個(gè)過程可以在 Build Phases 中指定。Phases:階段澄成、步驟胧洒。這個(gè) Tab 的意思就是編譯步驟。其實(shí)不僅我們的整個(gè)編譯步驟和順序可以被設(shè)定墨状,包括編譯過程中的編譯規(guī)則(Build Rules)和具體步驟的參數(shù)(Build Settings)卫漫,在對應(yīng)的 Tab 都可以看到。關(guān)于整個(gè)編譯流程的日志和設(shè)定肾砂,可以查看這篇文章:Build 過程列赎,跟著它的步驟來查看自己的項(xiàng)目將有助于你理解整個(gè)編譯流程。后面也會詳細(xì)講解這些內(nèi)容镐确。

3.文件編譯過程

3.1. 預(yù)處理

預(yù)處理顧名思義是預(yù)先處理,那預(yù)處理都做了哪些事情呢包吝?內(nèi)容如下。

(1) import 頭文件替換

代碼中會有很多 #import 宏辫塌,預(yù)處理的第一步就是將 import 引入的文件代碼放入對應(yīng)文件漏策。

(2) macro 宏展開

帶參數(shù)宏和不帶參數(shù)宏

(3)處理其他的預(yù)編譯指令(其實(shí)預(yù)編譯過程也是出了預(yù)編譯指令的過程)

條件編譯語句也是在預(yù)處理階段完成派哲,并且條件編譯只允許編譯源程序中滿足條件的程序段臼氨,使生成的目標(biāo)程序較短,從而減少了內(nèi)存的開銷并提高了程序的效率,如以下代碼就只會保留一個(gè)return語句:

#ifDEBUGreturnYES;#elsereturnNO;#endif

(4)總之

簡單來說芭届,“#”這個(gè)符號是編譯器預(yù)處理的標(biāo)志

//? Clang 方法: 在終端執(zhí)行一下$clang -E main.m// main.m#import<Foundation/Foundation.h>#define aa 10intmain(){NSObject*obj = [[NSObjectalloc] init];id__weakobj1 = obj;NSLog(@"------%@--%d--",[obj1class],aa);? }// 編譯后的代碼intmain(){NSObject*obj = [[NSObjectalloc] init];id__attribute__((objc_ownership(weak))) obj1 = obj;NSLog(@"------%@--%d--",[obj1class],10);}

3.2. Lexical Analysis - 詞法分析(輸出token流)

使用 clang 命令clang -Xclang -dump-tokens main.m轉(zhuǎn)化后的代碼如下(去掉了#import 的內(nèi)容):

詞法分析储矩,只需要將源代碼以字符文本的形式轉(zhuǎn)化成Token流的形式,不涉及交驗(yàn)語義褂乍,不需要遞歸持隧,是線性的。

什么是token流呢逃片?可以這么理解:就是有"類型"屡拨,有"值"的一些小單元。

詞法分析其實(shí)是編譯器開始工作真正意義上的第一個(gè)步驟褥实,其所做的工作主要為將輸入的代碼轉(zhuǎn)換為一系列符合特定語言的詞法單元呀狼,這些詞法單元類型包括了關(guān)鍵字,操作符损离,變量等等哥艇。

可以通過下發(fā)被編譯過的代碼對應(yīng)main.m文件,把所有的內(nèi)容都一一對應(yīng)起來僻澎。

這里貌踏,每一個(gè)符號都會標(biāo)記出來其位置十饥,這個(gè)位置是宏展開之前的位置,這樣后面如果發(fā)現(xiàn)報(bào)錯(cuò)祖乳,就可以正確的提示錯(cuò)誤位置了逗堵。

3.3.Semantic Analysis - 語法分析(輸出(AST)抽象語法樹)

對代碼進(jìn)行標(biāo)記并不是Clang最終的目的凡资,而是一個(gè)Clang的一個(gè)過程,其實(shí)標(biāo)記代碼為了讓代碼更便于轉(zhuǎn)化成機(jī)器語言垦藏,標(biāo)記代碼轉(zhuǎn)化成抽象語法樹(abstract syntax tree – AST)是一個(gè)必經(jīng)之路。

3.3.1 AST

使用 clang 命令clang -Xclang -ast-dump -fsyntax-only main.m伞访,轉(zhuǎn)化后的樹如下

這個(gè)main方法的抽象樹可以看出來樹頂是FunctionDecl:方法聲明(Function Declaration)。

這里因?yàn)榻厝×瞬糠执a厚掷,其實(shí)并不是整個(gè)樹的樹頂。真正的樹頂描述應(yīng)該是:TranslationUnitDecl冒黑。

詳細(xì)的AST語法不多介紹田绑,關(guān)于 AST 的詳細(xì)解釋可以查看:Introduction to the Clang AST

3.3.2 靜態(tài)分析

通過語法樹進(jìn)行代碼靜態(tài)分析掩驱,找出非語法性錯(cuò)誤

模擬代碼執(zhí)行路徑冬竟,分析出control-flow graph(CFG) 【MRC時(shí)代會分析出引用計(jì)數(shù)的錯(cuò)誤】

預(yù)置了常用Checker(檢查器)

3.4. CodeGen - (Intermediate Representation,簡稱IR)IR中間代碼生成

當(dāng)通過Clang語法解析涮帘,代碼沒有出現(xiàn)報(bào)錯(cuò)笑诅,Clang前端就將進(jìn)入最后一步:生成LLVM IR中間代碼,并將生成的LLVM IR代碼遞交給優(yōu)化器弦叶。

使用命令clang -S -emit-llvm main3.m -o main.ll生成LLVM 中間代碼LLVM IR

在這里簡單介紹一些 LLVM IR 的指令:

%:局部變量

@:全局變量

alloca:分配內(nèi)存堆棧

i32:32 位的整數(shù)

i32**:一個(gè)指向 32 位 int 值的指針的指針

align 4:向 4 個(gè)字節(jié)對齊早处,即便數(shù)據(jù)沒有占用 4 個(gè)字節(jié),也要為其分配四個(gè)字節(jié)

call:調(diào)用

LLVM IR 是Frontend的輸出默责,也是LLVM Backend的輸入,前后端的橋接語言, 更具生成的文件解析杖虾,其實(shí)生成的LLVM IR對Runtime進(jìn)行橋接的一個(gè)文件

1.Class/Meta Class/Protocol/Category內(nèi)存結(jié)構(gòu)生成媒熊,并存放在指定section中(如Class:_DATA,_objc_classrefs)2.Method/lvar/Property內(nèi)存結(jié)構(gòu)生成3.組成method_list/ivar_list/property_list并填入Class4.Non-Fragile ABI:為每個(gè)Ivar合成OBJC_IVAR_$_ 偏移值常量5.存取Ivar的語句(ivar =123;inta = ivar;)轉(zhuǎn)寫成base + OBJC_IVAR$_的形式6.將語法樹中的ObjcMessageExpr翻譯成相應(yīng)版本的objc_msgSend芦鳍,7.對super關(guān)鍵字的調(diào)用翻譯成objc_msgSendSuper8.根據(jù)修飾符strong/weak/copy/atomic合成@property自動(dòng)實(shí)現(xiàn)的setter/getter9.處理@synthesize10.生成block_layout的數(shù)據(jù)結(jié)構(gòu)11.變量的capture(__block/__weak)12.生成_block_invoke函數(shù)13.ARC:分析對象引用關(guān)系嚷往,將objc_storeStrong/objc_storeWeak等ARC代碼插入14.將ObjCAutoreleasePoolStmt轉(zhuǎn)譯成objc_autoreleasePoolPush/Pop15.實(shí)現(xiàn)自動(dòng)調(diào)用[superdealloc]16為每個(gè)擁有ivar的Class合成.cxx_destructor方法來自動(dòng)釋放類的成員變量,代替MRC時(shí)代的“self.xxx =nil”

3.5. Optimize - 優(yōu)化IR

使用命令clang -O3 -S -emit-llvm main3.m -o main3.ll

這一步驟的優(yōu)化是非常重要的柠衅,很多直接轉(zhuǎn)換來的代碼是不合適且消耗內(nèi)存的皮仁,因?yàn)槭侵苯愚D(zhuǎn)換菲宴,所以必然會有這樣的問題喝峦,而優(yōu)化放在這一步的好處在于前端不需要考慮任何優(yōu)化過程,減少了前端的開發(fā)工作粟耻。

3.6. 生成Target相關(guān)匯編

使用命令clang -S -o - main3.m | open -f可以查看生成的匯編代碼:

在這篇文章中漩怎,詳細(xì)解釋了這些匯編指令或代碼到底是如何工作的:Mach-O 可執(zhí)行文件勋锤。

3.7. Link生成Executable

在最后叁执,LLVM 將會把這些匯編代碼輸出成二進(jìn)制的可執(zhí)行文件矮冬,使用命令 clang main3.m -o main.out 即可查看

上面的代碼中,每個(gè) segment 的意義也不一樣:

__PAGEZEROsegment 它的大小為 4GB吆录。這 4GB 并不是文件的真實(shí)大小恢筝,但是規(guī)定了進(jìn)程地址空間的前 4GB 被映射為 不可執(zhí)行、不可寫和不可讀此改。

__TEXTsegment 包含了被執(zhí)行的代碼侄柔。它被以只讀和可執(zhí)行的方式映射。進(jìn)程被允許執(zhí)行這些代碼移剪,但是不能修改薪者。

__DATAsegment 以可讀寫和不可執(zhí)行的方式映射啸胧。它包含了將會被更改的數(shù)據(jù)。

__LINKEDITsegment 指出了 link edit 表(包含符號和字符串的動(dòng)態(tài)鏈接器表)的地址贝椿,里面包含了加載程序的元數(shù)據(jù)陷谱,例如函數(shù)的名稱和地址烟逊。

關(guān)于 section 中的內(nèi)容的研究可以查看:

Mach-O 可執(zhí)行文件

Mach-O 可執(zhí)行文件2

PARSING MACH-O FILES

關(guān)于更詳細(xì)的編譯過程可以查看:

深入剖析 iOS 編譯 Clang LLVM

4. Swift的編譯過程

Swift 編譯器結(jié)構(gòu)的官方文檔中描述了 Swift 編譯器是如何工作的,分為如下步驟:

解析:解析器是一個(gè)簡單的遞歸下降解析器(在lib / Parse中實(shí)現(xiàn))乔宿,帶有集成的手動(dòng)編碼詞法分析器访雪。解析器負(fù)責(zé)生成沒有任何語義或類型信息的抽象語法樹(AST)臣缀,并針對輸入源的語法問題發(fā)出警告或錯(cuò)誤。

語意分析:語義分析(在lib / Sema中實(shí)現(xiàn))負(fù)責(zé)解析 AST 并將其轉(zhuǎn)換為格式良好的完全檢查形式的 AST计寇,并在源代碼中發(fā)出語義問題的警告或錯(cuò)誤。語義分析包括類型推斷蹲堂,如果成功贝淤,則所得到的代碼是類型檢查安全的 AST 播聪。

Clang導(dǎo)入器:Clang導(dǎo)入器(在lib / ClangImporter中實(shí)現(xiàn))導(dǎo)入Clang模塊,并將它們導(dǎo)出的 C 或 Objective-C API 映射到相應(yīng)的 Swift API中稼虎。結(jié)果導(dǎo)入的 AST 可以通過語義分析來引用招刨。

SIL生成:Swift中間語言(Swift Intermediate Language沉眶,簡稱SIL)是一種高級的,Swift特有的中間語言柳击,適用于 Swift 代碼的進(jìn)一步分析和優(yōu)化片习。SIL 生成階段(在lib / SILGen中實(shí)現(xiàn))將類型檢查的 AST 降低到所謂的 “原始” SIL藕咏。SIL的設(shè)計(jì)描述在docs/ SIL.rst中可以看到。

SIL優(yōu)化:在SIL優(yōu)化(在lib/Analysis饥悴,lib/ ARC卦碾,lib/LoopTransforms洲胖,和lib/Transforms中實(shí)現(xiàn))執(zhí)行額外的高級別坯沪,Swift 特有的優(yōu)化的程序,包括(例如)自動(dòng)引用計(jì)數(shù)優(yōu)化叉弦,虛擬化和通用專業(yè)化淹冰。

LLVM IR生成:IR生成(在lib/IRGen中實(shí)現(xiàn))將 SIL 降到 LLVM IR,此時(shí)LLVM可以繼續(xù)對其進(jìn)行優(yōu)化并生成機(jī)器碼柠衍。

相關(guān)內(nèi)容不詳細(xì)講解晶乔,可參考:https://blog.csdn.net/aas319/article/details/78606342

三、相關(guān)衍生的內(nèi)容

1.Xcode 編譯設(shè)置

1.1 Build Settings

這里是編譯設(shè)置阵漏,針對編譯流程中的各個(gè)過程進(jìn)行參數(shù)和工具的配置:

Architectures:編譯目標(biāo) CPU 架構(gòu)履怯,這里比較常見的是Build Active Architectures Only(只編譯為當(dāng)前架構(gòu)裆泳,是指你在 scheme 中選定的設(shè)備的 CPU 架構(gòu))晾虑,debug設(shè)置為YES,Release設(shè)置為NO糙捺。

Assets:Assets.xcassets資源組的配置笙隙。

Build Locations:查看 Build 日志可以看到在編譯過程中的目標(biāo)文件夾竟痰。

Build Options:這里是一些編譯的選項(xiàng)設(shè)定,包含:

是否總是嵌入 Swift 標(biāo)準(zhǔn)庫铅檩,這個(gè)在靜態(tài)庫和動(dòng)態(tài)庫的第一篇文章中有講莽鸿,iOS 系統(tǒng)目前是不包含 Swift 標(biāo)準(zhǔn)庫的,都是被打包在項(xiàng)目中兔沃。

c/c++/objective-c 編譯器:Apple LLVM 9.0

是否打開 Bitcode

Deployment:iOS 部署設(shè)置乒疏。說白了就是安裝到手機(jī)的設(shè)置。

Headers:頭文件入偷?具體作用不詳械哟,知道的可以說一下暇咆。

Kernel Module:內(nèi)核模塊,作用不詳其骄。

Linking:鏈接設(shè)置扯旷,鏈接路徑钧忽、鏈接標(biāo)記、Mach-O 文件類型桃煎。

Packaging:打包設(shè)置大刊,info.plist 的路徑設(shè)置缺菌、Bundle ID 、App 顯示名稱的設(shè)置耿战。

Search Paths:庫的搜索路徑蛾绎、頭文件的搜索路徑租冠。

Signing:簽名設(shè)置,開發(fā)纤泵、生產(chǎn)的簽名設(shè)置捏题,這些都和你在開發(fā)者網(wǎng)站配置的證書相關(guān)肉渴。

Testing:測試設(shè)置,作用不詳循狰。

Text-Based API:基于文本的 API绪钥,字面翻譯关炼,作用不詳儒拂。

Versioning:版本管理。

Apple LLVM 9.0 系列:LLVM 的配置甸祭,包含路徑褥影、編譯器每一步的設(shè)置凡怎、語言設(shè)置。在這里Apple LLVM 9.0 - Warnings可以選擇在編譯的時(shí)候?qū)⒛男┣闆r認(rèn)定為錯(cuò)誤(Error)和警告(Warning)寨典,可以開啟困難模式耸成,任何一個(gè)小的警告都會被認(rèn)定為錯(cuò)誤。

Asset Catalog Compiler - Options:Asset 文件的編譯設(shè)置弦追。

Interface Builder Storyboard Compiler - Options:Storyboard 的編譯設(shè)置花竞。

以及一些靜態(tài)分析和 Swift 編譯器的設(shè)定约急。

1.2 Build Phases

編譯階段,編譯的時(shí)候?qū)⒏鶕?jù)順序來進(jìn)行編譯牵辣。這里固定的有:

Compile Sources:編譯源文件纬向。

Link Binary With Libraries:相關(guān)的鏈接庫拐云。

Copy Bundle Resources:要拷貝的資源文件叉瘩,有時(shí)候如果一個(gè)資源文件在開發(fā)過程中發(fā)現(xiàn)找不到,可以在這里找一下危彩,看看是不是加進(jìn)來了泳桦。

如果使用了 Cocoapods灸撰,那么將會被添加:

[CP] Check Pods Manifest.lock:檢查 Podfile.lock 和 Manifest.lock 文件的一致性浮毯,這個(gè)會再后面的 Cocoapods 原理中詳細(xì)解釋。

[CP] Embed Pods Frameworks:將所有 cocoapods 打的 framework 拷貝到包中壳鹤。

[CP] Copy Pods Resources:將所有 cocoapods 的資源文件拷貝到包中饰迹。

1.3 Build Rules

編譯規(guī)則,這里設(shè)定了不同文件的處理方式匿值,例如:

Copy Plist File:在編譯打包的時(shí)候葛圃,將 info.plist 文件拷貝库正。

Compress PNG File:在編譯打包的時(shí)候厘唾,將 PNG 文件壓縮抚垃。

Swift Compiler:Swift 文件的編譯方式,使用 Swift 編譯器铣焊。

….

2. Cocoapods 原理

使用了 Cocoapods 后曲伊,我們的編譯流程會多出來一些追他,雖然每個(gè) target 的編譯流程都是一致的邑狸,但是 Cocoapods 是如何將這些庫導(dǎo)入我們的項(xiàng)目、原項(xiàng)目和其他庫之間的依賴又是如何實(shí)現(xiàn)的仍然是一個(gè)需要了解的知識點(diǎn)赚哗。下面這幾篇文章從不同角度解釋了 Cocoapods 是如何工作的:

深入理解 CocoaPods

Cocoapods原理總結(jié)

CocoaPods 都做了什么屿储?

這些文章大部分都是講述了 Objective-C 語言下 Cocoapods 是如何使用靜態(tài)庫動(dòng)態(tài)庫并添加依賴的硬萍。特別說明一下 Swift 的實(shí)現(xiàn):

Cocoapods 對于 Swift 和 Objective-C 的操作區(qū)別其實(shí)并不是很大朴乖,Swift 是必須使用動(dòng)態(tài)庫的助赞,因此在 podfile 中我們必須添加代碼use_frameworks!來指明 Cocoapods 所有管理的庫都將被編譯成 framework 動(dòng)態(tài)庫雹食。這些庫的依賴過程和 Objective-C 一致群叶,我們的主項(xiàng)目依賴于Pods-ProjectName.frameworktarget钝荡,而這個(gè) pods 的 target 則依賴于其他我們使用的庫埠通。

需要說明一個(gè)概念,就是 Swift 的命名空間和 Module梁剔。

Module:在 Swift 中荣病,項(xiàng)目里的每個(gè) target渗柿、framework 都是一個(gè) Module做祝。

命名空間:每個(gè) Module 都擁有獨(dú)立的命名空間砾省。

因此混槐,我們在使用 Swift 庫的時(shí)候,例如Alamofire狠鸳,在某個(gè)文件中import Alamofire件舵,其實(shí)就是在引入這個(gè) Module脯厨,在代碼提示中可以看到,這樣其中的代碼才可以被我們使用涡扼。

在同一個(gè) Module 內(nèi)我們的 Swift 文件是不需要被引用的盟庞,可以直接使用其中public什猖、internal描述的類屬性和方法不狮。這也說明一個(gè)問題,在同一個(gè) Module 中垮耳,命名空間也是同一個(gè),重復(fù)的類都會在編譯的時(shí)候報(bào)錯(cuò)俊嗽。

總之

LLVM 的編譯過程是相當(dāng)復(fù)雜的绍豁,中間牽扯到的技術(shù)和語言要比我們做一個(gè)簡單的移動(dòng)開發(fā)要復(fù)雜的多竹揍,本文也只是非常淺顯的描述了相關(guān)的編譯過程,其主要目的是為了在 iOS 開發(fā)中更加得心應(yīng)手无拗。

研究編譯原理英染、深究編譯過程以及其相關(guān)內(nèi)容四康,在開發(fā)過程中是非常重要的一個(gè)環(huán)節(jié)狭握,任何開發(fā)者都應(yīng)該做深入研究。如果你看不懂相關(guān)代碼哎垦,也不要覺得困難撼泛,很多代碼的命名就能很清楚的表明它到底是干什么的,英語差的話翻譯一下就好了损俭。只是相對于英語好杆兵,底層代碼也略懂的情況來說琐脏,學(xué)習(xí)成本比較高缸兔,不過這不應(yīng)該是打敗你的理由惰蜜。

原理是我們構(gòu)建整個(gè)代碼世界的基礎(chǔ)昂拂,不懂原理(無論哪方面)都只能讓我們做一個(gè)壘磚的,而不是整個(gè)工程的控制者抛猖。

鏈接:http://www.reibang.com/p/5b2cce762106

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末格侯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子财著,更是在濱河造成了極大的恐慌联四,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撑教,死亡現(xiàn)場離奇詭異,居然都是意外死亡驮履,警方通過查閱死者的電腦和手機(jī)鱼辙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玫镐,“玉大人倒戏,你說我怎么就攤上這事】炙疲” “怎么了杜跷?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我葛闷,道長憋槐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任淑趾,我火速辦了婚禮阳仔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扣泊。我一直安慰自己近范,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布延蟹。 她就那樣靜靜地躺著评矩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪阱飘。 梳的紋絲不亂的頭發(fā)上斥杜,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機(jī)與錄音沥匈,去河邊找鬼蔗喂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛咐熙,可吹牛的內(nèi)容都是我干的弱恒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼棋恼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了锈玉?” 一聲冷哼從身側(cè)響起爪飘,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拉背,沒想到半個(gè)月后师崎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡椅棺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年犁罩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片两疚。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡床估,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出诱渤,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布捂刺,位于F島的核電站,受9級特大地震影響碑韵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缎脾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一祝闻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧遗菠,春花似錦治筒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至牲平,卻和暖如春堤框,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纵柿。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工蜈抓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人昂儒。 一個(gè)月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓沟使,卻偏偏與公主長得像,于是被迫代替她去往敵國和親渊跋。 傳聞我的和親對象是個(gè)殘疾皇子腊嗡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容