什么是 SIL

Bad:

IR(Intermediate Representation) 和源码抽象差异

IR 不合适源码级别分析

CFG(Control Flow Graph) 不够精准

CFG 不是在 hot path 上

CFG 和 IR 底层有许多重复性工作

Swift 是一门现代安全的语言

需要更多编译优化

支持更高级的特性(基于协议的范型等)

产生编译错误(检测未初始化变量等)

内存安全(边界和溢出检测等)

SIL 怎么看

保留程序语义

生成和分析代码而设计

在编译器的 pipeline 的 hot path 上

抽象程度在源码和 IR 之间

SIL 是 SSA(static single-assignment) 的 IR,变量不会重复赋值。

// 源码

y = 1

y = 2

x = y

// SSA 的 IR

y1 := 1

y2 := 2

x1 := y2

生成规范化 SIL 文件

$ swiftc xxx.swift -emit-sil | xcrun swift-demangle > xxx.sil

FYI:

如果代码 import UIKit 之类的,可以带上 -target x86_64-apple-ios14.0-simulator -sdk $(xcrun --show-sdk-path --sdk iphonesimulator)

如果需要还原 符号名字,可以带上 | xcrun swift-demangle > xxx.sil

如果需要原始 SIL

$ swiftc -emit-silgen xxx.swift -o xxx.sil

如果需要不同的优化等级,可以带上 -Onone 之类的优化等级

swiftc -h 获得更多帮助

基本语法

%0、%1 等:寄存器,相当于块中的局部变量

bb0、bb1 等:代码块,由指令组成,结束时从函数返回或者跳转其他代码块

$String:SIL 里的 String 类型

$*String:SIL 里的 String 类型的值地址

sil_global:全局变量

函数(生成时会贴心地备注是哪个函数生成的):

// AStruct.structFunc()

sil hidden @test.AStruct.structFunc() -> () : $@convention(method) (AStruct) -> () {

// ...

}

apply:调用函数,并传入参数

function_ref:直接函数引用调用

class_method:通过函数表来查找实现调用

sil_vtable:类的函数表

thin:静态的

thick:动态的,运行时的

cond_br:类似于三目运算符,判断寄存器上值进行代码块跳转

每行语句右边的注释:

bb0:

%0 = integer_literal $Builtin.Int64, 999 // user: %1 (id: %0, 被 id: %1 的语句使用)

%1 = struct $Int (%0 : $Builtin.Int64) // user: %2 (id: %1, 被 id: %2 的语句使用)

return %1 : $Int // id: %2 (我就是 id: %2)

更多的语法,可以查询 官方文档。

Builtin

Builtin 将 LLVM IR 的类型和方法直接暴露给 Swift 标准库,使用时没有额外的运行时负担。

// Int.init(_builtinIntegerLiteral:)

sil public_external [transparent] [serialized] @Swift.Int.init(_builtinIntegerLiteral: Builtin.IntLiteral) -> Swift.Int : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int {

// %0 // user: %2

bb0(%0 : $Builtin.IntLiteral, %1 : $@thin Int.Type):

%2 = builtin "s_to_s_checked_trunc_IntLiteral_Int64"(%0 : $Builtin.IntLiteral) : $(Builtin.Int64, Builtin.Int1) // user: %3

%3 = tuple_extract %2 : $(Builtin.Int64, Builtin.Int1), 0 // user: %4

%4 = struct $Int (%3 : $Builtin.Int64) // user: %5

return %4 : $Int // id: %5

} // end sil function 'Swift.Int.init(_builtinIntegerLiteral: Builtin.IntLiteral) -> Swift.Int'

SIL 能做什么

看编译器(不同级别)的优化(比如无用代码裁剪,不同写法对性能影响,target-action 为何需要 @objc)

了解函数派发方式(final/static/dynamic 等对函数的影响,KVO)

编写 SIL 层的 Pass 进行 Swift 代码插桩

解决(看起来像 Bug 的)奇怪面试题

protocol Logger {

func log1() -> String

}

extension Logger {

func log1() -> String {

return "Logger1"

}

func log2() -> String {

return "Logger2"

}

}

struct MyLogger: Logger {

func log1() -> String {

return "MyLogger1"

}

func log2() -> String {

return "MyLogger2"

}

}

let p1: Logger = MyLogger()

/// 下面两行返回什么?把 p1 的类型指定为 MyLogger 又会发生什么

p1.log1()

p1.log2()

引用

Apple SIL 官方文档

Chris Lattener 的介绍 PPT

Swift’s mysterious Builtin module