Swift’s use of SIL

Swift 是一门静态语言,在 Swift 中声明的方法和属性静态编译期就确定了的,并且Swift具有更灵活的高级特性,协议,泛型,方法重载,值引用等,所以其与OC运行时动态消息派发不同,需要支持静态派发以及动态派发,目前的这些特性Clang并不能完全支持。

因此苹果另外实现了一套 Swift() 作为Swift的编译前端,由于其语言特性,反而有更多的优化空间。

其使用SIL作为前端中间表示层,与IR差异很大,大致体现在:

  • 在编译期完全确定的调用关系会采用静态派发,即在可执行文件中直接指定方法实现的内存地址的指针,在运行时直接通过指针调用
  • 在编译期间无法确定最终合适的操作时,则会执行动态派发。Swift又不完全同于C++的方式,其除了支持继承使用的虚函数表vtable,还加入了支持面向协议编程的witness table
  • 通过dynamic关键字对方法标记,使用OC的运行时机制
  • 由于支持函数重载,也就引出了name mangling(名字修饰)机制,其目的是给同名的重载函数不同的签名
  • ...

1. Clang vs Swift():

Clang

  • Wide abstraction gap between source and LLVM IR
  • IR isn't suitable for source-level analysis
  • CFG lacks fidelity
  • CFG is off the hot path
  • Duplicated effort in CFG and IR lowering
clang.png

Swift

  • Higher-level language
    • Move more of the language into code
    • Protocol-based generics
  • Safe language
    • Uninitialized vars, unreachable code should be compiler errors
    • Bounds and overflow checks
swift.png


2. V-Table

常见的编译型语言的动态派发方式,编译器层使用一个表格结构来存储类型声明中的每一个函数指针。C++中称之为虚函数表VTable,也是其支持多态的基础。

在Swift中,拥有继承关系的Class采用此种方式,即每一个类会维护一个函数表,该表会记录该类中所有的函数指针:

  1. 由父类继承而来的方法执行地址。
  2. 如果子类重写父类方法,表中会保存被重载之后的函数。
  3. 子类新增的函数会加入到表的末尾。

然后在程序运行期间查表执行具体实现。

3. Protocol Witness Table

因为协议不一定具有继承关系,所以其创建了Protocol Witness Table。大致实现为:Swift会为每一个实现了该协议的对象生成一个大小一致的结构体,这个结构体被称为Existenial Container,它内部包含PWT,表中存储着一组函数的执行地址,而表中的每一个条目指向了符合该协议的类型信息。该结构体中还保留了三个字长的valueBuffer用来存储数据成员。

4. SIL

SIL是一种SSA形式的IR,具有高级语义信息,旨在实现Swift编程语言。 SIL适用于以下用例:

  • 一组有保证的高级优化,可为运行时和诊断行为提供可预测的基准。
  • 诊断数据流分析过程可满足Swift语言要求,例如变量和构造函数的确定性初始化,代码可访问性,开关覆盖率。
  • 高级优化过程,包括保留/释放优化,动态方法去虚拟化,闭包内联,将堆分配提升为堆栈分配,将堆分配提升为SSA寄存器,将标量替换为标量(将标量分配拆分为多个较小的标量)和通用函数实例化。
  • 一种稳定的分发格式,可用于分发带有Swift库模块的“易碎”的内联代码或通用代码,并将其优化为客户端二进制文件。

与LLVM IR相比,SIL是一种通常与目标无关的格式表示形式,可用于代码分发,但它也可以像LLVM一样表示特定于目标的概念。

  • 完全代表程序语义
  • 专为代码生成和分析而设计
  • 位于编译器管道的热路径上
  • 修复了源代码和LLVM之间的抽象鸿沟

在较高的层次上,Swift编译器遵循严格的管道架构:

  • Parse模块从Swift源代码构造AST。
  • Sema模块对AST进行类型检查,并使用类型信息对其进行注释。
  • SILGen模块从AST生成raw SIL
  • 在原始SIL上运行一系列“保证的优化Pass”和“诊断Pass”,以执行优化并发出特定于语言的诊断信息。即使在-Onone上,它们也始终运行,并生成规范SIL
  • 常规SIL 优化Pass(可选)在规范的SIL上运行,以提高生成的可执行文件的性能。这些是由优化级别启用和控制的,而不是在-Onone上运行。
  • IRGen将规范SIL降低为LLVM IR。
  • LLVM后端(可选)使用LLVM优化,运行LLVM代码生成器并产生二进制代码。

5. SILGen

SILGen通过遍历经过类型检查的Swift AST来生成“原始SIL”。 SILGen发出的SIL形式具有以下特性:

  • 变量通过加载和存储可变存储器位置来表示,而不是采用严格的SSA形式。 这类似于前端(如Clang)发出的初始“alloca”大量的LLVM IR。 但是,在最普通的情况下,Swift将变量表示为引用计数的“boxes”,可以将其保留,释放并捕获到闭包中。
  • 尚未执行数据流要求,例如最终分配,函数返回,开关覆盖率(TBD)等。
  • transparent功能优化尚未实现。

这些属性通过后续保证的优化和诊断遍历得到解决,这些遍历始终针对原始SIL运行。

6. General Optimization Passes

SIL捕获特定语言的类型信息,从而可以实现在LLVM IR上难以执行的高级优化。

  • Generic Specialization 分析对泛型函数的专门调用,并生成函数的新的指定版本。然后,它将泛型的所有指定用法重写为对指定功能的直接调用。
  • 给定类型的 Witness and VTable Devirtualization 从类的vtable或类型见证表中查找关联的方法,并将间接虚拟调用替换为对映射函数的调用。
  • Performance Inlining
  • Reference Counting Optimizations
  • Memory Promotion/Optimizations
  • High-level domain specific optimizations Swift编译器在基础的Swift容器(例如Array或String)上实现了高级优化。特定于域的优化需要在标准库和优化器之间定义接口。更多细节可以查看:HighLevelSILOptimizations

如下代码为swift实现代码在SIL下的展现形式:

sil_stage canonical 

import Swift 

//定义SIL函数使用的类型。

struct Point { 
  var x:Double 
  var y:Double 
} 

class Button { 
  func onClick()
  func onMouseDown()
  func onMouseUp()
} 

//声明一个Swift函数。主体被SIL忽略。
func taxicabNorm(_ a:Point)-> Double { 
  return a.x + a.y 
} 

//定义SIL函数。
//名称@_T5norms11taxicabNormfT1aV5norms5Point_Sd是 taxicabNorm Swift函数的错误名称。
sil @ _T5norms11taxicabNormfT1aV5norms5Point_Sd:$(Point)-> Double { 
bb0(%0:$ Point):
  // func Swift。+(Double,Double)-> Double 
  %1 = function_ref @ _Tsoi1pfTSdSd_Sd 
  %2 = struct_extract%0:$ Point,#Point.x 
  %3 = struct_extract%0:$ Point,#Point.y 
  %4 = apply%1(%2,%3):$(Double,Double)-> Double 
  return%4:Double 
} 

//定义SIL vtable。匹配动态分派的方法
//标识与其已知静态类类型的实现相匹配。
sil_vtable Button { 
  #Button.onClick!1:@ _TC5norms6Button7onClickfS0_FT_T_ 
  #Button.onMouseDown!1:@ _TC5norms6Button11onMouseDownfS0_FT_T_ 
  #Button.onMouseUp!1:@ _TC5norms6Button9onMouseUpfS0_FT_T_ 
}


7. Swift’s use of SIL 流程

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