【AI系统】GCC 主要特征

简介: GCC(GNU Compiler Collection)是由理查德·斯托曼于1987年创建,最初作为GNU项目的一部分,旨在为GNU/Linux系统提供高效C语言编译器。随着时间发展,GCC不仅支持多种编程语言如C++、Java、Swift等,还具备强大的可移植性、模块化设计及跨平台交叉编译能力,成为全球广泛使用的开源编译器之一。其编译过程分为预处理、编译、汇编和链接四个阶段,支持静态和动态链接方式,适用于本地编译和交叉编译场景。

GCC(GNU Compiler Collection,GNU 编译器集合)最初是作为 GNU 操作系统的编译器编写的,旨在为 GNU/Linux 系统开发一个高效的 C 编译器。其历史可以追溯到 1987 年,当时由理查德·斯托曼(Richard Stallman)创建,作为 GNU 课程的一部分。

最初,GCC 仅是一个用于编译 C 语言的编译器,但很快扩展以支持其他编程语言,其设计强调可移植性和模块化,使其能够适应多种硬件和操作系统环境。

在 1990 年代和 2000 年代,GCC 经历了几次重要的重构和扩展。改进包括引入新的优化技术、提升代码生成和分析能力,以及增强对新兴编程语言和硬件架构的支持。这些改进使得 GCC 成为编程社区中的重要工具,广泛应用于学术研究、商业开发和开源课程中。随着 GNU/Linux 系统的快速发展,GCC 逐渐扩展其支持范围,涵盖了包括 Microsoft Windows、BSD 系列、Solaris 和 Mac OS 在内的多种操作系统平台。

同时,GCC 的指导委员会根据其使命宣言做出重要决策,致力于持续发布高质量的版本。通过 Git 版本控制系统和每周的源代码快照,GCC 的最新源代码随时可供获取。任何人都被鼓励参与贡献或协助测试,以推动 GCC 的持续发展。

编译器

此外,GCC 还引入了与现代编程语言如 Swift 和 Java 相关的前端,使其成为一个全面而多功能的编译器。作为一个模块化设计的软件,GCC 提供了丰富的功能和灵活性,既能在本地平台上进行编译,也支持跨平台的交叉编译。作为自由软件的一部分,用户可以免费获取并自由使用 GCC,并且得到了自由软件基金会(Free Software Foundation,FSF)的强力支持。

GCC 具有以下主要特征:

  • 可移植性:支持多种硬件平台,使得用户可以在不同的硬件架构上进行编译。
  • 跨平台交叉编译:支持在一个平台上为另一个平台生成可执行文件,这对嵌入式开发尤为重要。
  • 多语言前端:除了 C 语言,还支持 C++、Objective-C、Fortran、Ada、Go 和 D 等多种编程语言。
  • 模块化设计:允许轻松添加新语言和 CPU 架构的支持,增强了扩展性。
  • 开源自由软件:源代码公开,用户可以自由使用、修改和分发。

GCC 编译流程

GCC 的编译过程可以大致分为预处理、编译、汇编和链接四个阶段。

编译器

源程序(文本)

当编写源程序时,通常会使用以 .c 或 .cpp 为扩展名的文件。下面以打印宏定义 HELLOWORD 为例,我们使用 C 语言编写 hello.c 源文件:

#include <stdio.h>

#define HELLOWORD ("hello world\n")

int main(void){
   
    printf(HELLOWORD);
    return 0;
}

预处理(cpp)

生成文件 hello.i

gcc -E hello.c -o hello.i

在预处理过程中,源代码会被读入,并检查其中包含的预处理指令和宏定义,然后进行相应的替换操作。此外,预处理过程还会删除程序中的注释和多余空白字符。最终生成的.i 文件包含了经过预处理后的代码内容。

当高级语言代码经过预处理生成.i 文件时,预处理过程会涉及宏替换、条件编译等操作。以下是对这些预处理操作的解释:

  • 头文件展开:

在预处理阶段,编译器会将源文件中包含的头文件内容插入到源文件中对应的位置,以便在编译时能够访问头文件中定义的函数、变量、宏等内容。

  • 宏替换:

在预处理阶段,编译器会将源文件中定义的宏在使用时进行替换,即将宏名称替换为其定义的内容。这样可以简化代码编写,提高代码的可读性和可维护性。

  • 条件编译:

通过预处理指令如 #if、#else、#ifdef 等,在编译前确定某些代码片段是否应被包含在最终的编译过程中。这样可以根据条件编译选择性地包含代码,实现不同平台、环境下的代码控制。

  • 删除注释:

在预处理阶段,编译器会删除源文件中的注释,包括单行注释(//)和多行注释(/.../),这样可以提高编译速度并减少编译后代码的大小。

  • 添加行号和文件名标识:

通过预处理指令如 #line,在预处理阶段添加行号和文件名标识到源文件中,便于在编译过程中定位错误信息和调试。

  • 保留 #pragma 命令:

在预处理阶段,编译器会保留以#pragma 开头的预处理指令,如#pragma once、#pragma pack 等,这些指令可以用来指导编译器进行特定的处理,如控制编译器的行为或优化代码。

hello.i 文件部分内容如下,详细可见../code/gcc/hello.i 文件。

int main(void){
   
    printf(("hello world\n"));
    return 0;
}

在该文件中,已经将头文件包含进来,宏定义 HELLOWORD 替换为字符串"hello world\n",并删除了注释和多余空白字符。

编译(ccl)

在这里,编译并不仅仅指将程序从源文件转换为二进制文件的整个过程,而是特指将经过预处理的文件(hello.i)转换为特定汇编代码文件(hello.s)的过程。

在这个过程中,经过预处理后的 .i 文件作为输入,通过编译器(ccl)生成相应的汇编代码.s 文件。编译器(ccl)是 GCC 的前端,其主要功能是将经过预处理的代码转换为汇编代码。编译阶段会对预处理后的.i 文件进行语法分析、词法分析以及各种优化,最终生成对应的汇编代码。

汇编代码是以文本形式存在的程序代码,接着经过编译生成.s 文件,是连接程序员编写的高级语言代码与计算机硬件之间的桥梁。

生成文件 hello.s:

gcc -S hello.i -o hello.s

打开 hello.s 后输出如下:

    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 10, 15    sdk_version 10, 15, 6
    .globl    _main                   # -- Begin function main
    .p2align    4, 0x90
_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
    subq    $16, %rsp
    movl    $0, -4(%rbp)
    leaq    L_.str(%rip), %rdi
    movb    $0, %al
    callq    _printf
    xorl    %ecx, %ecx
    movl    %eax, -8(%rbp)          # 4-byte Spill
    movl    %ecx, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
    .cfi_endproc
                                        # -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 # @.str
    .asciz    "hello world\n"

.subsections_via_symbols

现在 hello.s 文件中包含了完全是汇编指令的内容,表明 hello.c 文件已经被成功编译成了汇编语言。

汇编(as)

在这一步中,我们将汇编代码转换成机器指令。这一步是通过汇编器(as)完成的。汇编器是 GCC 的后端,其主要功能是将汇编代码转换成机器指令。

汇编器的工作是将人类可读的汇编代码转换为机器指令或二进制码,生成一个可重定位的目标程序,通常以 .o 作为文件扩展名。这个目标文件包含了逐行转换后的机器码,以二进制形式存储。这种可重定位的目标程序为后续的链接和执行提供了基础,使得汇编代码能够被计算机直接执行。

生成文件 hello.o

gcc -c hello.s -o hello.o

链接(ld)

链接过程中,链接器的作用是将目标文件与其他目标文件、库文件以及启动文件等进行链接,从而生成一个可执行文件。在链接的过程中,链接器会对符号进行解析、执行重定位、进行代码优化、确定空间布局,进行装载,并进行动态链接等操作。通过链接器的处理,将所有需要的依赖项打包成一个在特定平台可执行的目标程序,用户可以直接执行这个程序。

gcc -o hello.o -o hello

添加-v 参数,可以查看详细的编译过程:

gcc -v hello.c -o hello
  • 静态链接

静态链接是指在链接程序时,需要使用的每个库函数的一份拷贝被加入到可执行文件中。通过静态链接使用静态库进行链接,生成的程序包含程序运行所需要的全部库,可以直接运行。然而,静态链接生成的程序体积较大。

  • 动态链接

动态链接是指可执行文件只包含文件名,让载入器在运行时能够寻找程序所需的函数库。通过动态链接使用动态链接库进行链接,生成的程序在执行时需要加载所需的动态库才能运行。相比静态链接,动态链接生成的程序体积较小,但是必须依赖所需的动态库,否则无法执行。

GCC 编译方法

本地编译

所谓"本地编译",是指编译源代码的平台和执行源代码编译后程序的平台是同一个平台。这里的平台,可以理解为 CPU 架构+操作系统。比如,在 Intel x86 架构或者 Windows 平台下、使用 Visual C++ 编译生成的可执行文件,在同样的 Intel x86 架构/Windows 10 下运行。

交叉编译

所谓"交叉编译(Cross_Compile)",是指编译源代码的平台和执行源代码编译后程序的平台是两个不同的平台。比如,在 Intel x86 架构/Linux(Ubuntu)平台下、使用交叉编译工具链生成的可执行文件,在 ARM 架构/Linux 下运行。

与传统编译区别

传统的三段式划分是指将编译过程分为前端、优化、后端三个阶段,每个阶段都有专门的工具负责。

编译器

而在 GCC 中,编译过程被分成了预处理、编译、汇编、链接四个阶段 。其中 GCC 的预处理、编译阶段属于三段式划分的前端部分,汇编阶段属于三段式划分的后端部分。

GCC 的链接阶段是三段式划分后端部分的优化阶段合并,但其与端部分的目的一致,都是为了生成可执行文件。

GCC 编译过程的四个阶段与传统的三段式划分的前端、优化、后端三个阶段有一定的重合和对应关系,但 GCC 更为详细和全面地划分了编译过程,使得每个阶段的功能更加明确和独立。

如果您想了解更多AI知识,与AI专业人士交流,请立即访问昇腾社区官方网站https://www.hiascend.com/ 或者深入研读《AI系统:原理与架构》一书,这里汇聚了海量的AI学习资源和实践课程,为您的AI技术成长提供强劲动力。不仅如此,您还有机会投身于全国昇腾AI创新大赛和昇腾AI开发者创享日等盛事,发现AI世界的无限奥秘~

目录
相关文章
|
17小时前
|
人工智能 编译器 C语言
【AI系统】传统编译器发展
编译技术是计算机科学的重要组成部分,作为基础软件的核心,它将高级语言转换为机器码,极大提高了编程效率。从1957年的IBM Fortran开始,编译器经历了多个发展阶段,包括结构化程序设计、面向对象编程、并行计算及AI应用等,形成了如今如GCC、LLVM等成熟的编译体系。未来,随着多语言融合和跨平台需求的增长,编译技术将继续演进,支持更多新兴语言和平台。
13 3
|
17小时前
|
数据采集 DataWorks 搜索推荐
DataWorks产品最佳实践测评:用户画像分析实践
DataWorks产品最佳实践测评:用户画像分析实践
18 3
|
5天前
|
算法 调度 Python
深入理解操作系统中的进程调度算法
在操作系统中,进程调度是核心任务之一,它决定了哪个进程将获得CPU的使用权。本文通过浅显易懂的语言和生动的比喻,带领读者了解进程调度算法的重要性及其工作原理,同时提供代码示例帮助理解。
|
18小时前
|
监控 数据可视化 测试技术
如何通过看板工具简化ASIC设计中的沟通与决策流程,提高团队效率?
本文介绍了如何利用看板工具,特别是板栗看板(Banli Kanban),优化ASIC设计流程。从需求分析、设计开发、验证测试到制造交付及项目回顾,板栗看板通过任务可视化、实时信息同步和精准任务指派,有效提升了项目管理的精准性与灵活性,减少了沟通成本,增强了团队协作,促进了流程优化。
如何通过看板工具简化ASIC设计中的沟通与决策流程,提高团队效率?
|
18小时前
|
人工智能 并行计算 开发者
【AI系统】从 CUDA 对 AI 芯片思考
本文从技术角度探讨英伟达生态,特别是CUDA与SIMT的关系及其对AI芯片DSA架构的影响。通过分析流水编排、SIMT前端、分支预测及交互方式,指出英伟达CUDA的成功在于其硬件设计与软件易用性的结合,为未来AI芯片的设计提供了宝贵的经验和启示。
13 5
|
16小时前
|
搜索推荐 数据挖掘 项目管理
"揭秘:产品经理如何借助软件工具实现工作飞跃"
在互联网行业,产品经理是产品生命周期的核心驱动力。本文介绍了五款提升工作效率的必备软件:Boardmix博思白板、Miro、板栗看板、Figma和Google Analytics,涵盖在线协作、项目管理、设计协作及数据分析等多个方面,助力产品经理高效完成工作。
|
16小时前
|
人工智能 前端开发 编译器
【AI系统】LLVM 架构设计和原理
本文介绍了LLVM的诞生背景及其与GCC的区别,重点阐述了LLVM的架构特点,包括其组件独立性、中间表示(IR)的优势及整体架构。通过Clang+LLVM的实际编译案例,展示了从C代码到可执行文件的全过程,突显了LLVM在编译器领域的创新与优势。
13 3
|
17小时前
|
机器学习/深度学习 人工智能 编译器
【AI系统】AI 芯片的思考
为了应对数据中心算力需求,谷歌自2014年起研发TPU,专为深度学习设计的硬件加速器。TPU加速了谷歌的机器学习任务,尤其在大模型训练和推理方面表现突出。大卫·帕特森教授加入谷歌TPU团队后,分享了TPU发展历程及技术心得,强调了AI模型对内存和算力需求的快速增长、模型结构的快速演变、生产部署中的多租户需求、SRAM与DRAM的权衡、内存优化的重要性、DSA的专业与灵活性、半导体供应链选型、编译器优化及AI应用兼容性等方面的关键挑战与解决方案。
12 2
|
5天前
|
SQL 并行计算 数据挖掘
一份写给数据工程师的 Polars 迁移指南:将 Pandas 速度提升 20 倍代码重构实践
Polars作为现代化的数据处理框架,通过先进的工程实践和算法优化,为数据科学工作者提供了高效的数据处理工具。在从Pandas迁移时,理解这些核心概念和最佳实践将有助于充分发挥Polars的性能优势。
30 4
|
15小时前
|
机器学习/深度学习 人工智能 算法
【AI系统】LLVM 后端代码生成
本文介绍 LLVM 后端的代码生成过程,包括将优化后的 LLVM IR 转换为目标代码的关键步骤,如指令选择、寄存器分配、指令调度等,以及后端如何支持不同硬件平台的代码生成。
12 6