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

今から使えるSwiftとC++の新しいinterop手法

 今から使えるSwiftとC++の新しいinterop手法

omochimetaru

July 30, 2021
Tweet

More Decks by omochimetaru

Other Decks in Programming

Transcript

  1. ࠓ͔Β࢖͑Δ
    SwiftͱC++ͷ
    ৽͍͠interopख๏
    Θ͍Θ͍swiftc #29
    @omochimetaru
    1

    View Slide

  2. લճొஃ͸#20Ͱͨ͠
    • League of Legends ָ͍͠ʂ
    2

    View Slide

  3. ࠓ೔ͷ࿩
    C++ίʔυΛSwift͔Β࢖͏
    3

    View Slide

  4. ຊ໋
    • Interoperability between Swift and C++1
    • ଴ͯͳ͍ʂ
    1 https://github.com/apple/swift/blob/main/docs/CppInteroperabilityManifesto.md
    4

    View Slide

  5. libswift2
    Libswift is the part of the Swift compiler, which is implemented in
    Swift.
    ...
    Bridging SIL between C++ and Swift is toll-free, i.e. does not
    involve any "conversion" between C++ and Swift SIL.
    2 https://github.com/apple/swift/tree/main/libswift
    5

    View Slide

  6. Toll-free bridge
    • ͋ΔΦϒδΣΫτΛϝϞϦදݱͷม׵ແ͠Ͱͦͷ··ෳ਺ͷ
    ݴޠ͔Β࢖͏
    • CFDataRef (Cݴޠ) ͱ NSData (Objective-C)
    • NSData (Objective-C) ͱ Data (Swift)
    • [NEW] C++ ͱ Swift
    6

    View Slide

  7. C++ͰͷSILͷܕ
    7

    View Slide

  8. ValueBase
    class ValueBase :
    public SILNode,
    public SILAllocated
    {
    SILType Type;
    inline use_range getUses() const;
    }
    • ϕʔεΫϥε
    • SILNode: άϥϑػೳ + Swift Interop
    • SILAllocated: BumpΞϩέʔλ
    8

    View Slide

  9. SILNode
    class SILNode :
    public SwiftObjectHeader {}
    • SwiftObjectHeader: ޙड़
    9

    View Slide

  10. SILType
    class SILType {
    using ValueType = llvm::PointerIntPair;
    ValueType value;
    }
    • TypeBase͸ASTεςʔδͷܕ
    10

    View Slide

  11. SILValue
    class SILValue {
    ValueBase *Value;
    SILValue(const ValueBase *V = nullptr)
    }
    • Existential container
    11

    View Slide

  12. SILArgument
    class SILArgument : public ValueBase {}
    • BasicBlockͷҾ਺
    12

    View Slide

  13. SILInstruction
    class SILInstruction : public llvm::ilist_node {
    template
    void *operator new(size_t Bytes, const ContextTy &C,
    size_t Alignment = alignof(ValueBase)) {}
    SILNode *asSILNode();
    }
    class NonSingleValueInstruction : public SILInstruction, public SILNode {}
    ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
    • ࣮࣭తʹSILNodeΛ਌ʹ࣋ͭɻ
    13

    View Slide

  14. SILBasicBlock
    class SILBasicBlock :
    public llvm::ilist_node,
    public SILAllocated,
    public SwiftObjectHeader {
    using InstListType = llvm::iplist;
    TinyPtrVector ArgumentList;
    InstListType InstList;
    }
    14

    View Slide

  15. SwiftͰͷSILͷܕ
    15

    View Slide

  16. Value
    public protocol Value : AnyObject {
    var uses: UseList { get }
    var type: Type { get }
    }
    16

    View Slide

  17. Type
    public struct Type {
    var bridged: BridgedType
    }
    17

    View Slide

  18. Argument
    public class Argument : Value {}
    18

    View Slide

  19. Instruction
    public class Instruction {}
    public class SingleValueInstruction : Instruction, Value {}
    public final class MultipleValueInstructionResult : Value {}
    19

    View Slide

  20. BasicBlock
    final public class BasicBlock {}
    20

    View Slide

  21. Bridgingͷํ๏
    21

    View Slide

  22. ΫϥεͷϨΠΞ΢τޓ׵ΛऔΔ
    • SwiftͷclassͷΠϯελϯε͸ઌ಄ʹisaϙΠϯλͱࢀরΧ΢ϯλΛ࣋ͭ
    • C++ଆͰޓ׵ੑͷ͋Δߏ଄Λ࣋ͨͤΔ
    struct BridgedSwiftObject {
    SwiftMetatype metatype;
    int64_t refCounts;
    }
    struct SwiftObjectHeader : BridgedSwiftObject {}
    • ͞Βʹ͜ͷϑΟʔϧυΛͳΜͱ͔͢Δ
    22

    View Slide

  23. SwiftObjectHeaderͷ஫ೖ
    class SILNode: SwiftObjectHeader {}
    class ValueBase: SILNode {}
    class SILArgument: ValueBase {}
    class SingleValueInstruction: ValueBase, SILInstruction {}
    class NonSingleValueInstruction: SILInstruction, SILNode {}
    class SILBasicBlock: SwiftObjectHeader {}
    • Կ͔͠ΒSwiftObjectHeaderʹͨͲΓண͘
    23

    View Slide

  24. ࢀরΧ΢ϯλΛ௵͢
    ࢀরΧ΢ϯτͷಛఆͷϏοτΛཱ͓ͯͯ͘ͱSwift͸ແࢹ͢Δ3
    /*
    HeapObject {
    isa
    InlineRefCounts {
    atomic {
    strong RC + unowned RC + flags
    OR
    HeapObjectSideTableEntry*
    }
    }
    }
    */
    struct RefCountBitOffsets<8> {
    /*
    ---Immortal case---
    All bits set, the object does not deallocate or have a refcount
    */
    }
    3 https://github.com/apple/swift/blob/main/stdlib/public/SwiftShims/RefCount.h
    24

    View Slide

  25. struct SwiftObjectHeader : BridgedSwiftObject {
    SwiftObjectHeader(SwiftMetatype metatype) {
    this->metatype = metatype;
    this->refCounts = ~(uint64_t)0;
    }
    }
    25

    View Slide

  26. ϝλΫϥεΛ஫ೖ͢Δ
    ࣄલʹSwift͔Βऔ͖ͬͯͯC++ʹ౉͢
    // Swift
    @_cdecl("initializeLibSwift")
    public func initializeLibSwift() {
    registerSILClasses()
    registerSwiftPasses()
    }
    private func register(_ cl: T.Type) {
    String(describing: cl).withBridgedStringRef { nameStr in
    let metatype = unsafeBitCast(cl, to: SwiftMetatype.self)
    registerBridgedClass(nameStr, metatype)
    }
    }
    public func registerSILClasses() {
    ...
    register(BasicBlock.self)
    ...
    register(StoreInst.self)
    register(CopyAddrInst.self)
    register(DeallocStackInst.self)
    ...
    }
    26

    View Slide

  27. // C++
    SwiftMetatype nodeMetatypes[(unsigned)SILNodeKind::Last_SILNode + 1];
    void registerBridgedClass(BridgedStringRef className, SwiftMetatype metatype) {
    StringRef clName = getStringRef(className);
    if (clName == "BasicBlock")
    return SILBasicBlock::registerBridgedMetatype(metatype);
    ...
    nodeMetatypes[(unsigned)kind] = metatype;
    }
    ௚઀౉͔͢ςʔϒϧʹ͠·͏ɻ
    27

    View Slide

  28. class SILBasicBlock {
    static SwiftMetatype registeredMetatype;
    static void registerBridgedMetatype(SwiftMetatype metatype) {
    registeredMetatype = metatype;
    }
    }
    SILBasicBlock::SILBasicBlock() :
    SwiftObjectHeader(registeredMetatype), Parent(nullptr) {}
    28

    View Slide

  29. class SILNode {
    SILNode(SILNodeKind kind) : SwiftObjectHeader(getSILNodeMetatype(kind)) {}
    }
    SwiftMetatype SILNode::getSILNodeMetatype(SILNodeKind kind) {
    SwiftMetatype metatype = nodeMetatypes[(unsigned)kind];
    return metatype;
    }
    29

    View Slide

  30. Bridgingͷ࣮૷
    30

    View Slide

  31. ϒϦοδ༻ͷܕΛఆٛ͢Δ
    C࣮૷ͳͷͰC++ͱSwift͔Β࢖͑Δ
    typedef struct {
    void * _Nullable typePtr;
    } BridgedType;
    typedef struct {
    SwiftObject obj;
    } BridgedValue;
    typedef struct {
    SwiftObject obj;
    } BridgedArgument;
    typedef struct {
    SwiftObject obj;
    } BridgedInstruction;
    typedef struct {
    SwiftObject obj;
    } BridgedBasicBlock;
    31

    View Slide

  32. struct BridgedSwiftObject {
    SwiftMetatype metatype;
    int64_t refCounts;
    };
    typedef struct BridgedSwiftObject * _Nonnull SwiftObject;
    32

    View Slide

  33. NullabilityͷϒϦοδ
    typedef struct BridgedSwiftObject * _Nullable OptionalSwiftObject;
    typedef struct {
    OptionalSwiftObject obj;
    } OptionalBridgedInstruction;
    33

    View Slide

  34. Swift͔ΒC++ͷݺͼग़͠
    final public class BasicBlock {
    public var instructions: List {
    List(startAt: SILBasicBlock_firstInst(bridged).instruction)
    }
    var bridged: BridgedBasicBlock { BridgedBasicBlock(obj: SwiftObject(self)) }
    }
    34

    View Slide

  35. SwiftObject
    SwiftObjectΛSwiftͰ࠶ఆٛɻBridgedSwiftObject͸ڞ༗ɻ
    AnyObject ͷϙΠϯλΛ௚ม׵
    !
    public typealias SwiftObject = UnsafeMutablePointer
    extension UnsafeMutablePointer where Pointee == BridgedSwiftObject {
    init(_ object: T) {
    let ptr = Unmanaged.passUnretained(object).toOpaque()
    self = ptr.bindMemory(to: BridgedSwiftObject.self, capacity: 1)
    }
    func getAs(_ objectType: T.Type) -> T {
    return Unmanaged.fromOpaque(self).takeUnretainedValue()
    }
    }
    35

    View Slide

  36. Optional
    extension Optional where Wrapped == UnsafeMutablePointer {
    func getAs(_ objectType: T.Type) -> T? {
    if let pointer = self {
    return Unmanaged.fromOpaque(pointer).takeUnretainedValue()
    }
    return nil
    }
    }
    36

    View Slide

  37. ࠶ܝ
    // Swift
    final public class BasicBlock {
    var bridged: BridgedBasicBlock { BridgedBasicBlock(obj: SwiftObject(self)) }
    }
    // C++
    struct BridgedSwiftObject {
    SwiftMetatype metatype;
    int64_t refCounts;
    };
    typedef struct BridgedSwiftObject * _Nonnull SwiftObject;
    typedef struct {
    SwiftObject obj;
    } BridgedBasicBlock;
    37

    View Slide

  38. ॲཧͷϒϦοδ
    OptionalBridgedInstruction SILBasicBlock_firstInst(BridgedBasicBlock block) {
    SILBasicBlock *b = castToBasicBlock(block);
    if (b->empty())
    return {nullptr};
    return {b->front().asSILNode()};
    }
    inline SILBasicBlock *castToBasicBlock(BridgedBasicBlock block) {
    return static_cast(block.obj);
    }
    38

    View Slide

  39. ࠶ܝ
    class SILNode: SwiftObjectHeader {}
    struct SwiftObjectHeader : BridgedSwiftObject {}
    typedef struct BridgedSwiftObject * _Nullable OptionalSwiftObject;
    typedef struct {
    OptionalSwiftObject obj;
    } OptionalBridgedInstruction;
    39

    View Slide

  40. ࠶ܝ
    final public class BasicBlock {
    public var instructions: List {
    List(startAt: SILBasicBlock_firstInst(bridged).instruction)
    }
    var bridged: BridgedBasicBlock { BridgedBasicBlock(obj: SwiftObject(self)) }
    }
    40

    View Slide

  41. SwiftଆͰܕ෇͖ͰΞΫηε
    extension BridgedInstruction {
    public var instruction: Instruction { obj.getAs(Instruction.self) }
    public func getAs(_ instType: T.Type) -> T { obj.getAs(T.self) }
    }
    extension OptionalBridgedInstruction {
    var instruction: Instruction? { obj.getAs(Instruction.self) }
    }
    41

    View Slide

  42. Type
    // Swift
    public struct Type {
    var bridged: BridgedType
    public var isAddress: Bool { SILType_isAddress(bridged) != 0 }
    public var isObject: Bool { !isAddress }
    }
    // C++
    typedef struct {
    void * _Nullable typePtr;
    } BridgedType;
    42

    View Slide

  43. จࣈྻ
    // C++
    typedef struct {
    const unsigned char * _Nullable data;
    size_t length;
    } BridgedStringRef;
    // Swift
    extension BridgedStringRef {
    public var string: String {
    let buffer = UnsafeBufferPointer(start: data, count: Int(length))
    return String(decoding: buffer, as: UTF8.self)
    }
    func takeString() -> String {
    let str = string
    freeBridgedStringRef(self)
    return str
    }
    }
    43

    View Slide

  44. callerʹղ์੹೚͕౉Δ৔߹͕͋Δ
    inline BridgedStringRef getBridgedStringRef(llvm::StringRef str) {
    return { (const unsigned char *)str.data(), str.size() };
    }
    /// Copies the string in an malloc'ed memory and the caller is responsible for
    /// freeing it.
    inline BridgedStringRef getCopiedBridgedStringRef(std::string str,
    bool removeTrailingNewline = false) {
    // A couple of mallocs are needed for passing a std::string to libswift. But
    // it's currently only used or debug descriptions. So, its' maybe not so bad -
    // for now.
    // TODO: find a better way to pass std::strings to libswift.
    StringRef strRef(str);
    if (removeTrailingNewline)
    strRef.consume_back("\n");
    llvm::MallocAllocator allocator;
    StringRef copy = strRef.copy(allocator);
    return getBridgedStringRef(copy);
    }
    44

    View Slide

  45. public protocol Value {}
    extension Value {
    public var description: String {
    SILNode_debugDescription(bridgedNode).takeString()
    }
    var bridgedNode: BridgedNode {
    BridgedNode(obj: SwiftObject(self as AnyObject))
    }
    }
    45

    View Slide

  46. Πϯελϯεੜ੒
    ઐ༻ͷBuilderΛ௨ͯ͠C++ଆͰੜ੒͢Δ
    final public class BuiltinInst : SingleValueInstruction {}
    public class SingleValueInstruction : Instruction, Value {}
    public class Instruction {}
    public struct Builder {
    public func createBuiltinBinaryFunction(name: String,
    operandType: Type, resultType: Type, arguments: [Value]) -> BuiltinInst {
    notifyInstructionsChanged()
    return arguments.withBridgedValues { valuesRef in
    return name.withBridgedStringRef { nameStr in
    let bi = SILBuilder_createBuiltinBinaryFunction(
    bridgedInsPoint, location.bridgedLocation, nameStr,
    operandType.bridged, resultType.bridged, valuesRef)
    return bi.getAs(BuiltinInst.self)
    }
    }
    }
    }
    46

    View Slide

  47. ·ͱΊ
    • C++ଆͰSwiftͷΦϒδΣΫτϔομΛܧঝͤ͞Δ
    • ϝλλΠϓ͸ؤுͬͯSwift͔Β౉͢
    • ࢀরΧ΢ϯλΛimmortalϞʔυʹͯ͠C++ͰϝϞϦ؅ཧ͢Δ
    • Cߏ଄ମΛ࢖ͬͯϒϦοδΠϯλʔϑΣʔεʹܕΛ͚ͭΔ
    47

    View Slide