1. 程式人生 > >結構化編譯器前端 Clang 介紹

結構化編譯器前端 Clang 介紹

背景與概覽

Low Level Virtual Machine (LLVM) 是一個開源的編譯器架構,它已經被成功應用到多個應用領域。Clang ( 發音為 /klæŋ/) 是 LLVM 的一個編譯器前端,它目前支援 C, C++, Objective-C 以及 Objective-C++ 等程式語言。Clang 對源程式進行詞法分析和語義分析,並將分析結果轉換為 Abstract Syntax Tree ( 抽象語法樹 ) ,最後使用 LLVM 作為後端程式碼的生成器。

Clang 的開發目標是提供一個可以替代 GCC 的前端編譯器。與 GCC 相比,Clang 是一個重新設計的編譯器前端,具有一系列優點,例如模組化,程式碼簡單易懂,佔用記憶體小以及容易擴充套件和重用等。由於 Clang 在設計上的優異性,使得 Clang 非常適合用於設計原始碼級別的分析和轉化工具。Clang 也已經被應用到一些重要的開發領域,如 Static Analysis 是一個基於 Clang 的靜態程式碼分析工具。

本文將簡單介紹 Clang 的背景知識和功能特性,並通過一個小例子介紹如何使用 Clang 的庫來編寫一個小程式來統計原始碼中的函式。

Clang 的開發背景

由於 GNU 編譯器套裝 (GCC) 系統龐大,而且 Apple 大量使用的 Objective-C 在 GCC 中優先順序較低,同時 GCC 作為一個純粹的編譯系統,與 IDE 配合並不優秀,Apple 決定從零開始寫 C family 的前端,也就是基於 LLVM 的 Clang 了。Clang 由 Apple 公司開發,原始碼授權使用 BSD 的開源授權。

Clang 的特性

相比於 GCC,Clang 具有如下優點:

  • 編譯速度快:在某些平臺上,Clang 的編譯速度顯著的快過 GCC。
  • 佔用記憶體小:Clang 生成的 AST 所佔用的記憶體是 GCC 的五分之一左右。
  • 模組化設計:Clang 採用基於庫的模組化設計,易於 IDE 整合及其他用途的重用。
  • 診斷資訊可讀性強:在編譯過程中,Clang 建立並保留了大量詳細的元資料 (metadata),有利於除錯和錯誤報告。
  • 設計清晰簡單,容易理解,易於擴充套件增強。與程式碼基礎古老的 GCC 相比,學習曲線平緩。

當前 Clang 還處在不斷完善過程中,相比於 GCC, Clang 在以下方面還需要加強:

  • 支援更多語言:GCC 除了支援 C/C++/Objective-C, 還支援 Fortran/Pascal/Java/Ada/Go 和其他語言。Clang 目前支援的語言有 C/C++/Objective-C/Objective-C++。
  • 加強對 C++ 的支援:Clang 對 C++ 的支援依然落後於 GCC,Clang 還需要加強對 C++ 提供全方位支援。
  • 支援更多平臺:GCC 流行的時間比較長,已經被廣泛使用,對各種平臺的支援也很完備。Clang 目前支援的平臺有 Linux/Windows/Mac OS。

Clang 安裝

在這一節,我們將介紹如何獲取 Clang 原始碼,編譯和安裝 Clang。編譯 Clang 要求您的系統中安裝有 C++ 編譯器 ( 如 GCC)。如果您還要編譯 Clang 的測試集,那麼您還需要事先安裝 python。

獲取原始碼

由於 Clang 是 LLVM 的一部分,並且 Clang 也用到 LLVM 的庫,我們需要先下載 LLVM,然後下載 Clang 作為 LLVM 工具的一部分。下面的例子示意瞭如何 svn 在 Linux 下獲取最新的 LLVM 和 Clang。

1 .建立 LLVM 原始碼存放目錄 (llvm_source)

1 $mkdirpllvm_source

2 .進入建立的目錄

1 $cd llvm_source

3 .獲取 LLVM

1 $svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm

4 .獲取 Clang

12 $cd llvm/tools$svn co http://llvm.org/svn/llvm-project/cfe/trunk clang

編譯 Clang

