编译器的一般构成
- 三个部分: 前端(frontEnd) + 优化器(Optimizer) + 后端(backEnd)
- 前端 :词法和语法分析
- 优化器 :承前基础 + 优化代码 = 更加高效
- 后端 : 将中间代码转化为各个平台的机器代码!
- GCC可以处理
- C++
- Fortran
- Pascal
- Objective - C
- Java
- Ada
- 等
- LLVM(Low Level Virtual Machine,底层虚拟机)
- 提供了与编译器相关的支持,能够进行程序语言的编译期优化、链接优化、在线编译优化、代码生成。简而言之,可以作为多种编译器的后台来使用
- 苹果公司一直使用 GCC 作为官方的编译器。GCC 作为一款开源的编译器,一直做得不错,但 Apple 对编译工具会提出更高的要求
- 是 Apple 对 Objective-C 语言(包括后来对C语言)新增很多特性,但 GCC 开发者并不买Apple的账——不给实现,因此索性后来两者分成两条分支分别开发,这也造成 Apple 的编译器版本远落后于 GCC 的官方版本。
- GCC 的代码耦合度太高,很难独立,而且越是后期的版本,代码质量越差,但 Apple 想做的很多功能(比如更好的 IDE 支持),需要模块化的方式来调用 GCC,但 GCC一直不给做。
LLVM2.0 - Clang
- Apple 打算从零开始写 C、C++、Objective-C语言的前端 Clang,完全替代掉GCC。
- Clang 是LLVM的前端,可以用来编译C,C++,ObjectiveC等语言
- Clang则是以LLVM为后端的一款高效易用,并且与IDE 结合很好的编译前端。
- Clang 只支持C,C++和Objective-C三种语言。2007年开始开发,C编译器最早完成,因此在2009年时,已经完全可以用于生产环境。C++ 在后来也得到了支持。
GCC 和 Clang 对比
- Clang特性
- 速度快,通过编译 OS X 上几乎包含了所有 C 头文件的 carbon.h 的测试
- 内存占用小:Clang 内存占用是源码的 130%,Apple GCC 则超过 10 倍。
- 兼容性好:Clang 从一开始就被设计为一个API,允许它被源代码分析工具和 IDE 集成。GCC 被构建成一个单一的静态编译器,这使得它非常难以被作为 API 并集成到其他工具中。
- 诊断信息可读性强 :其中错误的语法不但有源码提示,还会在错误的调用和相关上下文的下方有 ~~~~~ 和^的提示,相比之下 GCC 的提示很天书。
- Clang有静态分析,GCC没有
![[Pasted image 20240905033026.png]]
- GCC 优势:支持 JAVA/ADA/FORTRAN
GCC 支持更多平台
GCC 更流行,广泛使用,支持完备
GCC 基于 C,不需要 C++ 编译器即可编译
GCC、LLVM 和 Clang 如何选择?
- 目前不推荐使用老的GCC4.2,因为苹果不会维持它了,而且LLVM-GCC看起来会更好。在项目中途改编译选项可是一个大变动,需要慎重。
- 对新的项目而言,LLVM-GCC 看起來应该是个安全的选择,苹果公司认为它够稳定够成熟,所以才把它当做Xcode 4的预设选项。而且,既然选项使用的是GCC parser,向后兼容性应该没问题。
- LLVM-GCC是个安全的选项,但并不是指Clang/LLVM比较不安全,只是成熟度还沒那么高效了。
总结 - 再探LLVM
- 但GCC到底有什么问题呢?LLVM的优点也正是GCC的缺点。传统编译器工作的时候前端负责解析源代码,检查语法错误,并将其翻译为抽象的语法树(Abstract Syntax Tree)。优化器对这一中间代码进行优化,试图使代码更高效。后端则负责将优化器优化后的中间代码转换为目标机器的代码,这一过程后端会最大化的利用目标机器的特殊指令,以提高代码的性能。事实上,不光静态语言如此,动态语言也符合上面这个模型,例如Java。JVM也利用上面这个模型,将Java代码翻译为Java bytecode。这一模型的好处是,当我们要支持多种语言时,只需要添加多个前端就可以了。当需要支持多种目标机器时,只需要添加多个后端就可以了。对于中间的优化器,我们可以使用通用的中间代码。
- 。这种三段式的结构还有一个好处,开发前端的人只需要知道如何将源代码转换为优化器能够理解的中间代码就可以了,他不需要知道优化器的工作原理,也不需要了解目标机器的知识
- 虽然这种三段式的编译器有很多优点,并且被写到了教科书上,但是在实际中这一结构却从来没有被完美实现过。做的比较好的应该属Java和.NET虚拟机。虚拟机可以将目标语言翻译为bytecode,所以理论上讲我们可以将任何语言翻译为bytecode,然后输入虚拟机中运行。但是这一动态语言的模型并不太适合C语言,所以硬将C语言翻译为bytecode并实现垃圾回收机制的效率是非常低的。GCC也将三段式做的比较好,并且实现了很多前端,支持了很多语言。但是上述这些编译器的致命缺陷是,他们是一个完整的可执行文件,没有给其它语言的开发者提供代码重用的接口。即使GCC是开源的,但是源代码重用的难度也比较大。
- LLVM最初的定位是比较底层的虚拟机。它的出现正是为了解决编译器代码重用的问题,LLVM一上来就站在比较高的角度,制定了LLVM IR这一中间代码表示语言。LLVM IR充分考虑了各种应用场景,例如在IDE中调用LLVM进行实时的代码语法检查,对静态语言、动态语言的编译、优化等。从上面这个图中我们发现LLVM与GCC在三段式架构上并没有本质区别。LLVM与其它编译器最大的差别是,它不仅仅是Compiler Collection,也是Libraries Collection。举个例子,假如说我要写一个X语言的优化器,我自己实现了PassX算法,用以处理X语言与其它语言差别最大的地方。而LLVM优化器提供的PassA和PassB算法则提供了X语言与其它语言共性的优化算法。那么我可以选择X优化器在链接的时候把LLVM提供的算法链接进来。
- LLVM不仅仅是编译器,也是一个SDK。Apple LLVM compiler 4.2是一个真正的LLVM编译器,前端使用的是Clang,基于最新的LLVM 3.2编译的。LLVM GCC 4.2编译器的核心仍然是LLVM,但是前端使用的是GCC 4.2编译器。从LLVM的下载页面可以看出,LLVM从1.0到2.5使用的都是GCC作为前端,直到2.6开始才提供了Clang前端。
- 如果你下载 LLVM 的代码,那么它就是一个IR到ARM/机器码的编译器。比如bin/opt就是对IR的优化器,bin/llc就是IR->ASM的翻译,bin/llvm-mc就是汇编器。如果你再从http://llvm.org下载Clang,那么就有了C->IR的翻译以及完整的编译器Driver。GDB是GNU的调试器。只要编译器支持DWARF格式,就可以用GDB调试。