Clang:LLVM 的 C 语言家族前端
Clang 项目为LLVM 项目的 C 语言家族(C、C++、Objective C/C++、OpenCL、CUDA 和 RenderScript)中,提供了语言前端和工具基础结构。提供了 GCC 兼容的编译器驱动程序 ( clang ) 和 MSVC 兼容的编译器驱动程序 ( clang-cl.exe )。可以立即获取并构建源代码。
特点和目标
该项目的一些目标包括:
最终用户特点:
• 快速编译和低内存使用
• 表情诊断(示例)
• GCC兼容性
实用程序和应用程序:
• 基于模块化库的架构
• 支持多样化的客户端(重构、静态分析、代码生成等)
• 允许与 IDE 紧密集成
• 使用 LLVM ‘Apache 2’ 许可证
内部设计和实现:
• 一个真实的、产品可靠的编译器
• 一个简单且可破解的代码库
• 用于 C、Objective C、C++ 和 Objective C++ 的单个统一解析器
• 与 C/C++/ObjC 及其变体的一致性
当然,这只是 Clang 的目标和特性的粗略概述。要真正了解全部内容,请参阅功能部分,对每个部分进行了分解,并进行了更详细的解释。
为什么?
新前端的开发是出于对编译器的需求,该编译器允许更好的诊断、与 IDE 的更好集成、与商业产品兼容的许可证,以及易于开发和维护的灵活编译器。所有这些都是启动开发可以满足这些需求的新前端的动机。
当前状态
当面向 X86-32、X86-64 和 ARM 时,Clang 被认为是产品可靠的 C、Objective-C、C++ 和 Objective-C++ 编译器(其它目标可能有警告,但通常很容易修复)。例如,Clang 在产品中用于构建 Chrome 或 Firefox 等性能关键软件。
如果正在寻找源分析或源到源转换工具,Clang 可能是绝佳解决方案。Clang 支持 C++11、C++14 和 C++17,请查看C++ 状态页面了解更多信息。
获取并参与其中!
首先获取代码、构建,并使用。可以做的各种事情,并将第一手获得“Clang 体验”:希望 “产生共鸣”。
完成此操作后,请考虑加入 Clang 社区。Clang 开发人员包括许多具有各种背景的志愿者,贡献者。如果有兴趣关注 Clang 的开发,注册邮件列表是了解该项目如何运作的好方法。
Clang - 功能和目标
详细地描述了Clang的特性和目标,提供了更广泛的解释。这些功能是:
最终用户特点:
• 快速编译和低内存使用
• 表达诊断
• GCC兼容性
实用程序和应用程序:
• 基于库的架构
• 支持多样化的客户
• 与 IDE 集成
• 使用 LLVM ‘BSD’ 许可证
内部设计与实现:
• 一个真实的、产品可靠的编译器
• 一个简单且可破解的代码库
• 用于 C、Objective C、C++ 和 Objective C++ 的单个统一解析器
• 与 C/C++/ObjC 及其变体的一致性
最终用户功能
快速编译和低内存使用
在 clang 上工作的一个主要重点是使其快速、轻便和可扩展。clang 基于库的架构,可以直接计算堆栈每一层的时间和成本,并且驱动程序有许多用于性能分析的选项。许多详细的指标参数可以在网上找到。
编译时性能很重要,当使用 clang 作为 API 时,通常内存使用更是如此:代码占用的内存越少,一次可以放入内存的代码就越多(例如,对于整个程序分析工具很有用) 。
除了在批处理模式下与 GCC 正面交锋时高效之外,clang 还使用基于库的架构构建,可以相对容易地适应,并使用构建新工具。这意味着通常可以应用开箱即用的思维和新技术以各种方式改进编译。
表情诊断
除了快速和实用之外,目标是使 Clang 非常用户友好。就命令行编译器而言,这基本上归结为使编译器生成的诊断(错误和警告消息)尽可能有用。有几种方法可以做到这一点,但最重要的是准确指出程序中的问题,突出显示相关信息以便一目了然,并使描述尽可能清晰。
这是一个简单的例子,说明了 Clang 诊断的可靠:
$ clang -fsyntax-only tc
tc:7:39: 错误:二进制表达式的无效操作数(‘int’ 和 ‘struct A’)
返回 y + func(y ? ((SomeA.X + 40) + SomeA) / 42 + SomeA.X : SomeA.X);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
可以看到,甚至不需要查看原始源代码,即可了解基于 Clang 错误的错误:因为 Clang 打印了一个插入符号,确切地知道它在抱怨哪个加号。范围信息突出显示了加号的左右两侧,这使得编译器正在谈论的内容一目了然,这对于涉及优先级问题和许多其它情况的情况非常有用。
Clang 诊断程序非常精致并且具有许多功能。有关更多信息和示例,请参阅“表情诊断”页面。
GCC兼容性
GCC 目前是当今事实上的标准开源编译器,经常编译大量代码。GCC 支持大量的扩展和特性(其中许多是未记录的),并且许多代码和头文件依赖于这些特性来构建。
虽然能够忽略这些扩展并专注于实现语言标准会很好,但实用主义迫使支持最常用的 GCC 扩展。许多用户只希望代码能够编译,不在乎争论是否是迂腐的 C99。
如上所述,所有扩展都被明确识别并标有扩展诊断,可以映射到警告、错误或只是忽略。
实用程序和应用程序
基于库的架构
clang 的一个主要设计理念是使用基于库的架构。在这种设计中,前端的各个部分可以清晰地划分为单独的库,然后可以根据不同的需求和用途进行混合。此外,基于库的方法支持良好的接口,并使新开发人员更容易参与(因为只需要了解大局的一小部分)。
“世界需要更好的编译器工具,即作为库构建的工具。这个设计点允许以新的和新颖的方式重用这些工具。然而,将工具构建为库是不够的:必须具有可靠的 API,尽可能解耦尽可能相互隔离,并且易于修改/扩展。这需要干净的分层、体面的设计,并保持库独立于任何特定客户端。”
目前,clang 分为以下库和工具:
• libsupport - 来自 LLVM 的基本支持库。
• libsystem - 来自 LLVM 的系统抽象库。
• libbasic - 诊断、SourceLocations、SourceBuffer 抽象、输入源文件的文件系统缓存。
• libast - 提供类来表示 C AST、C 类型系统、内置函数以及用于分析和操作 AST 的各种帮助程序(访问者、漂亮的打印机等)。
• liblex -词法分析和预处理、标识符哈希表、pragma 处理、标记和宏扩展。
• libparse - 解析。该库调用客户端提供的粗粒度“操作”(例如 libsema 构建 AST),但对 AST 或其它客户端特定的数据结构一无所知。
• libsema - 语义分析。这提供了一组解析器操作来为程序构建标准化的 AST。
• libcodegen - 将 AST 降低到 LLVM IR 以进行优化和代码生成。
• librewrite - 编辑文本缓冲区(对于代码重写转换很重要,例如重构)。
• libanalysis - 静态分析支持。
• clang - 驱动程序,各级库的客户端。
作为这个基于库的设计的强大功能的一个例子…如果想构建一个预处理器,可以使用 Basic 和 Lexer 库。如果想要一个索引器,可以使用前两个并添加解析器库和一些索引操作。如果需要重构、静态分析或源到源编译器工具,则可以添加 AST 构建和语义分析器库。
有关各种 clang 库的低级实现细节的更多信息,请参阅 clang 内部手册。
支持不同的客户
Clang 的设计和构建,如何使用宏伟计划。驱动力是每天都使用 C 和 C++ 的事实,并且由于缺乏可用的好工具而不得不痛苦。认为 C 和 C++ 工具生态系统受到了解析和表示这些语言源代码的难度的极大限制,目标是在 clang 中纠正这个问题。
这个目标的问题在于不同的客户有非常不同的要求。考虑代码生成,例如:解析代码生成的简单前端,必须分析代码的有效性并以某种中间形式发出代码以传递给优化器或后端。由于有效性分析和代码生成在很大程度上可以即时完成,因此前端实际上没有为代码中的所有表达式和语句构建完整的 AST 的硬性要求。TCC 和 GCC 是编译器的例子,要么不构建真正的 AST(在前一种情况下),要么构建一个精简和简化的 AST(在后一种情况下),因为主要关注代码生成。
另一方面,一些客户端(如重构)想要关于原始源代码的非常详细的信息,并想要一个完整的 AST 来描述。重构想要获得有关宏扩展的信息、每个括号表达式 ‘(((x)))’ 与 ‘x’ 的位置、完整位置信息等等。此外,重构希望查看 整个程序以确保它正在进行安全的转换。使其高效并正确地实现这一点需要大量的工程和算法工作,而这些工作对于简单的静态编译器来说是不必要的。
clang 方法的美妙之处在于它不限制如何使用它。特别是,可以使用 clang 预处理器和解析器来构建一个完全不构建 AST 的极其快速和轻量级的动态代码生成器(类似于 TCC)。作为中间步骤,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++所需的ad-hoc规则和其他奇怪的hacks,并且可以直接实现出色的诊断和错误恢复。
相信,在一个统一的解析器中实现 C/C++/ObjC 比维护一个单独的 C 和 C++ 解析器更容易维护和发展,因为 C 和 C++ 解析器必须相互独立地修复和维护。
与 C/C++/ObjC 及其变体的一致性
当开始着手实现一种语言时,会发现该语言的工作方式与大多数人对其工作的理解方式之间存在巨大差距。这个差距是普通程序员和(可怕的?超自然的?)“语言律师”之间的区别,知道语言的来龙去脉,可以轻松地理解标准。
在实践中,与语言一致意味着目标是支持完整的语言,包括黑暗和尘土飞扬的角落(如三合字母、预处理器奥秘、C99 VLA 等)。在支持超出标准官方允许的扩展的地方,努力在代码中明确地调用它并发出有关它的警告(默认情况下禁用,但可以选择映射到警告或错误),允许如果愿意,可以在“严格”模式下使用 clang。
参考链接:
https://clang.llvm.org/features.html