Clang - 功能和目标

此页面更详细地描述了 Clang 的功能和目标,并对我们所指的含义进行了更广泛的解释。这些功能包括:

最终用户功能

实用程序和应用程序

内部设计和实现

最终用户功能

快速编译和低内存使用

我们对 clang 的一项主要工作重点是使其快速、轻量级且可扩展。clang 基于库的架构使其可以轻松地对堆栈每一层的成本进行计时和分析,驱动程序还提供许多用于性能分析的选项。许多详细的基准测试可以在网上找到。

编译时间性能很重要,但当将 clang 用作 API 时,内存使用往往更为重要:代码占用的内存越少,你一次可以在内存中容纳的代码就越多(例如,对于整个程序分析工具非常有用)。

除了在批处理模式下与 GCC 正面竞争时效率更高之外,clang 还采用了 基于库的架构,这使得它相对容易适应和使用它构建新工具。这意味着通常可以使用现成的思维和新技术以各种方式改进编译。

富有表现力的诊断

除了快速和功能强大之外,我们还旨在使 Clang 极度用户友好。就命令行编译器而言,这基本上归结为使编译器生成的诊断(错误和警告消息)尽可能有用。我们有几种方法可以做到这一点,但最重要的包括精确地指出程序中的错误,突出显示相关信息以便一目了然,并使措辞尽可能清晰。

以下是一个简单的例子,说明了 Clang 诊断的质量

  $ clang -fsyntax-only t.c
  t.c:7:39: error: invalid operands to binary expression ('int' and 'struct A')
    return y + func(y ? ((SomeA.X + 40) + SomeA) / 42 + SomeA.X : SomeA.X);
                         ~~~~~~~~~~~~~~ ^ ~~~~~

在这里你可以看到,即使没有看到原始源代码,你也可以根据 Clang 错误理解问题所在:因为 Clang 打印了一个插入符号,所以你知道它到底是在抱怨哪个加号。范围信息突出显示了加号的左右两侧,这使得编译器要表达的内容立即变得一目了然,这对于涉及优先级问题和许多其他情况的情况非常有用。

Clang 诊断非常完善,具有许多功能。有关更多信息和示例,请参阅 富有表现力的诊断 页面。

GCC 兼容性

GCC 目前是当今事实上的标准开源编译器,它经常编译大量的代码。GCC 支持大量的扩展和功能(其中许多功能没有文档记录),并且许多代码和头文件依赖于这些功能才能构建。

虽然忽略这些扩展并专注于严格地实现语言标准会很好,但实用主义迫使我们支持最常用的 GCC 扩展。许多用户只是希望他们的代码能够编译,他们并不关心它是否严格符合 C99。

如上所述,所有扩展都明确地识别为扩展,并用扩展诊断标记,这些诊断可以映射到警告、错误或简单地忽略。

实用程序和应用程序

基于库的架构

clang 的一个主要设计理念是使用基于库的架构。在这个设计中,前端的各个部分可以干净地划分为独立的库,然后可以根据不同的需求和用途进行混合使用。此外,基于库的方法鼓励良好的接口,并使新开发人员更容易参与(因为他们只需要了解大局中的小部分)。

"世界需要更好的编译器工具,这些工具作为库构建。这种设计点允许以新的和新颖的方式重复使用这些工具。然而,将这些工具构建为库还不够:它们必须具有干净的 API,尽可能地彼此分离,并且易于修改/扩展。这需要干净的分层,良好的设计,以及保持库独立于任何特定的客户端。"

目前,clang 分为以下库和工具:

例如,如果要构建一个预处理器,你可以使用 Basic 和 Lexer 库。如果你想要一个索引器,你可以使用前两个库并添加 Parser 库以及一些用于索引的操作。如果你想要一个重构、静态分析或源到源编译器工具,那么你可以添加 AST 构建和语义分析库。

有关各种 clang 库的低级实现细节的更多信息,请参阅 clang 内部手册

支持多种客户端

Clang 的设计和构建是为了实现我们如何使用它的许多宏伟计划。推动力量是我们每天使用 C 和 C++,并且由于缺乏为此提供的良好工具而不得不忍受。我们相信,C 和 C++ 工具生态系统受到解析和表示这些语言源代码的难度的极大限制,我们旨在通过 clang 来纠正这个问题。

这个目标的问题在于,不同的客户端有非常不同的需求。例如,考虑代码生成:一个简单的用于代码生成的解析器前端必须分析代码的有效性,并以某种中间形式发出代码,传递给优化器或后端。由于有效性分析和代码生成可以在很大程度上动态完成,因此没有硬性要求前端实际为代码中的所有表达式和语句构建完整的 AST。TCC 和 GCC 是编译器的例子,它们要么不构建真正的 AST(在前一种情况下),要么构建一个简化的 AST(在后一种情况下),因为它们主要关注代码生成。

另一方面,一些客户端(如重构)希望获得有关原始源代码的非常详细的信息,并希望使用完整的 AST 来描述它。重构希望获得有关宏扩展、每个括号表达式 '(((x)))' 与 'x' 的位置、完整位置信息等等的信息。此外,重构希望查看整个程序以确保它正在进行安全的转换。使这种方法高效并使其正确需要大量的工程和算法工作,而这些工作对于简单的静态编译器来说是不必要的。

