成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

編譯器入門:沒有siri的那些年,我們?nèi)绾螌崿F(xiàn)人機對話?

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

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

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

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

傳統(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)硬件指令集上來生成機器代碼。llc 是 LLVM 項目的后端工具。

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

你好,編譯器

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

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

前端

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

C 預(yù)處理器在源代碼轉(zhuǎn)換成 IR 之前對其進行修改。預(yù)處理器會將外部文件包含進來,比如上面的 #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 的詞法分析:

compile_me.c 的詞法分析

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

compile_me.c 的 AST:

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 <stdio.h> 
  3.  
  4. int main() { 
  5.   int a = 5b = 10c = 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 行將變量進行內(nèi)聯(lián)。opt 對加法進行運算,因為所有的變量都是常量。很酷吧?

后端

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

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

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

  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 匯編語言程序,是計算機和程序員共通的語言。看似晦澀,但肯定有人懂我。

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

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

 

戳這里,看該作者更多好文

責(zé)任編輯:趙寧寧 來源: 51CTO專欄
相關(guān)推薦

2025-06-23 08:25:00

SFINAEC++編譯器

2020-10-20 11:16:04

人工智能

2010-01-27 16:39:48

C++編譯器

2021-06-08 07:48:26

lambda表達(dá)式編譯器

2017-12-06 17:48:42

谷歌AI人機對話

2017-03-20 18:01:55

編譯器匯編

2018-04-27 14:39:28

物聯(lián)網(wǎng)物聯(lián)網(wǎng)應(yīng)用智能

2022-03-28 10:25:27

前端文件編譯器

2015-08-14 09:49:25

u3dc#

2016-11-08 18:53:08

編譯器

2022-05-30 11:46:29

GNU C 編譯器的

2020-08-17 17:05:08

人工智能機器學(xué)習(xí)技術(shù)

2010-01-14 15:29:44

C++編譯器

2010-03-23 11:17:16

Python 動態(tài)編譯

2013-10-31 10:44:54

IDE工具

2022-09-23 08:16:55

數(shù)據(jù)庫高并發(fā)項目

2019-07-09 13:13:10

AI 數(shù)據(jù)人工智能

2010-10-20 13:43:37

C++編譯器

2021-06-25 10:38:05

JavaScript編譯器前端開發(fā)

2023-11-15 17:58:58

C++代碼
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 一级黄色毛片 | 国产在线播放一区二区三区 | 国产精品福利在线 | 亚洲成人国产综合 | 91精品国产91久久久久游泳池 | 91精品久久久久久久久 | 91在线电影 | 天天插天天狠天天透 | 欧美视频成人 | 欧美中文字幕一区二区 | 欧美一区二区三区免费在线观看 | 在线观看免费av网 | 国产精品高潮呻吟久久av黑人 | 亚洲精品日本 | 日韩精品视频在线 | 国产一区久久 | 91精品国产色综合久久 | 久久精品二区亚洲w码 | 日韩av在线免费 | 羞羞视频免费观 | 精品国产乱码久久久久久1区2区 | 亚洲一区二区三区观看 | 亚洲精品乱码久久久久久蜜桃91 | 久久久久成人精品免费播放动漫 | 国产精品久久久久一区二区三区 | 亚洲一区二区免费 | 超碰免费在 | 欧美亚洲综合久久 | 亚洲视频一 | 亚洲精品日韩在线 | 国产午夜视频 | 日本黄色大片免费看 | 日韩精品一区二区三区中文字幕 | 天堂男人av | 国产激情综合五月久久 | 亚洲第一免费播放区 | 人人爽人人爽 | 天堂资源最新在线 | 久久精品亚洲 | 国产精品一区在线观看 | 久久久久久av |