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'

大家看到 @s4BirdAAC3flyyyF 这样的符号肯定很懵逼,实际上这个是名称修饰,用于将实体的附加信息压缩为单个字符串。是由类型(类/结构体/枚举)、模块、上下文等编码后形成的。例如,在@s4Test4BirdC3flyyyF中,Bird后面的字母C意味着Bird是一个类。使用swift-demangle也可以将压缩的方法命名进行还原。

可以看到

  1. 方法是以sil为启始的函数块

  2. $@convention(method),代表方法需要上下文,通常为类方法调用

  3. 类方法调用,第一个参数都是self;

  4. 对引用参数会携带@owned

  5. $@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阶段。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容