美文网首页iOS专题
Swift编译器过程

Swift编译器过程

作者: zzzworm | 来源:发表于2023-08-15 16:37 被阅读0次

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

    相关文章

      网友评论

        本文标题:Swift编译器过程

        本文链接:https://www.haomeiwen.com/subject/obvhmdtx.html