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

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

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

3781f49ea2c76d6ecf0c6cda46096d49?s=128

omochimetaru

July 30, 2021
Tweet

More Decks by omochimetaru

Other Decks in Programming

Transcript

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

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

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

  4. ຊ໋ • Interoperability between Swift and C++1 • ଴ͯͳ͍ʂ 1

    https://github.com/apple/swift/blob/main/docs/CppInteroperabilityManifesto.md 4
  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
  6. Toll-free bridge • ͋ΔΦϒδΣΫτΛϝϞϦදݱͷม׵ແ͠Ͱͦͷ··ෳ਺ͷ ݴޠ͔Β࢖͏ • CFDataRef (Cݴޠ) ͱ NSData

    (Objective-C) • NSData (Objective-C) ͱ Data (Swift) • [NEW] C++ ͱ Swift 6
  7. C++ͰͷSILͷܕ 7

  8. ValueBase class ValueBase : public SILNode, public SILAllocated<ValueBase> { SILType

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

    9
  10. SILType class SILType { using ValueType = llvm::PointerIntPair<TypeBase *, 2,

    unsigned>; ValueType value; } • TypeBase͸ASTεςʔδͷܕ 10
  11. SILValue class SILValue { ValueBase *Value; SILValue(const ValueBase *V =

    nullptr) } • Existential container 11
  12. SILArgument class SILArgument : public ValueBase {} • BasicBlockͷҾ਺ 12

  13. SILInstruction class SILInstruction : public llvm::ilist_node<SILInstruction> { template <typename ContextTy>

    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
  14. SILBasicBlock class SILBasicBlock : public llvm::ilist_node<SILBasicBlock>, public SILAllocated<SILBasicBlock>, public SwiftObjectHeader

    { using InstListType = llvm::iplist<SILInstruction>; TinyPtrVector<SILArgument *> ArgumentList; InstListType InstList; } 14
  15. SwiftͰͷSILͷܕ 15

  16. Value public protocol Value : AnyObject { var uses: UseList

    { get } var type: Type { get } } 16
  17. Type public struct Type { var bridged: BridgedType } 17

  18. Argument public class Argument : Value {} 18

  19. Instruction public class Instruction {} public class SingleValueInstruction : Instruction,

    Value {} public final class MultipleValueInstructionResult : Value {} 19
  20. BasicBlock final public class BasicBlock {} 20

  21. Bridgingͷํ๏ 21

  22. ΫϥεͷϨΠΞ΢τޓ׵ΛऔΔ • SwiftͷclassͷΠϯελϯε͸ઌ಄ʹisaϙΠϯλͱࢀরΧ΢ϯλΛ࣋ͭ • C++ଆͰޓ׵ੑͷ͋Δߏ଄Λ࣋ͨͤΔ struct BridgedSwiftObject { SwiftMetatype metatype;

    int64_t refCounts; } struct SwiftObjectHeader : BridgedSwiftObject {} • ͞Βʹ͜ͷϑΟʔϧυΛͳΜͱ͔͢Δ 22
  23. SwiftObjectHeaderͷ஫ೖ class SILNode: SwiftObjectHeader {} class ValueBase: SILNode {} class

    SILArgument: ValueBase {} class SingleValueInstruction: ValueBase, SILInstruction {} class NonSingleValueInstruction: SILInstruction, SILNode {} class SILBasicBlock: SwiftObjectHeader {} • Կ͔͠ΒSwiftObjectHeaderʹͨͲΓண͘ 23
  24. ࢀরΧ΢ϯλΛ௵͢ ࢀরΧ΢ϯτͷಛఆͷϏοτΛཱ͓ͯͯ͘ͱSwift͸ແࢹ͢Δ3 /* HeapObject { isa InlineRefCounts { atomic<InlineRefCountBits> {

    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
  25. struct SwiftObjectHeader : BridgedSwiftObject { SwiftObjectHeader(SwiftMetatype metatype) { this->metatype =

    metatype; this->refCounts = ~(uint64_t)0; } } 25
  26. ϝλΫϥεΛ஫ೖ͢Δ ࣄલʹSwift͔Βऔ͖ͬͯͯC++ʹ౉͢ // Swift @_cdecl("initializeLibSwift") public func initializeLibSwift() { registerSILClasses()

    registerSwiftPasses() } private func register<T: AnyObject>(_ 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
  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
  28. class SILBasicBlock { static SwiftMetatype registeredMetatype; static void registerBridgedMetatype(SwiftMetatype metatype)

    { registeredMetatype = metatype; } } SILBasicBlock::SILBasicBlock() : SwiftObjectHeader(registeredMetatype), Parent(nullptr) {} 28
  29. class SILNode { SILNode(SILNodeKind kind) : SwiftObjectHeader(getSILNodeMetatype(kind)) {} } SwiftMetatype

    SILNode::getSILNodeMetatype(SILNodeKind kind) { SwiftMetatype metatype = nodeMetatypes[(unsigned)kind]; return metatype; } 29
  30. Bridgingͷ࣮૷ 30

  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
  32. struct BridgedSwiftObject { SwiftMetatype metatype; int64_t refCounts; }; typedef struct

    BridgedSwiftObject * _Nonnull SwiftObject; 32
  33. NullabilityͷϒϦοδ typedef struct BridgedSwiftObject * _Nullable OptionalSwiftObject; typedef struct {

    OptionalSwiftObject obj; } OptionalBridgedInstruction; 33
  34. Swift͔ΒC++ͷݺͼग़͠ final public class BasicBlock { public var instructions: List<Instruction>

    { List(startAt: SILBasicBlock_firstInst(bridged).instruction) } var bridged: BridgedBasicBlock { BridgedBasicBlock(obj: SwiftObject(self)) } } 34
  35. SwiftObject SwiftObjectΛSwiftͰ࠶ఆٛɻBridgedSwiftObject͸ڞ༗ɻ AnyObject ͷϙΠϯλΛ௚ม׵ ! public typealias SwiftObject = UnsafeMutablePointer<BridgedSwiftObject>

    extension UnsafeMutablePointer where Pointee == BridgedSwiftObject { init<T: AnyObject>(_ object: T) { let ptr = Unmanaged.passUnretained(object).toOpaque() self = ptr.bindMemory(to: BridgedSwiftObject.self, capacity: 1) } func getAs<T: AnyObject>(_ objectType: T.Type) -> T { return Unmanaged<T>.fromOpaque(self).takeUnretainedValue() } } 35
  36. Optional extension Optional where Wrapped == UnsafeMutablePointer<BridgedSwiftObject> { func getAs<T:

    AnyObject>(_ objectType: T.Type) -> T? { if let pointer = self { return Unmanaged<T>.fromOpaque(pointer).takeUnretainedValue() } return nil } } 36
  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
  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<SILBasicBlock *>(block.obj); } 38
  39. ࠶ܝ class SILNode: SwiftObjectHeader {} struct SwiftObjectHeader : BridgedSwiftObject {}

    typedef struct BridgedSwiftObject * _Nullable OptionalSwiftObject; typedef struct { OptionalSwiftObject obj; } OptionalBridgedInstruction; 39
  40. ࠶ܝ final public class BasicBlock { public var instructions: List<Instruction>

    { List(startAt: SILBasicBlock_firstInst(bridged).instruction) } var bridged: BridgedBasicBlock { BridgedBasicBlock(obj: SwiftObject(self)) } } 40
  41. SwiftଆͰܕ෇͖ͰΞΫηε extension BridgedInstruction { public var instruction: Instruction { obj.getAs(Instruction.self)

    } public func getAs<T: Instruction>(_ instType: T.Type) -> T { obj.getAs(T.self) } } extension OptionalBridgedInstruction { var instruction: Instruction? { obj.getAs(Instruction.self) } } 41
  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
  43. จࣈྻ // C++ typedef struct { const unsigned char *

    _Nullable data; size_t length; } BridgedStringRef; // Swift extension BridgedStringRef { public var string: String { let buffer = UnsafeBufferPointer<UInt8>(start: data, count: Int(length)) return String(decoding: buffer, as: UTF8.self) } func takeString() -> String { let str = string freeBridgedStringRef(self) return str } } 43
  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
  45. public protocol Value {} extension Value { public var description:

    String { SILNode_debugDescription(bridgedNode).takeString() } var bridgedNode: BridgedNode { BridgedNode(obj: SwiftObject(self as AnyObject)) } } 45
  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
  47. ·ͱΊ • C++ଆͰSwiftͷΦϒδΣΫτϔομΛܧঝͤ͞Δ • ϝλλΠϓ͸ؤுͬͯSwift͔Β౉͢ • ࢀরΧ΢ϯλΛimmortalϞʔυʹͯ͠C++ͰϝϞϦ؅ཧ͢Δ • Cߏ଄ମΛ࢖ͬͯϒϦοδΠϯλʔϑΣʔεʹܕΛ͚ͭΔ 47