天天看點

Clang:一個面向LLVM的C語言家族前端

Clang:一個用于 LLVM 的前端

      • 1. 特性和目标
        • 1.1 終端使用者特性
          • 1.1.1 快速編譯和低記憶體使用
          • 1.1.2 富有表現力的診斷
          • 1.1.3 GCC的相容性
        • 1.2 實用程式和應用程式
          • 1.2.1 基于庫的架構
          • 1.2.2 支援不同的 Clients
          • 1.2.3 與 IDEs 的內建
          • 1.2.4 使用 LLVM 'BSD' 許可證
        • 1.3 内部設計與實作
          • 1.3.1 一個真實的,高品質的編譯器
          • 1.3.2 一個簡單且可程式設計的代碼庫
          • 1.3.3 一個用于 C、Objective C、C++ 和 Objective C++ 的統一解析器
          • 1.3.4 符合 C/C++/Objective C 及其變體
      • 2. 為什麼?
      • 3. 目前的狀态
      • 4. 參與進來吧!

Clang

項目為 LLVM 項目提供了 C 語言家族(C、C++、Objective C/C++、OpenCL、CUDA 和 RenderScript)中的語言的語言前端和工具基礎結構。提供了一個 GCC 相容的編譯器驅動程式

(clang)

和一個 MSVC 相容的編譯器驅動程式

(clang-cl.exe)

。您可以在今天 擷取并建構源代碼。

1. 特性和目标

該項目的一些目标包括:

終端使用者的特性:

  • 快速編譯和低記憶體使用
  • 富有表現力的診斷(例子)
  • GCC 的相容性

實用程式和應用程式:

  • 基于子產品庫的體系結構
  • 支援不同的用戶端(重構、靜态分析、代碼生成等)
  • 允許與 IDEs 緊密內建
  • 使用 LLVM ‘BSD’ 許可證

内部設計與實作:

  • 一個真實的,高品質的編譯器
  • 一個簡單且可程式設計的代碼庫
  • 一個用于 C、Objective C、C++ 和 Objective C++ 的統一解析器
  • 符合 C/C++/ObjC 及其變體

1.1 終端使用者特性

1.1.1 快速編譯和低記憶體使用

clang 的主要工作重點是使其快速、輕便和可伸縮。clang基于庫的體系結構使它可以直接分析棧的每一層的時間和成本,并且驅動程式有許多用于性能分析的選項。許多詳細的基準可以在網上找到。

編譯時性能很重要,但是當使用clang作為API時,記憶體的使用往往更重要:代碼占用的記憶體越少,一次可以放入記憶體的代碼就越多(例如,對于整個程式分析工具很有用)。

除了在批處理模式中與GCC進行正面對抗時效率更高之外,clang還使用基于庫的架構建構,這使得對clang進行調整并使用它建構新工具變得相對容易。這意味着常常可以應用開箱即用的思想和新技術以各種方式改進編譯。

1.1.2 富有表現力的診斷

除了快速和功能,我們的目标是使Clang非常友好的使用者。就指令行編譯器而言,這基本上可以歸結為使編譯器生成的診斷(錯誤和警告消息)盡可能有用。我們有幾種方法可以做到這一點,但最重要的是準确地指出程式中的錯誤,突出顯示相關資訊,使其一目了然,并使措辭盡可能清晰。

下面是一個簡單的例子,說明了典型的GCC和Clang診斷之間的差別:

$ gcc-4.9 -fsyntax-only t.c
  t.c: In function 'int f(int, int)':
  t.c:7:39: error: invalid operands to binary + (have 'int' and 'struct A')
     return y + func(y ? ((SomeA.X + 40) + SomeA) / 42 + SomeA.X : SomeA.X);
                                         ^
  $ clang -fsyntax-only t.c
  t.c:7:39: error: invalid operands to binary expression ('int' and 'struct A')
    return y + func(y ? ((SomeA.X + 40) + SomeA) / 42 + SomeA.X : SomeA.X);
                         ~~~~~~~~~~~~~~ ^ ~~~~~
           

