支持库¶
摘要¶
本文档详细介绍了 LLVM 的支持库,其源代码位于 lib/Support
和 include/llvm/Support
中。该库的目的是屏蔽 LLVM 因操作系统差异而需要从操作系统获取的少量服务。LLVM 的大部分代码都是使用标准 C++ 的可移植性特性编写的。然而,在少数领域,需要依赖于系统的功能,而支持库是这些系统调用的包装器。
通过集中化 LLVM 对操作系统接口的使用,我们使得 LLVM 工具链和运行时库能够更容易地移植到新平台,因为(理论上)只需要移植 lib/Support
。该库还简化了 LLVM 其余部分的代码,避免了 #ifdef 的使用和针对特定操作系统的特殊情况。这些用法被替换为对 include/llvm/Support
中提供的接口的简单调用。
请注意,支持库并非旨在成为一个完整的操作系统包装器(例如自适应通信环境 (ACE) 或 Apache 可移植运行时 (APR)),而仅提供支持 LLVM 所必需的功能。
支持库最初被称为系统库,由 Reid Spencer 编写,他基于源自可扩展编程系统 (XPS) 的类似工作制定了设计。许多人为此付出了努力;特别是 Jeff Cohen 和 Henrik Bach 在 Win32 移植方面。
保持 LLVM 的可移植性¶
为了保持 LLVM 的可移植性,LLVM 开发人员应遵守一组与支持库相关的可移植性规则。遵守这些规则应有助于支持库实现其目标,即屏蔽 LLVM 因操作系统接口差异而造成的影响,并高效地完成此操作。以下部分定义了实现此目标所需的规则。
不要包含系统头文件¶
除非在 lib/Support
中,否则任何 LLVM 源代码都不应直接 #include
系统头文件。在开发 lib/Support
时,已注意从 LLVM 中删除所有此类 #includes
。具体来说,这意味着 LLVM 源代码禁止在 lib/Support
实现之外包含诸如 “unistd.h
”、“windows.h
”、“stdio.h
” 和 “string.h
” 之类的头文件。
要获取系统相关的功能,应使用 include/llvm/Support
中现有的系统接口。如果不存在合适的接口,则应将其添加到 include/llvm/Support
中,并在 lib/Support
中为所有受支持的平台实现。
不要暴露系统头文件¶
支持库必须屏蔽 LLVM 所有 系统头文件的影响。要获取系统级功能,LLVM 源代码必须 #include "llvm/Support/Thing.h"
,仅此而已。这意味着 Thing.h
不能暴露任何系统头文件。这可以保护 LLVM 免于意外使用系统特定的功能,并且仅允许通过 lib/Support
接口使用。
使用标准 C 头文件¶
标准 C 头文件(以 “c” 开头的头文件)可以通过 lib/Support
接口暴露。这些头文件及其声明的内容被认为是平台无关的。LLVM 源文件可以直接包含它们,也可以通过 lib/Support
接口包含它们。
使用标准 C++ 头文件¶
来自标准 C++ 库和标准模板库的 标准 C++ 头文件可以通过 lib/Support
接口暴露。这些头文件及其声明的内容被认为是平台无关的。LLVM 源文件可以直接包含它们,也可以通过 lib/Support
接口包含它们。
高级接口¶
在 lib/Support
接口中指定的入口点必须旨在完成 LLVM 所需的一些合理的高级任务。我们不希望简单地包装每个操作系统调用。最好包装 LLVM 总是结合使用的多个操作系统调用。
例如,考虑执行程序、等待其完成并返回其结果代码所需的操作。在 Unix 上,这涉及以下操作系统调用:getenv
、fork
、execve
和 wait
。lib/Support
应该提供的正确做法是提供一个函数,例如 ExecuteProgramAndWait
,它完全实现该功能。我们不希望对涉及的操作系统调用进行包装。
操作系统调用和支持库的接口之间 不 能存在一一对应的关系。任何此类接口函数都将是可疑的。
没有未使用的功能¶
在 lib/Support
接口中指定的任何功能都不能是 LLVM 实际未使用的。我们在这里不是编写通用的操作系统包装器,而只是编写足够满足 LLVM 需求的包装器。而且,LLVM 不需要太多。此设计目标旨在保持 lib/Support
接口的小巧和易于理解,这应促进其实际使用和采用。
没有重复的实现¶
给定平台的功能实现必须只编写一次。这意味着,如果多个操作系统可以共享相同的实现,则必须可以将一个功能的实现应用于多个操作系统。此规则适用于给定操作系统类别(例如 Unix、Win32)支持的操作系统集。
没有虚方法¶
LLVM 可以非常频繁地调用支持库接口。为了使这些调用尽可能高效,我们不鼓励使用虚方法。对于实现差异,没有必要使用继承,它只会增加复杂性。#include
机制工作良好。
没有暴露的函数¶
系统库定义的任何函数(即不是由 lib/Support
定义的函数)都不得通过 lib/Support
接口暴露,即使该函数的头文件未暴露也是如此。这可以防止意外使用系统特定功能。
例如,stat
系统调用因其提供的数据存在差异而臭名昭著。lib/Support
不得声明 stat
,也不得允许声明它。相反,它应该提供自己的接口来发现有关文件和目录的信息。这些接口可以使用 stat
来实现,但这严格来说是一个实现细节。支持库提供的接口必须在所有平台上实现(即使是那些没有 stat
的平台)。
没有暴露的数据¶
系统库定义的任何数据(即不是由 lib/Support
定义的数据)都不得通过 lib/Support
接口暴露,即使该函数的头文件未暴露也是如此。与函数一样,这可以防止意外使用可能在并非所有平台上都存在的数据。
最小化软错误¶
操作系统接口通常会为每个可能出错的小问题提供错误结果。在几乎所有情况下,您可以将这些错误结果分为两组:正常/良好/软错误和异常/不良/硬错误。也就是说,有些错误只是诸如“文件未找到”、“权限不足”等信息,而另一些错误则更严重,例如“空间不足”、“磁盘扇区损坏”或“系统调用中断”。我们将第一组称为“软”错误,将第二组称为“硬”错误。
lib/Support
必须始终尝试最小化软错误。这是一个设计要求,因为最小化软错误可能会影响接口的粒度和性质。一般来说,如果您发现自己想要抛出软错误,则必须审查接口的粒度,因为您很可能试图实现一些级别过低的东西。经验法则是提供 不会 失败的接口函数,除非遇到硬错误。
举一个简单的例子,假设我们想添加一个 “OpenFileForWriting
” 函数。对于许多操作系统,如果文件不存在,尝试打开文件会产生错误。但是,lib/Support
不应简单地抛出该错误(如果发生),因为它是一个软错误。问题在于接口函数 OpenFileForWriting
的级别太低。它应该是 OpenOrCreateFileForWriting
。在软错误“不存在”的情况下,此函数将仅创建它,然后打开它以进行写入。
此设计原则需要在 lib/Support
中保持,因为它避免了软错误处理在 LLVM 其余部分中的传播。硬错误通常只会导致 LLVM 工具终止,因此不要害怕抛出它们。
经验法则
不要抛出软错误,只抛出硬错误。
如果您想抛出软错误,请重新考虑接口。
在内部处理最常见的正常/良好/软错误情况,以便 LLVM 的其余部分不必处理。
没有 throw 规范¶
任何 lib/Support
接口函数都不得在其上声明 C++ throw()
规范。此要求确保编译器不会将额外的异常处理代码插入到接口函数中。这是一个性能考虑:lib/Support
函数位于许多调用链的底部,因此可以频繁调用。我们需要它们尽可能高效。但是,系统库中的任何例程都不应实际抛出异常。
代码组织¶
支持库接口的实现按其操作系统的一般类别进行分隔。目前仅定义了 Unix 和 Win32 类,但可以为其他操作系统分类添加更多类。为了区分要编译的实现,lib/Support
中的代码使用 LLVM_ON_UNIX
和 _WIN32
#defines
。lib/Support
中的每个源文件在实现通用(与操作系统无关)功能后,都需要使用一组 #if defined(LLVM_ON_XYZ)
指令包含正确的实现。例如,如果我们有 lib/Support/Path.cpp
,我们希望在该文件中看到
#if defined(LLVM_ON_UNIX)
#include "Unix/Path.inc"
#endif
#if defined(_WIN32)
#include "Windows/Path.inc"
#endif
lib/Support/Unix/Path.inc
中的实现应处理所有 Unix 变体。lib/Support/Windows/Path.inc
中的实现应处理所有 Windows 变体。这样做是为了快速包含将提供实现的基本操作系统类。给定平台的具体细节仍必须通过使用 #ifdef
来确定。
一致的语义¶
lib/Support
接口的实现可能因平台而异。只要接口函数的最终结果相同,就没问题。例如,在所有操作系统上,创建目录的函数都非常简单。另一方面,System V IPC 甚至在所有平台上都不受支持。与其“支持” System V IPC,不如 lib/Support
应该为进程间通信的基本概念提供一个接口。如果 System V IPC 可用,或者命名管道,或者任何可以为给定操作系统有效完成工作的工具,则实现可能会使用 System V IPC。在所有情况下,接口和实现都必须在语义上保持一致。