$30 off During Our Annual Pro Sale. View Details »

Getting Under Swift’s Skin

Greg Heo
February 07, 2018

Getting Under Swift’s Skin

Staying on the surface, we deal with the Swift language, runtime, and standard library all the time. In this talk, we’ll go one small level down and look underneath the surface: Swift Intermediate Language (SIL), reflection, and debug-enabled toolchains. What can we learn, why should we bother, and most importantly, how can it help improve our programming lives?

Greg Heo

February 07, 2018
Tweet

More Decks by Greg Heo

Other Decks in Technology

Transcript

  1. @gregheo
    NSMeetup
    February 7, 2018

    View Slide

  2. Stack views
    Auto Layout
    Frame calculations
    UIViews
    CALayers
    Core Animation
    OpenGL
    Metal
    putpixel()
    OLEDs
    Individual photons
    Quantum physics
    GUI toolkits
    Standard library
    Unix processes
    fork vs exec
    bash
    Kernel modules
    Operating systems
    Linkers
    Compilers
    Assembly
    Toggle switches
    emacs

    View Slide

  3. View Slide

  4. — Jesse Squires

    View Slide

  5. Swift
    Runtime
    Instances
    Standard
    Library
    SIL Reflection
    Debug
    Toolchains

    View Slide

  6. View Slide

  7. SIL IR Object
    CodeGen
    IRGen
    AST
    Sema
    SILGen
    Parse

    View Slide

  8. View Slide

  9. 00 e3
    8e c0
    〇〇〇
    〇〇〇〇〇〇〇〇
    〇〇〇〇〇〇
    〇〇〇〇
    add bl,ah
    mov es,eax

    View Slide

  10. ASM Binary

    View Slide

  11. ASM
    C Binary
    C++

    View Slide

  12. ASM
    C Binary
    C++
    Obj-C

    View Slide

  13. ASM
    C Binary
    C++
    Obj-C

    View Slide

  14. ASM
    C Binary
    C++
    IR
    Obj-C

    View Slide

  15. printf(“hello world!”);
    subq $32, %rsp
    leaq L_.str(%rip), %rax
    movl $0, -4(%rbp)
    movl %edi, -8(%rbp)
    movq %rsi, -16(%rbp)
    movq %rax, %rdi
    movb $0, %al
    callq_printf
    define i32 @main(i32, i8**) #0 {
    %3 = alloca i32, align 4
    %4 = alloca i32, align 4
    %5 = alloca i8**, align 8
    store i32 0, i32* %3, align 4
    store i32 %0, i32* %4, align 4
    store i8** %1, i8*** %5, align 8
    %6 = call i32 (i8*, …)

    @printf(i8* getelementptr inbounds…
    ret i32 0
    }

    View Slide

  16. ASM
    C Binary
    C++
    IR
    Obj-C

    View Slide

  17. SIL IR …

    View Slide

  18. View Slide

  19. class NSMeetup {
    func sayHello() {
    print("Hello")
    }
    }
    let m = NSMeetup()
    m.sayHello()

    View Slide

  20. sil_stage canonical
    import Builtin
    import Swift
    import SwiftShims
    // m
    sil_global hidden [let] @_T06simple1mAA8NSMeetupCv : $NSMeetup
    // _swiftEmptyArrayStorage
    sil_global [serialized] @_swiftEmptyArrayStorage :
    $_SwiftEmptyArrayStorage
    // main
    sil @main : $@convention(c) (Int32,
    UnsafeMutablePointer>>) -> Int32 {
    bb0(%0 : $Int32, %1 :

    View Slide

  21. sil_vtable NSMeetup {
    #NSMeetup.sayHello!1: (NSMeetup) -> () -> () :
    _T06simple8NSMeetupC8sayHelloyyF // NSMeetup.sayHello()
    #NSMeetup.init!initializer.1: (NSMeetup.Type) -> () -> NSMeetup :
    _T06simple8NSMeetupCACycfc // NSMeetup.init()
    #NSMeetup.deinit!deallocator: _T06simple8NSMeetupCfD //
    NSMeetup.__deallocating_deinit
    }
    sil_vtable _ContiguousArrayStorage {
    #_SwiftNativeNSArrayWithContiguousStorage.withUnsafeBufferOfObjects!1
    (_SwiftNativeNSArrayWithContiguousStorage) ->
    ((UnsafeBufferPointer) throws -> R) throws -> R :
    _T0s27_ContiguousArrayStorageBaseC25withUnsafeBufferOfObjectsxxSRyyXlGK
    KlF // _ContiguousArrayStorageBase.withUnsafeBufferOfObjects(_:)
    #_ContiguousArrayStorageBase._withVerbatimBridgedUnsafeBuffer!1:
    (_ContiguousArrayStorageBase) -> ((UnsafeBufferPointer)

    View Slide

  22. // NSMeetup.sayHello()
    sil hidden @_T06simple8NSMeetupC8sayHelloyyF : $@convention(method)
    (@guaranteed NSMeetup) -> () {
    // %0 // user: %1
    bb0(%0 : $NSMeetup):
    debug_value %0 : $NSMeetup, let, name "self", argno 1 // id: %1
    // function_ref print(_:separator:terminator:)
    %2 = function_ref @_T0s5printySayypGd_SS9separatorSS10terminatortF :
    $@convention(thin) (@owned Array, @owned String, @owned String) ->
    () // user: %23
    %3 = integer_literal $Builtin.Word, 1 // user: %5
    // function_ref specialized _allocateUninitializedArray(_:)
    %4 = function_ref
    @_T0s27_allocateUninitializedArraySayxG_BptBwlFyp_Tgq5 :
    $@convention(thin) (Builtin.Word) -> (@owned Array,
    Builtin.RawPointer) // user: %5
    %5 = apply %4(%3) : $@convention(thin) (Builtin.Word) -> (@owned

    View Slide

  23. $ xcrun swift-demangle

    _T0s5printySayypGd_SS9separatorSS10terminatortF

    Swift.print(Any...,

    separator: Swift.String,

    terminator: Swift.String) -> ()

    View Slide

  24. retain_value %6 : $Array // id: %7
    %8 = tuple_extract %5 : $(Array, Builtin.RawPointer), 1 // user:
    %10
    release_value %5 : $(Array, Builtin.RawPointer) // id: %9
    %10 = pointer_to_address %8 : $Builtin.RawPointer to [strict] $*Any /
    user: %11
    %11 = init_existential_addr %10 : $*Any, $String // user: %18
    %12 = string_literal utf8 "Hello" // user: %17
    %13 = integer_literal $Builtin.Word, 5 // user: %17
    %14 = integer_literal $Builtin.Int1, -1 // user: %17
    %15 = metatype $@thin String.Type // user: %17
    // function_ref
    String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
    %16 = function_ref
    @_T0S2SBp21_builtinStringLiteral_Bw17utf8CodeUnitCountBi1_7isASCIItcfC
    $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1,
    @thin String.Type) -> @owned String // user: %17
    %17 = apply %16(%12, %13, %14, %15) : $@convention(method)

    View Slide

  25. class NSMeetup {
    func sayHello() {
    print("Hello")
    }
    }
    // let m = NSMeetup()

    var m = NSMeetup()

    m.sayHello()

    View Slide

  26. %9 = load %8 : $*NSMeetup
    %10 = class_method %9 : $NSMeetup, #NSMeetup.sayHello!1 : (NSMeetup) ->
    () -> (), $@convention(method) (@guaranteed NSMeetup) -> ()
    %11 = apply %10(%9) : $@convention(method) (@guaranteed NSMeetup) -> ()

    View Slide

  27. %8 = begin_access [read] [dynamic] %3 : $*NSMeetup
    %9 = load %8 : $*NSMeetup
    strong_retain %9 : $NSMeetup
    end_access %8 : $*NSMeetup
    %10 = class_method %9 : $NSMeetup, #NSMeetup.sayHello!1 : (NSMeetup) ->
    () -> (), $@convention(method) (@guaranteed NSMeetup) -> ()
    %11 = apply %10(%9) : $@convention(method) (@guaranteed NSMeetup) -> ()
    strong_release %9 : $NSMeetup

    View Slide

  28. call void @llvm.lifetime.start(i64 -1, i8* %13)
    call void @swift_beginAccess(i8* bitcast (%TSi*
    @_T011simpleprint1aSiv to i8*), [24 x i8]* %access-scratch, i64
    0, i8* null) #6
    %14 = load i64, i64* getelementptr inbounds (%TSi, %TSi*
    @_T011simpleprint1aSiv, i32 0, i32 0), align 8
    call void @swift_endAccess([24 x i8]* %access-scratch) #6
    %15 = bitcast [24 x i8]* %access-scratch to i8*
    call void @llvm.lifetime.end(i64 -1, i8* %15)
    %._value = getelementptr inbounds %TSi, %TSi* %12, i32 0, i32
    0
    store i64 %14, i64* %._value, align 8
    %16 = call swiftcc { i64, i64, i64 }
    @_T0s5printySayypGd_SS9separatorSS10terminatortFfA0_()
    %17 = extractvalue { i64, i64, i64 } %16, 0
    %18 = extractvalue { i64, i64, i64 } %16, 1
    %19 = extractvalue { i64, i64, i64 } %16, 2

    View Slide

  29. call void @llvm.lifetime.start(i64 -1, i8* %13)
    call void @swift_beginAccess(i8* bitcast (%TSi*
    @_T011simpleprint1aSiv to i8*), [24 x i8]* %access-scratch, i64
    0, i8* null) #6


    call void @#__tsan_read8(i8* bitcast (%TSi*
    @_T011simpleprint1aSiv to i8*))
    %14 = load i64, i64* getelementptr inbounds (%TSi, %TSi*
    @_T011simpleprint1aSiv, i32 0, i32 0), align 8
    call void @swift_endAccess([24 x i8]* %access-scratch) #6
    %15 = bitcast [24 x i8]* %access-scratch to i8*
    call void @llvm.lifetime.end(i64 -1, i8* %15)
    %._value = getelementptr inbounds %TSi, %TSi* %12, i32 0, i32
    0
    store i64 %14, i64* %._value, align 8
    %16 = call swiftcc { i64, i64, i64 }
    @_T0s5printySayypGd_SS9separatorSS10terminatortFfA0_()

    View Slide

  30. • Your code, lowered
    • ARC & memory management
    • Change code; see diffs on SIL

    View Slide

  31. View Slide

  32. Queries Modifications Construction

    View Slide

  33. Reflection Demo

    View Slide

  34. • Objective-C runtime functions
    • Mirror for “read-only reflection”
    • Direct pointer access
    • More advanced reflection to come?

    View Slide

  35. ASM
    C Binary
    C++
    IR
    Obj-C

    View Slide

  36. Language Standard Library

    View Slide

  37. Standard library demo

    View Slide

  38. • Standard library is open source!
    • Standard library code in the debugger
    • Running custom Xcode toolchains

    View Slide

  39. Swift
    Runtime
    Instances
    Standard
    Library
    SIL Reflection
    Debug
    Toolchains

    View Slide

  40. So What?

    View Slide

  41. View Slide

  42. View Slide

  43. View Slide

  44. https://gregheo.com

    View Slide

  45. Getting Under

    Swift’s Skin @gregheo
    NSMeetup
    February 7, 2018

    View Slide