传统编译器系列 - 第2节 开源编译器的发展

79 阅读12分钟

ChatGPT Image 2025年6月27日 20_29_44.png


传统编译器系列 - 第2节 开源编译器的发展

系列课程来源:Ascend & MindSpore 联合出品
内容整理:夏驰和徐策


2.1 开源编译器的发展路径总览

开源编译器的发展,是现代计算技术体系持续演进的产物。它经历了从早期仅支持单一语言,到今天支持多语言、多平台、高可扩展性的编译基础设施体系变迁。

本节主要聚焦两条主线:

  • 传统开源编译器代表:GCC(GNU Compiler Collection)
  • 新一代开源架构代表:LLVM(Low Level Virtual Machine)

2.2 GCC:开源编译器的开端

▍发展历史

  • 1987 年,Richard Stallman 发布 GCC 1.0,仅支持 C 语言。
  • 命名最初为 GNU C Compiler,后发展为 GNU Compiler Collection,支持多语言。
  • 是 GNU 工程的重要组成部分。

▍技术特性

  • 多平台支持(x86, ARM, MIPS 等)
  • 多语言支持(C, C++, Fortran, Ada 等)
  • 高度成熟、稳定可靠,广泛用于类 Unix 系统(如 Linux)

2.3 Apple 的“出走”:GCC 与 Clang 分叉

由于 Apple 和 GCC 在技术路线、许可证 GPL 的限制等方面存在分歧,Apple 选择脱离 GCC 主线,自主维护分支版本,最终发展出基于 LLVM 架构的 Clang 编译器。

图示如下:

FSF GCC Master  ──►  Apple GCC Branch(Pull)
                     └────► 推送改动(Push)
                             └────► Mesh Up 分裂

正如幻灯片中所述:“GCC 是为解决现实问题而生的,它没有时间把一切做得完美。”

这个分叉最终促成了 LLVM 编译器体系的兴起。


2.4 LLVM:模块化开源编译基础设施

LLVM 起初是一个研究项目,如今已成为现代编译技术的主流:

▍技术理念

  • 模块化设计:各阶段组件解耦(前端 Clang,IR 优化模块,后端代码生成器等)
  • 中间表示(IR):基于 SSA 的中间层,支持多种优化 Pass
  • 多语言支持:支持 C/C++/Swift/Rust 等
  • 生态广泛:Xcode、Chrome、Rust、TensorFlow 等均使用 LLVM

2.5 LLVM 相较 GCC 的优势

对比维度GCCLLVM(+Clang)
架构设计单体架构模块化、组件解耦
中间表示GIMPLE + RTL统一的 SSA-based LLVM IR
易用性编译速度较慢更快的编译速度与错误提示友好性
扩展性插件开发困难Pass 插拔式优化器、支持 JIT
生态兼容传统 Linux 系统广泛应用更适合现代 IDE 与多语言混合环境

一、理论理解

开源编译器的发展历程,实质上是计算语言系统生态逐步演进的技术缩影。以 GCC 和 LLVM 为代表的两代开源编译器体系,在编译技术架构、可扩展性、可维护性、现代硬件支持等方面,体现出本质差异:

  • GCC 更偏传统单体式架构,源代码复杂,模块耦合度高,虽然性能稳健但扩展性有限;
  • LLVM 强调模块解耦和中间表示的抽象统一,尤其是 SSA 形式的 LLVM IR,使其更适合进行深层次优化、JIT 编译与多语言集成。

开源编译器的发展,还体现出一个关键趋势:从“语言编译”向“平台感知优化”与“场景驱动调度”演化。尤其在 AI、异构计算、移动端等场景中,传统 IR 优化不再足够,编译器需同时考虑硬件拓扑结构、计算图调度、内存访问模式等多重因素。

因此,“开源”不仅是社区协作模式,更代表了一种编译技术与生态适配能力的系统性重塑

二、大厂实战理解(结合头部技术公司)

● Google:LLVM 作为基础,主导 MLIR 构建统一 AI 编译中间层

Google 在 TensorFlow 和 XLA 项目中选择基于 LLVM 构建新的 MLIR 架构,用以统一不同 AI 语言(Tensor、Graph、ControlFlow)与硬件后端之间的表达能力,其核心理念正是“中间表示抽象 + 多级优化 + 多后端调度”。

现实例证:TPU 编译器 pipeline 是 MLIR → LLVM IR → HLO → XLA → TPU binary

● 字节跳动:大规模服务端编译加速选用 Clang + LLD + 分布式构建

字节跳动技术团队为了应对大规模微服务代码编译效率瓶颈,在内部推行基于 Clang 的高并发构建平台,借助 LLVM 的模块化特性快速替换目标平台(如 arm64、x86_64)后端,提升可维护性和可观测性。

● NVIDIA:在 CUDA 编译栈中引入 LLVM 做 GPU IR 优化

