Swift编译器结构分析
Swift介绍
Swift是一种高性能的语言,拥有整洁现代的语法。swift可以和C、OC的代码和框架无缝衔接,并且swift默认是内存安全的。 Swift的代码仓库包含了Swift编译器和标准库的源码,还有例如用于IDE集成的SourceKit等相关组件。
Swift编译器
概况来说,Swift编译器负责将Swift源码转成高效的可执行机器码。下面我们对Swift编译器的几个主要组件进行说明。
编译器基本知识
构建过程
构建过程分为
-
预处理(pre-process) 用于导入文件和展开宏.纯文本操作,不考虑语言语法含义
-
(狭义的)编译 对预处理器的输出进行编译,生成汇编语言(
assemble language
)。一般汇编语言代码的文件扩展名.s
-
汇编 汇编器转为机器语言,这个过程称为汇编(
assemble
),输出为目标文件(object file),一般扩展名为.o
-
链接 目标文件本身不能使用。将目标文件转换为最终可以使用的形式的过程,称为链接(
link
)。可执行文件默认名为a.out
Swift编译器结构
我们下面分析的Swift编译器,负责的流程是上面构建过程中的第二步:(狭义的)编译。
Swift的编译器主要组件包含以下几部分:
Parsing:语法分析
Swift的语法分析器是一个简单的,对整体通过递归向下的方式进行语法分析的手工编码词法分析器,他是在lib/Parse内实现的。该分析器负责生成不包含语义和类型信息的抽象语法树,简称AST
(Abstract Syntax Tree)。这个阶段生成的AST
也不包含警告和错误的注入。
lib/Parse
我们分析一下lib/Parse
内的文件结构
文件管理:CMakeLists.txt 内部包含一个CMakeLists.txt文件和多个以cpp为后缀名的C++文件。 CMakeLists.txt是CMake
的管理文件,想了解CMake可以在文末查看。CMakeLists.txt内容为
add_swift_library(swiftParse STATIC
xxx.cpp
...
LINK_LIBRARIES
swiftAST
swiftSyntax
)
源码分析文件:ParseXXX.cpp 内部的cpp文件是用于对代码进行语法分析的,例如Lexer.cpp
,ParseDecl.cpp
,ParseGeneric.cpp
,ParseStmt.cpp
等文件,从命名我们就能知道他们的功能是对声明,表达式或者泛型等进行语法分析。 语法分析是将代码逐字分析,通过特定的正则表达式匹配,在词的层面上拆分成多个token,之后生成AST
,想了解具体的知识可以查看之前整理的编译器学习系列,这里不展开细节。
流程管理文件 除了用于对源码进行具体分析的文件外,lib/Parse
内部还包含一些流程控制文件。 通过文件PersistentParserState.cpp
,SyntaxParsingCache.cpp
,SyntaxParsingContext.cpp
的命名和描述我们可以推断他们是用于对语法分析状态进行存储,缓存以及通过编译器不同条件的指令执行或者略过某些步骤的。
语法分析总结
语法分析使用以上的几种文件,对Swift源码进行逐字分析,并且可以通过编译器指令控制分析的流程和细节,词法分析生成的AST
是不包含语义和类型信息的,也不包含警告和错误等信息。
Senmantic analysis:语义分析
语义分析负责接收语法分析生成的AST
,并将其转换成格式正确,进行了全面类型检查的AST
,并且在源码中嵌入警告和错误等信息。这些功能是在lib/Sema内实现的。语义分析包含类型推断,确保可以从进行了类型检查的AST
安全的生成代码。
lib/Sema
我们对lib/Sema
内的结构进行一下分析:
CMakeLists.txt
语义分析的CMakeLists.txt内容如下
if (SWIFT_FORCE_OPTIMIZED_TYPECHECKER)
set(EXTRA_TYPECHECKER_FLAGS "FORCE_BUILD_OPTIMIZED")
endif()
add_swift_library(swiftSema STATIC
xxx.cpp
...
LINK_LIBRARIES
swiftParse
swiftAST
swiftSerialization
${EXTRA_TYPECHECKER_FLAGS}
)
和语法分析不同的是,
-
对条件
SWIFT_FORCE_OPTIMIZED_TYPECHECKER
是否进行强制类型检查优化进行了判断,这些条件可以在终端调试时通过指令进行控制,之后我们会在调试Swift编译器内进行说明。 -
链接库为
AST
库,语法分析库和Serialization
库,以及是否增加额外类型检查的标记
语义分析文件
lib/Sema
内部有大量使用C和C++语言编写的分析类,语义分析因为要进行符合当前语言规则对应的检查,所以相比语法分析,文件量要大很多。
我们进行粗略的归纳,可以划分为:
-
CSxxx.cpp/.h
约束系统的解决文件(solution to constraint system)
-
xxxDiag.cpp/.h
生成诊断的文件,我们在编译过程中的诊断,用于静态代码检查的诊断都是在语义分析阶段,通过这些诊断类去管理的。
-
CodeSyncxxx.cpp/.h
用于对表达式,声明等各种类型进行语义分析的文件。
-
Constraintxxx.cpp/.h
各种约束类
-
Debugxxx.cpp/.h
&Instrumenterxxx.cpp/.h
用于支持调试的类
-
Derivedxxx.cpp/.h
各种协议的隐式派生实现
-
NameBinding.cpp
实现Swift的name binding
-
TypeCheckxxx.cpp/.h
对各种类型进行检查和语义分析,以及检查修正
-
略
语义分析总结
语义分析生成经过了类型检查的AST
,嵌入警告和错误,以及进行类型推断。这一阶段将语法分析生成的不带有语义内容的AST
转换成符合Swift语义规则的AST
,这些推断和类型检查为后续的优化提供了基础。
Clang importer
这个模块用于导入Clang组件Clang modules ,并且将C和Objective-C输出的API对应到正确的Swift API。导入的AST
的结果也会传递给语义分析。lib/ClangImporter内实现的。
lib/ClangImporter
我们分析一下lib/ClangImporter内的结构
CMakeLists.txt
set(SWIFT_GYB_FLAGS
"-DCFDatabaseFile=${SWIFT_SOURCE_DIR}/lib/ClangImporter/CFDatabase.def")
add_gyb_target(generated_sorted_cf_database
SortedCFDatabase.def.gyb)
add_swift_library(swiftClangImporter STATIC
xxx.cpp
LINK_LIBRARIES
swiftAST
swiftParse
)
# This property is only set by calls to clang_tablegen. It will not be set on
# standalone builds, so it can always be safely passed.
get_property(CLANG_TABLEGEN_TARGETS GLOBAL PROPERTY CLANG_TABLEGEN_TARGETS)
add_dependencies(swiftClangImporter
generated_sorted_cf_database
${CLANG_TABLEGEN_TARGETS})
GYB
(Generate Your Boilerplate)指模板生成,用于添加数据库模块。
链接库为swiftAST
和swiftParse
,AST
和语法分析。
工具类
内容还有使用C和C++语言编写的工具类,用于方便的导入Clang实体提供正式的接口。内部的类有ImportEnumInfo.cpp/.h
,ImportName.cpp/.h
,ImportType.cpp/.h
等,通过查看这些类的内容,我们发现包含#include "swift/AST/ASTContext.h"
,#include "clang/AST/ASTContext.h"
等内容。
由此我们可以知道,Swift在使用Objective-C的代码时,通过lib/ClangImporter导入的不是Objective-C的源码,而是OC通过Clang编译器生成的AST
.
Swfit不能使用Objective-C的宏
Swift是不同使用OC的宏的,原因如下:
上面的编译器基本知识说到了,宏的展开是在编译的前一个阶段,预处理阶段进行的。在预处理阶段将宏展开之后,Clang才对代码进行编译。Swift导入的是OC代码通过Clang生成的抽象语法树,OC内的宏是未经处理的代码段,所以Swift不能使用OC的宏。
SIL generation:SIL生成
SIL
(Swift Intermediate Language),Swift的中间语言是一个Swift特定的用于进一步分析和优化Swift代码的高级的中间语言。
SIL
的生成阶段将类型检查的AST
降级为"raw"SIL
。这些内容是在lib/SILGen内实现的。SIL
的设计文档为docs/SIL.rst。
lib/SILGen
分析组件结构:
CMakeLists.txt
add_swift_library(swiftSILGen STATIC
xxx.cpp
...
LINK_LIBRARIES
swiftSIL
)
链接库只有swiftSIL
生成SIL的工具类
其他的SILGenxxx.cpp
是用于生成SIL
的工具类。
SIL详细分析
SIL
是Swift定制的中间语言,针对Swift进行了大量的优化,使得Swift性能得到提升。SIL
也是Swift编译器的精髓所在。这一部分内容我们在SIL详解内进行专项说明。
SIL guaranteed transformations:确保转换
该模块负责对影响程序准确性的数据流进行额外的诊断,例如使用未初始化的变量。转换的结果是生成"正式"(canonical)SIL
。这些内容在lib/Analysis, lib/ARC, lib/LoopTransforms,lib/Transforms内实现。
LLVM IR Generation:生成LLVM的中间语言
该模块将SIL
降级为LLVM IR,LLVM的中间语言,使得LLVM在此基础上可以进行进一步的优化,并且生成机器码。这些内容是在lib/IRGen内实现的。
信息补充
Cmake和Ninja介绍
我们这里介绍一下CMake 和 Ninja,对它们有个基本的认识. CMake
是一个开源,跨平台的用于构建,测试和打包的软件工具.CMake
使用简单的平台和编译器单独配置文件来控制软件的编译流程, 通过生成本地makefiles文件和workspace,之后用于用户选择编译环境.
Ninja
是一个小而快的构建系统.它和其他构建系统的区别主要有两点: 1.它旨在将输入文件通过高级别的构建系统来生成. 2.致力于快速构建,极致的追求速度.Ninja
意在取代在执行增量构建时非常慢的Make
.Make
在处理类似于Google Chrome这种单次就会编译超过30,000个输入文件的大型项目时,速度缓慢会体现的更加明显.Google Chrome是Ninja
的主要用户,Ninja
还支持安卓编译,也支持大部分使用LLVM的项目.