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

前言

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

1 什么是編譯器

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

print("hello world\n")

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

1

python是python的解釋器,這個(gè)就是解釋型語(yǔ)言的效果。
我們?cè)賮?lái)看C,代碼如下

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

我們通過(guò)命令clang hello.c,效果如下

2

我們看到并沒(méi)有執(zhí)行,而在我們的文件中多了一個(gè)a.out文件,在unix下,這是個(gè)可執(zhí)行文件,我們?cè)偻ㄟ^(guò)./a.out執(zhí)行下,效果如圖
3

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

2 LLVM介紹

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

4

編譯器前端(Frontend)
編譯器前端的任務(wù)是解析源代碼。它會(huì)進(jìn)行:詞法分析、語(yǔ)法分析、檢查源代碼是否存在錯(cuò)誤,然后構(gòu)建抽象語(yǔ)法樹(shù)(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ī)器語(yǔ)言,并且進(jìn)行機(jī)器相關(guān)的代碼優(yōu)化。

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


5

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


6

Clang是LLVM項(xiàng)目的中的一個(gè)子項(xiàng)目。它是基于LLVM架構(gòu)的輕量編譯器,誕生之初是為了替代GCC,提供更快的編譯速度。它是負(fù)責(zé)編譯C、C++、Objective-C語(yǔ)言的編譯器,它屬于整個(gè)LLVM架構(gòu)中的,編譯器前端。對(duì)于開(kāi)發(fā)者來(lái)說(shuō),研究Clang可以給我們帶來(lái)很多好處。

3 編譯流程分析

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

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

我們通過(guò)命令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)庫(kù),動(dòng)態(tài)庫(kù),生成鏡像文件即可執(zhí)行文件
  • bind-arch, "x86_64", {5}, image 根據(jù)不同的架構(gòu)生成不同的鏡像文件。

編譯流程的分析

1. 讀取代碼
讀取我們編寫(xiě)的源代碼。

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

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

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

直接把我們的C這個(gè)宏展開(kāi)直接替換成30。
我們還用過(guò)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;
}

沒(méi)有展開(kāi),typedef只是取別名,增強(qiáng)可讀性,不是預(yù)處理指令。
3.編譯價(jià)段
3.1詞法分析
我們?cè)賵?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語(yǔ)法分析
檢查語(yǔ)法是否正確,在詞法分析的基礎(chǔ)上將單詞序列組合成各類(lèi)語(yǔ)法短語(yǔ),如“程序”,“語(yǔ)句”,“表達(dá)式”等等,然后將所有節(jié)點(diǎn)組成抽像語(yǔ)法樹(shù)(Abstract Syntax Tree,AST)。語(yǔ)法分析程序判斷源程序在結(jié)構(gòu)上是否正確。
我們執(zhí)行clang -fmodules -fsyntax-only -Xclang -ast-dump main.m,

8

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

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

-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ù)名稱(chēng)arc,int類(lèi)型,參數(shù)argv const char
    類(lèi)型
  • |-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類(lèi)型。
  • |-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類(lèi)型值為0

3.4 生成中間代碼(intermediate representation )
代碼生成器(Code Generation)會(huì)將語(yǔ)法樹(shù)自頂向下遍歷逐步翻譯成LLVM IR。
IR基本語(yǔ)法
@全局標(biāo)識(shí)
%局部標(biāo)識(shí)
alloca 開(kāi)辟空間
align 內(nèi)存對(duì)齊
i32 32個(gè)bit
store寫(xiě)入內(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 #開(kāi)辟空間 4字節(jié)對(duì)齊 int a3;
  %4 = alloca i32, align 4 #開(kāi)辟空間 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代碼,這是沒(méi)有經(jīng)過(guò)優(yōu)化的。
IR的優(yōu)化
clang -Os -S -fobjc-arc -emit-llvm main.m -o main.ll
經(jīng)過(guò)優(yōu)化會(huì)簡(jiǎn)潔很多,這里不再贅述。
xcode中的Optimization Level可以設(shè)置。
bitCode
clang -emit-llvm -c main.ll -o main.bc

4 生成匯編代碼

我們通過(guò)最終的.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的匯編指令集。
我們?cè)賵?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)過(guò)優(yōu)化過(guò)的,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)文件。通過(guò)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)是外部可以訪問(wèn)的。

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

連接器把編譯產(chǎn)生的.o文件和(.dylib.a)文件,生成一個(gè)macho-o文件。
我們執(zhí)行命令clang main.o -o main生成了可執(zhí)行文件main。
我們?cè)偻ㄟ^(guò)命令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è)庫(kù)中(編譯期),綁定是在執(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é)編譯器的流程:

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

LLVM最大的好處:前后端分離。
pass的解釋?zhuān)壕褪恰氨闅v一遍IR,可以同時(shí)對(duì)它做一些操作”的意思。翻譯成中文應(yīng)該叫“趟”。 在實(shí)現(xiàn)上,LLVM的核心庫(kù)中會(huì)給你一些 Pass類(lèi) 去繼承。你需要實(shí)現(xiàn)它的一些方法。 最后使用LLVM的編譯器會(huì)把它翻譯得到的IR傳入Pass里,給你遍歷和修改

總結(jié)

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容