slidenumber: true autoscale: true
- League of Legends 楽しい!
-
Interoperability between Swift and C++1
-
待てない!
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.
-
あるオブジェクトをメモリ表現の変換無しでそのまま複数の言語から使う
-
CFDataRef
(C言語) とNSData
(Objective-C) -
NSData
(Objective-C) とData
(Swift) -
[NEW] C++ と Swift
class ValueBase :
public SILNode,
public SILAllocated<ValueBase>
{
SILType Type;
inline use_range getUses() const;
}
- ベースクラス
- SILNode: グラフ機能 + Swift Interop
- SILAllocated: Bumpアロケータ
class SILNode :
public SwiftObjectHeader {}
- SwiftObjectHeader: 後述
class SILType {
using ValueType = llvm::PointerIntPair<TypeBase *, 2, unsigned>;
ValueType value;
}
- TypeBaseはASTステージの型
class SILValue {
ValueBase *Value;
SILValue(const ValueBase *V = nullptr)
}
- Existential container
class SILArgument : public ValueBase {}
- BasicBlockの引数
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を親に持つ。
class SILBasicBlock :
public llvm::ilist_node<SILBasicBlock>,
public SILAllocated<SILBasicBlock>,
public SwiftObjectHeader {
using InstListType = llvm::iplist<SILInstruction>;
TinyPtrVector<SILArgument *> ArgumentList;
InstListType InstList;
}
public protocol Value : AnyObject {
var uses: UseList { get }
var type: Type { get }
}
public struct Type {
var bridged: BridgedType
}
public class Argument : Value {}
public class Instruction {}
public class SingleValueInstruction : Instruction, Value {}
public final class MultipleValueInstructionResult : Value {}
final public class BasicBlock {}
-
Swiftのclassのインスタンスは先頭にisaポインタと参照カウンタを持つ
-
C++側で互換性のある構造を持たせる
struct BridgedSwiftObject {
SwiftMetatype metatype;
int64_t refCounts;
}
struct SwiftObjectHeader : BridgedSwiftObject {}
- さらにこのフィールドをなんとかする
class SILNode: SwiftObjectHeader {}
class ValueBase: SILNode {}
class SILArgument: ValueBase {}
class SingleValueInstruction: ValueBase, SILInstruction {}
class NonSingleValueInstruction: SILInstruction, SILNode {}
class SILBasicBlock: SwiftObjectHeader {}
- 何かしらSwiftObjectHeaderにたどり着く
参照カウントの特定のビットを立てておくと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
*/
}
struct SwiftObjectHeader : BridgedSwiftObject {
SwiftObjectHeader(SwiftMetatype metatype) {
this->metatype = metatype;
this->refCounts = ~(uint64_t)0;
}
}
事前に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)
...
}
// 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;
}
直接渡すかテーブルにしまう。
class SILBasicBlock {
static SwiftMetatype registeredMetatype;
static void registerBridgedMetatype(SwiftMetatype metatype) {
registeredMetatype = metatype;
}
}
SILBasicBlock::SILBasicBlock() :
SwiftObjectHeader(registeredMetatype), Parent(nullptr) {}
class SILNode {
SILNode(SILNodeKind kind) : SwiftObjectHeader(getSILNodeMetatype(kind)) {}
}
SwiftMetatype SILNode::getSILNodeMetatype(SILNodeKind kind) {
SwiftMetatype metatype = nodeMetatypes[(unsigned)kind];
return metatype;
}
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;
struct BridgedSwiftObject {
SwiftMetatype metatype;
int64_t refCounts;
};
typedef struct BridgedSwiftObject * _Nonnull SwiftObject;
typedef struct BridgedSwiftObject * _Nullable OptionalSwiftObject;
typedef struct {
OptionalSwiftObject obj;
} OptionalBridgedInstruction;
final public class BasicBlock {
public var instructions: List<Instruction> {
List(startAt: SILBasicBlock_firstInst(bridged).instruction)
}
var bridged: BridgedBasicBlock { BridgedBasicBlock(obj: SwiftObject(self)) }
}
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()
}
}
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
}
}
再掲
// 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;
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);
}
再掲
class SILNode: SwiftObjectHeader {}
struct SwiftObjectHeader : BridgedSwiftObject {}
typedef struct BridgedSwiftObject * _Nullable OptionalSwiftObject;
typedef struct {
OptionalSwiftObject obj;
} OptionalBridgedInstruction;
再掲
final public class BasicBlock {
public var instructions: List<Instruction> {
List(startAt: SILBasicBlock_firstInst(bridged).instruction)
}
var bridged: BridgedBasicBlock { BridgedBasicBlock(obj: SwiftObject(self)) }
}
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) }
}
// 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;
// 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
}
}
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);
}
public protocol Value {}
extension Value {
public var description: String {
SILNode_debugDescription(bridgedNode).takeString()
}
var bridgedNode: BridgedNode {
BridgedNode(obj: SwiftObject(self as AnyObject))
}
}
専用の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)
}
}
}
}
- C++側でSwiftのオブジェクトヘッダを継承させる
- メタタイプは頑張ってSwiftから渡す
- 参照カウンタをimmortalモードにしてC++でメモリ管理する
- C構造体を使ってブリッジインターフェースに型をつける