iOS-深入了解LLVM編譯器架構(gòu)

前言

我們會(huì)經(jīng)常聽到編譯器這個(gè)詞語,我們就會(huì)想什么是編譯器,它的功能是什么飞袋,跟我們的開發(fā)又有什么關(guān)系,這篇文章就帶大家走入LLVM編譯器架構(gòu)链患,揭開編譯器的神秘面紗巧鸭。

1 什么是編譯器

我們用Python(解釋型)和C(編譯型)來先對(duì)比下
Python代碼如下

print("hello world\n")

我們通過python py1.py命令執(zhí)行下,看下效果麻捻,如圖

1

python是python的解釋器纲仍,這個(gè)就是解釋型語言的效果。
我們再來看C贸毕,代碼如下

#include<stdio.h>
int main(int argc,char * argv[]){
        printf("hello world\n");
        return 0;
}

我們通過命令clang hello.c郑叠,效果如下

2

我們看到并沒有執(zhí)行,而在我們的文件中多了一個(gè)a.out文件明棍,在unix下乡革,這是個(gè)可執(zhí)行文件,我們再通過./a.out執(zhí)行下摊腋,效果如圖
3

我們看到了執(zhí)行效果沸版。
從這兩個(gè)小小的案例可以看出,解釋型語言和編譯型語言的區(qū)別兴蒸,
解釋型語言讀取代碼就會(huì)執(zhí)行推穷,而編譯型語言要先翻譯成cpu可以讀的二進(jìn)制代碼。
我們剛才的用的clang命令就是C,C++和Objective-C的編譯器类咧。
python就是python的解釋器馒铃。
我們今天就從clang這個(gè)編譯器開始說起蟹腾。

2 LLVM介紹

LLVM概述
LLVM是構(gòu)架編譯器(compiler)的框架系統(tǒng),以C++編寫而成区宇,用于優(yōu)化以任意程序語言編寫的程序的編譯時(shí)間(compile-time)娃殖、鏈接時(shí)間(link-time)、運(yùn)行時(shí)間(run-time)以及空閑時(shí)間(idle-time)议谷,對(duì)開發(fā)者保持開話炉爆,并兼容已有腳本。
LLVM計(jì)劃啟動(dòng)于2000年卧晓,最初由由美國UIUC大學(xué)的Chris Lattner博士主持開展芬首。2006年Chris Lattner加盟Apple Inc.并致力LLVM在Apple開發(fā)體系中的應(yīng)用。Apple也是LLVM計(jì)劃的主要資助者逼裆。
目前LLVM已經(jīng)被蘋果iOS開發(fā)工具郁稍、Xilinx Vivado、Facebook胜宇、Google等各大公司采用耀怜。
傳統(tǒng)編譯器設(shè)計(jì)

4

編譯器前端(Frontend)
編譯器前端的任務(wù)是解析源代碼。它會(huì)進(jìn)行:詞法分析桐愉、語法分析财破、檢查源代碼是否存在錯(cuò)誤,然后構(gòu)建抽象語法樹(Abstract Syntax Tree AST),LLVM的前端還會(huì)生成中間代碼(intermediate representation,IR

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

后端(Backend)/代碼生成器(CodeGenerator)
將代碼映財(cái)?shù)侥繕?biāo)指令集系洛。生成機(jī)器語言俊性,并且進(jìn)行機(jī)器相關(guān)的代碼優(yōu)化。

iOS的編譯器架構(gòu)
Objcective C/C/C++使用的編譯器前端是Clang碎罚,Swift是Swift磅废,后端都是LLVM纳像。


5

LLVM的設(shè)計(jì)
當(dāng)編譯器決定支持多種源語言或多種硬架構(gòu)時(shí)荆烈,LLVM的最重要的地方就來了。
其它的編對(duì)器如GCC竟趾,它方法非常成功憔购,但由于它是作為整體應(yīng)用程序設(shè)計(jì)的,因此它的用途受到了很大的限制岔帽。
LLVM設(shè)計(jì)的最重要方便是玫鸟,使用通用的代碼表示形式(IR ),它是用來在編譯器中表示代碼的形式。所以LLVM可以為任何編譯語言獨(dú)立編寫前端犀勒,并且可以為任意硬件架構(gòu)獨(dú)立編寫后端屎飘。


