iOS llvm-1

LLVM


傳統(tǒng)編譯器


LLVM區(qū)別于傳統(tǒng)的編譯器笨枯,它前端和后端分開了


LLVM流程
1.所有的代碼會經(jīng)過[詞法分析,語法分析爆哑,語義分析]生成抽象語法數(shù)
2.將抽象語法樹編譯成IR
3.將IR輸送給Optimizer(優(yōu)化器)面哼,進(jìn)行優(yōu)化IR代碼
4.優(yōu)化之后的IR輸送給后端,根據(jù)架構(gòu)(x86 & arm)生成不同的指令集代碼(機(jī)器語言)

clang & swift都隸屬于LLVM,它們分別是LLVM針對前端語言編譯的一種格式乔妈,之后都會生成IR

clang

iOS里面 c,c++,oc 使用的是clang這個

swift

iOS里面 swift ,使用的是swift這個前端

  • 查看前端clang流程
clang -ccc-print-phases main.m
  • 查看預(yù)處理流程
// 生成main1.m文件氓皱,展開`頭`文件路召,展開`宏`

clang -E main.m >> mian1.m
  • typedef不是預(yù)處理指令(只是定義了一個別名)

  • #define預(yù)處理指令

    typedef不是預(yù)處理指令

  • ???通過詞法分析,將代碼加切成一段一段的token

clang -fmodules -fsyntax-only -Xclang -dump-tokens mian.m
  • 生成抽象語法樹(包含 語法分析)
// 地址是當(dāng)前文件中的偏移地址波材,并非真實地址
clang -fmodules -fsyntax-only -Xclang -ast-dump mian.m
  • 生成IR
clang -S -fobjc-arc -emit-llvm main.m

ls

// main.ll 即是生成的IR

IR語法解讀
  • llvm的優(yōu)化級別 O0/O1/O2/O3/Os(注意字母O是大寫)
  • 生成優(yōu)化之后的IR
clang -Os -S -fobjc-arc -emit-llvm main.m -o main.ll
  • bitcode一般不開啟股淡,優(yōu)化后的IR再次優(yōu)化生成.bc的中間代碼
clang -emit-llvm -c main.ll -o main.bc
優(yōu)化后的IR語法解讀
  • 生成匯編代碼([通過最終的.bc或.ll代碼生成匯編代碼] -> IR ->匯編)
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 mian.m -o main.s
或
clang -Os -S -fobjc-arc mian.ll -o main.s
或
clang -Os -S -fobjc-arc mian.bc -o main.s

