Clang AST介绍
AST(Abstracted Syntax Tree)即抽象語法樹,對于任何一門編程語言來說都是非常重要的工具,對于一般的compiler來說,都是將源碼轉換為AST,之后經由AST轉換到特定的IR,在IR上進行一些與硬件特性無關的優化,之后再將優化后的IR轉換為對應的匯編。因此AST直觀的反應了使用者的編程思想。AST上一般進行的轉換和優化不多,更多的是對語言特性的支持和檢查。AST的中文介紹https://blog.csdn.net/philosophyatmath/article/details/38170131(入門介紹)
對于AST是如何構建的,之前的詞法token分析是如何進行的,在https://www.twilio.com/blog/abstract-syntax-trees中已經進行了很好的說明,不在進行贅述。一般而言,每種編程語言都有對應的ast,包括Python,Perl,Fortran,C,Java等,也有很多開源的AST Viewer工具可供使用,比如https://astexplorer.net/。
本文主要針對LLVM的前端進行介紹。在現有LLVM的版本中,默認使用的前端是Clang,Clang包含token和AST構建的全過程。(Clang官方的introduction:https://clang.llvm.org/docs/IntroductionToTheClangAST.html)
AST樹是源代碼抽象語法結構,以樹的形式表示語言的語法結構。樹的每個節點都有相對應的源碼的支持,樹節點以不同的類型進行區分,下邊將分別進行介紹。
對于LLVM Clang來說,頂層結構是TranslationUnitDecl,對AST樹的遍歷,實際上是遍歷整個TranslationUnitDecl。遍歷它的方式,一般通過:
DeclContext::decl_iterator D = Context.getTranslationUnitDecl()->decls_begin(); DeclContext::decl_iterator DEnd = Context.getTranslationUnitDecl()->decls_end(); while (D != DEnd)
的方式進行。
Clang的使用命令
Clang盡最大可能兼容了gcc,因此將gcc修改為Clang,大概率只是換個名字就行,現在也有人嘗試使用Clang編譯一般的庫,一般都能編過,而且代碼運行效率一般不會特別差。有些系統下可能會面臨std庫無法找到的問題,需要額外添加-I和-L選項。
Clang相比gcc,額外增加了很多功能,比如AST樹的打印。對于源文件test.cpp:
void foo(int* a, int *b) {
if (a[0] > 1)
{
b[0] = 2;
}
}
使用Clang -fsyntax-only -Xclang -ast-dump test.cpp
簡單解釋下這里的命令,-fsyntax-only意味著只解析語法,不進行編譯和鏈接;
-Xclang 是要使用clang特定的Xclang功能
-ast-dump是要打印AST
這里會得到一個額外著色的ast,在源碼中,使用dump是沒有額外著色的,需要使用dumpColor()得到。
2. AST樹節點一般介紹(LangOptions()是CPP)
我會以這樣一段程序對AST樹節點進行介紹,包含了函數聲明、函數調用、變量聲明、for循環、if語句、指針變量等。
1 int foo(int a, int b,int *c){
2 int ret = 0;
3 if(a > b){
4 ret = a;
5 }
6 else {
7 ret = b;
8 }
9 for(int temp=0; temp<100; ++temp){
10 *c = (*c + temp);
11 }
12 return ret;
13 }
14
15 int main(){
16 int a = 1, b = 2;
17 int c =0;
18 int d = foo(a, b, &c);
19 return 0;
20 }
FunctionDeclaration
ParmVarDecl是參數節點
在AST層級,不區分函數聲明和函數定義,統一用FunctionDecl來標識,兩個區分主要看是否有函數體(Body),可以使用bool hasBody()來進行判斷。
CompoundStmt
代表大括號,函數實現、struct、enum、for的body等一般用此包起來。
DeclStmt
定義語句,里邊可能有VarDecl等類型的定義
VarDecl
變量定義,如果有初始化,可以通過getInit()獲取到對應的初始化Expr
IfStmt
If語句,包括三部分Cond、TrueBody、FalseBody三部分,分別可以通過getCond(),getThen(), getElse()三部分獲取,Cond和Then是必須要有的,Else可能為空
BinaryOperator
二元操作Op,=,>,<,<=,>=,==等各種二元操作都繼承它,從繼承關系來說:
通常通過getLHS()和getRHS()來分別獲得其左右子節點
ImplicitCastExpr
隱形轉換表達式,在左右值轉換和函數調用等各個方面都會用到。
IntegerLiteral
定點Integer值
UnaryOperator
一元操作
CallExpr
函數調用Expr,子節點有調用的參數列表
ReturnStmt
返回語句
ForStmt
For語句對應,包括Init/Cond/Inc 對應(int a=0;a<mm;a++)這三部分,還有一部分是body,可以分別使用getInit() / getCond() / getInc() / getBody()來分別進行獲取
ParenExpr
括號表達式
對于一些結構體的操作,比如struct或者enum,有自己的格式。
比如
struct dict {
int a;
int b;
};
void vpp(){
struct dict news;
struct dict olds = {1,2};
news.a = 1;
news.b = b + 2;
}
RecordDecl首先是struct的聲明,采用的是RecordDecl的形式。
后邊會出現對應的struct dict的類型
在使用時會發現,仍然使用DeclStmt的形式去聲明使用InitExpr的方式來初始化,只不過這種特殊的格式是使用了InitListExpr的方式。
對于成員變量的使用,采用MemberExpr的方式來取值。
Reference:
https://clang.llvm.org/docs/index.html
https://clang.llvm.org/docs/UsersManual.html
https://www.twilio.com/blog/abstract-syntax-trees
https://astexplorer.net/
總結
以上是生活随笔為你收集整理的Clang AST介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spark-streaming firs
- 下一篇: 如何安装从 Windows Update