clang 方法的优点在于它不会限制你的使用方式。特别是,可以使用 clang 预处理器和解析器构建一个极其快速且轻量级的即时代码生成器(类似于 TCC),它根本不构建 AST。作为中间步骤,clang 支持使用当前的 AST 生成和语义分析代码,并让代码生成客户端在代码生成后释放每个函数的 AST。最后,clang 提供了对构建和保留完整 AST 的支持,甚至支持将其写入磁盘。

用干净且简单的 API 设计库允许这些高级策略决策在客户端中确定,而不是在这些库的任何实现中强加“唯一正确的方法”。做到这一点很难,我们并不总是第一次就做到完美,但当我们意识到自己犯了错误时,我们会修复任何问题。

与 IDE 集成

我们相信,集成开发环境 (IDE) 是将开发难题的各个部分汇集在一起的好方法,并且旨在使 clang 在这样的环境中运行良好。IDE 的主要优势是它们通常对整个项目可见,并且是长期运行的进程,而独立的编译器工具通常在项目的每个单独文件上调用,因此范围有限。

这种差异有许多含义,但其中一个重要的差异与效率和缓存有关:在项目中的不同文件之间共享地址空间,意味着可以使用智能缓存和其他技术来显着减少分析/编译时间。

IDE 和批处理编译器之间的另一个区别是,它们通常对前端有非常不同的要求:它们依赖于高性能才能提供“快速”的体验,因此真正需要诸如“增量编译”、“模糊解析”等技术。最后,IDE 通常对代码生成有非常不同的要求,通常需要代码生成前端可以丢弃的信息。Clang 的专门设计和构建是为了捕获这些信息。

使用 LLVM 'Apache 2' 许可证

我们积极地希望 clang(以及整个 LLVM)被用于商业项目,不仅作为独立的编译器,而且作为嵌入在专有应用程序中的库。我们认为该许可证鼓励贡献者获取源代码并使用它,并相信这些个人和组织将回馈他们的工作,如果他们不想永远维护一个分支(这在合并时非常耗时且昂贵)。此外,现在没有人从编译器中赚钱,但许多人需要编译器才能完成更大的目标:每个人合作是有意义的。

有关 LLVM/clang 许可证的更多信息,请参阅 LLVM 许可证说明 以获取更多信息。

内部设计和实现

一个现实世界中,生产质量的编译器

Clang 由经验丰富的编译器开发人员设计和构建,他们越来越感到现有开源编译器存在的问题。Clang 经过精心设计和构建,旨在为新一代 C/C++/Objective C 开发工具提供基础,我们希望它能够达到生产质量。

成为生产质量的编译器意味着很多事情:意味着高性能、稳定且(相对)无错误,并且最终意味着被广泛的人使用和依赖。虽然我们仍处于早期开发阶段,但我们坚信这将成为现实。

一个简单且可修改的代码库

我们的目标是让任何具备编译器基础知识和 C/C++/ObjC 语言工作知识的人都能理解和扩展 clang 源代码库。这在很大程度上取决于我们决定让 AST 尽可能地反映语言的决定:您有友好的 if 语句、for 语句、括号表达式、结构体、联合体等,它们都以简单明了的方式表示。

除了简单设计之外,我们还努力通过良好的注释使源代码库更易于访问,包括在适当的地方引用语言标准,以及设计代码以求简单。除此之外,clang 还提供了一组 AST 转储器、打印器和可视化器,使您可以轻松地将代码放入并查看它的表示方式。

一个用于 C、Objective C、C++ 和 Objective C++ 的统一解析器

Clang 是“C 语言家族前端”,这意味着我们打算支持 C 家族中最受欢迎的成员。我们相信,这种语言类别的正确解析技术是手工构建的递归下降解析器。由于它是纯 C++ 代码,递归下降使新开发人员很容易理解代码,它很容易支持 C/C++ 所需的临时规则和其他奇怪的技巧,并使实现出色的诊断和错误恢复变得直截了当。

我们相信,在单个统一的解析器中实现 C/C++/ObjC,使最终结果比维护独立的 C 和 C++ 解析器更容易维护和演进,而这些解析器必须独立地进行错误修复和维护。

符合 C/C++/ObjC 及其变体

当您开始着手实现一种语言时,您会发现语言的工作方式与大多数人理解的工作方式之间存在很大差距。这种差距是普通程序员与(可怕的?超自然的?)“语言律师”之间的区别,他们了解语言的来龙去脉,并且可以轻松地理解标准。

实际上,符合语言标准意味着我们旨在支持完整的语言,包括黑暗和尘封的角落(如三元组、预处理器奥秘、C99 VLA 等)。如果我们支持超出标准允许范围的扩展,我们会努力在代码中明确指出这一点,并发出有关它的警告(默认情况下禁用,但可以选择映射为警告或错误),允许您在需要时以“严格”模式使用 clang。