當(dāng)選擇了優(yōu)化等級,不同的節(jié)點上還能進(jìn)行優(yōu)化

  • 生成目標(biāo)文件(匯編器以匯編代碼作為輸入各聘,將匯編代碼轉(zhuǎn)換成機(jī)器代碼,形成目標(biāo)文件
// .o文件是不能執(zhí)行的
clang -fmoduls -c main.s -o main.o
  • 查看目標(biāo)文件main.o
xcrun nm -nm main.o
  • 找不到外部的_printf,這時候就需要鏈接image(鏡像文件揣非,所依賴的庫)
// linker
clang main.o -o main
_printf找不到
  • 查看main 可執(zhí)行文件

xcrun nm -nm main

// 鏈接是在編譯期,綁定是在執(zhí)行期(dyld_stub_binder(from libsystem) 負(fù)責(zé)綁定操作)
// 外部函數(shù)都會生成符號表(符號表的地址在沒有運(yùn)行之前為NULL躲因,然后執(zhí)行dyld_stub_binder找到對應(yīng)庫的符號綁定地址)
查看main可執(zhí)行文件

符號綁定地址細(xì)節(jié)圖解
  • 執(zhí)行 main可執(zhí)行文件
./main

// 打印結(jié)果:6%

寫一個clang插件

  • 1.下載llvm工程

由于國內(nèi)網(wǎng)絡(luò)限制早敬,需要借助鏡像下載llvm的源碼
llvm源碼

  • 下載llvm項目
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/llvm.git
  • 在llvm的tools目錄下下載clang
cd llvm/tools

git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang.git
  • 在llvm的projects目錄下下載 compiler-rt,libcxx大脉,libcxxabi
cd ../projects

git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/compiler-rt.g it
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/libcxx.git
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/libcxxabi.git
  • 在Clang的tools下安裝extra工具
cd ../tools/clang/tools

git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang-tools-e xtra.git

llvm編譯

由于最新的llvm只支持cmake來編譯了搞监,so還需要安裝cmake

安裝cmake
  • 查看brew是否安裝cmake如果有調(diào)過下面步驟
brew list

// 如果沒有找到cmake才去安裝
  • 通過brew安裝cmake
brew install cmake

通過xcode編譯llvm

  • cmake編譯成xcode項目
mkdir build_xcode
cd build_xcode
cmake -G Xcode ../llvm
  • 使用xcode編譯clang
    • 選擇自動創(chuàng)建Schemes
  • 編譯選擇ALL_BUILD Secheme進(jìn)行編譯,預(yù)計1+小時

編譯時間胡比較長

創(chuàng)建插件

  • 在/llvm/tools/clang/tools目錄下新建插件HKPlugin
  • 修改/llvm/tools/clang/tools目錄下的CMakeLists.txt文件镰矿,新增add_clang_subdirectory(HKPlugin)
  • 在HKPlugin目錄下新建一個名為HKPlugi.cpp文件和CMakeLists.txt文件琐驴,在CMakeLists.txt中寫上
add_llvm_library( HKPlugin MODULE BUILDTREE_ONLY
  HKPlugin.cpp
)
  • 接下來利用cmake重新生成一下Xcode項目,在build_xcode中
cmake -G Xcode ../llvm
  • 最后可以在LLVM的Xcode項目中可以看到Loadable modules目錄下有自己的Plugin目錄,可以在里面編寫插件代碼

編寫插件代碼


#include <iostream>
#include "clang/AST/AST.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/FrontendPluginRegistry.h"

using namespace clang;
using namespace std;
using namespace llvm;
using namespace clang::ast_matchers;

//命名空間秤标,和插件同名
namespace HKPlugin {

//第三步:掃描完畢的回調(diào)函數(shù)
//4绝淡、自定義回調(diào)類,繼承自MatchCallback
class HKMatchCallback: public MatchFinder::MatchCallback {
    
private:
    //CI傳遞路徑:HKASTAction類中的CreateASTConsumer方法參數(shù) - HKConsumer的構(gòu)造函數(shù) - HKMatchCallback的私有屬性苍姜,通過構(gòu)造函數(shù)從HKASTConsumer構(gòu)造函數(shù)中獲取

    CompilerInstance &CI;
    
    //判斷是否是用戶源文件
    bool isUserSourceCode(const string filename) {
        //文件名不為空
        if (filename.empty()) return  false;
        //非xcode中的源碼都認(rèn)為是用戶的
        if (filename.find("/Applications/Xcode.app/") == 0) return false;
        return  true;
    }

    //判斷是否應(yīng)該用copy修飾
    bool isShouldUseCopy(const string typeStr) {
        //判斷類型是否是NSString | NSArray | NSDictionary
        if (typeStr.find("NSString") != string::npos ||
            typeStr.find("NSArray") != string::npos ||
            typeStr.find("NSDictionary") != string::npos/*...*/)
        {
            return true;
        }
        
        return false;
    }
    
public:
    HKMatchCallback(CompilerInstance &CI) :CI(CI) {}
    
    //重寫run方法
    void run(const MatchFinder::MatchResult &Result) {
        //通過result獲取到相關(guān)節(jié)點 -- 根據(jù)節(jié)點標(biāo)記獲壤谓汀(標(biāo)記需要與HKASTConsumer構(gòu)造方法中一致)
        const ObjCPropertyDecl *propertyDecl = Result.Nodes.getNodeAs<ObjCPropertyDecl>("objcPropertyDecl");
        //判斷節(jié)點有值,并且是用戶文件
        if (propertyDecl && isUserSourceCode(CI.getSourceManager().getFilename(propertyDecl->getSourceRange().getBegin()).str()) ) {
            //15衙猪、獲取節(jié)點的描述信息
            ObjCPropertyDecl::PropertyAttributeKind attrKind = propertyDecl->getPropertyAttributes();
            //獲取節(jié)點的類型馍乙,并轉(zhuǎn)成字符串
            string typeStr = propertyDecl->getType().getAsString();
//            cout<<"---------拿到了:"<<typeStr<<"---------"<<endl;
            
            //判斷應(yīng)該使用copy,但是沒有使用copy
            if (propertyDecl->getTypeSourceInfo() && isShouldUseCopy(typeStr) && !(attrKind & ObjCPropertyDecl::OBJC_PR_copy)) {

                //使用CI發(fā)警告信息
                //通過CI獲取診斷引擎
                DiagnosticsEngine &diag = CI.getDiagnostics();

                //通過診斷引擎 report報告 錯誤垫释,即拋出異常
                /*
                錯誤位置:getBeginLoc 節(jié)點開始位置
                錯誤:getCustomDiagID(等級丝格,提示)
                 */
                diag.Report(propertyDecl->getBeginLoc(), diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0 - 這個地方推薦使用copy!!"))<< typeStr;
            }
        }
    }
};


//第二步:掃描配置完畢
//3、自定義HKASTConsumer棵譬,繼承自ASTConsumer显蝌,用于監(jiān)聽AST節(jié)點的信息 -- 過濾器
class HKASTConsumer: public ASTConsumer {
private:
    //AST節(jié)點的查找過濾器
    MatchFinder matcher;
    //定義回調(diào)類對象
    HKMatchCallback callback;
    
public:
    //構(gòu)造方法中創(chuàng)建matcherFinder對象
    HKASTConsumer(CompilerInstance &CI) : callback(CI) {
        //添加一個MatchFinder,每個objcPropertyDecl節(jié)點綁定一個objcPropertyDecl標(biāo)識(去匹配objcPropertyDecl節(jié)點)
        //回調(diào)callback订咸,其實是在HKMatchCallback里面重寫run方法(真正回調(diào)的是回調(diào)run方法)
        matcher.addMatcher(objcPropertyDecl().bind("objcPropertyDecl"), &callback);
    }
    
    //實現(xiàn)兩個回調(diào)方法 HandleTopLevelDecl 和 HandleTranslationUnit
    //解析完一個頂級的聲明曼尊,就回調(diào)一次(頂級節(jié)點扭屁,相當(dāng)于一個全局變量、函數(shù)聲明)
    bool HandleTopLevelDecl(DeclGroupRef D){
//        cout<<"正在解析..."<<endl;
        return  true;
    }
    
    //整個文件都解析完成的回調(diào)
    void HandleTranslationUnit(ASTContext &context) {
//        cout<<"文件解析完畢!"<<endl;
        //將文件解析完畢后的上下文context(即AST語法樹) 給 matcher
        matcher.matchAST(context);
    }
};

//2涩禀、繼承PluginASTAction,實現(xiàn)我們自定義的Action然眼,即自定義AST語法樹行為
class HKASTAction: public PluginASTAction {
    
public:
    //重載ParseArgs 和 CreateASTConsumer方法
    bool ParseArgs(const CompilerInstance &ci, const std::vector<std::string> &args) {
        return true;
    }
    
    //返回ASTConsumer類型對象艾船,其中ASTConsumer是一個抽象類,即基類
    /*
     解析給定的插件命令行參數(shù)高每。
     - param CI 編譯器實例屿岂,用于報告診斷。
     - return 如果解析成功鲸匿,則為true爷怀;否則,插件將被銷毀带欢,并且不執(zhí)行任何操作运授。該插件負(fù)責(zé)使用CompilerInstance的Diagnostic對象報告錯誤。
     */
    unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef iFile) {
        //返回自定義的CJLASTConsumer,即ASTConsumer的子類對象
        /*
         CI用于:
         - 判斷文件是否使用戶的
         - 拋出警告
         */
        return unique_ptr<HKASTConsumer> (new HKASTConsumer(CI));
    }
    
};

}

//第一步:注冊插件乔煞,并自定義AST語法樹Action類
//1吁朦、注冊插件
static FrontendPluginRegistry::Add<HKPlugin::HKASTAction> HK("HKPlugin", "This is HKPlugin");

測試插件

自己編譯的clang文件路徑 -isysroot /Applications/Xcode.app/Contents/Deve loper/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulat or12.2.sdk/ -Xclang -load -Xclang 插件(.dylib)路徑 -Xclang -add-plugin
-Xclang 插件名 -c 源碼路徑
測試結(jié)果:
測試插件

Xcode集成插件

加載插件
  • 打開測試項目,在Build Settings -> Other C Flags添加上如下內(nèi)容
 -Xclang -load -Xclang (.dylib)動態(tài)庫路徑 -Xclang -add-plugin -Xclang H KPlugin

設(shè)置編譯器

  • 由于Clang插件需要使用對應(yīng)的版本去加載渡贾,如果版本不一致就會導(dǎo)致編譯錯誤逗宜,會出現(xiàn)如下圖所示:


  • 在Build Settings欄中新增亮相用戶定義的設(shè)置

  • 分別是 CC & CXX
    • CC對應(yīng)的是自己編譯的clang的絕對路徑
    • CXX對應(yīng)的是自己編譯的clang++的絕對路徑
  • 接下來在Build Settings欄中index,將Enable Index-Wihle-Building Functionality的DefaultNO
  • 拓展 c 語法
chanr *argv[] == chanr **argv
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末空骚,一起剝皮案震驚了整個濱河市纺讲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌囤屹,老刑警劉巖熬甚,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異牺丙,居然都是意外死亡则涯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門冲簿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粟判,“玉大人,你說我怎么就攤上這事峦剔〉到福” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵吝沫,是天一觀的道長呻澜。 經(jīng)常有香客問我递礼,道長,這世上最難降的妖魔是什么羹幸? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任杏瞻,我火速辦了婚禮,結(jié)果婚禮上窍仰,老公的妹妹穿的比我還像新娘岩四。我一直安慰自己,他們只是感情好屏镊,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布依疼。 她就那樣靜靜地躺著,像睡著了一般而芥。 火紅的嫁衣襯著肌膚如雪律罢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天棍丐,我揣著相機(jī)與錄音误辑,去河邊找鬼。 笑死歌逢,一個胖子當(dāng)著我的面吹牛稀余,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播趋翻,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼睛琳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了踏烙?” 一聲冷哼從身側(cè)響起师骗,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎讨惩,沒想到半個月后辟癌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡荐捻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年黍少,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片处面。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡厂置,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出魂角,到底是詐尸還是另有隱情昵济,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站访忿,受9級特大地震影響瞧栗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜海铆,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一迹恐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧卧斟,春花似錦系草、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唇辨。三九已至廊酣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赏枚,已是汗流浹背亡驰。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留饿幅,地道東北人凡辱。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像栗恩,于是被迫代替她去往敵國和親透乾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

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