12345 $cd../../(返回llvm_source)$mkdir build(建立編譯的工作目錄)$cd build$../llvm/configureprefix=$HOME/llvm(配置LLVM,將目標安裝目錄設定為$HOME/llvm)$make(DEBUG模式來編譯LLVMClang)

開始使用 Clang

您可以像使用普通的編譯器一樣使用 Clang。首先你需要把 Clang 的安裝路徑加入 PATH 環境變數中。以下例子假定您使用的是 Linux 的 bash:

12345678910 $export PATH=$HOME/llvm/bin:$PATH$export LD_LIBRARY_PATH=$HOME/llvm/lib/:$LD_LIBRARY_PATH在本文中,我們使用一個常見的hello world程式來演示Clang。在這裡我們把這個檔案命名為test.c。它的內容如下:#include <stdio.h> intmain(intargc,char**argv){printf("hello world\n");return0;}

您可以使用任何編輯器輸入並生成這個檔案。

有了這個檔案以後,您可以試試以下命令列的命令:

12345 $clang--help(檢視幫助資訊)$clang test.c-fsyntax-only(檢查語法和詞法正確性)$clang test.c-S-emit-llvm-otest.bc(生成優化前的llvm bitcode)$clang test.c-S-emit-llvm-otest.bc-O3(生成優化的llvm bitcode)$clang test.c-S-O3-otest(生成可執行程式碼)

與 GCC 相比,Clang 的一大優點是更加清晰明確的錯誤提示。

您不妨嘗試著刪除”printf(“hello world\n”);”語句後面的分號。編譯這個程式,GCC 給出的錯誤資訊將是:

12 test.c:Infunction'main':test.c:6:2:error:expected';'before'return'

而 Clang 給出的錯誤資訊則是:

1234 test.c:5:26:error:expected';'after expression printf("hello world.\n")^;

抽象語法樹

前面我們提到過 Clang 是一個編譯器前端,這也就是說:Clang 將目標程式進行分析,然後生成結構化的,樹狀的語法表示 , 即抽象語法樹 AST(Abstract Syntax Tree)。

例如,下面簡單的語句可以表示為語法樹(如圖 1):

1234 while(x<=5){fun(x)}
圖 1. 抽象語法樹

imadvegfewge001

將程式程式碼表示為抽象語法樹的一個好處是能極大方便編譯,分析和優化。比如,對於一個單純的 C++ 程式來說,重新命名一個變數就比較困難,我們不能簡單地搜尋變數名稱替換成一個新的名稱,因為這可能會改變太多。例如:不同的類或者名字空間裡也有相同名稱的變數名。將程式表示為抽象語法樹以後,替換變數名稱就會變得很簡單:我們只需要改變對應的變數宣告抽象語法樹節點的名稱欄位,然後把抽象語法樹轉換回原始碼,這是因為每個變數的定義都會對應一個獨一無二的抽象語法樹節點。

基於 Clang 的程式設計

與許多編譯器前端相比,Clang 的最大特點就是良好的結構化設計,每部分都是一個單獨的庫 (library)。也就是說您可以單獨的使用其中的一部分,來編寫自己的程式。在這裡,我們將通過一個小例子來介紹如何使用 Clang 的庫來編寫一個小程式來統計原始碼中的函式。通過這個例子,您將對於如何遍歷抽象語法樹有一個基本的概念。

BoostConASTConsumer

我們的這個小程式將基於 Clang 的一個叫做 BoostConASTConsumer 的例子,這個例子的原始碼位於:”tools/clang/lib/Frontend/BoostConAction.cpp”。

BoostConASTConsumer 的程式碼如下:

12345678910111213141516171819202122232425262728293031323334 #include "clang/Frontend/FrontendActions.h"#include "clang/AST/ASTConsumer.h"#include "clang/AST/RecursiveASTVisitor.h"#include <cstdio> #include <iostream> using namespaceclang;namespace{classBoostConASTConsumer:publicASTConsumer,publicRecursiveASTVisitor<BoostConASTConsumer>{public:/// HandleTranslationUnit - This method is called when the /// ASTsfor entire translation unit have been parsed. virtual voidHandleTranslationUnit(ASTContext&Ctx);boolVisitCXXRecordDecl(CXXRecordDecl *D){std::cout<<D->getNameAsString()<<std::endl;returntrue;}};}ASTConsumer *BoostConAction::CreateASTConsumer(CompilerInstance&CI,llvm::StringRef InFile){returnnewBoostConASTConsumer();}voidBoostConASTConsumer::HandleTranslationUnit(ASTContext&Ctx){fprintf(stderr,"Welcome to BoostCon!\n");TraverseDecl(Ctx.getTranslationUnitDecl());}

