操屁眼的视频在线免费看,日本在线综合一区二区,久久在线观看免费视频,欧美日韩精品久久综

新聞資訊

    機器之心編譯

    參與:朱乾樹、路雪

    編譯器可將源代碼轉換成計算機理解的可執行的機器代碼,或將源代碼轉換成另一種編程語言。本文從 LLVM 入手介紹了編譯器工具。

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

    傳統的編譯器設計包括三個部分:

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

    你好沒有編譯器能運行c嗎,編譯器

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

    // compile_me.c
    // Wave to the compiler. The world can wait.

    #include

    int main() {
    ?printf("Hello, Compiler!\n"); ?return 0;
    }

    前端

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

    C 預處理器在源代碼轉換成 IR 之前對其進行修改。預處理器會將外部文件包含進來,比如上面的 # 。它會用 C 標準庫文件 stdio.h 的所有代碼替換 # 這一行,stdio.h 頭文件包含了 函數的聲明。通過執行以下命令觀察預處理器的輸出:

    clang -E compile_me.c -o preprocessed.i

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

    .c 的詞法分析:

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

    .c 的 AST:

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

    IR 生成器將 AST 轉換為 IR。

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

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

    .ll 中的 main 函數:

    ; llvm_ir.ll

    @.str = private unnamed_addr constant [18 x i8] c"Hello, Compiler!\0A\00", align 1

    define i32 @main() { ?%1 = alloca i32, align 4 ; <- memory allocated on the stack ?store i32 0, i32* %1, align 4 ?%2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @.str, i32 0, i32 0))?
    ret i32 0

    }

    declare i32 @printf(i8*, ...)

    優化器

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

    看一下優化器優化之前的 LLVM IR 代碼和優化后的代碼:

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

    .ll 的 main 函數:

    ; optimized.ll

    @str = private unnamed_addr constant [17 x i8] c"Hello, Compiler!\00"

    define i32 @main() {
    ?%puts = tail call i32 @puts(i8* getelementptr inbounds ([17 x i8], [17 x i8]* @str, i64 0, i64 0)) ret i32 0

    }

    declare i32 @puts(i8* nocapture readonly)

    優化后沒有編譯器能運行c嗎,main 函數沒有在棧上分配內存,因為它沒有使用任何內存。優化后的代碼調用了 puts 函數而不是 函數,因為它沒有使用 函數的任何格式化功能。當然了,優化器不僅僅知道什么時候該用 puts 代替 。優化器也會展開循環,內聯簡單計算的結果。思考以下代碼,它將兩個數加起來并打印結果:

    // add.c
    #include

    int main() { ?int a = 5, b = 10, c = a + b; ?printf("%i + %i = %i\n", a, b, c);
    }

    未優化的 LLVM IR:

    @.str = private unnamed_addr constant [14 x i8] c"%i + %i = %i\0A\00", align 1

    define i32 @main() { ?%1 = alloca i32, align 4 ; <- allocate stack space for var a ?%2 = alloca i32, align 4 ; <- allocate stack space for var b ?%3 = alloca i32, align 4 ; <- allocate stack space for var c ?store i32 5, i32* %1, align 4 ?; <- store 5 at memory location %1 ?store i32 10, i32* %2, align 4 ; <- store 10 at memory location %2 ?%4 = load i32, i32* %1, align 4 ; <- load the value at memory address %1 into register %4 ?%5 = load i32, i32* %2, align 4 ; <- load the value at memory address %2 into register %5 ?%6 = add nsw i32 %4, %5 ; <- add the values in registers %4 and %5. put the result in register %6 ?store i32 %6, i32* %3, align 4 ; <- put the value of register %6 into memory address %3 ?%7 = load i32, i32* %1, align 4 ; <- load the value at memory address %1 into register %7 ?%8 = load i32, i32* %2, align 4 ; <- load the value at memory address %2 into register %8 ?%9 = load i32, i32* %3, align 4 ; <- load the value at memory address %3 into register %9 ? = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i32 0, i32 0), i32 %7, i32 %8, i32 %9) ?ret i32 0
    }

    declare i32 @printf(i8*, ...)

    優化后的 LLVM IR:

    @.str = private unnamed_addr constant [14 x i8] c"%i + %i = %i\0A\00", align 1

    define i32 @main() { ?%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) ?ret i32 0
    }

    declare i32 @printf(i8* nocapture readonly, ...)

    優化后的 main 函數實際上就是在未優化版本的 17 和 18 行將變量進行內聯。opt 對加法進行運算,因為所有的變量都是常量。很酷吧?

    后端

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

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

    llc -o compiled-assembly.s optimized.ll

    _main:
    	pushq	%rbp
    	movq	%rsp, %rbp
    	leaq	L_str(%rip), %rdi
    	callq	_puts
    	xorl	%eax, %eax
    	popq	%rbp
    	retq
    L_str:
    	.asciz	"Hello, Compiler!"

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

    相關資源

    1. a ()

    2. with LLVM Core ()

    原文鏈接:

網站首頁   |    關于我們   |    公司新聞   |    產品方案   |    用戶案例   |    售后服務   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區    電話:010-     郵箱:@126.com

備案號:冀ICP備2024067069號-3 北京科技有限公司版權所有