Swift编译器过程
Swift编译器
Swift编译器分为前端和后端,LLVM架构下的都是如此(Objective-C编译器的前端是Clang,后端是LLVM),下图是Swift的编译器结构:
image.png各阶段的功能和作用:
1.Parse-语法分析阶段
语法分析器对Swift源码进行逐字分析,生成不包含语义和类型信息的抽象语法树,简称AST(Abstract Syntax Tree)。这个阶段生成的AST也不包含警告和错误的注入。AST的每个节点表示一个语法块的构造。对一个简单的swift源文件分析,
import Foundation
class Bird {
func fly() { }
}
func isFlyHigh(bird: Bird) -> Bool { return false }
class Sparrow: Bird {
override func fly() { }
func add(x: Int, y: Int) -> Int { return x + y }
}
可以使用命令 xcrun swiftc -dump-ast <filename>.swift 导出Swift的语法树
xcrun swiftc -dump-ast Bird.swift
(source_file "Bird.swift"
(import_decl range=[Bird.swift:1:1 - line:1:8] 'Foundation')
(class_decl range=[Bird.swift:3:1 - line:5:1] "Bird" interface type='Bird.Type' access=internal non-resilient
(func_decl range=[Bird.swift:4:3 - line:4:16] "fly()" interface type='(Bird) -> () -> ()' access=internal
(parameter "self")
(parameter_list range=[Bird.swift:4:11 - line:4:12])
(brace_stmt range=[Bird.swift:4:14 - line:4:16]))
(destructor_decl implicit range=[Bird.swift:3:7 - line:3:7] "deinit" interface type='(Bird) -> () -> ()' access=internal
(parameter "self")
(parameter_list)
(brace_stmt implicit range=[Bird.swift:3:7 - line:3:7]))
(constructor_decl implicit range=[Bird.swift:3:7 - line:3:7] "init()" interface type='(Bird.Type) -> () -> Bird' access=internal designated
(parameter "self")
(parameter_list)
(brace_stmt implicit range=[Bird.swift:3:7 - line:3:7]
(return_stmt range=[Bird.swift:3:7 - line:3:7]))))
(func_decl range=[Bird.swift:7:1 - line:7:51] "isFlyHigh(bird:)" interface type='(Bird) -> Bool' access=internal
(parameter_list range=[Bird.swift:7:15 - line:7:26]
(parameter "bird" apiName=bird type='Bird' interface type='Bird'))
(result ...
也可以使用https://swift-ast-explorer.com/工具分析
从上面的输出可以看到一些Swift实现的细节:
(func_decl range=[Bird.swift:4:3 - line:4:16] "fly()" interface type='(Bird) -> () -> ()' access=internal
(parameter "self")
执行类方法调用时,方法默认会传递“self”作为内部参数。
(destructor_decl implicit range=[Bird.swift:3:7 - line:3:7] "deinit" interface type='(Bird) -> () -> ()' access=internal
(parameter "self")
(parameter_list)
(brace_stmt implicit range=[Bird.swift:3:7 - line:3:7]))
类的默认析构函数是编译器填充的,并标记为implicit
(class_decl range=[Bird.swift:9:1 - line:12:1] "Sparrow" interface type='Sparrow.Type' access=internal non-resilient inherits: Bird
(func_decl range=[Bird.swift:10:12 - line:10:25] "fly()" interface type='(Sparrow) -> () -> ()' access=internal override=Bird.(file).Bird.fly()@Bird.swift:4:8
(parameter "self")
(parameter_list range=[Bird.swift:10:20 - line:10:21])
(brace_stmt range=[Bird.swift:10:23 - line:10:25]))
类的继承关系和方法重载。
2.Sema-语义分析分析阶段和SILGen-Swift中级语言生成
语义分析器会进行工作并生成一个通过类型检查的AST,并且在源码中嵌入警告和错误等信息。
Swift中级语言生成(SILGen)阶段将通过语义分析生成的AST转换为Raw SIL,再对Raw SIL进行了一些优化(例如泛型特化,ARC优化等)之后生成了Canonical SIL。SIL是Swift定制的中间语言,针对Swift进行了大量的优化,使得Swift性能得到提升。SIL也是Swift编译器的精髓所在。
使用 xcrun swiftc -emit-silgen <filename>.swift 可生成SIL中间语言文件:
// main
sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
%2 = integer_literal $Builtin.Int32, 0 // user: %3
%3 = struct $Int32 (%2 : $Builtin.Int32) // user: %4
return %3 : $Int32 // id: %4
} // end sil function 'main'
// Bird.fly()
sil hidden [ossa] @$s4BirdAAC3flyyyF : $@convention(method) (@guaranteed Bird) -> () {
// %0 "self" // user: %1
bb0(%0 : @guaranteed $Bird):
debug_value %0 : $Bird, let, name "self", argno 1, implicit // id: %1
%2 = tuple () // user: %3
return %2 : $() // id: %3
} // end sil function '$s4BirdAAC3flyyyF'
大家看到 @s4Test4BirdC3flyyyF中,Bird后面的字母C意味着Bird是一个类。使用swift-demangle也可以将压缩的方法命名进行还原。
可以看到
-
方法是以sil为启始的函数块
-
$@convention(method),代表方法需要上下文,通常为类方法调用
-
类方法调用,第一个参数都是self;
-
对引用参数会携带@owned
-
$@convention(thin)则作用于不需要上下文的函数
3.IRGen-生成LLVM的中间语言阶段
IRGen将SIL编译为LLVM IR,LLVM的中间语言,以便LLVM后端进行优化。
要分析IR低阶中间语言,可以调用xcrun swiftc -emit-ir <filename>.swift 。
@"\01l_entry_point" = private constant { i32, i32 } { i32 trunc (i64 sub (i64 ptrtoint (i32 (i32, i8**)* @main to i64), i64 ptrtoint ({ i32, i32 }* @"\01l_entry_point" to i64)) to i32), i32 0 }, section "__TEXT, __swift5_entry, regular, no_dead_strip", align 4
@"$sBoWV" = external global i8*, align 8
@"$s4BirdAACMm" = hidden global %objc_class { %objc_class* @"OBJC_METACLASS_$__TtCs12_SwiftObject", %objc_class* @"OBJC_METACLASS_$__TtCs12_SwiftObject", %swift.opaque* @_objc_empty_cache, %swift.opaque* null, %swift.opaque* bitcast ({ i32, i32, i32, i32, i8*, i8*, i8*, i8*, i8*, i8*, i8* }* @_METACLASS_DATA__TtC4Bird4Bird to %swift.opaque*) }, align 8
@"OBJC_CLASS_$__TtCs12_SwiftObject" = external global %objc_class, align 8
@_objc_empty_cache = external global %swift.opaque
@"OBJC_METACLASS_$__TtCs12_SwiftObject" = external global %objc_class, align 8
@.str.14._TtC4Bird4Bird = private unnamed_addr constant [15 x i8] c"_TtC4Bird4Bird\00"
@_METACLASS_DATA__TtC4Bird4Bird = internal constant { i32, i32, i32, i32, i8*, i8*, i8*, i8*, i8*, i8*, i8* } { i32 129, i32 40, i32 40, i32 0, i8* null, i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.14._TtC4Bird4Bird, i64 0, i64 0), i8* null, i8* null, i8* null, i8* null, i8* null }, section "__DATA, __objc_const", align 8
这里的IR语言比较难懂,大概可以看出一些全局定义和访问关键词,
后半段一些接近汇编的语言转换:
define i32 @main(i32 %0, i8** %1) #0 {
entry:
%2 = bitcast i8** %1 to i8*
ret i32 0
}
define hidden swiftcc void @"$s4BirdAAC3flyyyF"(%T4BirdAAC* swiftself %0) #0 {
entry:
%self.debug = alloca %T4BirdAAC*, align 8
%1 = bitcast %T4BirdAAC** %self.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
store %T4BirdAAC* %0, %T4BirdAAC** %self.debug, align 8
ret void
}
s4BirdAAC3flyyyF就是我们之前看的Bird的fly方法名,参数为局部参数Bird类实例。
4.LLVM 编译后端处理
前面几个阶段属于Swift编译器,相当于OC中的Clang,属于LLVM编译器架构下的前端,这里的LLVM是编译器架构下的后端,对LLVM IR进一步优化并生成目标文件(.o), 可调用命令:xcrun swiftc -emit-object <filename>.swift
组装程序
编译为目标文件后,要形成可以执行程序,还需要经过Assembler,link和Loader阶段。