NVIDIA 的 CUDA 编译工具链近年来引入了 LLVM 后端,以增强对 PTX 代码的调度能力,并支持 Triton、OpenACC 等新型模型语言。LLVM 的 Pass 框架成为其图融合、内存重排、Warp 优化等策略实现的基础。

● OpenAI:用 LLVM 做嵌入式编译与代码生成(Codex 背后)

在代码生成模型 Codex 背后,为了对用户输入的代码片段进行安全检查与 IR 分析,OpenAI 使用 LLVM IR 进行结构转换与嵌入式解释器构建,使得 Codex 能支持“片段生成 + 可运行验证”。

● 阿里/华为:自研 AI 编译器 MindSpore/BladeDISC 多基于 LLVM 改造

包括昇腾 MindIR、阿里 TVM/BladeDISC 等国产 AI 编译系统,其底层都或多或少基于 LLVM 的 IR、Pass 架构进行改写与裁剪,强化对张量算子的融合与优化策略。


2.6 开源编译器的未来挑战

  • 如何 更智能地优化异构平台(CPU+GPU+NPU)上的代码
  • 如何 支持 AI 模型图与传统 IR 的统一融合
  • 如何 在保障安全与稳定的同时,快速适配前沿语言(如 Mojo、Carbon)
  • 如何 提升面向模型编译的 DSL 接入能力(如 TVM、Glow、XLA)

面试题 1:请简述 GCC 与 LLVM 在架构设计上的核心差异。

参考答案:
GCC 采用的是传统的单体式架构,其中前端、优化器和后端高度耦合,模块之间边界不清晰,难以单独替换与复用;而 LLVM 是以模块化为设计核心的开源编译框架,前端(如 Clang)、中间优化层(LLVM IR)以及后端(目标码生成)之间接口清晰,支持独立部署与二次开发,并且 LLVM IR 是基于 SSA 的通用中间表示,更适合做跨语言、跨平台的统一编译优化。


面试题 2:LLVM IR 有哪些设计优势,使其适合构建现代编译基础设施?

参考答案:
LLVM IR 的最大优势在于其基于静态单赋值(SSA)形式构建,可以显著降低变量依赖分析的复杂度;其次,LLVM IR 是平台无关的中间层,支持跨平台后端生成(如 x86、ARM、NVPTX);此外,LLVM IR 具备强表达力与低层抽象的平衡,可以用于高级语言编译、中间优化分析以及 AI 模型图表示等多种场景;而且 LLVM IR 与 Pass 框架解耦,可灵活插入、调度、组合多种优化策略。


面试题 3:为什么 Apple 要从 GCC 迁移到 Clang/LLVM?有哪些工程层面的考量?

参考答案:
Apple 迁移的核心原因包括:
1)GCC 的 GPLv3 协议与 Apple 商业闭源模式冲突;
2)GCC 架构封闭,难以集成进现代 IDE(如 Xcode)进行增量编译和诊断增强;
3)Clang 提供更清晰的错误信息、更快的编译速度和更易于 IDE 集成的编译 API;
4)LLVM 模块化架构方便 Apple 为 macOS/iOS 系统构建高度定制的编译优化链条。


面试题 4:LLVM 被广泛用于哪些主流产品和框架中?请列举两个并说明其作用。

参考答案:
1)Google 的 TensorFlow 使用 LLVM/MLIR 作为后端,进行张量图编译与优化,用于支持 TPU/NPU 等硬件加速。
2)Rust 编程语言的默认编译器 rustc 使用 LLVM 后端进行中间优化与目标代码生成,提升性能并支持多平台交叉编译。


面试题 5:当前 AI 编译器体系(如 MindSpore、TensorRT、TVM)是否还依赖传统 LLVM 架构?为什么?

参考答案:
是的,当前主流 AI 编译器体系仍依赖 LLVM 的中间表示(IR)和后端优化体系,原因包括 LLVM 提供了稳定的 Pass 框架、丰富的后端目标平台支持、以及易于扩展的中间层机制;即使如 TensorRT 或 MindIR 有自己的图表示,它们最终也需要将部分算子转换成 LLVM IR 进行指令调度或融合优化,因此 LLVM 是许多 AI 编译器不可或缺的技术基础。

场景题 1:你所在团队基于 LLVM 构建了一个用于深度学习模型部署的编译链,在进行端侧 ARM 平台推理优化时发现,部分图算算子在 Clang 编译阶段生成的机器码指令效率不如 GCC,执行时存在显著瓶颈。你如何分析并解决这个问题?

