Share
Embed
今から使えるSwiftとC++の新しいinterop手法
omochimetaru
July 30, 2021
Transcript
47 slides
-
ຊ໋ • Interoperability between Swift and C++1 • ͯͳ͍ʂ 1
https://github.com/apple/swift/blob/main/docs/CppInteroperabilityManifesto.md 4 -
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 -
Toll-free bridge • ͋ΔΦϒδΣΫτΛϝϞϦදݱͷมແ͠Ͱͦͷ··ෳͷ ݴޠ͔Β͏ • CFDataRef (Cݴޠ) ͱ NSData
(Objective-C) • NSData (Objective-C) ͱ Data (Swift) • [NEW] C++ ͱ Swift 6 -
ValueBase class ValueBase : public SILNode, public SILAllocated<ValueBase> { SILType
Type; inline use_range getUses() const; } • ϕʔεΫϥε • SILNode: άϥϑػೳ + Swift Interop • SILAllocated: BumpΞϩέʔλ 8 -
SILType class SILType { using ValueType = llvm::PointerIntPair<TypeBase *, 2,
unsigned>; ValueType value; } • TypeBaseASTεςʔδͷܕ 10 -
SILValue class SILValue { ValueBase *Value; SILValue(const ValueBase *V =
nullptr) } • Existential container 11 -
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 -
SILBasicBlock class SILBasicBlock : public llvm::ilist_node<SILBasicBlock>, public SILAllocated<SILBasicBlock>, public SwiftObjectHeader
{ using InstListType = llvm::iplist<SILInstruction>; TinyPtrVector<SILArgument *> ArgumentList; InstListType InstList; } 14 -
Value public protocol Value : AnyObject { var uses: UseList
{ get } var type: Type { get } } 16 -
Instruction public class Instruction {} public class SingleValueInstruction : Instruction,
Value {} public final class MultipleValueInstructionResult : Value {} 19 -
ΫϥεͷϨΠΞτޓΛऔΔ • SwiftͷclassͷΠϯελϯεઌ಄ʹisaϙΠϯλͱࢀরΧϯλΛ࣋ͭ • C++ଆͰޓੑͷ͋ΔߏΛ࣋ͨͤΔ struct BridgedSwiftObject { SwiftMetatype metatype;
int64_t refCounts; } struct SwiftObjectHeader : BridgedSwiftObject {} • ͞Βʹ͜ͷϑΟʔϧυΛͳΜͱ͔͢Δ 22 -
SwiftObjectHeaderͷೖ class SILNode: SwiftObjectHeader {} class ValueBase: SILNode {} class
SILArgument: ValueBase {} class SingleValueInstruction: ValueBase, SILInstruction {} class NonSingleValueInstruction: SILInstruction, SILNode {} class SILBasicBlock: SwiftObjectHeader {} • Կ͔͠ΒSwiftObjectHeaderʹͨͲΓண͘ 23 -
ࢀরΧϯλΛ௵͢ ࢀরΧϯτͷಛఆͷϏοτΛཱ͓ͯͯ͘ͱ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 -
struct SwiftObjectHeader : BridgedSwiftObject { SwiftObjectHeader(SwiftMetatype metatype) { this->metatype =
metatype; this->refCounts = ~(uint64_t)0; } } 25 -
ϝλΫϥεΛೖ͢Δ ࣄલʹ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 -
// 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 -
class SILBasicBlock { static SwiftMetatype registeredMetatype; static void registerBridgedMetatype(SwiftMetatype metatype)
{ registeredMetatype = metatype; } } SILBasicBlock::SILBasicBlock() : SwiftObjectHeader(registeredMetatype), Parent(nullptr) {} 28 -
class SILNode { SILNode(SILNodeKind kind) : SwiftObjectHeader(getSILNodeMetatype(kind)) {} } SwiftMetatype
SILNode::getSILNodeMetatype(SILNodeKind kind) { SwiftMetatype metatype = nodeMetatypes[(unsigned)kind]; return metatype; } 29 -
ϒϦοδ༻ͷܕΛఆٛ͢Δ 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 -
struct BridgedSwiftObject { SwiftMetatype metatype; int64_t refCounts; }; typedef struct
BridgedSwiftObject * _Nonnull SwiftObject; 32 -
NullabilityͷϒϦοδ typedef struct BridgedSwiftObject * _Nullable OptionalSwiftObject; typedef struct {
OptionalSwiftObject obj; } OptionalBridgedInstruction; 33 -
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 -
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 -
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 -
࠶ܝ // 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 -
ॲཧͷϒϦοδ 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 -
࠶ܝ class SILNode: SwiftObjectHeader {} struct SwiftObjectHeader : BridgedSwiftObject {}
typedef struct BridgedSwiftObject * _Nullable OptionalSwiftObject; typedef struct { OptionalSwiftObject obj; } OptionalBridgedInstruction; 39 -
࠶ܝ final public class BasicBlock { public var instructions: List<Instruction>
{ List(startAt: SILBasicBlock_firstInst(bridged).instruction) } var bridged: BridgedBasicBlock { BridgedBasicBlock(obj: SwiftObject(self)) } } 40 -
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 -
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 -
จࣈྻ // 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 -
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 -
public protocol Value {} extension Value { public var description:
String { SILNode_debugDescription(bridgedNode).takeString() } var bridgedNode: BridgedNode { BridgedNode(obj: SwiftObject(self as AnyObject)) } } 45 -
Πϯελϯεੜ ઐ༻ͷ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