目录
GNU Compiler Collection
GCC的优势
编译产生的中间文件
Clang
Clang的特点
什么是LLVM?
Clang编译过程中产生的中间表示文件
关于Clang的调试
C++ 编译工具链中有几个主要的编译工具,包括:
- GNU Compiler Collection (GCC)
- Clang
- Microsoft Visual C++
- Intel C++ Compiler
这里主要来聊聊常用的GCC与Clang。
GNU Compiler Collection
GNU Compiler Collection 是一个由 GNU 项目开发的编译器套件,包括 C、C++、Objective-C、Fortran 等语言的编译器。它包括预处理器、编译器、汇编器和链接器等工具。
最新的标准包包括 C、C++、Object-C、Objective-C++、Fortran、Ada、Go 和 D的编译器,以及它们的包括库。 在版本 7 之前,还支持 Java。可以在许多不同的操作系统上运行,包括 Linux、Unix、Windows 等。
GCC的优势
广泛支持: GCC 有着广泛的平台和语言支持,是一个多用途的编译器集合,可以满足不同语言的编译需求,且适用于许多不同的架构和操作系统。
成熟的优化: 在一些情况下,GCC 的优化能力可能比 Clang 更好,尤其是针对一些特定的架构。
标准兼容性: 由于 GCC 的历史悠久,它在符合各种语言标准方面有着丰富的经验和成熟的实现。
开源和社区支持: GCC 是一个开源项目,拥有庞大的社区支持,这意味着它可以根据用户的需求进行定制和扩展,同时可以从社区中获取支持和反馈。
丰富的工具链: GCC 提供了丰富的工具链,包括调试器(GDB)、性能分析工具(gprof)、代码分析工具等,这些工具可以帮助开发人员进行程序开发、调试和优化。
以下是一些常用的 GCC 命令,包括编译 C 程序、C++ 程序,生成调试信息,指定优化级别等:
# 编译 C 程序
gcc -o output_file input_file.c# 编译 C++ 程序
g++ -o output_file input_file.cpp# 生成调试信息
gcc -g -o output_file input_file.c# 指定 C 标准版本
gcc -std=c11 -o output_file input_file.c# 指定 C++ 标准版本
g++ -std=c++11 -o output_file input_file.cpp# 生成汇编代码
gcc -S -o output_file.s input_file.c# 生成目标文件
gcc -c -o output_file.o input_file.c# 指定优化级别
gcc -O3 -o output_file input_file.c# 静态分析
gcc --analyze input_file.c# 指定头文件搜索路径
gcc -I/path/to/include -o output_file input_file.c# 指定库文件搜索路径
gcc -L/path/to/lib -o output_file input_file.c# 链接库文件
gcc -o output_file input_file.c -lmylibrary
编译产生的中间文件
GCC 生成的中间文件包括汇编代码文件(.s
)、目标文件(.o
)、预处理文件(.i
)等
GCC 的中间文件中的汇编代码文件是以特定的汇编语言形式表示源代码的中间状态,目标文件则包含了机器代码的中间表示形式。
GCC 的中间文件则更多地依赖于 GCC 自身的优化和代码生成能力。
Clang
Clang 是一个由苹果公司开发的 C、C++、Objective-C 和 Objective-C++ 编程语言的编译器前端,它支持大部分 C++11 和 C++14 的特性,并且被设计成更加模块化和易于扩展。
Clang的特点
-
Clang 以其快速的编译速度而闻名。相比于 GCC,Clang 在大型项目的编译速度上有明显的优势。
-
Clang 提供了更好的诊断能力,它能够生成更加详细和易于理解的错误和警告信息,这有助于开发者更快地发现和修复代码中的问题。
-
Clang 对于 C++11 和 C++14 标准的支持非常好,这使得开发者能够充分利用这些新标准提供的特性。
-
Clang 可以在多个平台上运行,包括 macOS、Linux 和 Windows。
-
Clang 的设计非常模块化,这使得它更容易被集成到其他软件中,也使得它更容易被其他开发者扩展和改进。
Clang本身性能优异,其生成的AST所耗用掉的内存仅仅是GCC的20%左右。测试证明Clang编译Objective-C代码时速度为GCC的3倍,还能针对用户发生的编译错误准确地给出建议。Clang 诊断非常完善,并且具有许多功能。
使用 Clang 编译器必须依赖于 LLVM。Clang 作为 LLVM 项目的一部分,它的工作流程中需要将生成的中间表示(IR)传递给 LLVM 核心进行优化和代码生成。因此,在使用 Clang 编译器时,需要同时安装 LLVM,以便完成整个编译流程。
什么是LLVM?
LLVM(Low Level Virtual Machine)是一个开源的编译器基础设施项目,和GNU一样,它也是一个跨平台的编译器和工具链。由苹果公司在2000年发起的,最初的目标是创建一个可以用于苹果公司内部开发的编译器基础设施。后来,LLVM逐渐发展成为一个独立的开源项目,目前已经成为一个非常流行的编译器基础设施。
在性能和功能上,两者都有自己的优势和劣势。GCC在某些方面可能更成熟和稳定,而LLVM则可能在某些方面更快和更灵活。
当你安装 Clang 时,通常会自动包含 LLVM 核心,因为 Clang 依赖于 LLVM。这使得 Clang 和 LLVM 成为一个完整的编译器基础设施,为用户提供了强大的编译和优化能力。
Clang 命令行选项与 GCC 等其他编译器有些相似,但也有一些自己独特的命令和选项。以下是一些常用的 Clang 编译命令及其选项(clang的命令与gcc基本是一样的):
# 编译 C 程序
clang -o output_file input_file.c# 编译 C++ 程序
clang++ -o output_file input_file.cpp# 生成调试信息
clang -g -o output_file input_file.c# 指定 C++ 标准版本
clang++ -std=c++11 -o output_file input_file.cpp# 生成汇编代码
clang -S -o output_file.s input_file.c# 生成 LLVM IR(中间表示)
clang -emit-llvm -o output_file.bc input_file.c# 指定优化级别
clang -O3 -o output_file input_file.c# 静态分析
clang --analyze input_file.c# 指定头文件搜索路径
clang -I/path/to/include -o output_file input_file.c# 指定库文件搜索路径
clang -L/path/to/lib -o output_file input_file.c# 链接库文件
clang -o output_file input_file.c -lmylibrary
Clang编译过程中产生的中间表示文件
当使用 Clang 编译源代码时,可以选择生成 LLVM IR 文件作为中间表示。LLVM IR 是一种低级的、静态单赋值(SSA)形式的中间表示,它是 LLVM 编译器框架的核心。
clang -emit-llvm -o output_file.bc input_file.c #生成中间文件
中间表示文件的特点:
类似于汇编语言,但具有高级语言的特性,如类型推断、高级数据结构等。
LLVM IR 是一种高度抽象的表示形式,它捕捉了源代码的基本结构和操作,但不涉及特定的机器细节。
LLVM IR 是与机器无关的,可以在多种架构上进行优化和代码生成。
LLVM IR 提供了丰富的信息和结构,使得编译器可以进行各种优化,例如常量传播、死代码消除、循环优化等。
能够被 LLVM 工具链所处理,从而获得 LLVM 提供的丰富优化和分析能力。
对于 LLVM IR 文件,通常使用的后缀名是 .ll
。因此,当使用 Clang 生成 LLVM IR 文件时,通常会将输出文件命名为 output.ll
。这个后缀名表示该文件包含 LLVM IR 代码。
以下是一个简单的 C 语言函数的示例及其对应的 LLVM IR 代码:
//C代码
int add(int a, int b) {return a + b;
}
对应的 LLVM IR 代码:
define i32 @add(i32 %a, i32 %b) {
entry:%add = add nsw i32 %a, %bret i32 %add
}
让我们逐行简单解释下这段代码:
define i32 @add(i32 %a, i32 %b) {
:这一行定义了一个名为 "add" 的函数,它接受两个 i32 类型的参数%a
和%b
。
entry:
:这一行表示接下来的代码段是该函数的入口点。
%add = add nsw i32 %a, %b
:这一行表示将%a
和%b
相加,并将结果存储在%add
中。add
是 LLVM IR 中的指令,它表示执行加法操作。nsw
表示“no signed wrap”,表示在执行加法时不考虑溢出。
ret i32 %add
:这一行表示从函数中返回%add
的值。
关于Clang的调试
-
Clang 生成的可执行文件可以使用多种调试器进行调试,包括 GDB(GNU 调试器)和 LLDB(LLVM 调试器)。LLDB 是 LLVM 项目的一部分,因此与 Clang 更加紧密相关。
-
使用 LLDB 进行调试时,可以在命令行中输入
lldb 可执行文件名
来启动 LLDB 调试器。然后可以使用 LLDB 提供的命令来进行调试。
Clang相关传送门
《Clang用户手册》https://clang.llvm.org/docs/UsersManual.htmlLLVM 下载页面https://releases.llvm.org/download.htmlClang - C++ 编程语言状态https://clang.llvm.org/cxx_status.html