slidenumber: true
- thin: ただの関数ポインタ
- thick: 関数ポインタ + コンテキストポインタ
- block: Objective-Cのブロック。ObjectiveCにおけるオブジェクトポインタ
生成コード
func main1() {
let a = { (x: Int) -> Int in
return x * 2
}
}
生成コード
define hidden swiftcc void @"$s1d5main1yyF"() #0 {
entry:
%a.debug = alloca %swift.function, align 8
%0 = bitcast %swift.function* %a.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 16, i1 false)
%1 = bitcast %swift.function* %a.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %1)
%a.debug.fn = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 0
store i8* bitcast (i64 (i64)* @"$s1d5main1yyFS2icfU_" to i8*), i8** %a.debug.fn, align 8
%a.debug.data = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 1
store %swift.refcounted* null, %swift.refcounted** %a.debug.data, align 8
ret void
}
クロージャ本体関数の取得
[.code-highlight: 9]
define hidden swiftcc void @"$s1d5main1yyF"() #0 {
entry:
%a.debug = alloca %swift.function, align 8
%0 = bitcast %swift.function* %a.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 16, i1 false)
%1 = bitcast %swift.function* %a.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %1)
%a.debug.fn = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 0
store i8* bitcast (i64 (i64)* @"$s1d5main1yyFS2icfU_" to i8*), i8** %a.debug.fn, align 8
%a.debug.data = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 1
store %swift.refcounted* null, %swift.refcounted** %a.debug.data, align 8
ret void
}
クロージャ本体関数
define internal swiftcc i64 @"$s1d5main1yyFS2icfU_"(i64) #0 {
entry:
%x.debug = alloca i64, align 8
%1 = bitcast i64* %x.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
store i64 %0, i64* %x.debug, align 8
%2 = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 %0, i64 2)
%3 = extractvalue { i64, i1 } %2, 0
%4 = extractvalue { i64, i1 } %2, 1
br i1 %4, label %6, label %5
; <label>:5: ; preds = %entry
ret i64 %3
; <label>:6: ; preds = %entry
call void @llvm.trap()
unreachable
}
そのままのシグネチャ
define internal swiftcc
i64 @"$s1d5main1yyFS2icfU_"(i64)
Swiftで書くなら
(Int) -> Int
thick関数はキャプチャがあると生成される。 キャプチャしたメモリ空間を関数と一緒に取り回す。
生成コード
func main2() {
var t = 3
let a = { (x: Int) -> Int in
t += x
return t
}
}
生成コード
define hidden swiftcc void @"$s1d5main2yyF"() #0 {
entry:
%t = alloca %TSi, align 8
%0 = bitcast %TSi* %t to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
%a.debug = alloca %swift.function, align 8
%1 = bitcast %swift.function* %a.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 16, i1 false)
%2 = bitcast %TSi* %t to i8*
call void @llvm.lifetime.start.p0i8(i64 8, i8* %2)
%t._value = getelementptr inbounds %TSi, %TSi* %t, i32 0, i32 0
store i64 3, i64* %t._value, align 8
%3 = call noalias %swift.refcounted* @swift_allocObject(
%swift.type* getelementptr inbounds (
%swift.full_boxmetadata, %swift.full_boxmetadata* @metadata, i32 0, i32 2),
i64 24, i64 7) #3
%4 = bitcast %swift.refcounted* %3 to <{ %swift.refcounted, %TSi* }>*
%5 = getelementptr inbounds <{ %swift.refcounted, %TSi* }>,
<{ %swift.refcounted, %TSi* }>* %4, i32 0, i32 1
store %TSi* %t, %TSi** %5, align 8
%6 = bitcast %swift.function* %a.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %6)
%a.debug.fn = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 0
store i8* bitcast (i64 (i64, %swift.refcounted*)* @"$s1d5main2yyFS2icfU_Tf0ns_nTA" to i8*),
i8** %a.debug.fn, align 8
%a.debug.data = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 1
store %swift.refcounted* %3, %swift.refcounted** %a.debug.data, align 8
call void @swift_release(%swift.refcounted* %3) #3
%7 = bitcast %TSi* %t to i8*
call void @llvm.lifetime.end.p0i8(i64 8, i8* %7)
ret void
}
クロージャ本体関数の取得
[.code-highlight: 24-25]
define hidden swiftcc void @"$s1d5main2yyF"() #0 {
entry:
%t = alloca %TSi, align 8
%0 = bitcast %TSi* %t to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
%a.debug = alloca %swift.function, align 8
%1 = bitcast %swift.function* %a.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 16, i1 false)
%2 = bitcast %TSi* %t to i8*
call void @llvm.lifetime.start.p0i8(i64 8, i8* %2)
%t._value = getelementptr inbounds %TSi, %TSi* %t, i32 0, i32 0
store i64 3, i64* %t._value, align 8
%3 = call noalias %swift.refcounted* @swift_allocObject(
%swift.type* getelementptr inbounds (
%swift.full_boxmetadata, %swift.full_boxmetadata* @metadata, i32 0, i32 2),
i64 24, i64 7) #3
%4 = bitcast %swift.refcounted* %3 to <{ %swift.refcounted, %TSi* }>*
%5 = getelementptr inbounds <{ %swift.refcounted, %TSi* }>,
<{ %swift.refcounted, %TSi* }>* %4, i32 0, i32 1
store %TSi* %t, %TSi** %5, align 8
%6 = bitcast %swift.function* %a.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %6)
%a.debug.fn = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 0
store i8* bitcast (i64 (i64, %swift.refcounted*)* @"$s1d5main2yyFS2icfU_Tf0ns_nTA" to i8*),
i8** %a.debug.fn, align 8
%a.debug.data = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 1
store %swift.refcounted* %3, %swift.refcounted** %a.debug.data, align 8
call void @swift_release(%swift.refcounted* %3) #3
%7 = bitcast %TSi* %t to i8*
call void @llvm.lifetime.end.p0i8(i64 8, i8* %7)
ret void
}
クロージャ本体関数
define internal swiftcc i64 @"$s1d5main2yyFS2icfU_Tf0ns_nTA"(i64, %swift.refcounted* swiftself) #0 {
entry:
%2 = bitcast %swift.refcounted* %1 to <{ %swift.refcounted, %TSi* }>*
%3 = getelementptr inbounds <{ %swift.refcounted, %TSi* }>, <{ %swift.refcounted, %TSi* }>* %2, i32 0, i32 1
%4 = load %TSi*, %TSi** %3, align 8
%5 = tail call swiftcc i64 @"$s1d5main2yyFS2icfU_Tf0ns_n"(i64 %0, %TSi* nocapture dereferenceable(8) %4)
ret i64 %5
}
シグネチャにコンテキストポインタを含む
define internal swiftcc
i64 @"$s1d5main2yyFS2icfU_Tf0ns_nTA"(
i64,
%swift.refcounted* swiftself)
Swiftで書くなら
(Int, Context) -> Int
呼び出しコード
func invoke(_ f: (Int) -> Int) {
f(33)
}
呼び出しコード
define hidden swiftcc void @"$s1d6invokeyyS2iXEF"(i8*, %swift.opaque*) #0 {
entry:
%f.debug = alloca %swift.noescape.function, align 8
%2 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 16, i1 false)
%3 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %3)
%f.debug.fn = getelementptr inbounds %swift.noescape.function, %swift.noescape.function* %f.debug, i32 0, i32 0
store i8* %0, i8** %f.debug.fn, align 8
%f.debug.data = getelementptr inbounds %swift.noescape.function, %swift.noescape.function* %f.debug, i32 0, i32 1
store %swift.opaque* %1, %swift.opaque** %f.debug.data, align 8
%4 = bitcast i8* %0 to i64 (i64, %swift.refcounted*)*
%5 = bitcast %swift.opaque* %1 to %swift.refcounted*
%6 = call swiftcc i64 %4(i64 33, %swift.refcounted* swiftself %5)
ret void
}
関数ポインタとコンテキストポインタ
[.code-highlight: 1]
define hidden swiftcc void @"$s1d6invokeyyS2iXEF"(i8*, %swift.opaque*) #0 {
entry:
%f.debug = alloca %swift.noescape.function, align 8
%2 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 16, i1 false)
%3 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %3)
%f.debug.fn = getelementptr inbounds %swift.noescape.function, %swift.noescape.function* %f.debug, i32 0, i32 0
store i8* %0, i8** %f.debug.fn, align 8
%f.debug.data = getelementptr inbounds %swift.noescape.function, %swift.noescape.function* %f.debug, i32 0, i32 1
store %swift.opaque* %1, %swift.opaque** %f.debug.data, align 8
%4 = bitcast i8* %0 to i64 (i64, %swift.refcounted*)*
%5 = bitcast %swift.opaque* %1 to %swift.refcounted*
%6 = call swiftcc i64 %4(i64 33, %swift.refcounted* swiftself %5)
ret void
}
関数ポインタの取り出し
[.code-highlight: 1, 12]
define hidden swiftcc void @"$s1d6invokeyyS2iXEF"(i8*, %swift.opaque*) #0 {
entry:
%f.debug = alloca %swift.noescape.function, align 8
%2 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 16, i1 false)
%3 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %3)
%f.debug.fn = getelementptr inbounds %swift.noescape.function, %swift.noescape.function* %f.debug, i32 0, i32 0
store i8* %0, i8** %f.debug.fn, align 8
%f.debug.data = getelementptr inbounds %swift.noescape.function, %swift.noescape.function* %f.debug, i32 0, i32 1
store %swift.opaque* %1, %swift.opaque** %f.debug.data, align 8
%4 = bitcast i8* %0 to i64 (i64, %swift.refcounted*)*
%5 = bitcast %swift.opaque* %1 to %swift.refcounted*
%6 = call swiftcc i64 %4(i64 33, %swift.refcounted* swiftself %5)
ret void
}
コンテキストポインタの取り出し
[.code-highlight: 1, 13]
define hidden swiftcc void @"$s1d6invokeyyS2iXEF"(i8*, %swift.opaque*) #0 {
entry:
%f.debug = alloca %swift.noescape.function, align 8
%2 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 16, i1 false)
%3 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %3)
%f.debug.fn = getelementptr inbounds %swift.noescape.function, %swift.noescape.function* %f.debug, i32 0, i32 0
store i8* %0, i8** %f.debug.fn, align 8
%f.debug.data = getelementptr inbounds %swift.noescape.function, %swift.noescape.function* %f.debug, i32 0, i32 1
store %swift.opaque* %1, %swift.opaque** %f.debug.data, align 8
%4 = bitcast i8* %0 to i64 (i64, %swift.refcounted*)*
%5 = bitcast %swift.opaque* %1 to %swift.refcounted*
%6 = call swiftcc i64 %4(i64 33, %swift.refcounted* swiftself %5)
ret void
}
コンテキストポインタを渡して呼び出し
[.code-highlight: 12, 13, 14]
define hidden swiftcc void @"$s1d6invokeyyS2iXEF"(i8*, %swift.opaque*) #0 {
entry:
%f.debug = alloca %swift.noescape.function, align 8
%2 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 16, i1 false)
%3 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %3)
%f.debug.fn = getelementptr inbounds %swift.noescape.function, %swift.noescape.function* %f.debug, i32 0, i32 0
store i8* %0, i8** %f.debug.fn, align 8
%f.debug.data = getelementptr inbounds %swift.noescape.function, %swift.noescape.function* %f.debug, i32 0, i32 1
store %swift.opaque* %1, %swift.opaque** %f.debug.data, align 8
%4 = bitcast i8* %0 to i64 (i64, %swift.refcounted*)*
%5 = bitcast %swift.opaque* %1 to %swift.refcounted*
%6 = call swiftcc i64 %4(i64 33, %swift.refcounted* swiftself %5)
ret void
}
受け渡しコード
func main3() {
var t = 3
let a = { (x: Int) -> Int in
t += x
return t
}
invoke(a)
}
受け渡しコード
define hidden swiftcc void @"$s1d5main3yyF"() #0 {
entry:
%a.debug = alloca %swift.function, align 8
%0 = bitcast %swift.function* %a.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 16, i1 false)
%1 = call noalias %swift.refcounted* @swift_allocObject(
%swift.type* getelementptr inbounds (%swift.full_boxmetadata,
%swift.full_boxmetadata* @metadata.3, i32 0, i32 2), i64 24, i64 7) #2
%2 = bitcast %swift.refcounted* %1 to <{ %swift.refcounted, [8 x i8] }>*
%3 = getelementptr inbounds <{ %swift.refcounted, [8 x i8] }>,
<{ %swift.refcounted, [8 x i8] }>* %2, i32 0, i32 1
%4 = bitcast [8 x i8]* %3 to %TSi*
call void asm sideeffect "", "r"(%TSi* %4)
%._value = getelementptr inbounds %TSi, %TSi* %4, i32 0, i32 0
store i64 3, i64* %._value, align 8
%5 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%6 = bitcast %swift.function* %a.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %6)
%a.debug.fn = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 0
store i8* bitcast (i64 (i64, %swift.refcounted*)* @"$s1d5main3yyFS2icfU_TA" to i8*),
i8** %a.debug.fn, align 8
%a.debug.data = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 1
store %swift.refcounted* %1, %swift.refcounted** %a.debug.data, align 8
%7 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%8 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%9 = bitcast %swift.refcounted* %1 to %swift.opaque*
call swiftcc void @"$s1d6invokeyyS2iXEF"(i8* bitcast
(i64 (i64, %swift.refcounted*)* @"$s1d5main3yyFS2icfU_TA" to i8*), %swift.opaque* %9)
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
ret void
}
キャプチャオブジェクトのメモリ確保
[.code-highlight: 6-8]
define hidden swiftcc void @"$s1d5main3yyF"() #0 {
entry:
%a.debug = alloca %swift.function, align 8
%0 = bitcast %swift.function* %a.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 16, i1 false)
%1 = call noalias %swift.refcounted* @swift_allocObject(
%swift.type* getelementptr inbounds (%swift.full_boxmetadata,
%swift.full_boxmetadata* @metadata.3, i32 0, i32 2), i64 24, i64 7) #2
%2 = bitcast %swift.refcounted* %1 to <{ %swift.refcounted, [8 x i8] }>*
%3 = getelementptr inbounds <{ %swift.refcounted, [8 x i8] }>,
<{ %swift.refcounted, [8 x i8] }>* %2, i32 0, i32 1
%4 = bitcast [8 x i8]* %3 to %TSi*
call void asm sideeffect "", "r"(%TSi* %4)
%._value = getelementptr inbounds %TSi, %TSi* %4, i32 0, i32 0
store i64 3, i64* %._value, align 8
%5 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%6 = bitcast %swift.function* %a.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %6)
%a.debug.fn = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 0
store i8* bitcast (i64 (i64, %swift.refcounted*)* @"$s1d5main3yyFS2icfU_TA" to i8*),
i8** %a.debug.fn, align 8
%a.debug.data = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 1
store %swift.refcounted* %1, %swift.refcounted** %a.debug.data, align 8
%7 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%8 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%9 = bitcast %swift.refcounted* %1 to %swift.opaque*
call swiftcc void @"$s1d6invokeyyS2iXEF"(i8* bitcast
(i64 (i64, %swift.refcounted*)* @"$s1d5main3yyFS2icfU_TA" to i8*), %swift.opaque* %9)
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
ret void
}
キャプチャ変数用のメモリ参照
[.code-highlight: 9-12]
define hidden swiftcc void @"$s1d5main3yyF"() #0 {
entry:
%a.debug = alloca %swift.function, align 8
%0 = bitcast %swift.function* %a.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 16, i1 false)
%1 = call noalias %swift.refcounted* @swift_allocObject(
%swift.type* getelementptr inbounds (%swift.full_boxmetadata,
%swift.full_boxmetadata* @metadata.3, i32 0, i32 2), i64 24, i64 7) #2
%2 = bitcast %swift.refcounted* %1 to <{ %swift.refcounted, [8 x i8] }>*
%3 = getelementptr inbounds <{ %swift.refcounted, [8 x i8] }>,
<{ %swift.refcounted, [8 x i8] }>* %2, i32 0, i32 1
%4 = bitcast [8 x i8]* %3 to %TSi*
call void asm sideeffect "", "r"(%TSi* %4)
%._value = getelementptr inbounds %TSi, %TSi* %4, i32 0, i32 0
store i64 3, i64* %._value, align 8
%5 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%6 = bitcast %swift.function* %a.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %6)
%a.debug.fn = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 0
store i8* bitcast (i64 (i64, %swift.refcounted*)* @"$s1d5main3yyFS2icfU_TA" to i8*),
i8** %a.debug.fn, align 8
%a.debug.data = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 1
store %swift.refcounted* %1, %swift.refcounted** %a.debug.data, align 8
%7 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%8 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%9 = bitcast %swift.refcounted* %1 to %swift.opaque*
call swiftcc void @"$s1d6invokeyyS2iXEF"(i8* bitcast
(i64 (i64, %swift.refcounted*)* @"$s1d5main3yyFS2icfU_TA" to i8*), %swift.opaque* %9)
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
ret void
}
t = 3
[.code-highlight: 14-15]
define hidden swiftcc void @"$s1d5main3yyF"() #0 {
entry:
%a.debug = alloca %swift.function, align 8
%0 = bitcast %swift.function* %a.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 16, i1 false)
%1 = call noalias %swift.refcounted* @swift_allocObject(
%swift.type* getelementptr inbounds (%swift.full_boxmetadata,
%swift.full_boxmetadata* @metadata.3, i32 0, i32 2), i64 24, i64 7) #2
%2 = bitcast %swift.refcounted* %1 to <{ %swift.refcounted, [8 x i8] }>*
%3 = getelementptr inbounds <{ %swift.refcounted, [8 x i8] }>,
<{ %swift.refcounted, [8 x i8] }>* %2, i32 0, i32 1
%4 = bitcast [8 x i8]* %3 to %TSi*
call void asm sideeffect "", "r"(%TSi* %4)
%._value = getelementptr inbounds %TSi, %TSi* %4, i32 0, i32 0
store i64 3, i64* %._value, align 8
%5 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%6 = bitcast %swift.function* %a.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %6)
%a.debug.fn = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 0
store i8* bitcast (i64 (i64, %swift.refcounted*)* @"$s1d5main3yyFS2icfU_TA" to i8*),
i8** %a.debug.fn, align 8
%a.debug.data = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 1
store %swift.refcounted* %1, %swift.refcounted** %a.debug.data, align 8
%7 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%8 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%9 = bitcast %swift.refcounted* %1 to %swift.opaque*
call swiftcc void @"$s1d6invokeyyS2iXEF"(i8* bitcast
(i64 (i64, %swift.refcounted*)* @"$s1d5main3yyFS2icfU_TA" to i8*), %swift.opaque* %9)
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
ret void
}
関数本体と一緒に渡す
[.code-highlight: 26-28]
define hidden swiftcc void @"$s1d5main3yyF"() #0 {
entry:
%a.debug = alloca %swift.function, align 8
%0 = bitcast %swift.function* %a.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 16, i1 false)
%1 = call noalias %swift.refcounted* @swift_allocObject(
%swift.type* getelementptr inbounds (%swift.full_boxmetadata,
%swift.full_boxmetadata* @metadata.3, i32 0, i32 2), i64 24, i64 7) #2
%2 = bitcast %swift.refcounted* %1 to <{ %swift.refcounted, [8 x i8] }>*
%3 = getelementptr inbounds <{ %swift.refcounted, [8 x i8] }>,
<{ %swift.refcounted, [8 x i8] }>* %2, i32 0, i32 1
%4 = bitcast [8 x i8]* %3 to %TSi*
call void asm sideeffect "", "r"(%TSi* %4)
%._value = getelementptr inbounds %TSi, %TSi* %4, i32 0, i32 0
store i64 3, i64* %._value, align 8
%5 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%6 = bitcast %swift.function* %a.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %6)
%a.debug.fn = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 0
store i8* bitcast (i64 (i64, %swift.refcounted*)* @"$s1d5main3yyFS2icfU_TA" to i8*),
i8** %a.debug.fn, align 8
%a.debug.data = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 1
store %swift.refcounted* %1, %swift.refcounted** %a.debug.data, align 8
%7 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%8 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #2
%9 = bitcast %swift.refcounted* %1 to %swift.opaque*
call swiftcc void @"$s1d6invokeyyS2iXEF"(i8* bitcast
(i64 (i64, %swift.refcounted*)* @"$s1d5main3yyFS2icfU_TA" to i8*), %swift.opaque* %9)
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
call void @swift_release(%swift.refcounted* %1) #2
ret void
}
thin関数はthick関数として扱える
受け渡しコード
func main4() {
let a = { (x: Int) -> Int in
return x * 2
}
invoke(a)
}
受け渡しコード
define hidden swiftcc void @"$s1d5main4yyF"() #0 {
entry:
%a.debug = alloca %swift.function, align 8
%0 = bitcast %swift.function* %a.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 16, i1 false)
%1 = bitcast %swift.function* %a.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %1)
%a.debug.fn = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 0
store i8* bitcast (i64 (i64)* @"$s1d5main4yyFS2icfU_" to i8*), i8** %a.debug.fn, align 8
%a.debug.data = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 1
store %swift.refcounted* null, %swift.refcounted** %a.debug.data, align 8
call swiftcc void @"$s1d6invokeyyS2iXEF"(
i8* bitcast (i64 (i64)* @"$s1d5main4yyFS2icfU_" to i8*),
%swift.opaque* null)
ret void
}
thin関数ポインタがそのまま渡されている コンテキストポインタはnull
[.code-highlight: 12-14]
define hidden swiftcc void @"$s1d5main4yyF"() #0 {
entry:
%a.debug = alloca %swift.function, align 8
%0 = bitcast %swift.function* %a.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 16, i1 false)
%1 = bitcast %swift.function* %a.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %1)
%a.debug.fn = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 0
store i8* bitcast (i64 (i64)* @"$s1d5main4yyFS2icfU_" to i8*), i8** %a.debug.fn, align 8
%a.debug.data = getelementptr inbounds %swift.function, %swift.function* %a.debug, i32 0, i32 1
store %swift.refcounted* null, %swift.refcounted** %a.debug.data, align 8
call swiftcc void @"$s1d6invokeyyS2iXEF"(
i8* bitcast (i64 (i64)* @"$s1d5main4yyFS2icfU_" to i8*),
%swift.opaque* null)
ret void
}
引数に受けられないはずの、 コンテキストポインタが渡される!
; invokeに関数を渡すところ
call swiftcc void @"$s1d6invokeyyS2iXEF"(
i8* bitcast (i64 (i64)* @"$s1d5main4yyFS2icfU_" to i8*),
%swift.opaque* null)
; invokeが関数を呼び出すところ
%6 = call swiftcc i64 %4(
i64 33,
%swift.refcounted* swiftself %5)
Swift用の呼び出し規約(Calling Convention)
swiftself
: コンテキストポインタ属性
%6 = call swiftcc i64 %4(i64 33, %swift.refcounted* swiftself %5)
swiftself
指定された引数は専用のレジスタで渡される。つまり、他の通常の引数はこのレジスタには割り当てられない。よって、コンテキストポインタが不要な関数ではこのレジスタを読まないというだけで互換性が得られる。
https://github.com/apple/swift/blob/master/docs/CallingConvention.rst
メソッドにselfを引き渡すときにもswiftself
が使われる
メソッド生成コード
class Cat {
var age: Int = 3
func sleep(year: Int) -> Int {
age += year
return age
}
}
define hidden swiftcc i64 @"$s1d3CatC5sleep4yearS2i_tF"(
i64, %T1d3CatC* swiftself) {
...
}
生成コード
extension Int : Error {}
func main5() {
let a = { (x: Int) -> Int in
throw x
}
}
生成されたクロージャ
define internal swiftcc i64 @"$s1d5main5yyFS2iKcfU_"(i64,
%swift.refcounted* swiftself,
%swift.error** noalias nocapture swifterror dereferenceable(8)) #0 {
entry:
%x.debug = alloca i64, align 8
%3 = bitcast i64* %x.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %3, i8 0, i64 8, i1 false)
store i64 %0, i64* %x.debug, align 8
%4 = call i8** @"$sS2is5Error1dWl"() #5
%5 = call swiftcc { %swift.error*, %swift.opaque* }
@swift_allocError(%swift.type* @"$sSiN", i8** %4, %swift.opaque* null, i1 false)
%6 = extractvalue { %swift.error*, %swift.opaque* } %5, 0
%7 = extractvalue { %swift.error*, %swift.opaque* } %5, 1
%8 = bitcast %swift.opaque* %7 to %TSi*
%._value = getelementptr inbounds %TSi, %TSi* %8, i32 0, i32 0
store i64 %0, i64* %._value, align 8
store %swift.error* %6, %swift.error** %2, align 8
call swiftcc void @swift_willThrow(i8* swiftself undef,
%swift.error** noalias nocapture readonly swifterror dereferenceable(8) %2) #2
store %swift.error* null, %swift.error** %2, align 8
store %swift.error* %6, %swift.error** %2, align 8
ret i64 undef
}
シグネチャ
define internal swiftcc i64 @"$s1d5main5yyFS2iKcfU_"(i64,
%swift.refcounted* swiftself,
%swift.error** noalias nocapture swifterror dereferenceable(8))
Swiftで書くなら
(Int, Context, inout Error) -> Int
throwsな関数は%swift.error
のダブルポインタを引数に受け、そこへの書き込みとしてエラーを送出する。この引数はswifterror
属性を与えられる。swifterror
の引数がある関数は、swiftself
の引数も必須とされている。そのため、このクロージャはキャプチャをしていないがswiftself
を引数に持っている。
呼び出し側
func invokeThrowing(_ f: (Int) throws -> Int) {
try! f(44)
}
呼び出し側
define hidden swiftcc void @"$s1d14invokeThrowingyyS2iKXEF"(i8*, %swift.opaque*) #0 {
entry:
%f.debug = alloca %swift.noescape.function, align 8
%2 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 16, i1 false)
%swifterror = alloca swifterror %swift.error*, align 8
store %swift.error* null, %swift.error** %swifterror, align 8
%3 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %3)
%f.debug.fn = getelementptr inbounds %swift.noescape.function,
%swift.noescape.function* %f.debug, i32 0, i32 0
store i8* %0, i8** %f.debug.fn, align 8
%f.debug.data = getelementptr inbounds %swift.noescape.function,
%swift.noescape.function* %f.debug, i32 0, i32 1
store %swift.opaque* %1, %swift.opaque** %f.debug.data, align 8
%4 = bitcast i8* %0 to i64 (i64, %swift.refcounted*, %swift.error**)*
%5 = bitcast %swift.opaque* %1 to %swift.refcounted*
%6 = call swiftcc i64 %4(i64 44, %swift.refcounted* swiftself %5,
%swift.error** noalias nocapture swifterror dereferenceable(8) %swifterror)
%7 = load %swift.error*, %swift.error** %swifterror, align 8
%8 = icmp ne %swift.error* %7, null
br i1 %8, label %11, label %9
; <label>:9: ; preds = %entry
%10 = phi i64 [ %6, %entry ]
ret void
; <label>:11: ; preds = %entry
%12 = phi %swift.error* [ %7, %entry ]
store %swift.error* null, %swift.error** %swifterror, align 8
call swiftcc void @swift_unexpectedError(%swift.error* %12,
i8* getelementptr inbounds ([8 x i8], [8 x i8]* @0, i64 0, i64 0),
i64 7, i1 true, i64 52)
unreachable
}
エラーを受けるメモリの用意
[.code-highlight: 7, 8]
define hidden swiftcc void @"$s1d14invokeThrowingyyS2iKXEF"(i8*, %swift.opaque*) #0 {
entry:
%f.debug = alloca %swift.noescape.function, align 8
%2 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 16, i1 false)
%swifterror = alloca swifterror %swift.error*, align 8
store %swift.error* null, %swift.error** %swifterror, align 8
%3 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %3)
%f.debug.fn = getelementptr inbounds %swift.noescape.function,
%swift.noescape.function* %f.debug, i32 0, i32 0
store i8* %0, i8** %f.debug.fn, align 8
%f.debug.data = getelementptr inbounds %swift.noescape.function,
%swift.noescape.function* %f.debug, i32 0, i32 1
store %swift.opaque* %1, %swift.opaque** %f.debug.data, align 8
%4 = bitcast i8* %0 to i64 (i64, %swift.refcounted*, %swift.error**)*
%5 = bitcast %swift.opaque* %1 to %swift.refcounted*
%6 = call swiftcc i64 %4(i64 44, %swift.refcounted* swiftself %5,
%swift.error** noalias nocapture swifterror dereferenceable(8) %swifterror)
%7 = load %swift.error*, %swift.error** %swifterror, align 8
%8 = icmp ne %swift.error* %7, null
br i1 %8, label %11, label %9
; <label>:9: ; preds = %entry
%10 = phi i64 [ %6, %entry ]
ret void
; <label>:11: ; preds = %entry
%12 = phi %swift.error* [ %7, %entry ]
store %swift.error* null, %swift.error** %swifterror, align 8
call swiftcc void @swift_unexpectedError(%swift.error* %12,
i8* getelementptr inbounds ([8 x i8], [8 x i8]* @0, i64 0, i64 0),
i64 7, i1 true, i64 52)
unreachable
}
渡して呼び出し
[.code-highlight: 18, 19]
define hidden swiftcc void @"$s1d14invokeThrowingyyS2iKXEF"(i8*, %swift.opaque*) #0 {
entry:
%f.debug = alloca %swift.noescape.function, align 8
%2 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 16, i1 false)
%swifterror = alloca swifterror %swift.error*, align 8
store %swift.error* null, %swift.error** %swifterror, align 8
%3 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %3)
%f.debug.fn = getelementptr inbounds %swift.noescape.function,
%swift.noescape.function* %f.debug, i32 0, i32 0
store i8* %0, i8** %f.debug.fn, align 8
%f.debug.data = getelementptr inbounds %swift.noescape.function,
%swift.noescape.function* %f.debug, i32 0, i32 1
store %swift.opaque* %1, %swift.opaque** %f.debug.data, align 8
%4 = bitcast i8* %0 to i64 (i64, %swift.refcounted*, %swift.error**)*
%5 = bitcast %swift.opaque* %1 to %swift.refcounted*
%6 = call swiftcc i64 %4(i64 44, %swift.refcounted* swiftself %5,
%swift.error** noalias nocapture swifterror dereferenceable(8) %swifterror)
%7 = load %swift.error*, %swift.error** %swifterror, align 8
%8 = icmp ne %swift.error* %7, null
br i1 %8, label %11, label %9
; <label>:9: ; preds = %entry
%10 = phi i64 [ %6, %entry ]
ret void
; <label>:11: ; preds = %entry
%12 = phi %swift.error* [ %7, %entry ]
store %swift.error* null, %swift.error** %swifterror, align 8
call swiftcc void @swift_unexpectedError(%swift.error* %12,
i8* getelementptr inbounds ([8 x i8], [8 x i8]* @0, i64 0, i64 0),
i64 7, i1 true, i64 52)
unreachable
}
参照先がnullかどうか調べて分岐
[.code-highlight: 20, 21, 22, 24, 28]
define hidden swiftcc void @"$s1d14invokeThrowingyyS2iKXEF"(i8*, %swift.opaque*) #0 {
entry:
%f.debug = alloca %swift.noescape.function, align 8
%2 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 16, i1 false)
%swifterror = alloca swifterror %swift.error*, align 8
store %swift.error* null, %swift.error** %swifterror, align 8
%3 = bitcast %swift.noescape.function* %f.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %3)
%f.debug.fn = getelementptr inbounds %swift.noescape.function,
%swift.noescape.function* %f.debug, i32 0, i32 0
store i8* %0, i8** %f.debug.fn, align 8
%f.debug.data = getelementptr inbounds %swift.noescape.function,
%swift.noescape.function* %f.debug, i32 0, i32 1
store %swift.opaque* %1, %swift.opaque** %f.debug.data, align 8
%4 = bitcast i8* %0 to i64 (i64, %swift.refcounted*, %swift.error**)*
%5 = bitcast %swift.opaque* %1 to %swift.refcounted*
%6 = call swiftcc i64 %4(i64 44, %swift.refcounted* swiftself %5,
%swift.error** noalias nocapture swifterror dereferenceable(8) %swifterror)
%7 = load %swift.error*, %swift.error** %swifterror, align 8
%8 = icmp ne %swift.error* %7, null
br i1 %8, label %11, label %9
; <label>:9: ; preds = %entry
%10 = phi i64 [ %6, %entry ]
ret void
; <label>:11: ; preds = %entry
%12 = phi %swift.error* [ %7, %entry ]
store %swift.error* null, %swift.error** %swifterror, align 8
call swiftcc void @swift_unexpectedError(%swift.error* %12,
i8* getelementptr inbounds ([8 x i8], [8 x i8]* @0, i64 0, i64 0),
i64 7, i1 true, i64 52)
unreachable
}
nothrowな関数はそのままthrowsな関数として使える
渡すコード
func main6() {
let a = { (x: Int) -> Int in
return x * 2
}
invokeThrowing(a)
}
渡すコード
define hidden swiftcc void @"$s1d5main6yyF"() #0 {
entry:
%a.debug = alloca %swift.function, align 8
%0 = bitcast %swift.function* %a.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 16, i1 false)
%1 = bitcast %swift.function* %a.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %1)
%a.debug.fn = getelementptr inbounds %swift.function,
%swift.function* %a.debug, i32 0, i32 0
store i8* bitcast (i64 (i64)* @"$s1d5main6yyFS2icfU_" to i8*),
i8** %a.debug.fn, align 8
%a.debug.data = getelementptr inbounds %swift.function,
%swift.function* %a.debug, i32 0, i32 1
store %swift.refcounted* null, %swift.refcounted** %a.debug.data, align 8
call swiftcc void @"$s1d14invokeThrowingyyS2iKXEF"(
i8* bitcast (i64 (i64)* @"$s1d5main6yyFS2icfU_" to i8*), %swift.opaque* null)
ret void
}
クロージャ本体をそのまま渡す
[.code-highlight: 15-17]
define hidden swiftcc void @"$s1d5main6yyF"() #0 {
entry:
%a.debug = alloca %swift.function, align 8
%0 = bitcast %swift.function* %a.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 16, i1 false)
%1 = bitcast %swift.function* %a.debug to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %1)
%a.debug.fn = getelementptr inbounds %swift.function,
%swift.function* %a.debug, i32 0, i32 0
store i8* bitcast (i64 (i64)* @"$s1d5main6yyFS2icfU_" to i8*),
i8** %a.debug.fn, align 8
%a.debug.data = getelementptr inbounds %swift.function,
%swift.function* %a.debug, i32 0, i32 1
store %swift.refcounted* null, %swift.refcounted** %a.debug.data, align 8
call swiftcc void @"$s1d14invokeThrowingyyS2iKXEF"(
i8* bitcast (i64 (i64)* @"$s1d5main6yyFS2icfU_" to i8*),
%swift.opaque* null)
ret void
}
引数に受けられないはずの、 エラーダブルポインタが渡される!
; invokeThrowingに関数を渡すところ
call swiftcc void @"$s1d14invokeThrowingyyS2iKXEF"(
i8* bitcast (i64 (i64)* @"$s1d5main6yyFS2icfU_" to i8*),
%swift.opaque* null)
; invokeThrowingが関数を呼び出すところ
%6 = call swiftcc i64 %4(
i64 44,
%swift.refcounted* swiftself %5,
%swift.error** noalias nocapture swifterror dereferenceable(8) %swifterror)
swiftcc
ではエラーを受け取るためのダブルポインタ引数にswifterror
を指定する。これはswiftself
同様に専用のレジスタを使用する。そのため、エラーを返さない関数においては単にこれを無視することで互換性が得られる。
キャプチャがあるとthick関数になる
thin関数はswiftself
によりthick関数と互換性がある
throwsな関数はエラーのダブルポインタを引数に受ける
nothrowな関数はswifterror
によりthrowsな関数と互換性がある