6

Clang是LLVM項(xiàng)目的中的一個(gè)子項(xiàng)目妥曲。它是基于LLVM架構(gòu)的輕量編譯器,誕生之初是為了替代GCC,提供更快的編譯速度钦购。它是負(fù)責(zé)編譯C檐盟、C++、Objective-C語言的編譯器押桃,它屬于整個(gè)LLVM架構(gòu)中的葵萎,編譯器前端。對(duì)于開發(fā)者來說唱凯,研究Clang可以給我們帶來很多好處羡忘。

3 編譯流程分析

我們先看下一段代碼,如下

#import <stdio.h>
int main(int argc, const char * argv[]) {
    return 0;
}

我們通過命令clang -ccc-print-phases main.m執(zhí)行

7

我們看編譯的流程是什么樣的磕昼。

  • +- 0: input, "main.m", objective-c 讀取代碼卷雕。
  • +- 1: preprocessor, {0}, objective-c-cpp-output 預(yù)處理價(jià)段,把宏替換掰烟,.h的導(dǎo)入進(jìn)去爽蝴。
  • +- 2: compiler, {1}, ir 編譯價(jià)段,前端編譯器的任務(wù)纫骑。
  • +- 3: backend, {2}, assembler 編譯器后端蝎亚,pass(環(huán)節(jié),節(jié)點(diǎn))優(yōu)化先馆,生成匯編代碼发框。
  • +- 4: assembler, {3}, object 生成目標(biāo)文件。
  • +- 5: linker, {4}, image 鏈接外部函數(shù)煤墙,靜態(tài)庫梅惯,動(dòng)態(tài)庫,生成鏡像文件即可執(zhí)行文件
  • bind-arch, "x86_64", {5}, image 根據(jù)不同的架構(gòu)生成不同的鏡像文件仿野。

編譯流程的分析

1. 讀取代碼
讀取我們編寫的源代碼铣减。

2. 預(yù)處理
我們改下源碼,如

#import <stdio.h>
#define C 30
int main(int argc, const char * argv[]) {
    int a = 10;
    int b = 20;
    printf("%d",a + b +C);
    return 0;
}

接著執(zhí)行clang -E main.m >> main1.m脚作,我們看下main1.m文件葫哗,

# 1 "main.m"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 379 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "main.m" 2

這里是宏展開,我們看下main函數(shù)

int main(int argc, const char * argv[]) {
    int a = 10;
    int b = 20;
    printf("%d",a + b +30);
    return 0;
}

直接把我們的C這個(gè)宏展開直接替換成30球涛。
我們還用過typedef劣针,我們改下代碼

#import <stdio.h>
typedef int RO_INT_64
int main(int argc, const char * argv[]) {
    RO_INT_64 a = 10;
    RO_INT_64 b = 20;
    printf("%d",a + b);
    return 0;
}

執(zhí)行clang -E main.m >> main1.m,如

typedef int RO_INT_64
int main(int argc, const char * argv[]) {
    RO_INT_64 a = 10;
    RO_INT_64 b = 20;
    printf("%d",a + b);
    return 0;
}

沒有展開亿扁,typedef只是取別名捺典,增強(qiáng)可讀性,不是預(yù)處理指令从祝。
3.編譯價(jià)段
3.1詞法分析
我們再執(zhí)行命令clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m襟己,詞法分析引谜,會(huì)把代碼切成token,如下所示