在這裡,您可以看到甚至不需要檢視原始源代碼就可以根據Clang錯誤了解出了什麼問題:因為Clang列印了一個插入符号,是以您确切地知道它所抱怨的是哪個。範圍資訊突出了加号的左邊和右邊,這使編譯器所談論的内容一目了然,這對于涉及優先級問題和許多其他情況的情況非常有用。

Clang診斷學非常完善,具有許多特性。有關更多資訊和示例,請參見富有表現力的診斷頁面。

1.1.3 GCC的相容性

GCC目前是标準的開源編譯器,它通常會編譯大量的代碼。GCC支援大量的擴充和特性(其中許多是沒有文檔說明的),為了建構,許多代碼和頭檔案都依賴于這些特性。

雖然能夠忽略這些擴充并将重點放在嚴格實作語言标準上是很好的,但是語用學迫使我們支援使用最多的GCC擴充。許多使用者隻想編譯他們的代碼,他們不關心它是否是C99。

如上所述,所有擴充都被顯式地識别為擴充診斷,并使用擴充診斷進行标記,擴充診斷可以映射到警告、錯誤或忽略。

1.2 實用程式和應用程式

1.2.1 基于庫的架構

Clang

的一個主要設計概念是使用基于庫的體系結構。在這個設計中,前端的各個部分可以被清晰地劃分為單獨的庫,然後可以根據不同的需求和用途混合使用。此外,基于庫的方法鼓勵良好的接口,并使新開發人員更容易參與其中(因為他們隻需要了解全局的一小部分)。

“世界需要更好的編譯器工具,這些工具是作為庫建構的。這個設計點允許以新穎的方式重用工具。然而,僅僅将工具建構為庫是不夠的:它們必須具有清晰的 APIs,盡可能彼此解耦,并且易于修改/擴充。這就需要清晰的分層、良好的設計、以及保持庫獨立于任何特定的用戶端。”

目前,

Clang

分為以下庫和工具:

  • libsupport

    —— 基本支援庫,來自 LLVM。
  • libsystem

    —— 系統抽象庫,來自 LLVM。
  • libbasic

    —— 診斷、源代碼定位、源代碼緩沖區抽象、輸入源檔案的檔案系統緩存。
  • libast

    —— 提供類來表示 C AST、C 類型系統、内置函數和用于分析和操作 AST 的各種幫助程式(visitors、很棒的 printers 等)。
  • liblex

    —— 詞法分析和預處理、辨別符哈希表、編譯訓示處理、tokens 和宏展開。
  • libparse

    —— 解析。這個庫調用用戶端提供的粗粒度“Actions”(例如 libsema 建構 ASTs),但對 ASTs 或其他用戶端特定的資料結構一無所知。
  • libsema

    —— 語義分析。這提供了一組解析器 actions 來為程式建構一個标準化的 AST。
  • libcodegen

    —— 将 AST 降低到 LLVM IR 以進行優化和代碼生成。
  • librewrite

    —— 文本緩沖區的編輯(對于代碼重寫轉換非常重要,比如重構)。
  • libanalysis

    —— 靜态分析支援。
  • clang

    —— 一個驅動程式,各級庫的用戶端。

作為這個基于庫的設計的強大功能的一個例子…… 如果您想要建構一個預處理器(preprocessor),您可以使用 Basic 庫和 Lexer 庫。如果您想要建構一個索引器(indexer),您可以使用前兩個庫,并添加 Parser 庫和一些用于索引的 actions。如果您想要建構一個重構、靜态分析或源代碼到源代碼的編譯器工具,那麼您可以添加 AST building 和 semantic analyzer 庫。

有關各種 Clang 庫的底層實作細節的更多資訊,請參閱 clang 内部手冊。

1.2.2 支援不同的 Clients

Clang 的設計和建構有許多宏偉的計劃,以便我們可以如何使用它。驅動因素是我們每天都在使用 C 和 C++,而且由于缺乏可用的好工具,我們不得不為此付出代價。我們相信 C 和 C++ 工具的生态系統已經被這些語言的源代碼的解析和表示的困難程度顯著地限制了,我們的目标是在 Clang 中解決這個問題。

這個目标的問題是不同的 clients 有非常不同的需求。考慮代碼生成(code generation),例如:一個用于代碼生成解析的簡單前端必須分析代碼的有效性,并以某種中間形式發出代碼,以便傳遞給優化器或後端。由于有效性分析和代碼生成在很大程度上可以動态完成,是以并不要求前端為代碼中的所有表達式和語句建構一個完整的 AST。TCC 和 GCC 是編譯器的例子,它們要麼不建構真正的 AST(在前一種情況下),要麼建構簡化的 AST(在後一種情況下),因為它們主要關注 codegen。

另一方面,一些 clients(比如重構) 需要關于原始源代碼的非常詳細的資訊,并且需要一個完整的 AST 來描述它。重構需要關于宏擴充、每個 paren 表達式 ‘(((x)))’ vs ‘x’ 的位置、完整的位置資訊等等的資訊。此外,重構希望檢視整個程式,以確定正在進行的轉換是安全的。要使它高效并正确地運作,需要大量的工程和算法工作,而這些工作對于一個簡單的靜态編譯器來說是不必要的。

Clang 解決方案的美妙之處在于它不限制您如何使用它。特别地,可以使用 clang preprocessor 和 parser 建構一個非常快速、輕量級的實時代碼生成器(類似于TCC),它根本不建構 AST。作為一個中間步驟,clang 支援使用目前 AST 生成和語義分析代碼,并在代碼生成後為每個函數釋放AST。最後,clang 提供了對建構和保留成熟的 ASTs 的支援,甚至支援将它們寫入磁盤。

使用清晰而簡單的 APIs 設計庫允許在 clients 中确定這些進階政策決策,而不是在實作這些庫時強制采用“一種真正的方法”。做對一件事很難,而且我們并不總是第一次就能做對,但是當我們意識到自己犯了錯誤時,我們就能解決任何問題。

1.2.3 與 IDEs 的內建

我們相信,內建開發環境(IDE)是一個很好的方法,可以将開發難題的各個部分組合在一起,并使 clang 能夠在這樣的環境中很好地工作。IDE 的主要優點是,它們通常在整個項目中具有可視性,并且是長生命周期的程序,而獨立編譯器工具通常在項目中的每個單獨檔案上調用,是以範圍有限。

這種差異有很多含義,但一個重要的含義與效率和緩存有關:在一個項目中跨不同檔案共享一個位址空間,意味着可以使用智能緩存和其他技術來顯著減少分析/編譯時間。

IDE 和批處理編譯器的另一個差別是,它們對前端的要求常常非常不同:它們依賴于高性能來提供“快速”的體驗,是以非常需要“增量編譯”、“模糊解析”等技術。最後,IDE 通常與代碼生成有非常不同的需求,通常需要僅使用 codegen 的前端就可以丢棄的資訊。Clang 是專門為捕獲這些資訊而設計和建構的。

1.2.4 使用 LLVM ‘BSD’ 許可證

我們積極地打算将 Clang(以及整個 LLVM) 用于商業項目,不僅作為獨立的編譯器,而且作為嵌入到專有應用程式中的庫。BSD許可是實作這一點的最簡單方法。我們認為,許可鼓勵貢獻者選擇源代碼并使用它,并且相信,如果這些個人群組織不想永遠維護一個fork(當涉及到合并時,這是費時且昂貴的),那麼他們将貢獻自己的工作。此外,現在沒有人靠編譯器賺錢,但許多人需要它們來實作更大的目标:每個人一起工作是有意義的。

有關 LLVM/Clang 許可證的更多資訊,請參見 LLVM 許可證描述。

1.3 内部設計與實作

1.3.1 一個真實的,高品質的編譯器

Clang 是由經驗豐富的編譯器開發人員設計和建構的,他們對現有的開源編譯器所存在的問題越來越感到沮喪。Clang 經過精心設計和建構,為新一代 C/C++/Objective C 開發工具的開發打下了基礎,我們希望它的目标是産品品質。

成為一個高品質的産品編譯器意味着很多事情:它意味着高性能、可靠性和(相對)沒有bug,并且最終被廣泛的人使用和依賴。雖然我們仍處于早期發展階段,但我們堅信這将成為現實。

1.3.2 一個簡單且可程式設計的代碼庫

我們的目标是使任何對編譯器有基本了解并具有 C/C++/Objective C 語言工作知識的人都能夠了解和擴充 clang 源代碼庫。這在很大程度上是因為我們決定讓 AST 鏡像盡可能地接近這些語言:您有友好的 if 語句、for 語句、圓括号表達式、結構體、union等等,所有這些都以簡單而明确的方式表示。

除了簡單的設計之外,我們還通過對源代碼進行良好的注釋,包括在适當的地方引用語言标準,并為簡單性而設計代碼,進而使源代碼變得可通路。除此之外,clang 還提供了一組 AST 轉儲程式、printers 和可視化工具,可以友善地将代碼放入其中并檢視代碼是如何表示的。

1.3.3 一個用于 C、Objective C、C++ 和 Objective C++ 的統一解析器

Clang 是“C語言家族前端”,這意味着我們打算支援 C 家族中最受歡迎的成員。我們确信,這類語言的正确解析技術是手工建構的遞歸下降解析器。由于它是普通的 C++ 代碼,遞歸下降使新開發人員非常容易了解代碼,它很容易支援 C/C++ 所需的特殊規則和其他奇怪的技巧,并使它能夠直接實作出色的診斷和錯誤恢複。

我們認為,在一個統一的解析器中實作 C/C++/Objective C 比維護一個單獨的 C 和 C++ 解析器更容易維護和發展最終結果。

1.3.4 符合 C/C++/Objective C 及其變體

當你開始着手實作一種語言時,你會發現這種語言的工作原理和大多數人了解它的工作原理之間存在着巨大的差距。這個差距是一個普通程式員和一個(可怕的?超自然?)“語言律師”,通曉語言的來由,能輕松掌握标準語言。

在實踐中,與語言保持一緻意味着我們的目标是支援完整的語言,包括黑暗和塵土飛揚的角落(如 trigraphs、預處理器 arcana、C99 VLAs 等)。我們支援擴充超出标準正式允許,我們努力顯式地調用這個代碼中,發出警告(預設是禁用的,但可以被映射到警告或錯誤),允許您使用 Clang 在“嚴格”模式,如果你願意的話。

我們還打算支援這些語言的“方言”,如 C89、K&R C、C++'03、Objective-C 2 等。

2. 為什麼?

開發新的前端是出于對編譯器的需求,它允許更好的診斷、更好地與ide內建、與商業産品相容的許可證,以及易于開發和維護的靈活編譯器。所有這些都是開始一個新的前端工作的動機,可以滿足這些需求。

有關Clang與其他編譯器之間的更詳細比較,請參見 Clang 比較頁面。

3. 目前的狀态

當以X86-32、X86-64和ARM為目标時,Clang被認為是一個高品質的 C、Objective C、C++ 和 Objective C++ 編譯器(其他目标可能有一些警告,但通常很容易修複)。例如,Clang在生産中用于建構性能關鍵的軟體,如 Chrome 或Firefox。

如果您正在尋找源代碼分析或源代碼到源代碼轉換工具,Clang可能是一個很好的解決方案。Clang支援c++ 11、c++ 14和c++ 17,更多資訊請參見C++狀态頁。

4. 參與進來吧!

首先擷取代碼、建構代碼、并使用它。這将向你展示我們今天可以做的事情,并讓你有第一手的“Clang體驗”:希望它能與你産生“共鳴”。)

一旦你這樣做了,請考慮加入 Clang 社群。Clang 開發人員包括許多具有不同背景的志願者。如果您對Clang的開發感興趣,那麼注冊一個郵件清單是了解項目如何工作的好方法。