Save 37% off PRO during our Black Friday Sale! »

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

F6f299e604f7a8a540bbe7b86057522f?s=47 rikusouda
September 07, 2019

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

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

F6f299e604f7a8a540bbe7b86057522f?s=128

rikusouda

September 07, 2019
Tweet

Transcript

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

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

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

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

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

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

  7. ୊ࡐͷSwiftίʔυ func doAnExperiment() -> Int { var variable = twofold(100)

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

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

    variable += 1000 return variable } func twofold(_ arg: Int) -> Int { return arg * 2 } Λฦ͢
  10. func doAnExperiment() -> Int { var variable = twofold(100) variable

    += 1000 return variable } func twofold(_ arg: Int) -> Int { return arg * 2 } ୊ࡐͷSwiftίʔυ ͜ͷWBSJBCMF͸ɺϝϞϦʹ ॻ͖ࠐ·ͳͯ͘΋ྑ͍ͱࢥͬͨ ϨδελͰࣄ଍ΓΔͷͰ
  11. ίϯύΠϥͷίʔυΛ ಡΊ͹Θ͔ΔΒ͍͠

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

  13. ௐ΂͖͔͚ͨͬ

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

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

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

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

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

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

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

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

  23. ୊ࡐͷSwiftίʔυ func doAnExperiment() -> Int { var variable = twofold(100)

    variable += 1000 return variable } func twofold(_ arg: Int) -> Int { return arg * 2 }
  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
  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 ; <label>: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 ; <label>:9: ; preds = %entry call void @llvm.trap() unreachable }
  26. ΍ͬͺΓ೉͍͠

  27. ݕࡧΛۦ࢖ͯ͠ղಡ

  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 ; <label>: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 ; <label>:9: ; preds = %entry call void @llvm.trap() unreachable }
  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 }
  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͸ϝϞϦ ελοΫ Λ֬อ͠ ͦͷྖҬͷΞυϨεΛฦ͢
  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 }
  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͸ࢦఆΞυϨεͷ ϝϞϦʹ஋Λॻ͖ࠐΉ
  33. ͳΜͱɺϝϞϦʹ ॻ͖ࠐΜͰ͍·ͨ͠

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

  36. ୊ࡐͷSwiftίʔυ func doAnExperiment() -> Int { var variable = twofold(100)

    variable += 1000 return variable } func twofold(_ arg: Int) -> Int { return arg * 2 }
  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
  38. LLVM IRίʔυ: ࠷దԽ define hidden swiftcc i64 @"$s8register14doAnExperimentSiyF"() local_unnamed_addr #0

    { entry: ret i64 1200 }
  39. define hidden swiftcc i64 @"$s8register14doAnExperimentSiyF"() local_unnamed_addr #0 { entry: ret

    i64 1200 } LLVM IRίʔυ: ࠷దԽ
  40. define hidden swiftcc @"$s8register14doAnEx local_unnamed_addr #0 entry: ret i64 1200

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

  42. ରࡦ

  43. ୊ࡐͷSwiftίʔυ: վ func doAnExperiment() -> Int { var variable =

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

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

    twofold(100) variable += 1000 return variable } func twofoldImpl(_ arg: Int) -> Int { return arg * 2 } let twofold = twofoldImpl ͜ΕΛܦ༝ͯ͠ݺͼग़͢
  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
  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 ; <label>:6: ; preds = %entry %7 = extractvalue { i64, i1 } %4, 0 ret i64 %7 ; <label>:8: ; preds = %entry tail call void asm sideeffect "", "n"(i32 0) #3 tail call void @llvm.trap() unreachable }
  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 ; <label>:6: ; preds = %entry %7 = extractvalue { i64, i1 } %4, 0 ret i64 %7 ; <label>:8: ; preds = %entry tail call void asm sideeffect "", "n"(i32 0) #3 tail call void @llvm.trap() unreachable } LLVM IRίʔυվ: ࠷దԽ
  49. func doAnExperiment() -> Int { let cpu = CPU.shared cpu.r3

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

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

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

    = twofold(100) cpu.r7 = cpu.r3 + 1000 return cpu.r7 } LLVM IRվ࠷దԽ: ݕࡧͷྗ
  53. ແବͳϝϞϦॻ͖ࠐΈ ͳͦ͞͏

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

  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
  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
  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
  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Ξηϯϒϥվ: ࠷దԽ
  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 }
  60. ΍ͬͺΓແବͳϝϞϦ ॻ͖ࠐΈ͋Γ·ͤΜ

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

  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