annot_module_include '#import <stdio.h>
#d'     Loc=<main.m:2:1>
int 'int'    [StartOfLine]  Loc=<main.m:4:1>
identifier 'main'    [LeadingSpace] Loc=<main.m:4:5>
l_paren '('     Loc=<main.m:4:9>
int 'int'       Loc=<main.m:4:10>
identifier 'argc'    [LeadingSpace] Loc=<main.m:4:14>
comma ','       Loc=<main.m:4:18>
const 'const'    [LeadingSpace] Loc=<main.m:4:20>
char 'char'  [LeadingSpace] Loc=<main.m:4:26>
star '*'     [LeadingSpace] Loc=<main.m:4:31>
identifier 'argv'    [LeadingSpace] Loc=<main.m:4:33>
l_square '['        Loc=<main.m:4:37>
r_square ']'        Loc=<main.m:4:38>
r_paren ')'     Loc=<main.m:4:39>
l_brace '{'  [LeadingSpace] Loc=<main.m:4:41>
int 'int'    [StartOfLine] [LeadingSpace]   Loc=<main.m:5:5>
identifier 'a'   [LeadingSpace] Loc=<main.m:5:9>
equal '='    [LeadingSpace] Loc=<main.m:5:11>
numeric_constant '10'    [LeadingSpace] Loc=<main.m:5:13>
semi ';'        Loc=<main.m:5:15>
int 'int'    [StartOfLine] [LeadingSpace]   Loc=<main.m:6:5>
identifier 'b'   [LeadingSpace] Loc=<main.m:6:9>
equal '='    [LeadingSpace] Loc=<main.m:6:11>
numeric_constant '20'    [LeadingSpace] Loc=<main.m:6:13>
semi ';'        Loc=<main.m:6:15>
identifier 'printf'  [StartOfLine] [LeadingSpace]   Loc=<main.m:7:5>
l_paren '('     Loc=<main.m:7:11>
string_literal '"%d"'       Loc=<main.m:7:12>
comma ','       Loc=<main.m:7:16>
identifier 'a'      Loc=<main.m:7:17>
plus '+'     [LeadingSpace] Loc=<main.m:7:19>
identifier 'b'   [LeadingSpace] Loc=<main.m:7:21>
plus '+'     [LeadingSpace] Loc=<main.m:7:23>
numeric_constant '30'       Loc=<main.m:7:24 <Spelling=main.m:3:11>>
r_paren ')'     Loc=<main.m:7:25>
semi ';'        Loc=<main.m:7:26>
return 'return'  [StartOfLine] [LeadingSpace]   Loc=<main.m:8:5>
numeric_constant '0'     [LeadingSpace] Loc=<main.m:8:12>
semi ';'        Loc=<main.m:8:13>
r_brace '}'  [StartOfLine]  Loc=<main.m:9:1>
eof ''      Loc=<main.m:9:2>

會(huì)把代碼切成token擎浴,比如大小括號(hào)煌张,等于號(hào)還有字符串等。

3.2語法分析
檢查語法是否正確退客,在詞法分析的基礎(chǔ)上將單詞序列組合成各類語法短語骏融,如“程序”,“語句”萌狂,“表達(dá)式”等等档玻,然后將所有節(jié)點(diǎn)組成抽像語法樹(Abstract Syntax Tree,AST)。語法分析程序判斷源程序在結(jié)構(gòu)上是否正確茫藏。
我們執(zhí)行clang -fmodules -fsyntax-only -Xclang -ast-dump main.m,

8

我們把代碼改錯(cuò)误趴,看下效果
9

這里有錯(cuò)誤提示。
我分析下語法樹