参考答案:
面对该问题,我首先会在 LLVM 后端开启 -emit-llvm-S 选项,分别导出中间 IR 和汇编代码进行比对,从而定位 Clang 编译生成的指令序列是否存在结构性低效问题,例如是否未启用 SIMD 指令或存在多余 Load/Store 操作;接着我会比对 GCC 在相同编译选项下生成的汇编结构,检查 GCC 是否采用了更激进的优化策略如 loop unrolling 或 auto-vectorization;如果确认 LLVM 在某些特定 pattern 下优化能力不足,我将尝试通过自定义 Pass 或调整 TargetTransformInfo 配置来引导 LLVM 做更贴近硬件的调度;同时我会评估是否引入 clang -mcpu=xxx -mtune=xxx 精确指定架构 micro-variant,确保 LLVM 在代码生成阶段利用到特定 ARM 架构(如 Cortex-A76)的所有特性;最后,为避免未来多平台维护开销,我会记录该 case 并将其纳入 regression 测试集,确保优化策略的可复现性与稳定性。


场景题 2:你在对接一个前端 DSL 编译系统(如 PyTorch JIT)与后端 LLVM 时发现,两者之间的中间表示不兼容,导致 Pass 插入困难且难以统一优化逻辑。如何构建一个中间层设计,兼顾可维护性与通用性?

参考答案:
针对前端 DSL 表达形式(如 TorchScript)的语义结构与后端 LLVM IR 存在映射难度的问题,我会首先进行语义映射抽象梳理,将 DSL 层中涉及的控制流、张量计算、函数作用域等元素归纳为一组标准 IR 节点类型,并设计一个中间转换 IR(Intermediate Dialect),这个 IR 应该兼容前端高阶语义与后端低层语义的接口边界;我会优先参考 MLIR 的 Dialect 系统构建方案,设计如 TensorOp, ControlFlowOp, MemAllocOp 等原语操作,通过转换器将 DSL 的语法树映射为 MLIR 的自定义方言节点,再通过 MLIR-Pass 系列做优化后导出 LLVM IR;这样既可以保证前端语义不丢失,也可以充分复用 LLVM 的成熟 Pass 体系;同时为了保证工程可维护性,我会建立转换图谱文档与自动化验证测试,确保每一种 DSL 节点在转化后的中间层中语义等价,从而提升整条编译链的稳定性与扩展性。


场景题 3:你的编译器系统需要同时支持 x86 和 RISC-V 两种架构,但当前 GCC 后端仅对 x86 做了深度优化,RISC-V 表现不佳。你如何基于现有 GCC 或迁移到 LLVM 以解决这一问题?

参考答案:
当面临 GCC 对 RISC-V 支持不足而导致性能退化的问题时,我会优先评估 GCC 后端针对 RISC-V 架构的维护现状和 Patch 活跃度,如果存在维护滞后或优化欠缺现象,我会考虑迁移到 LLVM 编译体系,因为 LLVM 的 RISC-V 后端支持更活跃,并拥有广泛的社区维护与贡献者;在迁移过程中,我会构建基准测试集,在 x86 和 RISC-V 架构上分别通过 Clang 编译器生成目标码并比对性能数据,确保迁移不引入准确率与性能回退;同时我会针对架构差异调整 LLVM 的 Target Machine 参数,并根据实际部署场景对 Pipeline 中的 Loop Vectorize、LICM、Mem2Reg 等 Pass 顺序进行调优;为了实现体系统一,我也会将两套架构的后端编译控制逻辑抽象为配置文件或编译策略模块,使得业务开发者可以通过修改配置自动选择合适的 Target,使得整个多平台支持更灵活、高效、统一。


场景题 4:你负责的 AI 编译器系统中,前端语言支持多个 DSL(如 PyTorch、ONNX、MindIR),而后端又需要输出不同硬件指令(x86、GPU、昇腾),你如何统一中间表示与优化体系?

参考答案:
面对多 DSL 前端与多后端异构指令生成的复杂性问题,我会将中间表示统一视为核心抽象层,采用 MLIR 的多层次 IR 框架设计,将不同 DSL 映射为多个子方言(Dialect),如 TorchDialect、ONNXDialect、MindDialect,然后在中间层通过共享的 Optimization Dialect 实现通用图融合、常量折叠、算子内联等逻辑,并在后端针对不同硬件注入 Hardware-Specific Lowering Pass,如 GPU Lowering、Ascend Lowering;整个过程我会保持 Pass 之间解耦、可插拔,设计一套调度图(Pass Pipeline Graph),用于描述不同场景下编译策略的组合方式;此外,我会引入 Profile-guided Optimization(PGO)机制,使得优化过程能感知数据分布与运行特征,从而进一步提升跨平台的编译效率与执行性能;最终,我还会结合 CI/CD 构建多后端自动验证系统,确保不同 DSL + 后端组合在任何提交中都能保持功能正确与性能一致。

总结与展望

开源编译器的发展,走过了从 GCC 单核主导到 LLVM 多核繁荣的演变之路。
这不仅推动了计算架构与语言创新,更为 AI 编译器、模型编译器的发展打下坚实基础。

在下节《03 GCC 编译过程和原理》中,我们将深入解析 GCC 的完整编译流程与构建原理。


image.png