Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

C++ͰͷSILͷܕ 7

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

SwiftͰͷSILͷܕ 15

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

Type public struct Type { var bridged: BridgedType } 17

Slide 18

Slide 18 text

Argument public class Argument : Value {} 18

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

BasicBlock final public class BasicBlock {} 20

Slide 21

Slide 21 text

Bridgingͷํ๏ 21

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

ࢀরΧ΢ϯλΛ௵͢ ࢀরΧ΢ϯτͷಛఆͷϏοτΛཱ͓ͯͯ͘ͱ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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

ϝλΫϥεΛ஫ೖ͢Δ ࣄલʹ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

Slide 27

Slide 27 text

// 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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Bridgingͷ࣮૷ 30

Slide 31

Slide 31 text

ϒϦοδ༻ͷܕΛఆٛ͢Δ 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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

࠶ܝ // 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

Slide 38

Slide 38 text

ॲཧͷϒϦοδ 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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

จࣈྻ // 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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

Πϯελϯεੜ੒ ઐ༻ͷ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

Slide 47

Slide 47 text

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