三大编译器GCC、LLVM和clang

详解三大编译器:gcc、llvm 和 clang - 知乎 (zhihu.com)

【编译原理】GCC/Clang/LLVM的区别与联系 - 掘金 (juejin.cn)

GCC

简介

GCC(GNU Compiler Collection,GNU编译器套装),是一套由GNU开发的编程语言编译器。GCC原名为GNU C语言编译器,因为它原本只能处理C语言。GCC快速演进,变得可以处理C++、Fortran、Pascal、Objective-C、java以及Ada等其他语言

GCC是传统的编译器主要工作原理分为三段式(该模型同样适用于解释器和JIT编译器)

  • 前端:解析源代码、检查语法错误、翻译为抽象语法树
  • 优化器:将抽象语法翻译生成中间代码,并对中间代码进行优化
  • 后端:将优化后的中间代码转换为目标机器的代码,

架构优势

  1. 使用通用的中间代码,新增支持语言只需要增加一个前端,新增支持目标机器只需要增加一个后端
  2. 针对前后端开发技术栈不同,开发人员只需要关注自身的技术栈,有利于让更多人员参与进来。

架构缺陷

  1. GCC三段式必须配套使用,很难做到部分重用
  2. 不能将 GCC 部分功能作为库重用的原因有很多,包括滥用全局变量、不可变变量不能更改限制不严、设计不良的数据结构、庞大的代码库、以及使用宏来防止代码库一次编译可以支持多个编译前端-目标机对。不过,最难解决的问题是其早期设计和那个时代所固有的架构设计。具体来说,GCC 饱受分层问题和抽象漏洞的困扰:编译后端遍历编译前端抽象语法树(AST)来生成调试信息(debug info),编译前端生成编译后端数据的结构,整个编译器依赖命令行设置的全局数据结构。

LLVM

简介

LLVM的提出是为了解决编译器代码重用的问题

LLVM是三段式编译器优化器+后端的SDK集合,提供了一些列对外接口供开发者调用

  1. 前端:支持各种组件的前端(Clang、llvm-gcc、GHC)需要遵守LVVM规则,输出中间代码 LLVM IR
  2. 优化器:LLVM IR通过一些列分析和优化从而改进代码,然后输入代码生成器生成目标机器码
  3. 后端:将优化器优化后的中间代码转换为目标机器的代码

架构优势

  1. LLVM IR既有清晰地规范,又是与优化器的唯一接口。这就意味着为 LLVM 编写编译前端唯一要做的就是生成 LLVM IR。由于LLVM IR具有一流的文本格式,构建一个将 LLVM IR 作为文本输出的前端既可行又合理
  2. 在设计完 LLVM IR 之后,LLVM 下一个重要的点就是要把 LLVM 设计成一系列独立的库,而不是像 GCC 那样的不可分离命令行编译器以优化器的设计为例:它将 LLVM IR 作为输入,然后逐步处理输入并生成执行效率更高的 LLVM IR。LLVM 优化器(和其他编译器一样)以流水线的方式为输入做不同的优化。常见的例子是内联(替换调用位置的函数实体)、重新组合表达式、移动循环不变代码等等。根据优化级别,运行不同程度的优化:例如 -O0 是不做优化,-O3 将运行 67 道优化程序(LLVM 2.8 版本)。
  3. LLVM 优化器基于库的设计可以让我们可以灵活选择要组合的优化程序以及自定义优化程序之间的执行顺序
  4. 多目标机兼容的 LLVM 代码生成器的设计,LLVM 的代码生成器将代码生成分为多个独立的过程——指令选择、寄存器分配、调度、代码布局优化和代码组装
  5. 使用BugPoint减少测试用例

Clang

  1. Clang是一个C、C++、Objective-C和Objective-C++编程语言的编译器前端
  2. 底层是由LLVM作为后端
  3. Clang项目包括Clang前端和Clang静态分析器等,目的是输出代码对应的抽象语法树并将代码编译成LLVM bitcode
  4. 接着后端使用LLVM编译成平台相关的机器语言

关系

Clang在前端生成的抽象语法树是GCC内存的20%

Clang+LLVM能做到GCC类似的功能

KKVM提供了灵活的代码复用能力,将编译后的算法封装成为了一个个独立的模块并提供对外的接口,而GCC传统编译器耦合很重,很难小粒度复用代码

Last modification:January 18, 2024
如果觉得我的文章对你有用,请随意赞赏