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