MSVC,GCC,Clang——不同C/C++编译器对比

在线的 C/CPP 编译器:Compiler Explorer (godbolt.org)

20 世纪 50 年代,计算机程序主要使用汇编语言开发,这对开发者来说太难了。IBM 也希望销售更多计算机让人们编程,John Backus 针对汇编语言的缺点着手研究开发 Fortran 语言,科学家们希望利用高级语言编写程序,但同时提供接近手动调整的机器代码的性能,在 1957 年,首个编译器 IBM Fortran 诞生了。

MSVC

MSVC(Microsoft Visual C++)是微软公司的 C++开发工具,具有集成开发环境,可提供编辑 C 语言,C++以及 C++/CLI 等编程语言。

VC++集成了便利的调试工具,特别是集成了微软 Windows 操作系统应用程序接口(Windows API)、三维动画 DirectX API,微软. NET 框架。

GCC

GCC(GNU Compiler Collection)是由 GNU 项目开发的一套优化编译器,它支持多种编程语言、操作系统和计算机架构。作为自由软件,GCC 由自由软件基金会(FSF)在 GNU 通用公共许可证(GNU GPL)下分发。

GCC 最初名为 GNU C 编译器(GNU C Compiler),因为它最初只支持 C 语言。然而,不久之后,GCC 就扩展了对 C++ 的支持,并逐渐增加了对 Fortran、Pascal、Objective-C、Java、Ada、Go 等多种语言的支持。

GCC 不仅是 GNU 操作系统的官方编译器,也是许多类 UNIX 系统和 Linux 发行版的标准编译器。在 GCC 出现之后,BSD 家族的大部分操作系统也开始使用 GCC。不过,一些系统如 FreeBSD、OpenBSD 和 Apple 的 macOS 已经转而使用 Clang 编译器。

MinGW(Minimalist GNU for Windows)是将 GCC 编译器和 GNU Binutils 移植到 Windows 32位平台的项目。它包括了一系列头文件(用于 Win 32 API)、库和可执行文件。由于最初的 MinGW 项目更新缓慢,且不支持64位环境开发,OneVision Software 于2005年开始开发 MinGW-w64。MinGW-w64 支持 GCC 所支持的大多数编程语言,包括 C、C++、Objective-C、Objective-C++、Fortran 和 Ada。

GCC 实践:

1
2
3
4
5
6
# hello.c
#include <stdio.h>
#define HELLO "hello\n"
int main() {
  printf(HELLO);
}

预处理生成.i 文件。

1
gcc -E hello.c -o hello.i
1
2
3
4
5
6
# hello.i
...
...
int main() {
  printf("hello\n");
}

将预处理的文件转换为特定的汇编文件。

1
gcc -S hello.i -o hello.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
.file	"hello.c"
.text
.section .rodata
.LC0:
.string "hello"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rax
movq %rax, %rdi
call puts@PLT
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:

将汇编代码转换为机器指令,由汇编器完成。

1
gcc -c hello.c -o hello.o

链接。

1
gcc -o hello.o -o hello

Clang

Clang 是一个编译器前端,支持多种编程语言,包括 C、C++、Objective-C、Objective-C++,以及支持 OpenMP、OpenCL、RenderScript、CUDA、SYCL 和 HIP 等软件框架。它可以作为 GNU 编译器集合(GCC)的替代品,并且兼容 GCC 的大多数编译标志和非官方的语言扩展。Clang 使用 LLVM 作为其编译器的后端。

LLVM 是一套编译器和工具链技术,它允许开发者为任何编程语言创建前端,为任何指令集架构创建后端。LLVM 的设计核心是一种与语言无关的中间表示(IR),这种 IR 可以看作是一种可移植的高级汇编语言,它可以通过多次转换来进行优化。LLVM 项目始于 2000 年,在伊利诺伊大学厄巴纳-香槟分校由 Vikram Adve 和 Chris Lattner 领导。最初,LLVM 是作为研究静态和动态编程语言动态编译技术的研究基础设施而开发的。

