LLVM 學習笔记
LLVM 學習
LLVM 全稱是 Low Level Virtual Machine,開始時還不太理解 LLVM 到底有啥用,爲什麽這麽好用。
衆所周知 gcc 也可以劃分爲相應的 前端 中端 後端。爲什麽 gcc 沒有 llvm 怎麽好用,可見 llvm 的結構設計的比較優秀,非常適合代碼 reuse。把 AST 等 IR 做了隔離,讓我們可以非常方便的利用 IR 做一些工作。
接下來我打算從前端開始學習 LLVM 的源碼。 我認爲(書上寫的)還是先試一下這個 reuse 的特性。To be honest,之前并沒有學過 cmake,遇到問題了,基本也只是在源代碼的上面進行一點修改,並不需要掌握這個能力,趁現在我們也可以學習鞏固一番相關的知識。
今天不知道爲什麽效率有一點低,可能早上被 4 節思政課折磨的緣故,下午就看了會兒漫畫打了會鐵道,晚上再吃一個肯爺爺(鐵道周邊),穿插一些 b 站短視頻。有一絲絲罪惡感。
晚上稍微對 AST 進行了一點思考,LLVM 爲什麽要把諸如 “詞法分析” “語法分析” ”語義分析“ 進行一定的抽離,只是爲了代碼復用嗎,既然要代碼復用,他的目的是什麽呢,如果沒有代碼復用的必要,必然沒有拆離的必要。這也許可以部分解釋之前 GCC 爲什麽沒有這樣設計。也就是説,詞法分析後的 Token,語法分析樹、三地址代碼、抽象語法樹等中間表示(IR)是有各自的特點(優點/缺點的),如何結合各自的優缺點進行工作,也許是非常值得研究的方向。
爲了熟悉 AST,我們先看一下 llvm 的 AST 是怎麽樣的
\\ hello.c
#include <stdio.h>
int main(){
int i = 0, j = 3, k = i+j;
printf("hello wolrd %d\n", k);
return 0;
}
clang -target riscv64 -march=rv64g -Xclang -ast-dump -fsyntax-only hello.c
其中 -Xclang 表示把後面的參數傳給 clang 前端,-ast-dump 打印出抽象語法樹。
......
|-TypedefDecl 0x7527a8 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list 'void *'
| `-PointerType 0x71aa10 'void *'
| `-BuiltinType 0x719ab0 'void'
|-FunctionDecl 0x777bc8 <hello.c:3:1, line:7:1> line:3:5 main 'int ()'
| `-CompoundStmt 0x7782e8 <col:11, line:7:1>
| |-DeclStmt 0x777f20 <line:4:5, col:30>
| | |-VarDecl 0x777cc8 <col:5, col:13> col:9 used i 'int' cinit
| | | `-IntegerLiteral 0x777d30 <col:13> 'int' 0
| | |-VarDecl 0x777d68 <col:5, col:20> col:16 used j 'int' cinit
| | | `-IntegerLiteral 0x777dd0 <col:20> 'int' 3
| | `-VarDecl 0x777e08 <col:5, col:29> col:23 used k 'int' cinit
| | `-BinaryOperator 0x777ee0 <col:27, col:29> 'int' '+'
| | |-ImplicitCastExpr 0x777eb0 <col:27> 'int' <LValueToRValue>
| | | `-DeclRefExpr 0x777e70 <col:27> 'int' lvalue Var 0x777cc8 'i' 'int'
| | `-ImplicitCastExpr 0x777ec8 <col:29> 'int' <LValueToRValue>
| | `-DeclRefExpr 0x777e90 <col:29> 'int' lvalue Var 0x777d68 'j' 'int'
| |-CallExpr 0x778240 <line:5:5, col:33> 'int'
| | |-ImplicitCastExpr 0x778228 <col:5> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
| | | `-DeclRefExpr 0x778118 <col:5> 'int (const char *, ...)' Function 0x777f78 'printf' 'int (const char *, ...)'
| | |-ImplicitCastExpr 0x778288 <col:12> 'const char *' <NoOp>
| | | `-ImplicitCastExpr 0x778270 <col:12> 'char *' <ArrayToPointerDecay>
| | | `-StringLiteral 0x778178 <col:12> 'char[16]' lvalue "hello wolrd %d\n"
| | `-ImplicitCastExpr 0x7782a0 <col:32> 'int' <LValueToRValue>
| | `-DeclRefExpr 0x7781a0 <col:32> 'int' lvalue Var 0x777e08 'k' 'int'
| `-ReturnStmt 0x7782d8 <line:6:5, col:12>
| `-IntegerLiteral 0x7782b8 <col:12> 'int' 0
`-FunctionDecl 0x777f78 <line:5:5> col:5 implicit used printf 'int (const char *, ...)' extern
|-ParmVarDecl 0x778070 <<invalid sloc>> <invalid sloc> 'const char *'
|-BuiltinAttr 0x778018 <<invalid sloc>> Implicit 824
`-FormatAttr 0x7780e0 <col:5> Implicit printf 1 2
我們很清楚的知道 1+2 的 AST 描述,但是如果只考慮 1+2,顯然無法表示整個程序。雖然在構建 AST 的過程中會丟失信息,但是至少還是要能表示程序的。如何構建 AST,AST 有什麽樣的作用,僅僅是一個中間環節嗎,還需要更深的理解。