iOS专题

Swift编译器过程

2023-08-15  本文已影响0人  zzzworm

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阶段。

上一篇下一篇

猜你喜欢

热点阅读