2005 年,苹果公司聘请了 Chris Lattner 并组建了一个团队,致力于将 LLVM 系统应用于苹果开发系统中的多种用途。最初,LLVM 项目计划使用 GCC 作为前端,但由于 GCC 源代码庞大且实现复杂,加之苹果的软件大量使用 Objective-C,而 GCC 对 Objective-C 的支持优先级不高,以及 GCC 的 GNU 通用公共许可证(GPL)第 3 版要求分发 GCC 扩展或修改版本的开发者必须提供源代码,这与 LLVM 的宽松许可证不符。因此,苹果公司开发了 Clang,自 Xcode 4 起,苹果的默认编译器改为了 Clang/LLVM。

Clang/LLVM 实践:

1
2
3
4
5
6
# hello.c
#include <stdio.h>
#define HELLO "hello\n"
int main() {
  printf(HELLO);
}

预处理生成.i 文件。

1
clang -E -c ./hello.c -o ./hello.i
1
2
3
4
5
6
# hello.i
...
...
int main() {
  printf("hello\n");
}

将.i 文件转化为.bc 文件,这是 LLVM 位代码(bitcode)文件,是中间表示形式,可供进一步优化和转换。

1
clang -emit-llvm ./hello.i -c -o ./hello.bc

将.c 文件编译成 LLVM 中间表示的汇编代码文件.ll,人类可读的中间表示形式。

1
clang -emit-llvm ./hello.c -S -o ./hello.ll
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
; ModuleID = './hello.c'
source_filename = "./hello.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

@.str = private unnamed_addr constant [7 x i8] c"hello\0A\00", align 1

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 {
%1 = call i32 (i8*, ...) @printf(i8* noundef getelementptr inbounds ([7 x i8], [7 x i8]* @.str, i64 0, i64 0))
ret i32 0
}

declare i32 @printf(i8* noundef, ...) #1

attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1, !2, !3, !4}
!llvm.ident = !{!5}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 1}
!4 = !{i32 7, !"frame-pointer", i32 2}
!5 = !{!"Ubuntu clang version 14.0.0-1ubuntu1.1"}

使用 llc 将中间表示文件(.bc 或 .ll)转换为目标平台的汇编代码。

1
2
3
# 这两种转换生成的汇编代码相同
llc ./hello.ll -o ./hello.s
llc ./hello.bc -o ./hello.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
.text
.file "hello.c"
.globl main # -- Begin function main
.p2align 4, 0x90
.type main,@function
main: # @main
.cfi_startproc
# %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
movabsq $.L.str, %rdi
movb $0, %al
callq printf@PLT
xorl %eax, %eax
popq %rbp
.cfi_def_cfa %rsp, 8
retq
.Lfunc_end0:
.size main, .Lfunc_end0-main
.cfi_endproc
# -- End function
.type .L.str,@object # @.str
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "hello\n"
.size .L.str, 7

.ident "Ubuntu clang version 14.0.0-1ubuntu1.1"
.section ".note.GNU-stack","",@progbits

转变为可执行的二进制文件。

1
clang ./hello.s -o hello
MSVC GCC Clang/LLVM
开源协议 GNU GPL Apache 2.0
支持平台 Windows UNIX、Windows、Mac Unix、Mac
构建工具 Make CMake
链接器 ld lld
调试器 GDB LLDB
  1. 编译器的「五个十年」发展史
  2. 维基百科
  3. chenzomi12/AISystem: AISystem 主要是指AI系统,包括AI芯片、AI编译器、AI推理和训练框架等AI全栈底层技术 (github.com)

MSVC,GCC,Clang——不同C/C++编译器对比
https://kaysonyu.github.io/2024/10/MSVC-GCC-Clang/
Author
Yu Kang
Posted on
October 4, 2024
Licensed under