Skip to content

Instantly share code, notes, and snippets.

@omochi
Created December 17, 2018 10:23
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save omochi/3a6c7790a7a53c0f844c7aea01709044 to your computer and use it in GitHub Desktop.
Save omochi/3a6c7790a7a53c0f844c7aea01709044 to your computer and use it in GitHub Desktop.

slidenumber: true

Swiftの関数型の

実行時表現

わいわいswiftc

omochimetaru


3種類の表現

  • thin: ただの関数ポインタ
  • thick: 関数ポインタ + コンテキストポインタ
  • block: Objective-Cのブロック。ObjectiveCにおけるオブジェクトポインタ

thin関数

生成コード

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関数

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の互換性

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)

swiftcc

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


メソッドとswiftself

メソッドに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) {
  ...
}

throwsな関数

生成コード

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の互換性

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)

swifterror

swiftccではエラーを受け取るためのダブルポインタ引数にswifterrorを指定する。これはswiftself同様に専用のレジスタを使用する。そのため、エラーを返さない関数においては単にこれを無視することで互換性が得られる。


まとめ

キャプチャがあるとthick関数になる

thin関数はswiftselfによりthick関数と互換性がある

throwsな関数はエラーのダブルポインタを引数に受ける

nothrowな関数はswifterrorによりthrowsな関数と互換性がある

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment