GCC

GNU 編譯器套裝（GNU Compiler Collection，縮寫為 GCC）是 GNU 計劃製作的一種最佳化編譯器，支援各種程式語言、作業系統、計算機系統結構。該編譯器是以 GPL 及 LGPL 許可證所發行的自由軟體，也是 GNU 計劃的關鍵部分，還是 GNU 工具鏈的主要組成部份之一。GCC（特別是其中的 C 語言編譯器）也常被認為是跨平臺編譯器的事實標準。1985 年由理查德・馬修・斯托曼開始發展，現在由自由軟體基金會負責維護工作。截至 2019 年，GCC 大約有 1500 萬行程式碼，是現存最大的自由程式之一。 它在自由軟體的發展中發揮了重要作用，不僅是一個工具，還是一個典例。

原名為 GNU C 語言編譯器（GNU C Compiler），因為它原本只能處理 C 語言。同年 12 月，新的 GCC 編譯器可以編譯 C++ 語言。後來又為 Fortran、Pascal、Objective-C、Java、Ada，Go 等其他語言開發了前端。C 和 C++ 編譯器也支援 OpenMP 和 OpenACC 規範。

GCC 編譯器已經被移植到比其他編譯器更多的平臺和指令集架構上，並被廣泛部署在開發自由和專有軟體的工具中。GCC 還可用於許多嵌入式系統，包括基於 ARM 和的晶片。

GCC 不僅是 GNU 作業系統的官方編譯器，還是許多類 UNIX 系統和 Linux 發行版的標準編譯器。BSD 家族中的大部分作業系統也在 GCC 釋出之後轉用 GCC；不過 FreeBSD、OpenBSD 和 Apple macOS 已經轉向了 Clang 編譯器，主要是因為許可問題。GCC 也可以編譯 Windows、Android、iOS、Solaris、HP-UX、IBM AIX 和 DOS 系統的程式碼。GCC 原本用 C 開發，後來因為 LLVM、Clang 的崛起，它更快地將開發語言轉換為 C++。許多 C 的愛好者在對 C++ 一知半解的情況下主觀認定 C++ 的效能一定會輸給 C，但是 Ian Lance Taylor 給出了不同的意見，並表明 C++ 不但效能不輸給 C，而且能設計出更好，更容易維護的程式。

歷史

1983 年底，為了引導 GNU 作業系統，理查德・馬修・斯托曼向阿姆斯特丹編譯器套件（自由大學編譯器套件）的作者安德魯・塔能鮑姆請求在 GNU 上允許使用該編譯器；但是作者告知他該編譯器僅對大學免費。因此，他打算開發一個不同的編譯器。一開始他打算與 Len Tower 和其他人將勞倫斯利佛摩國家實驗室的一個現有編譯器從 Pastel 改寫成 C。但是他在給利弗莫爾編譯器寫了一個新的 C 前端後，發現它需要數兆位元組的堆疊空間，只有 64KB 的 68000 Unix 系統上無法執行。因此，他打算自己從頭寫一個編譯器。總而言之，儘管斯托曼確實使用了他自己寫的 C 前端，他並沒有將任何 Pastel 編譯器的程式碼放在 GCC 中。

GCC 於 1987 年 3 月 22 日在麻省理工學院的檔案傳輸協議上釋出，斯托曼被列為作者，也提及了其他人並感謝他們的貢獻：Jack Davidson 和 Christopher Fraser 給出了使用暫存器傳遞語言作為中間語言的思路；Paul Rubin 為預處理器貢獻良多；以及 Leonard Tower 寫了 “部分解析器、RTL 生成器、RTL 定義和 Vax 機器描述”。被譽為 “自由軟體第一擊” 的 GNU 編譯器釋出正值太陽微系統將其作業系統與其開發工具解綁，並提價單獨出售。這使得許多客戶購買或下載 GCC 而非供應商的工具。儘管斯托曼認為 GNU Emacs 是他的主要工程，但截至 1990 年，GCC 支援 13 種電腦架構，效能比其他編譯器優越併為商業所用。

1.1 EGCS 克隆

由於 GCC 是在 GPL 許可下授權的，其他為 C 以外語言編寫介面的程式設計師可以自由的開發其自己的編譯器分支，只要他們遵守 GPL 許可條款。但是，多分叉在日後體現出低效和不便的特點；而且人們很難使熱愛穩定性勝過新特性的 GCC 官方專案接受他們的分支。FSF 對新增到 GCC 2.x 官方版本（1992 年開始開發）中的內容進行了相當嚴格的控制，以至於被 Eric S. Raymond 在《大教堂與集市》中形容為 "大教堂" 開發模式。

在 1997 年，一群不滿 GCC 緩慢且封閉的創作環境者，組織了一個名為實驗性 / 增強型 GNU 編譯器系統（Experimental/Enhanced GNU Compiler System）的專案，將幾個實驗性分叉合併為一個專案。其基礎是 GCC 的開發快照（大概取自 2.7.2，後來跟進到 2.8.1）。合併內容包括 g77（Fortran）、PGCC（P5 Pentium 最佳化的 GCC），許多 C++ 的改進，以及許多新的架構和作業系統變種。

這兩個專案都密切觀察著彼此的動態，但是 EGCS 的發展明顯更活躍，因此 FSF 正式停止他們對 GCC 2.x 編譯器的開發並希望 EGCS 成為 GCC 的官方版本。在 1999 年 4 月 EGCS 專案被任命為為 GCC 的維護者。隨著 1999 年 7 月 GCC 2.95 的釋出，這兩個專案再次聯合起來。此後，GCC 在一個指導委員會的指導下，來自各國的程式設計師小組會對其進行維護。

由於缺乏維護，GCC 3 (2002) 移除了 CHILL 的前端支援。在版本 4.0 之前，GCC 3 中的 Fortran 前端是 g77，只支援 FORTRAN 77。該前端後來被廢棄，取而代之的是新 GNU Fortran 前端，支援 Fortran 95 和 Fortran 2003 及 Fortran 2008 的大部分內容 。從 GCC 4.8 版開始，GCC 由 C++ 語言編寫。從 GCC 5 到 GCC 7 都保留了對 Cilk Plus 的支援。

GCC 已經被移植到各種指令集架構上，並被廣泛部署為開發自由或專有軟體的工具。GCC 還可用於許多嵌入式系統，包括 Symbian（稱為 gcce）、基於 ARM 和基於 Power ISA 的晶片。該編譯器可以在各種平臺上輸出，包括遊戲控制器中的 PS2、Cell 微處理器架構的 PS3 以及 Dreamcast。相比於其他編譯器，GCC 編譯器被部署在更多的作業系統和處理器上。

目前支援的語言

截至 2022 年 9 月，GCC 12.2 版內含 C（gcc）、C++（g++）、Objective-C、Fortran（gfortran）、Ada（GNAT）、Go （gccgo）以及 D (gdc，從 9.1 版開始) 程式語言的前端。OpenMP 和 OpenACC 並行語言拓展從 GCC 5.1 開始支援。GCC 7 之前的版本也支援 Java（gcj），允許將 java 編譯為機器語言。

有關 C++ 和 C 的語言版本支援，從 GCC 11.1 開始預設為 gnu++17，C++17 的超集；以及 gnu11，C11 的超集，還提供嚴格的標準支援。GCC 也對 C++20 和即將到來的 C++23 標準提供實驗性部分支援。

有許多為其它語言編寫的第三方前端，比如 Pascal、Modula-2、Modula-3、Mercury 語言以及 VHDL（GHDL）。一些實驗性分支可支援更多語言，比如 GCC UPC 編譯器還支援 UPC 和 Rust。

支援的處理器架構

GCC 11.1 版本支援的處理器包括：

AArch64
Alpha
ARM
AVR
（GCC 4.8）
IA-32 (x86)
IA-64 （Intel 安騰）
MIPS
Motorola 68000
MSP430
Nvidia GPU
PA-RISC
PDP-11
PowerPC
/ /
RISC-V
SPARC
SuperH
/
VAX
x86-64

標準版本支援的少見處理器如下：

CR16
IBM ROMP
Intel i960
MIL-STD-1750A
Motorola 88000

非 FSF 維護的 GCC 版本支援的處理器如下：

和
MSP430
Nios II 和
PDP-10
Saturn (HP48XGCC)
(變種)

GCJ Java 編譯器可以輸出機器語言或者 Java 虛擬機器的 Java 位元組碼。當重定向 GCC 到新的平臺上，經常會用到。 Motorola 68000，Zilog Z80 以及其他處理器也可在為德州儀器、惠普、夏普以及卡西歐可程式設計圖形計算器設計的 GCC 編譯器上輸出。

設計

GCC 的外部介面遵循 UNIX 使用慣例。使用者輸入特定語言的驅動程式碼（C 語言為 gcc，C++ 為 g++，如此不一而足），該程式解釋命令語句，呼叫實際編譯器，在輸出介面上執行彙編器，然後選擇性地執行連結器，產生一個完整的可執行二進位制檔案。

每種語言的編譯器都是一個獨立的程式，可讀取原始碼並輸出機器碼。所有語言的編譯器都擁有共通的中介架構：各語言前端解析符合此語言的原始碼，並產生一抽象語法樹。如有必要，這些程式碼會被轉換為中介端的輸入表示，即所謂的 GENERIC 形式；然後中介端會逐漸將程式轉換為最終形式。編譯器最佳化和靜態程式碼分析技術（例如 FORTIFY_SOURCE，一種嘗試發現緩衝區溢位的編譯器指令）也會在原始碼編譯時應用。這些操作都是在多種表示法上工作，其中主要是獨立於架構的 GIMPLE 表示法和獨立於架構的 RTL 表示法。最終，機器碼由與發明的演算法產生。

除了 Ada 前端主要以 Ada 寫成，GCC 大部分是用 C 語言編寫的。GCC 發行版包含主要以各自語言編寫的 Ada 和 C++ 標準庫。在一些平臺上，GCC 發行版還包括一個低階執行庫 libgcc 該執行庫由獨立於機器的 C 語言和特定處理器的機器碼組合編寫，可處理目標處理器不能直接執行的複雜算術運算。

GCC 使用了許多額外的工具。雖然這些工具在 UNIX 和 Linux 發行版中基本為預設安裝的，但是 Windows 系統通常沒有。這些工具包括 Perl、Flex、Bison 和其他常用工具；還需要額外的依賴庫 GMP、MPC 和。

2010 年 5 月，GCC 指導委員會決定允許使用 C++ 編譯器來編譯 GCC。 編譯器計劃主要用 C 語言編寫，並加上 C++ 的一個子集特性。之所以這樣做是為了了讓 GCC 的開發者能夠使用 C++ 的析構器和泛型功能。2012 年 8 月，GCC 指導委員會宣佈，GCC 將以 C++ 為源語言。這意味著，要從原始碼編寫 GCC 編譯器，需要一個能夠理解 ISO/IEC C++03 標準的 C++ 編譯器。2020 年 5 月 18 日，GCC 從 ISO/IEC C++03 標準轉向 ISO/IEC C++11 標準（即需要改寫編譯器本身；預設情況下可編譯 C++ 早期版本）。

4.1 前端介面

每個前端都使用一個分析器來產生給定的原始碼的一個抽象語法樹。由於語法樹的抽象性，不同語言的原始碼都可以被同一個後端處理。GCC 一開始使用 bison 生成的 LALR 語法分析器，但在 2004 年逐漸轉向用於 C++ 的遞迴下降解析器，並在 2006 年用於 C 和 Objective-C。2021 年開始，所有前端都使用遞迴下降解析器。

在 GCC 4.0 之前，程式的語法樹結構不完全獨立於輸出的目標處理器架構。對於不同語言的前端來說，語法樹的含義可能不同；而且前端可以提供它們特別的語法樹規則。隨著 GENERIC 和 GIMPLE 的引入，這種情況得以避免。這是兩種新的獨立於語言的語法樹形式，隨 GCC 4.0 引入編譯器前端。GENERIC 更復雜，是一種基於 GCC 3.x Java 前端的中介表示。 GIMPLE 是一個簡化的 GENERIC，其中各種結構被簡化為多個 GIMPLE 指令。 C、C++ 和 Java 前端直接在前端生成 GENERIC。 相反，其他前端在解析後會有不同的中介表示，這些中介表示將轉換為 GENERIC。前端生成 GENERIC 之後再使用「gimplifier」技術簡化 GENERIC 的複雜結構，成為一較簡單的以 SSA 為基礎的 GIMPLE 形式，一種強大的，獨立於語言和體系結構的全域性（函式範圍）最佳化的通用語言。

4.2 中介介面

4.2.1 GENERIC 和 GIMPLE

GENERIC 是一種中間表示語言，在將原始碼編譯成可執行二進位制檔案時用作 “中介端”。GCC 的所有前端都指向 GENERIC 的子集 GIMPLE。GCC 的中間階段進行所有的獨立於編譯語言和目標架構的程式碼分析和最佳化，從 GENERIC 表示法開始將其轉譯為暫存器傳遞語言（RTL）。GENERIC 表示只包含中介端最佳化後的指令式程式設計結構的子集。

在將原始碼轉譯為 GIMPLE 表示時，會使用臨時變數將複雜表示式拆分為三位址碼。這種表示法的靈感來自於 Laurie J. Hendren 在 McCAT 編譯器中提出的 SIMPLE 表示法，用於簡化指令式程式的分析和最佳化。

4.2.2 最佳化

一般編譯器作者會將語法樹的最佳化放在前端，但其實此步驟並不看語言的種類而有不同，且不需要用到語法解析器。因此 GCC 作者們將此步驟歸入通稱為中介階段的部分裡。此類的最佳化包括消解死碼、消解重複運算與全域數值重編碼等。許多最佳化技巧也正在實作中。

4.3 後端介面

GCC 的後端部分是由預處理器宏和目標架構特有的函式指定的，例如定義其位元組序、字大小和呼叫約定。後端的前半部分使用這些來決定 RTL 的生成形式，因此雖然 GCC 的 RTL 理論上不受處理器影響，但在此階段其抽象指令已被轉換成目標架構的格式。在任何時候，構成程式的實際 RTL 指令都必須符合目標架構的機器描述標準。

機器描述檔案包含了 RTL 模式、運算元約束和輸出最終彙編的程式碼片段。這些約束條件表明，一個特定的 RTL 模式可能只適用於某些硬體暫存器，或者某些只允許有限大小的即時運算元偏移（例如 12、16、24、... 位偏移，等等）的架構。在 RTL 生成過程中，給定目標架構的約束條件會被檢查。為了釋出一個給定的 RTL 片段，它必須與機器描述檔案中的一個或多個 RTL 模式相匹配，並滿足該模式的約束條件；否則，就無法將最終的 RTL 轉換成機器程式碼。

在編譯結束時，有效 RTL 會被簡化為嚴格的形式，其中每條指令都指向真實的機器暫存器、和目標機器描述檔案中的一種模式。嚴格化 RTL 是個相當複雜的工作：首先是暫存器分配，選擇真實的硬體暫存器來取代最初分配的偽暫存器；還有過載，未分配實際硬體暫存器的偽暫存器都會被溢位到堆疊中，並生成執行此溢位的 RTL。過大的偏移量無法適合實際指令，故會被分解成服從偏移量約束的 RTL 序列。

後端在最後透過呼叫與每個模式相關聯的一小段程式碼來構建機器程式碼，以使用在過載時選擇的最終暫存器、偏移量和地址從目標指令集中生成真正的指令。當彙編生成片段只是一個字串時，就會執行暫存器、偏移量和 / 或地址到字串的簡單字串替換。彙編生成片段也可以是一個簡短的 C 程式碼塊，但最終也會返回一個包含有效彙編程式碼的字串。

4.4 C++ 標準庫

GCC 專案在 GPLv3 的許可下實現了 C++ 標準庫（libstdc++）。目前的最新版本是 11。

替 GCC 程式除錯

GNU 除錯器是一個為 GCC 除錯的程式。其他特殊用途的除錯工具是 Valgrind，用以發現記憶體洩漏（memory leak）。而 GNU 測量器（gprof）可以得知程式中某些函式花費多少時間，以及其呼叫頻率；此功能需要使用者在編譯時選定測量（profiling）選項。

GCC 內嵌彙編

內嵌彙編也稱行內彙編，是把組合語言程式碼塊插在 C 語言語句之間。詳情參見 GCC-Inline-Assembly-HOWTO.html