BoostConASTConsumer 是一個遍歷抽象語法樹 (AST) 的例子。當 Clang 讀入原始檔,Clang 將根據源程式的資訊構造一棵抽象語法樹。BoostConASTConsumer 就是一個 AST Consumer,提供了訪問抽象語法樹的介面。

我們可以看到:BoostConASTConsumer 是 ASTConsumer 的子類,同時也是 RecursiveASTVisitor 這個 C++ 模板的宣告。其中對 ASTConsumer 的繼承主要是過載了 HandleTranslationUnit 這個函式,這是 BoostConASTConsumer 的入口。當整個抽象語法樹 (AST) 構造完成以後,HandleTranslationUnit 這個函式將會被 Clang 的驅動程式呼叫。

在本小節中,我們將著重介紹 RecursiveASTVisitor,這是一個重要的函式模板。通過介紹這個模板,我們將向您簡單介紹遍歷抽象語法樹的一些基本概念。

RecursiveASTVisitor 是一個深度優先遍歷 AST 和訪問節點的類。對於一個已經構造好的語法樹,它將完成以下三方面的工作:

  1. 遍歷 AST 的每個節點;
  2. 在某一個節點,訪問這個節點的層次結構 ( 每個節點也是一個樹 );
  3. 如果某一個節點是一種型別的動態類別 ( 比如是一個子類等 ),呼叫一個使用者過載的函式來訪問這個節點;

上述工作由下面三組方法完成,分別是:

  1. TraverseDecl(Decl *x) 完成工作 1,它是遍歷 AST 的入口。這個方法是用來訪問有關變數和函式的宣告。TraverseDecl 只是簡單的根據節點的型別來呼叫相應的 TraverseFoo(Foo *x),然後遞迴訪問 x 的子節點。TraverseStmt(Stmt *x) 和 TraverseType(QualType x) 則是用來訪問一條語句和一個型別的(如結構體),它們的工作方式和 TraverseDecl 類似。
  2. WalkUpFromFoo(Foo *x) 完成工作 2。它不會嘗試訪問 x 的任何子節點,而是先呼叫 WalkUpFromBar(x),其中 Bar 是 Foo 的直接父類(除非 Foo 沒有父類), 然後呼叫 VisitFoo(x)。
  3. VisitFoo(Foo *x)完成工作 3。

上述三組方法是分層次的 (Traverse* > WalkUpFrom * > Visit*)。一個方法 ( 如 Traverse*) 可以呼叫同一層次的方法 ( 例如其他 Traverse*) 或低一層次的方法 ( 如 WalkUpFrom*),它不能呼叫更高層次的方法。這個結構確保同樣型別的 AST 節點會被同時訪問,也就是說不會出現交替訪問不同節點的情況。

下面的虛擬碼,簡單描述了 TraverseDecl 的工作情況。假設我們有一個 AST 節點叫做 x。在入口處,TraverseDecl 將根據 x 的型別來呼叫相應的訪問函式。比如,如果節點型別是一個函式宣告,那麼將呼叫 TraverseFunctionDecl。在 TraverseFunctionDecl 中則會遞迴呼叫函式的內容,如首先訪問函式入口引數,然後訪問函式體,在訪問函式體的過程中又會呼叫 TraverseDecl 和 TraverseStmt 訪問函式體內的變數宣告和每一條語句。

12345678910 switch(x->type()){caseFunctionDeclType:TraverseFunctionDecl(dyn_cast<FunctionDecl>(x));break;caseDeclType:TraverseDecl(dyn_cast<Decl>(x));break;...}

統計原始碼中的函式

