Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swiftのスタック変数とCPUレジスタの関係を読み解いた

rikusouda
September 07, 2019

 Swiftのスタック変数とCPUレジスタの関係を読み解いた

iOSDC Japan 2019 でお話ししたLTです。

rikusouda

September 07, 2019
Tweet

More Decks by rikusouda

Other Decks in Programming

Transcript

  1. SwiftͷελοΫม਺ͱ
    CPUϨδελͷؔ܎Λ
    ಡΈղ͍ͨ
    2019/09/07 iOSDC Japan 2019 LT
    @rikusouda
    #iosdc #a

    View Slide

  2. ࣗݾ঺հ
    • @rikusouda (٢Ԭ༞थ)
    • גࣜձࣾGunosy
    ͜ΕͷJ04ΞϓϦ࡞͍ͬͯΔ

    View Slide

  3. ௿ϨΠϠʔ޷͖
    • جຊ৘ใٕज़ऀࢼݧͷݴޠ໰୊
    ͸ΞηϯϒϥΛબ୒ͨ͠
    • iOSΞϓϦ։ൃͷલ͸C++Ͱ
    WindowsΞϓϦ։ൃ͍ͯͨ͠

    View Slide

  4. SwiftͷϨδελར༻͕ؾʹͳΔ
    • WWDC16#416ΑΓɺ஋ܕϩʔΧ
    ϧม਺͸ελοΫϝϞϦΛ࢖͏
    • ࣮ߦ࣌͸Ϩδελ࢖͏͔ΒɺϝϞ
    Ϧ΁ͷॻ͖ࠐΈ͸লུͰ͖Δ?

    View Slide

  5. Tips:ϨδελͬͯԿ?
    • CPU಺ʹ͋ΔϝϞϦΈ͍ͨͳͷ
    • Ξηϯϒϥ(࣮ߦϑΝΠϧ)͕࢖͏
    • ϝϞϦΑΓ௒ઈߴ଎
    • ͨͩ͠௒ઈখ༰ྔ

    View Slide

  6. SwiftͷϨδελར༻͕ؾʹͳͬ
    ͨ
    • WWDC16#416ΑΓɺ஋ܕϩʔΧ
    ϧม਺͸ελοΫϝϞϦΛ࢖͏
    • ࣮ߦ࣌͸Ϩδελ࢖͏͔ΒɺϝϞ
    Ϧ΁ͷॻ͖ࠐΈ͸লུͰ͖Δ?

    View Slide

  7. ୊ࡐͷSwiftίʔυ
    func doAnExperiment() -> Int {
    var variable = twofold(100)
    variable += 1000
    return variable
    }
    func twofold(_ arg: Int) -> Int {
    return arg * 2
    }

    View Slide

  8. ୊ࡐͷSwiftίʔυ
    func doAnExperiment() -> Int {
    var variable = twofold(100)
    variable += 1000
    return variable
    }
    func twofold(_ arg: Int) -> Int {
    return arg * 2
    }

    View Slide

  9. ୊ࡐͷSwiftίʔυ
    func doAnExperiment() -> Int {
    var variable = twofold(100)
    variable += 1000
    return variable
    }
    func twofold(_ arg: Int) -> Int {
    return arg * 2
    }
    Λฦ͢

    View Slide

  10. func doAnExperiment() -> Int {
    var variable = twofold(100)
    variable += 1000
    return variable
    }
    func twofold(_ arg: Int) -> Int {
    return arg * 2
    }
    ୊ࡐͷSwiftίʔυ
    ͜ͷWBSJBCMF͸ɺϝϞϦʹ
    ॻ͖ࠐ·ͳͯ͘΋ྑ͍ͱࢥͬͨ
    ϨδελͰࣄ଍ΓΔͷͰ

    View Slide

  11. ίϯύΠϥͷίʔυΛ
    ಡΊ͹Θ͔ΔΒ͍͠

    View Slide

  12. Ͱ΋ίϯύΠϥΘ͔Βͳ͍
    • C++஌͍ͬͯͯ΋ίϯύΠϥ
    Θ͔ΔΘ͚͡Όͳ͍
    • ௐ΂Δ·Ͱͷϋʔυϧ͕ߴͦ
    ͏

    View Slide

  13. ௐ΂͖͔͚ͨͬ

    View Slide

  14. View Slide

  15. WWDC19ʹߦ͖ͬͯͨ
    • ॳΊͯWWDCʹߦͬͨ
    • WWDCͰ͸Labͱ͍͏ͱ͜ΖͰ
    AppleͷΤϯδχΞʹ࣭໰Ͱ͖Δ

    View Slide

  16. ࣗ෼Ͱௐ΂ͳͯ͘΋
    ౴͑Θ͔ΔͷͰ͸

    View Slide

  17. WWDCͷLabͰฉ͍ͯΈͨ
    • CݴޠͰͷ౴͑͸ฉ͚͚ͨͲɺ
    SwiftͰͷ౴͑͸ฉ͚ͳ͔ͬͨ
    • LLVM IRͱ͔ΞηϯϒϥಡΉͱ
    ͍Ζ͍ΖΘ͔Δͱฉ͍ͨ

    View Slide

  18. Tips: LLVM IRͬͯԿ?
    • ࣮ߦϑΝΠϧ(Ξηϯϒϥ)ʹͳΔ
    Ұาखલͷதؒදݱ
    • BitcodeʹରԠ͢Διʔείʔυ

    View Slide

  19. WWDCͷLabͰฉ͍ͯΈͨ
    • CݴޠͰͷ౴͑͸ฉ͚͚ͨͲɺ
    SwiftͰͷ౴͑͸ฉ͚ͳ͔ͬͨ
    • LLVM IRͱ͔ΞηϯϒϥಡΉͱ
    ͍Ζ͍ΖΘ͔Δͱฉ͍ͨ

    View Slide

  20. LLVM IR΍ΞηϯϒϥΛग़ྗ͠
    ͯಡΉํ๏Λख΄Ͳ͖ͯ͘͠Ε
    ͨ (ͨͩ͠CݴޠΛݩʹͯ͠)

    View Slide

  21. ͳΜ͔Ͱ͖ͦ͏ͳ
    ؾ࣋ͪʹͳΓ·ͨ͠

    View Slide

  22. ͖ͬ͞ͷSwiftͷ
    LLVM IRΛಡΜͰΈΑ͏

    View Slide

  23. ୊ࡐͷSwiftίʔυ
    func doAnExperiment() -> Int {
    var variable = twofold(100)
    variable += 1000
    return variable
    }
    func twofold(_ arg: Int) -> Int {
    return arg * 2
    }

    View Slide

  24. func doAnExperiment() -> Int {
    var variable = twofold(100)
    variable += 1000
    return variable
    }
    func twofold(_ arg: Int) -> Int {
    return arg * 2
    }
    ୊ࡐͷSwiftίʔυ
    swiftc -emit-ir
    register.swift
    > register.ll

    View Slide

  25. LLVM IRίʔυ
    define hidden swiftcc i64 @"$s8register14doAnExperimentSiyF"() #0 {
    entry:
    %0 = alloca %TSi, align 8
    %1 = bitcast %TSi* %0 to i8*
    call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
    %2 = bitcast %TSi* %0 to i8*
    call void @llvm.lifetime.start.p0i8(i64 8, i8* %2)
    %3 = call swiftcc i64 @"$s8register7twofoldyS2iF"(i64 100)
    %._value = getelementptr inbounds %TSi, %TSi* %0, i32 0, i32 0
    store i64 %3, i64* %._value, align 8
    %4 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %3, i64 1000)
    %5 = extractvalue { i64, i1 } %4, 0
    %6 = extractvalue { i64, i1 } %4, 1
    br i1 %6, label %9, label %7
    ; :7: ; preds = %entry
    %._value1 = getelementptr inbounds %TSi, %TSi* %0, i32 0, i32 0
    store i64 %5, i64* %._value1, align 8
    %8 = bitcast %TSi* %0 to i8*
    call void @llvm.lifetime.end.p0i8(i64 8, i8* %8)
    ret i64 %5
    ; :9: ; preds = %entry
    call void @llvm.trap()
    unreachable
    }

    View Slide

  26. ΍ͬͺΓ೉͍͠

    View Slide

  27. ݕࡧΛۦ࢖ͯ͠ղಡ

    View Slide

  28. LLVM IRίʔυ
    define hidden swiftcc i64 @"$s8register14doAnExperimentSiyF"() #0 {
    entry:
    %0 = alloca %TSi, align 8
    %1 = bitcast %TSi* %0 to i8*
    call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
    %2 = bitcast %TSi* %0 to i8*
    call void @llvm.lifetime.start.p0i8(i64 8, i8* %2)
    %3 = call swiftcc i64 @"$s8register7twofoldyS2iF"(i64 100)
    %._value = getelementptr inbounds %TSi, %TSi* %0, i32 0, i32 0
    store i64 %3, i64* %._value, align 8
    %4 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %3, i64 1000)
    %5 = extractvalue { i64, i1 } %4, 0
    %6 = extractvalue { i64, i1 } %4, 1
    br i1 %6, label %9, label %7
    ; :7: ; preds = %entry
    %._value1 = getelementptr inbounds %TSi, %TSi* %0, i32 0, i32 0
    store i64 %5, i64* %._value1, align 8
    %8 = bitcast %TSi* %0 to i8*
    call void @llvm.lifetime.end.p0i8(i64 8, i8* %8)
    ret i64 %5
    ; :9: ; preds = %entry
    call void @llvm.trap()
    unreachable
    }

    View Slide

  29. LLVM IRίʔυ: ݕࡧͷྗ
    func doAnExperiment() -> Int {
    let cpu = CPU.shared
    cpu.r0 = alloca()
    cpu.r3 = twofold(100)
    store(cpu.r3, address: cpu.r0)
    cpu.r5 = cpu.r3 + 1000
    store(cpu.r5, address: cpu.r0)
    return cpu.r5
    }

    View Slide

  30. LLVM IRίʔυ: ݕࡧͷྗ
    func doAnExperiment() -> Int {
    let cpu = CPU.shared
    cpu.r0 = alloca()
    cpu.r3 = twofold(100)
    store(cpu.r3, address: cpu.r0)
    cpu.r5 = cpu.r3 + 1000
    store(cpu.r5, address: cpu.r0)
    return cpu.r5
    }
    BMMPDB͸ϝϞϦ ελοΫ
    Λ֬อ͠
    ͦͷྖҬͷΞυϨεΛฦ͢

    View Slide

  31. LLVM IRίʔυ: ݕࡧͷྗ
    func doAnExperiment() -> Int {
    let cpu = CPU.shared
    cpu.r0 = alloca()
    cpu.r3 = twofold(100)
    store(cpu.r3, address: cpu.r0)
    cpu.r5 = cpu.r3 + 1000
    store(cpu.r5, address: cpu.r0)
    return cpu.r5
    }

    View Slide

  32. LLVM IRίʔυ: ݕࡧͷྗ
    func doAnExperiment() -> Int {
    let cpu = CPU.shared
    cpu.r0 = alloca()
    cpu.r3 = twofold(100)
    store(cpu.r3, address: cpu.r0)
    cpu.r5 = cpu.r3 + 1000
    store(cpu.r5, address: cpu.r0)
    return cpu.r5
    }
    TUPSF͸ࢦఆΞυϨεͷ
    ϝϞϦʹ஋Λॻ͖ࠐΉ

    View Slide

  33. ͳΜͱɺϝϞϦʹ
    ॻ͖ࠐΜͰ͍·ͨ͠

    View Slide

  34. View Slide

  35. ͋ͬɺ࠷దԽ
    ͍ͯ͠ͳ͔ͬͨ

    View Slide

  36. ୊ࡐͷSwiftίʔυ
    func doAnExperiment() -> Int {
    var variable = twofold(100)
    variable += 1000
    return variable
    }
    func twofold(_ arg: Int) -> Int {
    return arg * 2
    }

    View Slide

  37. ୊ࡐͷSwiftίʔυ
    func doAnExperiment() -> Int {
    var variable = twofold(100)
    variable += 1000
    return variable
    }
    func twofold(_ arg: Int) -> Int {
    return arg * 2
    }
    ௥Ճ
    swiftc -emit-ir -O
    register2.swift
    > register2.ll

    View Slide

  38. LLVM IRίʔυ: ࠷దԽ
    define hidden swiftcc i64
    @"$s8register14doAnExperimentSiyF"()
    local_unnamed_addr #0 {
    entry:
    ret i64 1200
    }

    View Slide

  39. define hidden swiftcc i64
    @"$s8register14doAnExperimentSiyF"()
    local_unnamed_addr #0 {
    entry:
    ret i64 1200
    }
    LLVM IRίʔυ: ࠷దԽ

    View Slide

  40. define hidden swiftcc
    @"$s8register14doAnEx
    local_unnamed_addr #0
    entry:
    ret i64 1200
    }

    View Slide

  41. ͏Θͬ…ࢲͷSwift
    ࠷దԽ͞Ε͗͢…?

    View Slide

  42. ରࡦ

    View Slide

  43. ୊ࡐͷSwiftίʔυ: վ
    func doAnExperiment() -> Int {
    var variable = twofold(100)
    variable += 1000
    return variable
    }
    func twofoldImpl(_ arg: Int) -> Int {
    return arg * 2
    }
    let twofold = twofoldImpl

    View Slide

  44. ୊ࡐͷSwiftίʔυ: վ
    func doAnExperiment() -> Int {
    var variable = twofold(100)
    variable += 1000
    return variable
    }
    func twofoldImpl(_ arg: Int) -> Int {
    return arg * 2
    }
    let twofold = twofoldImpl

    View Slide

  45. ୊ࡐͷSwiftίʔυ: վ
    func doAnExperiment() -> Int {
    var variable = twofold(100)
    variable += 1000
    return variable
    }
    func twofoldImpl(_ arg: Int) -> Int {
    return arg * 2
    }
    let twofold = twofoldImpl
    ͜ΕΛܦ༝ͯ͠ݺͼग़͢

    View Slide

  46. func doAnExperiment() -> Int {
    var variable = twofold(100)
    variable += 1000
    return variable
    }
    func twofoldImpl(_ arg: Int) -> Int {
    return arg * 2
    }
    let twofold = twofoldImpl
    ୊ࡐͷSwiftίʔυ: վ
    swiftc -emit-ir -O
    register2.swift
    > register2.ll

    View Slide

  47. LLVM IRίʔυվ: ࠷దԽ
    define hidden swiftcc i64 @"$s9register214doAnExperimentSiyF"() local_unnamed_addr #1 {
    entry:
    %0 = load i64 (i64, %swift.refcounted*)*,
    i64 (i64, %swift.refcounted*)** bitcast (%swift.function* @"$s9register27twofoldyS2icvp"
    to i64 (i64, %swift.refcounted*)**),
    align 8
    %1 = load %swift.refcounted*,
    %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function*
    @"$s9register27twofoldyS2icvp", i64 0, i32 1),
    align 8
    %2 = tail call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #3
    %3 = tail call swiftcc i64 %0(i64 100, %swift.refcounted* swiftself %1)
    tail call void @swift_release(%swift.refcounted* %1) #3
    %4 = tail call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %3, i64 1000)
    %5 = extractvalue { i64, i1 } %4, 1
    br i1 %5, label %8, label %6
    ; :6: ; preds = %entry
    %7 = extractvalue { i64, i1 } %4, 0
    ret i64 %7
    ; :8: ; preds = %entry
    tail call void asm sideeffect "", "n"(i32 0) #3
    tail call void @llvm.trap()
    unreachable
    }

    View Slide

  48. define hidden swiftcc i64 @"$s9register214doAnExperimentSiyF"() local_unnamed_addr #1 {
    entry:
    %0 = load i64 (i64, %swift.refcounted*)*,
    i64 (i64, %swift.refcounted*)** bitcast (%swift.function* @"$s9register27twofoldyS2icvp"
    to i64 (i64, %swift.refcounted*)**),
    align 8
    %1 = load %swift.refcounted*,
    %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function*
    @"$s9register27twofoldyS2icvp", i64 0, i32 1),
    align 8
    %2 = tail call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #3
    %3 = tail call swiftcc i64 %0(i64 100, %swift.refcounted* swiftself %1)
    tail call void @swift_release(%swift.refcounted* %1) #3
    %4 = tail call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %3, i64 1000)
    %5 = extractvalue { i64, i1 } %4, 1
    br i1 %5, label %8, label %6
    ; :6: ; preds = %entry
    %7 = extractvalue { i64, i1 } %4, 0
    ret i64 %7
    ; :8: ; preds = %entry
    tail call void asm sideeffect "", "n"(i32 0) #3
    tail call void @llvm.trap()
    unreachable
    }
    LLVM IRίʔυվ: ࠷దԽ

    View Slide

  49. func doAnExperiment() -> Int {
    let cpu = CPU.shared
    cpu.r3 = twofold(100)
    cpu.r7 = cpu.r3 + 1000
    return cpu.r7
    }
    LLVM IRվ࠷దԽ: ݕࡧͷྗ

    View Slide

  50. func doAnExperiment() -> Int {
    let cpu = CPU.shared
    cpu.r3 = twofold(100)
    cpu.r7 = cpu.r3 + 1000
    return cpu.r7
    }
    LLVM IRվ࠷దԽ: ݕࡧͷྗ

    View Slide

  51. func doAnExperiment() -> Int {
    let cpu = CPU.shared
    cpu.r3 = twofold(100)
    cpu.r7 = cpu.r3 + 1000
    return cpu.r7
    }
    LLVM IRվ࠷దԽ: ݕࡧͷྗ

    View Slide

  52. func doAnExperiment() -> Int {
    let cpu = CPU.shared
    cpu.r3 = twofold(100)
    cpu.r7 = cpu.r3 + 1000
    return cpu.r7
    }
    LLVM IRվ࠷దԽ: ݕࡧͷྗ

    View Slide

  53. ແବͳϝϞϦॻ͖ࠐΈ
    ͳͦ͞͏

    View Slide

  54. ೦ͷҝ
    Ξηϯϒϥ΋֬ೝ

    View Slide

  55. ୊ࡐͷSwiftίʔυ: վ
    func doAnExperiment() -> Int {
    var variable: Int = twofold(100)
    variable += 1000
    return variable
    }
    func twofoldImpl(_ arg: Int) -> Int {
    return arg * 2
    }
    let twofold = twofoldImpl

    View Slide

  56. ୊ࡐͷSwiftίʔυ: վ
    func doAnExperiment() -> Int {
    var variable: Int = twofold(100)
    variable += 1000
    return variable
    }
    func twofoldImpl(_ arg: Int) -> Int {
    return arg * 2
    }
    let twofold = twofoldImpl
    xcrun -sdk iphoneos
    swiftc -emit-assembly
    -O -target arm64-
    apple-ios12.0
    register2.swift >
    register2.s

    View Slide

  57. ARM64Ξηϯϒϥվ: ࠷దԽ
    _$s9register214doAnExperimentSiyF:
    .cfi_startproc
    stp x20, x19, [sp, #-32]!
    stp x29, x30, [sp, #16]
    add x29, sp, #16
    .cfi_def_cfa w29, 16
    .cfi_offset w30, -8
    .cfi_offset w29, -16
    .cfi_offset w19, -24
    .cfi_offset w20, -32
    Lloh4:
    adrp x8, _$s9register27twofoldyS2icvp@PAGE
    Lloh5:
    add x8, x8, _$s9register27twofoldyS2icvp@PAGEOFF
    ldp x19, x20, [x8]
    mov x0, x20
    bl _swift_retain
    mov w0, #100
    blr x19
    mov x19, x0
    mov x0, x20
    bl _swift_release
    adds x0, x19, #1000
    b.vs LBB1_2
    ldp x29, x30, [sp, #16]
    ldp x20, x19, [sp], #32
    ret

    View Slide

  58. _$s9register214doAnExperimentSiyF:
    .cfi_startproc
    stp x20, x19, [sp, #-32]!
    stp x29, x30, [sp, #16]
    add x29, sp, #16
    .cfi_def_cfa w29, 16
    .cfi_offset w30, -8
    .cfi_offset w29, -16
    .cfi_offset w19, -24
    .cfi_offset w20, -32
    Lloh4:
    adrp x8, _$s9register27twofoldyS2icvp@PAGE
    Lloh5:
    add x8, x8, _$s9register27twofoldyS2icvp@PAGEOFF
    ldp x19, x20, [x8]
    mov x0, x20
    bl _swift_retain
    mov w0, #100
    blr x19
    mov x19, x0
    mov x0, x20
    bl _swift_release
    adds x0, x19, #1000
    b.vs LBB1_2
    ldp x29, x30, [sp, #16]
    ldp x20, x19, [sp], #32
    ret
    ARM64Ξηϯϒϥվ: ࠷దԽ

    View Slide

  59. ARM64Ξηϯϒϥ: ݕࡧͷྗ
    func doAnExperiment() -> Int {
    let cpu = CPU.shared
    cpu.w0 = 100
    cpu.x0 = twofold(cpu.w0)
    cpu.x19 = cpu.x0
    cpu.x0 = cpu.x19 + 1000
    return cpu.x0
    }

    View Slide

  60. ΍ͬͺΓແବͳϝϞϦ
    ॻ͖ࠐΈ͋Γ·ͤΜ

    View Slide

  61. ·ͱΊ
    • SwiftίϯύΠϥಡΊͳͯ͘΋׬
    શʹ͖͋ΒΊͳͯ͘Α͍
    • SwiftίϯύΠϥͷੌ͞Λମײ
    • ࠓճͷiOSDC΋ɺԿ͔Λ࢝ΊΔ

    ͖͔͚ͬʹ͍ͨ͠

    View Slide

  62. ࢀߟࢿྉ
    • WWDC16#416: Understanding Swift Performance
    • https://developer.apple.com/videos/play/wwdc2016/416/
    • Contributing to Open Source Swift
    • https://academy.realm.io/jp/posts/tryswift-jesse-squires-contributing-open-
    source-swift/
    • LLVM Language Reference Manual
    • https://llvm.org/docs/LangRef.html
    • Arm64(ARMv8) Assembly Programming (00)
    • https://www.mztn.org/dragon/arm6400idx.html

    View Slide