神秘的 Swift 内置模块

前言

本文翻译自 Swift's mysterious Builtin module
翻译的不对的地方还请多多包涵指正,谢谢~

神秘的 Swift 内置模块

... 好吧,自从 swift 开源后也许不那么神秘。但不管怎样,如果你在playground 按住 Command 并单击Int类型,你也许已经注意到类似这种代码:

/// A 64-bit signed integer value
/// type.
public struct Int : SignedIntegerType, Comparable, Equatable {
    public var value: Builtin.Int64
...
}

或者如果你已经阅读过 Swift 的 stdlib 库,那大概率注意到了有很多 Builtin.* 类的函数,诸如:

  • Builtin.Int1
  • Builtin.RawPointer
  • Builtin.NativeObject
  • Builtin.allocRaw(size._builtinWordValue, Builtin.alignof(Memory.self)))

因此,神秘的 Builtin 到底是什么呢?

Clang, Swift Compiler, SIL, IR, LLVM

为理解Builtin真正的作用,让我们快速简要地看看 Objective-CSwift 编译器是如何工作的。

Objective-C

oc.png

(隐藏了很多细节,但用于解释本篇文章完全ok)

Objective-C 代码进入 Clang 前端处理后会产出叫 LLVM 中间表示语言(Intermediate Representation = IR),IR 随后会被送入 LLVM,经过处理后二进制机器码就出来了。

LLVM 中间表示语言是一种类似高级的汇编语言,它是独立于架构的(如i368,ARM 等)。为了给使用 LLVM 的新语言创造编译器,我们只需要实现一个前端,它能够将新语言的代码转换成 LLVM 中间表示语言(IR),并将 IR 传递给 LLVM 生成给任何它所支持平台的汇编或者二进制代码。

Swift

swift.png

Swift 首先生成 Swift 中间表示语言 SIL(Swift Intermediate Representation),它能够被转换成 LLVM IR 中间表示语言,接着 LLVM IRLLVM 编辑器编译。

如你所见 SILLLVM IR 的快速化封装,它被创建出来是有很多原因的,比如:

  1. 保证变量在使用前被初始化;
  2. 检测不可达(未使用)的代码;
  3. 在代码发送给 LLVM 前进行优化;

你可以看这个 Youtube 视频找到更多 SIL 存在的原因及它做的事情。

这里的主要内容就是 LLVM IR。对于像以下简单的 Swift 程序:

let a = 5
let b = 6
let c = a + b

转换成 LLVM IR 后如下:(可通过 swiftc -emit-ir addswift.swift 命令生成)

...
  //^ 将 5 数值存入 a 变量
  store i64 5, i64* getelementptr inbounds (%Si* @_Tv8addswift1aSi, i32 0, i32 0), align 8
  //^ 将 6 数值存入 b 变量
  store i64 6, i64* getelementptr inbounds (%Si* @_Tv8addswift1bSi, i32 0, i32 0), align 8
  //^ 将 a 加载到虚拟寄存器 %5 内
  %5 = load i64* getelementptr inbounds (%Si* @_Tv8addswift1aSi, i32 0, i32 0), align 8
  //^ 将 b 加载到虚拟寄存器 %6 内
  %6 = load i64* getelementptr inbounds (%Si* @_Tv8addswift1bSi, i32 0, i32 0), align 8
  //^ 调用 llvm 有符号可溢出相加方法(返回值是两者之和及一个是否溢出标识)
  %7 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %5, i64 %6)
  //^ 提取第一个值放入 %8
  %8 = extractvalue { i64, i1 } %7, 0 
  //^ 提取第二个值放入 %9
  %9 = extractvalue { i64, i1 } %7, 1
  //^ 如果溢出跳转trap分支(label 11),否则跳到 label10(类似汇编的 goto)
  br i1 %9, label %11, label %10

; <label>:10                                      ; preds = %once_done
  //^ 将结果存入变量 c
  store i64 %8, i64* getelementptr inbounds (%Si* @_Tv8addswift1cSi, i32 0, i32 0), align 8
  ret i32 0
...

在上述代码中,可以通过 //^ 符号找到我对于 LLVM IR 代码注释。

尽管上述代码看起来像一坨垃圾,但你只要知道这两件事:

  1. LLVM 里有个数据类型叫做 i64,它是64位整数;
  2. LLVM 里有个函数叫做 llvm.sadd.with.overflow.i64,它能将两个 i64 整数相加并返回两个值,一个是和,另一个是1bit的溢出标识(如果相加失败);

可以解释 Bulitin

ok,回到 Swift,我们知道 SwiftInt 类型实际上是 Swift struct 类型,而且 + 操作符实际是个全局函数,是作为 Int 对于 lhsrhs 的重载。Int 语句不是语言的一部分,从某种意义上来说被语言直接理解的符号是诸如这 struct, class, if, guard,它们才是语言的一部分。

Int+Swift stdlib 库的一部分,意味着它们也就不是原生构造,那就可以说这样消耗很大 or Swift 很慢?并不是。

这就是 Builtin 发挥作用的地方。BuiltinLLVM IR 类型直接暴露给 stdlib,因而没有运行时查找的消耗,也能够让 Int 作为 struct 来做类似的事情:extension Int { func times(otherInt: Int) -> Int { return self * otherInt } }; 5.times(6)

Swift struct Int 类型只包含了一个类型为 Builtin.Int64 名叫 value 存储属性,因为我们可使用 unsafeBitCast 对它来回转换,而且 stdlib 也提供了将 Builtin.Int64 转换为 Swift struct Intinit 初始化的重载方法。

类似的,UnsafePointer 及相关类是对 Builtin 直接内存访问方法的封装。例如:alloc 函数的定义是这样:

public static func alloc(num: Int) -> UnsafeMutablePointer {
  let size = strideof(Memory.self) * num
  return UnsafeMutablePointer(
    Builtin.allocRaw(size._builtinWordValue, Builtin.alignof(Memory.self)))
}

现在咱知道了 Swift Int 不会引起性能问题,但 + 操作符呢。它还是一个函数,定义如下:

@_transparent
public func + (lhs: Int, rhs: Int) -> Int {
  let (result, error) = Builtin.sadd_with_overflow_Int64(
    lhs._value, rhs._value, true._value)
  // return overflowChecked((Int(result), Bool(error)))
  Builtin.condfail(error)
  return Int(result)
}
  • @_transparent 表示函数是以内联方式被调用;
  • Builtin.sadd_with_overflow_Int64 对应我们之前在 LLVM IR 看到的会返回元组(Builtin.Int64 类型的结果, Builtin.Int1 类型的错误)的 llvm.sadd.with.overflow.i64 方法;
  • 结果通过 Int(result) 方法转换回 Swift struct Int 型,并且返回;

因此,这些都是内联调用的话,意味着将会生成非常好的能生成又快又好的二进制 LLVM IR 代码 _

我可以玩玩 Builtin

因为显而易见的原因,Swift 内的 Builtin 只在 stdlib 库及特殊 Swift 程序内可见。我们可以通过swiftc-parse-stdlib 标识试试 Builtin

例:

import Swift //Import swift stdlib

let result = Builtin.sadd_with_overflow_Int64(5.value, 6.value, true._getBuiltinLogicValue())
print(Int(result.0))

let result2 = Builtin.sadd_with_overflow_Int64(unsafeBitCast(5, Builtin.Int64), unsafeBitCast(6, Builtin.Int64), true._getBuiltinLogicValue())
print(unsafeBitCast(result2.0, Int.self))



swiftc -parse-stdlib add.swift && ./add

翻译完毕~

个人总结

本文主要解释了 Builtin 存在的原因:

  1. 加快编译速度。Swift 很多 struct 值类型,最终内部都封装了 IILV IR 基础类型,不需要过多转换;
  2. 提高运行性能。由于不需要做过多转换,直接使用的 IILV IR 的函数,相当于使用很多类似底层函数在开发,性能更高;

其次,文章给我们对比 Objective-CSwift 语言大致的编译过程。LLVM 后端流程一样:LLVM 通过将 LLVM IR 转换成二进制代码。那么两者语言区别点在于 LLVM IR 代码的生成。 Objective-C 通过 Clang,而 Swift 通过自身的编译器先生成 SIL,再通过 IRGen 生成 LLVM IRSwift 在这个过程可以做很多优化(类型校验,初始化检验,不可达代码优化等)。

小结语

不像 Objective-C 部分代码只能靠猜,Swift 开源给了程序员更多的可探索性,开发信心及趣味性~ 大家一起学起来吧_

(PS:后面 swiftc -parse-stdlib add.swift && ./add 一直报错,知道的老铁可以告知下哟,ths~)

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

推荐阅读更多精彩内容