明白了 BoostConASTConsumer 以後,我們就可以著手編寫一個用來統計原始碼中函式的例子了。我們所要做的工作其實很簡單,只需要過載 VisitFunctionDecl。這樣每當遇見一個函式的定義 (FunctionDecl) 的節點時,VisitFunctionDecl 將會被呼叫。

為了統計函式個數和資訊,我們加入兩個類資料成員:

12345678910111213141516 intfc;// 用於統計函式個數std::vector<FunctionDecl*>funcs;// 用於記錄已經遍歷的函式然後我們在VisitFunctionDecl裡統計函式資訊:boolVisitFunctionDecl(FunctionDecl *D){if(D->isThisDeclarationADefinition())// 只關心提供了定義的函式//(忽略只有宣告而沒有在源代    // 碼中出現定義的函式){fc++;funcs.push_back(D->getNameAsString());// 獲取函式名並儲存到 funcs }returntrue;}

由於 HandleTranslationUnit 是函式的入口,因此我們在 HandleTranslationUnit 對變數 fc 進行初始化:

1 fc=0;

最後,我們在解構函式裡列印統計資訊:

12345678 ~BoostConASTConsumer(){std::cout<<"I have seen "<<fc<<" functions. \ They are: "<<endl;for(unsignedi=0;i<funcs.size();i++){std::cout<<funcs[i]<<endl;}}

編譯

相關推薦

結構編譯器前端 Clang 介紹

背景與概覽 Low Level Virtual Machine (LLVM) 是一個開源的編譯器架構,它已經被成功應用到多個應用領域。Clang ( 發音為 /klæŋ/) 是 LLVM 的一個編譯器前端,它目前支援 C, C++, Objective-C 以及 O

SQL結構查詢語言分類介紹

sql結構化查詢語言分類介紹SQL結構化查詢語言分類介紹SQL:結構化查詢語言,它是一種對關系型數據進行定義和操作的語言方法。SQL結構化查詢語言包含6個部分:一、數據查詢語言(DQL)DQL全稱Data Query Language,其語句也稱“數據檢索語句”,作用是從表中獲取數據,確定數據怎樣在應用程序給

2.4 通用的網頁信息結構信息提取的方法

ges 標題 分析 控制 我們 beautiful html 官方文檔 txt 我們在抓網頁的時候,希望獲得的是網頁中的有效信息。事實上,直接抓取下來的內容是網頁全部的HTML代碼,代碼中有很多標簽、格式控制等等無效的信息。這一節我們就來講解對於任意一個網頁,我們怎麽提

解析結構異常處理(SEH)(第二部分)

ont .com 裏的 之間 地址 htm 完全 href 執行 書接上一篇:http://www.cnblogs.com/ONDragon/p/6855174.html 雖然這個異常回調機制很好,但它並不是一個完美的解決方案。對於稍微復雜一些的應用程序來說,僅用

結構、半結構和非結構數據

tracking 數據庫 能夠 二維 表示 結構化數據 媒體 acl 面向 在實際應用中,我們會遇到各式各樣的數據庫如nosql非關系數據庫(memcached,redis。mangodb)。RDBMS關系數據庫(oracle,mysql等),另一些其他的數據庫如hba

匯編試驗七:尋址方式在結構數據訪問中的應用

知識 分享 logs 訪問 alt nbsp mage 結構化數據 div 預備知識: (1)尋址方式 《匯編語言》P169 (2)div指令 被除數 dx + ax,除數 bx ,商 ax,dx 余數; (3)dd :雙字數據 (4)dup :重復賦值指令

結構查詢語句

比較運算符 like 比較 mysql 類型 sum 影響 star 結束 By TreeDream 基本表的定義,修改,刪除簡單查詢單表查詢連接查詢等值與非等值查詢外連接查詢復合條件查詢自身連接查詢嵌套查詢數據更新插入數據修改數據刪除數據視圖創建視圖查詢視圖

ES6語法將扁平的JSON對象結構

-1 col for 結構 form表單提交 語法 console ble json 適用於支持ES6語法的項目中。 Form表單提交整個Table的數據,每單個表單必須有唯一的name,當數據量較大時,不可能去手動設置每一個name名。 因此通過遍歷出有規律的name,然

結構TensorFlow模型代碼