-FunctionDecl 0x7f9aed0bee00 <line:5:1, line:10:1> line:5:5 main 'int (int, const char **)'
  |-ParmVarDecl 0x7f9aed01e140 <col:10, col:14> col:14 argc 'int'
  |-ParmVarDecl 0x7f9aed01e288 <col:20, col:38> col:33 argv 'const char **':'const char **'
  `-CompoundStmt 0x7f9aed0bf7d0 <col:41, line:10:1>
    |-DeclStmt 0x7f9aed0bf010 <line:6:5, col:21>
    | `-VarDecl 0x7f9aed0bef88 <col:5, col:19> col:15 used a 'RO_INT_64':'int' cinit
    |   `-IntegerLiteral 0x7f9aed0beff0 <col:19> 'int' 10
    |-DeclStmt 0x7f9aed0bf538 <line:7:5, col:21>
    | `-VarDecl 0x7f9aed0bf038 <col:5, col:19> col:15 used b 'RO_INT_64':'int' cinit
    |   `-IntegerLiteral 0x7f9aed0bf0a0 <col:19> 'int' 20
    |-CallExpr 0x7f9aed0bf740 <line:8:5, col:25> 'int'
    | |-ImplicitCastExpr 0x7f9aed0bf728 <col:5> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
    | | `-DeclRefExpr 0x7f9aed0bf550 <col:5> 'int (const char *, ...)' Function 0x7f9aed0bf0c8 'printf' 'int (const char *, ...)'
    | |-ImplicitCastExpr 0x7f9aed0bf788 <col:12> 'const char *' <NoOp>
    | | `-ImplicitCastExpr 0x7f9aed0bf770 <col:12> 'char *' <ArrayToPointerDecay>
    | |   `-StringLiteral 0x7f9aed0bf5b0 <col:12> 'char [3]' lvalue "%d"
    | `-BinaryOperator 0x7f9aed0bf6b0 <col:17, line:3:11> 'int' '+'
    |   |-BinaryOperator 0x7f9aed0bf670 <line:8:17, col:21> 'int' '+'
    |   | |-ImplicitCastExpr 0x7f9aed0bf640 <col:17> 'RO_INT_64':'int' <LValueToRValue>
    |   | | `-DeclRefExpr 0x7f9aed0bf5d0 <col:17> 'RO_INT_64':'int' lvalue Var 0x7f9aed0bef88 'a' 'RO_INT_64':'int'
    |   | `-ImplicitCastExpr 0x7f9aed0bf658 <col:21> 'RO_INT_64':'int' <LValueToRValue>
    |   |   `-DeclRefExpr 0x7f9aed0bf608 <col:21> 'RO_INT_64':'int' lvalue Var 0x7f9aed0bf038 'b' 'RO_INT_64':'int'
    |   `-IntegerLiteral 0x7f9aed0bf690 <line:3:11> 'int' 30
    `-ReturnStmt 0x7f9aed0bf7c0 <line:9:5, col:12>
      `-IntegerLiteral 0x7f9aed0bf7a0 <col:12> 'int' 0
  • FunctionDecl 0x7f9aed0bee00 <line:5:1, line:10:1> line:5:5 main 'int (int, const char )'
    |-ParmVarDecl 0x7f9aed01e140 <col:10, col:14> col:14 argc 'int'
    |-ParmVarDecl 0x7f9aed01e288 <col:20, col:38> col:33 argv 'const char ':'const char ** 這里就是main函數(shù)务傲,返回值int凉当,參數(shù)int和char,參數(shù)名稱arc,int類型,參數(shù)argv const char
    類型
  • |-CallExpr 0x7f9aed0bf740 <line:8:5, col:25> 'int'
    | |-ImplicitCastExpr 0x7f9aed0bf728 <col:5> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
    | | `-DeclRefExpr 0x7f9aed0bf550 <col:5> 'int (const char *, ...)' Function 0x7f9aed0bf0c8 'printf' 'int (const char *, ...)'這里有一個(gè)函數(shù)的調(diào)用printf售葡,返回int類型看杭。
  • |-ImplicitCastExpr 0x7f9aed0bf788 <col:12> 'const char *' <NoOp>
    | | -ImplicitCastExpr 0x7f9aed0bf770 <col:12> 'char *' <ArrayToPointerDecay> | |-StringLiteral 0x7f9aed0bf5b0 <col:12> 'char [3]' lvalue "%d" 這是第一個(gè)參數(shù)
  • |-DeclStmt 0x7f9aed0bf010 <line:6:5, col:21>
    | -VarDecl 0x7f9aed0bef88 <col:5, col:19> col:15 used a 'RO_INT_64':'int' cinit |-IntegerLiteral 0x7f9aed0beff0 <col:19> 'int' 10
    |-DeclStmt 0x7f9aed0bf538 <line:7:5, col:21>
    | `-VarDecl 0x7f9aed0bf038 <col:5, col:19> col:15 used b 'RO_INT_64':'int' 這里是a,b
  • | | -ImplicitCastExpr 0x7f9aed0bf770 <col:12> 'char *' <ArrayToPointerDecay> | |-StringLiteral 0x7f9aed0bf5b0 <col:12> 'char [3]' lvalue "%d"這是第一個(gè)參數(shù)
  • BinaryOperator 0x7f9aed0bf6b0 <col:17, line:3:11> 'int' '+'是+運(yùn)算結(jié)果,
  • BinaryOperator 0x7f9aed0bf670 <line:8:17, col:21> 'int' '+'
    | | |-ImplicitCastExpr 0x7f9aed0bf640 <col:17> 'RO_INT_64':'int' <LValueToRValue>
    | | | -DeclRefExpr 0x7f9aed0bf5d0 <col:17> 'RO_INT_64':'int' lvalue Var 0x7f9aed0bef88 'a' 'RO_INT_64':'int' | |-ImplicitCastExpr 0x7f9aed0bf658 <col:21> 'RO_INT_64':'int' <LValueToRValue>
    | | -DeclRefExpr 0x7f9aed0bf608 <col:21> 'RO_INT_64':'int' lvalue Var 0x7f9aed0bf038 'b' 'RO_INT_64':'int' |-IntegerLiteral 0x7f9aed0bf690 <line:3:11> 'int' 30 第一個(gè)加法運(yùn)算的結(jié)果+30
  • ReturnStmt 0x7f9aed0bf7c0 <line:9:5, col:12> 這里是返回
  • 返回int類型值為0

3.4 生成中間代碼(intermediate representation )
代碼生成器(Code Generation)會(huì)將語法樹自頂向下遍歷逐步翻譯成LLVM IR挟伙。
IR基本語法
@全局標(biāo)識(shí)
%局部標(biāo)識(shí)
alloca 開辟空間
align 內(nèi)存對(duì)齊
i32 32個(gè)bit
store寫入內(nèi)存
load讀取數(shù)據(jù)
call調(diào)用函數(shù)
ret返回

我們改下代碼

#import <stdio.h>
#define C 30
typedef int RO_INT_64;

int test(int a, int b) {
    return a+ b +3;
}

int main(int argc, const char * argv[]) {
    int a = test(1, 2);
    printf("%d", a);
    return 0;
}

我們執(zhí)行命令clang -S -fobjc-arc -emit-llvm main.m楼雹,會(huì)生成main.ll文件,我們看下main.ll文件內(nèi)容

define i32 @test(i32 %0, i32 %1) #0 { #test(int a, int b )
  %3 = alloca i32, align 4 #開辟空間 4字節(jié)對(duì)齊 int a3;
  %4 = alloca i32, align 4 #開辟空間 4字節(jié)對(duì)齊 int a4;
  store i32 %0, i32* %3, align 4 # a3=a;
  store i32 %1, i32* %4, align 4 # a4=b;
  %5 = load i32, i32* %3, align 4 # int a5=a3;
  %6 = load i32, i32* %4, align 4 # int a6=a4;
  %7 = add nsw i32 %5, %6 # int a7 = a5+a6;
  %8 = add nsw i32 %7, 3 # int a8= a7+ 3;
  ret i32 %8 # return a8;
}

這就是test函數(shù)IR代碼尖阔,這是沒有經(jīng)過優(yōu)化的贮缅。
IR的優(yōu)化
clang -Os -S -fobjc-arc -emit-llvm main.m -o main.ll
經(jīng)過優(yōu)化會(huì)簡潔很多,這里不再贅述介却。
xcode中的Optimization Level可以設(shè)置谴供。
bitCode
clang -emit-llvm -c main.ll -o main.bc

4 生成匯編代碼

我們通過最終的.bc或者.ll代碼生成匯編代碼
命令
clang -S -fobjc-arc main.bc -o main.s
clang -S -fobjc-arc main.ll -o main.s
生成匯編代碼也可以進(jìn)行優(yōu)化
clang -Os -S -fobjc-arc main.m -o main.s
執(zhí)行命令
clang -S -fobjc-arc main.ll -o main.s

_test:                                  ## @test
    .cfi_startproc
## %bb.0:
    pushq   %rbp 
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -4(%rbp), %eax
    addl    -8(%rbp), %eax
    addl    $3, %eax
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .globl  _main                           ## -- Begin function main
    .p2align    4, 0x90

這是x86的匯編指令集。
我們再執(zhí)行這個(gè)clang -Os -S -fobjc-arc main.m -o main.s優(yōu)化的命令

_test:                                  ## @test
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
                                        ## kill: def $esi killed $esi def $rsi
                                        ## kill: def $edi killed $edi def $rdi
    leal    3(%rdi,%rsi), %eax
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .globl  _main                           ## -- Begin function main
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    leaq    L_.str(%rip), %rdi
    movl    $6, %esi

這是經(jīng)過優(yōu)化過的齿坷,main的函數(shù)調(diào)用的test直接優(yōu)化成了6桂肌。

5 生成目標(biāo)文件(匯編器)

目標(biāo)文件的生成,是匯編器以匯編代碼作為輸入胃夏,將匯編代碼轉(zhuǎn)換為機(jī)器代碼轴或,最后輸了目標(biāo)文件(object file)昌跌。這里屬于后端的作務(wù)仰禀。
執(zhí)行命令clang -fmodules -c main.s -o main.o,生成的main.o就是目標(biāo)文件蚕愤。通過xcrun nm -nm main.o查看符號(hào)答恶,如下所示

                 (undefined) external _printf
0000000000000000 (__TEXT,__text) external _test
000000000000000a (__TEXT,__text) external _main

_printf是一個(gè)undefined external的符號(hào)饺蚊。
undefined表示當(dāng)前文件暫時(shí)找不到符號(hào)。
external表示這個(gè)符號(hào)是外部可以訪問的悬嗓。

5 生成可執(zhí)行文件(鏈接)

連接器把編譯產(chǎn)生的.o文件和(.dylib.a)文件污呼,生成一個(gè)macho-o文件。
我們執(zhí)行命令clang main.o -o main生成了可執(zhí)行文件main包竹。
我們再通過命令xcrun nm -nm main燕酷,如下

              (undefined) external _printf (from libSystem)
                 (undefined) external dyld_stub_binder (from libSystem)
0000000100000000 (__TEXT,__text) [referenced dynamically] external __mh_execute_header
0000000100003f6d (__TEXT,__text) external _test
0000000100003f77 (__TEXT,__text) external _main
0000000100008008 (__DATA,__data) non-external __dyld_private

這里有兩個(gè)外部符號(hào)_printf(可以找到)和dyld_stub_binder
當(dāng)我們的程序進(jìn)入內(nèi)存的的時(shí)候周瞎,外部函數(shù)會(huì)立即跟dyld_stub_binder綁定苗缩,這個(gè)dyld是強(qiáng)制執(zhí)行,鏈接是打個(gè)標(biāo)記声诸,符號(hào)在哪個(gè)庫中(編譯期)酱讶,綁定是在執(zhí)行的時(shí)候把外部函數(shù)地址和符號(hào)進(jìn)行綁定(運(yùn)行期),一定會(huì)有dyld_stub_binder這個(gè)符號(hào)彼乌,先綁定這個(gè)符號(hào)泻肯,其它函數(shù)的綁定由dyld_stub_binder執(zhí)行。

總結(jié)編譯器的流程:

  • 前端:讀取代碼慰照,詞法分析灶挟,語法分析,語義分析毒租,生成AST(生成IR)
  • 優(yōu)化器:根據(jù)一個(gè)個(gè)的pass進(jìn)行優(yōu)化膏萧,
  • 后端:生成匯編,根據(jù)不同的架構(gòu)生成可執(zhí)行文件

LLVM最大的好處:前后端分離蝌衔。
pass的解釋:就是“遍歷一遍IR榛泛,可以同時(shí)對(duì)它做一些操作”的意思。翻譯成中文應(yīng)該叫“趟”噩斟。 在實(shí)現(xiàn)上曹锨,LLVM的核心庫中會(huì)給你一些 Pass類 去繼承。你需要實(shí)現(xiàn)它的一些方法剃允。 最后使用LLVM的編譯器會(huì)把它翻譯得到的IR傳入Pass里沛简,給你遍歷和修改

總結(jié)

這篇文章帶大家初步了解了編譯器的原理,LLVM的架構(gòu)斥废。分析了編譯的流程椒楣,希望這篇文章可以讓大家學(xué)習(xí)到新的知識(shí)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末牡肉,一起剝皮案震驚了整個(gè)濱河市捧灰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌统锤,老刑警劉巖毛俏,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炭庙,死亡現(xiàn)場離奇詭異,居然都是意外死亡煌寇,警方通過查閱死者的電腦和手機(jī)焕蹄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阀溶,“玉大人腻脏,你說我怎么就攤上這事∫停” “怎么了迹卢?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長徒仓。 經(jīng)常有香客問我腐碱,道長,這世上最難降的妖魔是什么掉弛? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任症见,我火速辦了婚禮,結(jié)果婚禮上殃饿,老公的妹妹穿的比我還像新娘谋作。我一直安慰自己,他們只是感情好乎芳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布遵蚜。 她就那樣靜靜地躺著,像睡著了一般奈惑。 火紅的嫁衣襯著肌膚如雪吭净。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天肴甸,我揣著相機(jī)與錄音寂殉,去河邊找鬼。 笑死原在,一個(gè)胖子當(dāng)著我的面吹牛友扰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播庶柿,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼村怪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了浮庐?” 一聲冷哼從身側(cè)響起甚负,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后腊敲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡维苔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年碰辅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片介时。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡没宾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沸柔,到底是詐尸還是另有隱情循衰,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布褐澎,位于F島的核電站会钝,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏工三。R本人自食惡果不足惜迁酸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俭正。 院中可真熱鬧奸鬓,春花似錦、人聲如沸掸读。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽儿惫。三九已至澡罚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肾请,已是汗流浹背始苇。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留筐喳,地道東北人催式。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像避归,于是被迫代替她去往敵國和親荣月。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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

  • LLVM概述 LLVM是構(gòu)架編譯器的框架系統(tǒng)梳毙,以C++編寫而成哺窄,用于優(yōu)化任意程序語言編寫的程序編譯時(shí)間,鏈接時(shí)間,...
    大橘豬豬俠閱讀 1,082評(píng)論 3 4
  • LLVM是Low Level Virtual Machine的簡稱婴程。這個(gè)庫提供了與編譯器相關(guān)的支持,能夠進(jìn)行程序語...
    小貓仔閱讀 16,486評(píng)論 0 9
  • 前言 語言類型 我們有很多維度可以將計(jì)算機(jī)語言進(jìn)行分類抱婉,其中以編譯/執(zhí)行方式為維度档叔,可以將計(jì)算機(jī)語言分為: 編譯型...
    AiLearn閱讀 2,400評(píng)論 1 6
  • 前言 官網(wǎng)地址 : LLVM LLVM項(xiàng)目是模塊化衙四,可重用的編譯器以及工具鏈技術(shù)的集合 創(chuàng)始人,亦是Swift之父...
    咖啡豆8888閱讀 1,652評(píng)論 0 1
  • 目錄 傳統(tǒng)編譯器設(shè)計(jì) 輸入源代碼(Obj-C, Swift, ...) → 編譯器處理 → 輸出機(jī)器碼(01010...
    小瞎_MarkDash閱讀 1,246評(píng)論 0 2