什么是 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()