現在 拆分 註意 計算 bbf val div graph sof 譯自http://danijar.com/structuring-your-tensorflow-models/ 使用TensorFlow構建神經網絡模型很容易導致較大的代碼量,那麽如何以可讀和可復用的

SQL結構查詢語言

cit sql結構 delet having 處理 ner transacti 檢索 order 一: 數據查詢語言( DQL:Data Query Language): 其語句,也稱為“數據檢索 語句”,用以從表中獲得數據,確定數據怎樣在應用程序給出。保留字 S

hbase非結構數據庫與結構數據庫比較

數據可靠性 插入 聯網 定位 海量數據 倍增 關系型 字符類型 文件 目的:了解hbase與支持海量數據查詢的特性以及實現方式 傳統關系型數據庫特點及局限 傳統數據庫事務性特別強,要求數據完整性及安全性,造成系統可用性以及伸縮性大打折扣。對於高並發的訪問量,數據庫性

Odoo10學習筆記三:模型(結構的應用數據)、視圖(用戶界面設計)

其他 描述 用戶界面 列表 支持 字段 界面設計 允許 學習 一:模型 1:創建模型 模型屬性:模型類可以使用一些屬性來控制它們的一些行為: _name :創建odoo模型的內部標識符,必含項。 _description :當用戶界面顯示模型時,一個方便用戶的模型記錄標題。

ElasticSearch常用結構搜索

rop tar 情況 test art ice 都是 其中 ngs 最近,需要用到ES的一些常用的結構化搜索命令,因此,看了一些官方的文檔,學習了一下。結構化查詢指的是查詢那些具有內在結構的數據,比如日期、時間、數字都是結構化的。 它們都有精確的格式,我們可以對這些數據進行

ElasticSearch 結構搜索全文

一個 http tps 文本查詢 bar asi 這樣的 listitem ria   1、介紹     上篇介紹了搜索結構化數據的簡單應用示例,現在來探尋 全文搜索(full-text search) :怎樣在全文字段中搜索到最相關的文檔。     全文搜索兩個最重要

如何訓練結構思維能力?它是一種工作方法還是思維方式?

結構化思維結構化思維(Structured Thinking)是指人在面對工作任務或者難題時,能從多個側面進行思考,深刻分析導致問題出現的原因,系統制定行動方案,並采取恰當的手段使工作得以高效率開展,取得高績效的思維方式。當人這樣做事的時候,就擁有了結構化思維這種思維方式。1、結構化思維的原則結構化思維研究的

數據結構與保存

數據結構 點擊 head model odin pan exc return source 1.結構化: 單條新聞的詳情字典:news 一個列表頁所有單條新聞匯總列表:newsls.append(news) 所有列表頁的所有新聞匯總列表:newstotal.extend(

作業題:輸入4個整數,找出其中最大的數。用一個函數來實現. 分別使用結構方法和函數嵌套的方法。

system 是否 進行 如果 div 使用 clu 函數 整型 之前在main()函數中的思路是: #include <iostream> using namespace std; int main(){ //求四個數中最大的數? /

專利:結構大數據通信協議(2)

結構化大數據通信協議說明2:數據的唯一性是實現“數據全球通”的基礎在班、組這樣的小環境中可以用每個人的姓名而區分出每一個人,然而在全國範圍內,由於人數太多,重名的很多,因此僅靠姓名就不能準確無誤地識別出每一個人。大數據時代以前的關系數據庫中的數據只是應用於某個機構內部,因此各個數據就容易識別,然而如果把關系數

Python前端HTML介紹

tex 自己 實體 ref 分類 一行 http 默認值 button 一、HTML介紹 HTML定義:  超級文本標記語言是標準通用標記語言下的一個應用,也是一種規範,一種標準,它通過標記符號來標記要顯示的網頁中的各個部分。網頁文件本身是一種文本文件,通過在文本文件中添加

視頻結構相關調研

down flickr 關系 微博 資源分配 雅虎 learning 視頻 migration 視頻結構化是一種視頻內容信息提取的技術,它對視頻內容按照語義關系,采用時空分割、特征提取、對象識別等處理手段,組織成可供計算機和人理解的文本信息的技術。 深度學習為視覺和語言之間