日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
編譯器入門:沒有siri的那些年,我們?nèi)绾螌崿F(xiàn)人機(jī)對話?

編譯器可將源代碼轉(zhuǎn)換成計算機(jī)理解的可執(zhí)行的機(jī)器代碼,或?qū)⒃创a轉(zhuǎn)換成另一種編程語言。本文從 LLVM 入手介紹了編譯器工具。

編譯器不過就是一個翻譯其它程序的程序。傳統(tǒng)的編譯器將源代碼轉(zhuǎn)換成計算機(jī)可理解的可執(zhí)行的機(jī)器代碼。(一些編譯器將源代碼轉(zhuǎn)換為另一種編程語言,這些編譯器被稱為源到源轉(zhuǎn)換器或轉(zhuǎn)譯器)。LLVM 是一個廣泛使用的編譯器項目,包括多個模塊化的編譯器工具。

傳統(tǒng)的編譯器設(shè)計包括三個部分:

  • 前端將源代碼轉(zhuǎn)換成一種中間表示(IR)。clang (http://clang.llvm.org/) 是 LLVM 項目中 C 類語言的前端工具。
  • 優(yōu)化器解析 IR 并將其轉(zhuǎn)換成一種更高效的形式。opt是 LLVM 項目的優(yōu)化器工具。
  • 后端通過將 IR 映射到目標(biāo)硬件指令集上來生成機(jī)器代碼。llc 是 LLVM 項目的后端工具。

LLVM IR 是一種類似匯編的低級語言。但是,它不針對特定的硬件信息編程。

你好,編譯器

下面是一個簡單的打印「Hello,Compiler」字符串的 C 語言程序。雖然程序員可以讀懂 C 語言語法,但是計算機(jī)卻看的一臉懵逼。接下來我要過一遍編譯的三個階段,以便將以下程序轉(zhuǎn)換成機(jī)器可執(zhí)行的程序。

 
 
 
 
  1. // compile_me.c 
  2. // Wave to the compiler. The world can wait. 
  3.  
  4. #include  
  5.  
  6. int main() { 
  7.   printf("Hello, Compiler!\n"); 
  8.   return 0; 

前端

前文講到,clang 是 LLVM C 類語言的前端工具。Clang 由一個 C 預(yù)處理器、詞法分析器(lexer)、解析器、語義分析器和中間表示生成器組成。

C 預(yù)處理器在源代碼轉(zhuǎn)換成 IR 之前對其進(jìn)行修改。預(yù)處理器會將外部文件包含進(jìn)來,比如上面的 #include 。它會用 C 標(biāo)準(zhǔn)庫文件 stdio.h 的所有代碼替換 #include 這一行,stdio.h 頭文件包含了 printf 函數(shù)的聲明。通過執(zhí)行以下命令觀察預(yù)處理器的輸出:

 
 
 
 
  1. clang -E compile_me.c -o preprocessed.i 

詞法分析器(Lexer,也叫 scanner 或 tokenizer)將一串字符轉(zhuǎn)換成一串詞。每個詞或符號,按其屬性被分配到對應(yīng)的句法類別:標(biāo)點符號、關(guān)鍵詞、標(biāo)識符、常量或注釋。

compile_me.c 的詞法分析:

解析器判定由詞法分析器生成的一串詞是否包含源語言中的有效語句。在分析完詞的語法以后,解析器輸出了一個抽象語法樹(AST)。Clang AST 中的節(jié)點分別表示聲明與類型。

compile_me.c 的 AST:

語義分析器遍歷 AST,判定語句的涵義是否有效。這個階段會檢查類型錯誤。如果 compile_me.c 中的 main 函數(shù)返回了 "zero" 而不是 0, 語義分析器就會拋出一個錯誤,因為 "zero" 不是 int 類型。

IR 生成器將 AST 轉(zhuǎn)換為 IR。

在 compile_me.c 上運行 clang 前端,生成 LLVM IR:

 
 
 
 
  1. clang -S -emit-llvm -o llvm_ir.ll compile_me.c 

llvm_ir.ll 中的 main 函數(shù):

 
 
 
 
  1. ; llvm_ir.ll 
  2.  
  3. @.str = private unnamed_addr constant [18 x i8] c"Hello, Compiler!\0A\00", align 1 
  4.  
  5. define i32 @main() { 
  6.   %1 = alloca i32, align 4 ; <- memory allocated on the stack 
  7.   store i32 0, i32* %1, align 4 
  8.   %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @.str, i32 0, i32 0)) ret i32 0 
  9.  
  10.  
  11. declare i32 @printf(i8*, ...) 

優(yōu)化器

優(yōu)化器的任務(wù)是基于對程序運行時行為的理解,提升代碼的效率。優(yōu)化器的輸入為 IR,輸出為優(yōu)化后的 IR。LLVM 的優(yōu)化器工具 opt 將使用 -O2(大寫字母 o,數(shù)字 2)標(biāo)記優(yōu)化處理器速度,使用-Os(大寫字母 o,s)標(biāo)記優(yōu)化生成目標(biāo)的大小。

看一下優(yōu)化器優(yōu)化之前的 LLVM IR 代碼和優(yōu)化后的代碼:

 
 
 
 
  1. opt -O2 -S llvm_ir.ll -o optimized.ll 

optimized.ll 的 main 函數(shù):

 
 
 
 
  1. ; optimized.ll 
  2.  
  3. @str = private unnamed_addr constant [17 x i8] c"Hello, Compiler!\00" 
  4.  
  5. define i32 @main() { 
  6.  
  7.   %puts = tail call i32 @puts(i8* getelementptr inbounds ([17 x i8], [17 x i8]* @str, i64 0, i64 0)) ret i32 0 
  8.  
  9.  
  10. declare i32 @puts(i8* nocapture readonly) 

優(yōu)化后,main 函數(shù)沒有在棧上分配內(nèi)存,因為它沒有使用任何內(nèi)存。優(yōu)化后的代碼調(diào)用了 puts 函數(shù)而不是 printf 函數(shù),因為它沒有使用 printf 函數(shù)的任何格式化功能。當(dāng)然了,優(yōu)化器不僅僅知道什么時候該用 puts 代替 printf。優(yōu)化器也會展開循環(huán),內(nèi)聯(lián)簡單計算的結(jié)果。思考以下代碼,它將兩個數(shù)加起來并打印結(jié)果:

 
 
 
 
  1. // add.c 
  2. #include  
  3.  
  4. int main() { 
  5.   int a = 5, b = 10, c = a + b; 
  6.   printf("%i + %i = %i\n", a, b, c); 

未優(yōu)化的 LLVM IR:

 
 
 
 
  1. @.str = private unnamed_addr constant [14 x i8] c"%i + %i = %i\0A\00", align 1 
  2.  
  3. define i32 @main() { 
  4.   %1 = alloca i32, align 4 ; <- allocate stack space for var a 
  5.   %2 = alloca i32, align 4 ; <- allocate stack space for var b 
  6.   %3 = alloca i32, align 4 ; <- allocate stack space for var c 
  7.   store i32 5, i32* %1, align 4  ; <- store 5 at memory location %1 
  8.   store i32 10, i32* %2, align 4 ; <- store 10 at memory location %2 
  9.   %4 = load i32, i32* %1, align 4 ; <- load the value at memory address %1 into register %4 
  10.   %5 = load i32, i32* %2, align 4 ; <- load the value at memory address %2 into register %5 
  11.   %6 = add nsw i32 %4, %5 ; <- add the values in registers %4 and %5. put the result in register %6 
  12.   store i32 %6, i32* %3, align 4 ; <- put the value of register %6 into memory address %3 
  13.   %7 = load i32, i32* %1, align 4 ; <- load the value at memory address %1 into register %7 
  14.   %8 = load i32, i32* %2, align 4 ; <- load the value at memory address %2 into register %8 
  15.   %9 = load i32, i32* %3, align 4 ; <- load the value at memory address %3 into register %9 
  16.   %10 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i32 0, i32 0), i32 %7, i32 %8, i32 %9) 
  17.   ret i32 0 
  18.  
  19. declare i32 @printf(i8*, ...) 

優(yōu)化后的 LLVM IR:

 
 
 
 
  1. @.str = private unnamed_addr constant [14 x i8] c"%i + %i = %i\0A\00", align 1 
  2.  
  3. define i32 @main() { 
  4.   %1 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0), i32 5, i32 10, i32 15) 
  5.   ret i32 0 
  6.  
  7. declare i32 @printf(i8* nocapture readonly, ...) 

優(yōu)化后的 main 函數(shù)實際上就是在未優(yōu)化版本的 17 和 18 行將變量進(jìn)行內(nèi)聯(lián)。opt 對加法進(jìn)行運算,因為所有的變量都是常量。很酷吧?

后端

LLVM 的后端工具是 llc。它經(jīng)歷了三個階段,最終把 LLVM IR 輸入轉(zhuǎn)化生成機(jī)器代碼:

  • 指令選取(instruction selection)是從 IR 指令到目標(biāo)機(jī)器指令集的映射。這一步使用了虛擬寄存器一個***的命名空間。
  • 寄存器分配(register allocation)是從虛擬寄存器到目標(biāo)架構(gòu)真實寄存器的映射。我的 CPU 是 x86 架構(gòu)的,也就是說只能使用 16 個寄存器。但是,編譯器會盡可能少地使用寄存器。
  • 指令調(diào)度(instruction scheduling)是對操作的重新安排,它反映了目標(biāo)機(jī)器上的性能限制。

執(zhí)行以下命令將生成部分機(jī)器代碼!

 
 
 
 
  1. llc -o compiled-assembly.s optimized.ll 
 
 
 
 
  1. _main: 
  2.     pushq   %rbp 
  3.     movq    %rsp, %rbp 
  4.     leaq    L_str(%rip), %rdi 
  5.     callq   _puts 
  6.     xorl    %eax, %eax 
  7.     popq    %rbp 
  8.     retq 
  9. L_str: 
  10.     .asciz  "Hello, Compiler!" 

這是一個 x86 匯編語言程序,是計算機(jī)和程序員共通的語言。看似晦澀,但肯定有人懂我。

原文:https://nicoleorchard.com/blog/compilers

【本文是專欄機(jī)構(gòu)“機(jī)器之心”的原創(chuàng)譯文,微信公眾號“機(jī)器之心( id: almosthuman2014)”】


標(biāo)題名稱:編譯器入門:沒有siri的那些年,我們?nèi)绾螌崿F(xiàn)人機(jī)對話?
URL鏈接:http://www.dlmjj.cn/article/dppijss.html