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ίʔυ func doAnExperiment() -> Int { var variable = twofold(100)

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

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

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

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

    variable += 1000 return variable } func twofold(_ arg: Int) -> Int { return arg * 2 }
  6. 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
  7. 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 }
  8. 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 }
  9. 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 }
  10. 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͸ϝϞϦ ελοΫ Λ֬อ͠ ͦͷྖҬͷΞυϨεΛฦ͢
  11. 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 }
  12. 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͸ࢦఆΞυϨεͷ ϝϞϦʹ஋Λॻ͖ࠐΉ
  13. ୊ࡐͷSwiftίʔυ func doAnExperiment() -> Int { var variable = twofold(100)

    variable += 1000 return variable } func twofold(_ arg: Int) -> Int { return arg * 2 }
  14. ୊ࡐͷ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
  15. ୊ࡐͷSwiftίʔυ: վ func doAnExperiment() -> Int { var variable =

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

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

    twofold(100) variable += 1000 return variable } func twofoldImpl(_ arg: Int) -> Int { return arg * 2 } let twofold = twofoldImpl ͜ΕΛܦ༝ͯ͠ݺͼग़͢
  18. 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
  19. 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 }
  20. 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ίʔυվ: ࠷దԽ
  21. func doAnExperiment() -> Int { let cpu = CPU.shared cpu.r3

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

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

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

    = twofold(100) cpu.r7 = cpu.r3 + 1000 return cpu.r7 } LLVM IRվ࠷దԽ: ݕࡧͷྗ
  25. ୊ࡐͷSwiftίʔυ: վ func doAnExperiment() -> Int { var variable: Int

    = twofold(100) variable += 1000 return variable } func twofoldImpl(_ arg: Int) -> Int { return arg * 2 } let twofold = twofoldImpl
  26. ୊ࡐͷ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
  27. 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
  28. _$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Ξηϯϒϥվ: ࠷దԽ
  29. 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 }
  30. ࢀߟࢿྉ • 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