mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Merge pull request #85602 from aschwaighofer/wip_embedded_exit_cast
[embedded] Implement swift_dynamicCast suport for casts from existential to concrete type
This commit is contained in:
@@ -90,6 +90,10 @@ private struct FunctionChecker {
|
||||
is ExistentialMetatypeInst:
|
||||
if !context.options.enableEmbeddedSwiftExistentials {
|
||||
throw Diagnostic(.embedded_swift_existential_type, instruction.operands[0].value.type, at: instruction.location)
|
||||
} else if let ie = instruction as? InitExistentialAddrInst {
|
||||
for conf in ie.conformances {
|
||||
try checkConformance(conf, location: ie.location)
|
||||
}
|
||||
}
|
||||
|
||||
case let aeb as AllocExistentialBoxInst:
|
||||
@@ -116,7 +120,20 @@ private struct FunctionChecker {
|
||||
|
||||
case is CheckedCastAddrBranchInst,
|
||||
is UnconditionalCheckedCastAddrInst:
|
||||
throw Diagnostic(.embedded_swift_dynamic_cast, at: instruction.location)
|
||||
if !context.options.enableEmbeddedSwiftExistentials {
|
||||
throw Diagnostic(.embedded_swift_dynamic_cast, at: instruction.location)
|
||||
} else {
|
||||
if let checkedCast = instruction as? CheckedCastAddrBranchInst {
|
||||
if !checkedCast.supportedInEmbeddedSwift {
|
||||
throw Diagnostic(.embedded_swift_dynamic_cast, at: instruction.location)
|
||||
}
|
||||
} else {
|
||||
let checkedCast = instruction as! UnconditionalCheckedCastAddrInst
|
||||
if !checkedCast.supportedInEmbeddedSwift {
|
||||
throw Diagnostic(.embedded_swift_dynamic_cast, at: instruction.location)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case let abi as AllocBoxInst:
|
||||
// It needs a bit of work to support alloc_box of generic non-copyable structs/enums with deinit,
|
||||
@@ -221,7 +238,8 @@ private struct FunctionChecker {
|
||||
else {
|
||||
return
|
||||
}
|
||||
if !conformance.protocol.requiresClass {
|
||||
if !context.options.enableEmbeddedSwiftExistentials &&
|
||||
!conformance.protocol.requiresClass {
|
||||
throw Diagnostic(.embedded_swift_existential_protocol, conformance.protocol.name, at: location)
|
||||
}
|
||||
|
||||
|
||||
@@ -1001,6 +1001,23 @@ func canDynamicallyCast(from sourceType: CanonicalType, to destType: CanonicalTy
|
||||
}
|
||||
}
|
||||
|
||||
func isCastSupportedInEmbeddedSwift(from sourceType: Type,
|
||||
to destType: Type ) -> Bool {
|
||||
if !sourceType.isExistential {
|
||||
return false
|
||||
}
|
||||
if destType.hasArchetype {
|
||||
return false
|
||||
}
|
||||
|
||||
// Tuple?
|
||||
if !destType.isStruct && !destType.isClass && !destType.isEnum {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
extension CheckedCastAddrBranchInst {
|
||||
var dynamicCastResult: Bool? {
|
||||
switch classifyDynamicCastBridged(bridged) {
|
||||
@@ -1010,6 +1027,18 @@ extension CheckedCastAddrBranchInst {
|
||||
default: fatalError("unknown result from classifyDynamicCastBridged")
|
||||
}
|
||||
}
|
||||
|
||||
var supportedInEmbeddedSwift: Bool {
|
||||
return isCastSupportedInEmbeddedSwift(from: source.type,
|
||||
to: destination.type)
|
||||
}
|
||||
}
|
||||
|
||||
extension UnconditionalCheckedCastAddrInst {
|
||||
var supportedInEmbeddedSwift: Bool {
|
||||
return isCastSupportedInEmbeddedSwift(from: source.type,
|
||||
to: destination.type)
|
||||
}
|
||||
}
|
||||
|
||||
extension CopyAddrInst {
|
||||
|
||||
@@ -879,7 +879,11 @@ final public
|
||||
class OpenExistentialValueInst : SingleValueInstruction, UnaryInstruction {}
|
||||
|
||||
final public
|
||||
class InitExistentialAddrInst : SingleValueInstruction, UnaryInstruction {}
|
||||
class InitExistentialAddrInst : SingleValueInstruction, UnaryInstruction {
|
||||
public var conformances: ConformanceArray {
|
||||
ConformanceArray(bridged: bridged.InitExistentialAddrInst_getConformances())
|
||||
}
|
||||
}
|
||||
|
||||
final public
|
||||
class DeinitExistentialAddrInst : Instruction, UnaryInstruction {}
|
||||
|
||||
@@ -808,6 +808,7 @@ struct BridgedInstruction {
|
||||
BRIDGED_INLINE bool IndexAddrInst_needsStackProtection() const;
|
||||
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedConformanceArray InitExistentialRefInst_getConformances() const;
|
||||
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedCanType InitExistentialRefInst_getFormalConcreteType() const;
|
||||
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedConformanceArray InitExistentialAddrInst_getConformances() const;
|
||||
BRIDGED_INLINE bool OpenExistentialAddr_isImmutable() const;
|
||||
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedGlobalVar GlobalAccessInst_getGlobal() const;
|
||||
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedGlobalVar AllocGlobalInst_getGlobal() const;
|
||||
|
||||
@@ -1288,6 +1288,9 @@ BridgedCanType BridgedInstruction::InitExistentialRefInst_getFormalConcreteType(
|
||||
return getAs<swift::InitExistentialRefInst>()->getFormalConcreteType();
|
||||
}
|
||||
|
||||
BridgedConformanceArray BridgedInstruction::InitExistentialAddrInst_getConformances() const {
|
||||
return {getAs<swift::InitExistentialAddrInst>()->getConformances()};
|
||||
}
|
||||
bool BridgedInstruction::OpenExistentialAddr_isImmutable() const {
|
||||
switch (getAs<swift::OpenExistentialAddrInst>()->getAccessKind()) {
|
||||
case swift::OpenedExistentialAccess::Immutable: return true;
|
||||
|
||||
@@ -85,7 +85,14 @@ llvm::Value *irgen::emitCheckedCast(IRGenFunction &IGF,
|
||||
src = IGF.Builder.CreateElementBitCast(src, IGF.IGM.OpaqueTy);
|
||||
|
||||
// Load type metadata for the source's static type and the target type.
|
||||
llvm::Value *srcMetadata = IGF.emitTypeMetadataRef(srcType);
|
||||
llvm::Value *srcMetadata = nullptr;
|
||||
|
||||
// Embedded swift currently only supports existential -> concrete type casts.
|
||||
if (IGF.IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
srcMetadata = llvm::ConstantPointerNull::get(IGF.IGM.TypeMetadataPtrTy);
|
||||
} else {
|
||||
srcMetadata = IGF.emitTypeMetadataRef(srcType);
|
||||
}
|
||||
llvm::Value *targetMetadata = IGF.emitTypeMetadataRef(targetType);
|
||||
|
||||
llvm::Value *args[] = {
|
||||
|
||||
@@ -96,7 +96,11 @@ typedef struct {
|
||||
void* (*initializeWithCopyFn)(void*, void*, void*);
|
||||
#endif
|
||||
void *assignWithCopyFn;
|
||||
void *initializeWithTakeFn;
|
||||
#if __has_feature(ptrauth_calls)
|
||||
void* (* __ptrauth(0, 1, 0x48d8) initializeWithTakeFn)(void*, void*, void*);
|
||||
#else
|
||||
void* (*initializeWithTakeFn)(void *, void*, void*);
|
||||
#endif
|
||||
void *assignWithTakeFn;
|
||||
void *getEnumTagSinglePayloadFn;
|
||||
void *storeEnumTagSinglePayload;
|
||||
@@ -113,43 +117,105 @@ typedef struct {
|
||||
#endif
|
||||
} EmbeddedMetaDataPrefix;
|
||||
|
||||
typedef enum {
|
||||
AlignmentMask = 0x000000FF,
|
||||
IsNonInline = 0x00020000,
|
||||
} ValueWitnessTableFlags;
|
||||
|
||||
static inline
|
||||
EmbeddedMetaDataPrefix *_swift_embedded_get_full_metadata(void *metadata) {
|
||||
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
|
||||
return fullmeta;
|
||||
}
|
||||
|
||||
static inline __swift_size_t
|
||||
_swift_embedded_metadata_get_size(void *metadata) {
|
||||
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
|
||||
EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata);
|
||||
return fullmeta->vwt->size;
|
||||
}
|
||||
|
||||
static inline __swift_size_t
|
||||
_swift_embedded_metadata_get_align_mask_impl(EmbeddedMetaDataPrefix *fullMetadata) {
|
||||
unsigned flags = fullMetadata->vwt->flags;
|
||||
unsigned embeddedValueWitnessTableFlagsMask = 0xFF;
|
||||
|
||||
return flags & embeddedValueWitnessTableFlagsMask;
|
||||
ValueWitnessTableFlags alignMask = AlignmentMask;
|
||||
return flags & alignMask;
|
||||
}
|
||||
|
||||
static inline __swift_size_t
|
||||
_swift_embedded_metadata_get_align_mask(void *metadata) {
|
||||
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
|
||||
EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata);
|
||||
return _swift_embedded_metadata_get_align_mask_impl(fullmeta);
|
||||
}
|
||||
|
||||
static inline void
|
||||
_swift_embedded_invoke_box_destroy(void *object) {
|
||||
void *metadata = ((EmbeddedHeapObject *)object)->metadata;
|
||||
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
|
||||
static inline void *
|
||||
_swift_embedded_box_project(void *object, EmbeddedMetaDataPrefix *fullmeta) {
|
||||
__swift_size_t alignMask = _swift_embedded_metadata_get_align_mask_impl(fullmeta);
|
||||
__swift_size_t headerSize = sizeof(void*) + sizeof(__swift_size_t);
|
||||
__swift_size_t startOfBoxedValue = (headerSize + alignMask) & ~alignMask;
|
||||
void *addrInBox = (void *)(((unsigned char *)object) + startOfBoxedValue);
|
||||
return addrInBox;
|
||||
}
|
||||
static inline void
|
||||
_swift_embedded_invoke_box_destroy(void *object) {
|
||||
void *metadata = ((EmbeddedHeapObject *)object)->metadata;
|
||||
EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata);
|
||||
void *addrInBox = _swift_embedded_box_project(object, fullmeta);
|
||||
fullmeta->vwt->destroyFn(addrInBox, metadata);
|
||||
}
|
||||
|
||||
static inline void
|
||||
_swift_embedded_initialize_box(void *metadata, void *newObjectAddr, void *oldObjectAddr) {
|
||||
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
|
||||
EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata);
|
||||
fullmeta->vwt->initializeWithCopyFn(newObjectAddr, oldObjectAddr, metadata);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
void *inlineBuffer[3];
|
||||
void *metadata;
|
||||
} ExistentialValue;
|
||||
|
||||
static inline void
|
||||
_swift_embedded_existential_destroy(void *exist) {
|
||||
ExistentialValue* existVal = (ExistentialValue*)exist;
|
||||
void *metadata = existVal->metadata;
|
||||
EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata);
|
||||
ValueWitnessTableFlags isNonInlineMask = IsNonInline;
|
||||
if (fullmeta->vwt->flags & IsNonInline) {
|
||||
void *addrInBox = _swift_embedded_box_project(existVal->inlineBuffer[0], fullmeta);
|
||||
fullmeta->vwt->destroyFn(addrInBox, metadata);
|
||||
} else {
|
||||
fullmeta->vwt->destroyFn(&(existVal->inlineBuffer[0]), metadata);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
_swift_embedded_existential_init_with_take(void *dst, void *srcExist) {
|
||||
ExistentialValue* existVal = (ExistentialValue*)srcExist;
|
||||
void *metadata = existVal->metadata;
|
||||
EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata);
|
||||
ValueWitnessTableFlags isNonInlineMask = IsNonInline;
|
||||
if (fullmeta->vwt->flags & IsNonInline) {
|
||||
void *addrInBox = _swift_embedded_box_project(existVal->inlineBuffer[0], fullmeta);
|
||||
fullmeta->vwt->initializeWithTakeFn(dst, addrInBox, metadata);
|
||||
} else {
|
||||
fullmeta->vwt->initializeWithTakeFn(dst, &(existVal->inlineBuffer[0]), metadata);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
_swift_embedded_existential_init_with_copy(void *dst, void *srcExist) {
|
||||
ExistentialValue* existVal = (ExistentialValue*)srcExist;
|
||||
void *metadata = existVal->metadata;
|
||||
EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata);
|
||||
ValueWitnessTableFlags isNonInlineMask = IsNonInline;
|
||||
if (fullmeta->vwt->flags & IsNonInline) {
|
||||
void *addrInBox = _swift_embedded_box_project(existVal->inlineBuffer[0], fullmeta);
|
||||
fullmeta->vwt->initializeWithCopyFn(dst, addrInBox, metadata);
|
||||
} else {
|
||||
fullmeta->vwt->initializeWithCopyFn(dst, &(existVal->inlineBuffer[0]), metadata);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@@ -315,6 +315,113 @@ public func swifft_makeBoxUnique(buffer: Builtin.RawPointer, metadata: Builtin.R
|
||||
return (box, oldObjectAddr._rawValue)
|
||||
}
|
||||
}
|
||||
/// Dynamic cast support
|
||||
/// Only supports existential container to concrete casts.
|
||||
struct DynamicCastFlags {
|
||||
static let Default = UInt(bitPattern: 0x0)
|
||||
static let Unconditional = UInt(bitPattern: 0x1)
|
||||
static let TakeOnSuccess = UInt(bitPattern: 0x2)
|
||||
static let DestroyOnFailure = UInt(bitPattern: 0x4)
|
||||
}
|
||||
|
||||
struct MetadataKind {
|
||||
static let LastEnumerated = UInt(bitPattern: 0x7FF)
|
||||
}
|
||||
|
||||
func projectExistentialMetadata(
|
||||
_ exist: Builtin.RawPointer
|
||||
) -> Builtin.RawPointer {
|
||||
|
||||
let offset = (3 * MemoryLayout<Builtin.RawPointer>.size)
|
||||
let addrOfMetadata = unsafe UnsafeMutableRawPointer(exist) + offset
|
||||
let metadataPtrAddr = unsafe addrOfMetadata.bindMemory(to: Builtin.RawPointer.self, capacity: 1)
|
||||
return unsafe metadataPtrAddr.pointee
|
||||
}
|
||||
|
||||
func isClassMetadata(_ metadata: Builtin.RawPointer) -> Bool {
|
||||
let addrOfMetadataKind = unsafe UnsafePointer<UInt>(metadata)
|
||||
let kind = unsafe addrOfMetadataKind.pointee
|
||||
return kind == 0 || kind > MetadataKind.LastEnumerated
|
||||
}
|
||||
|
||||
func projectHeapObject(_ exist: Builtin.RawPointer) -> UnsafeMutableRawPointer{
|
||||
let addrOfHeapObject = unsafe UnsafePointer<UnsafeMutableRawPointer>(exist)
|
||||
return unsafe addrOfHeapObject.pointee
|
||||
}
|
||||
|
||||
@c
|
||||
public func swift_dynamicCast(
|
||||
_ dest: Builtin.RawPointer, /* points to a concrete type */
|
||||
_ src: Builtin.RawPointer, /* points to an existential */
|
||||
_ srcMetadata: Builtin.RawPointer, /* always nullptr */
|
||||
_ dstMetadata: Builtin.RawPointer,
|
||||
_ flags: UInt
|
||||
) -> Bool {
|
||||
|
||||
let isUnconditionalCast : Bool = (flags & DynamicCastFlags.Unconditional) != 0
|
||||
let isTakeOnSuccess : Bool = (flags & DynamicCastFlags.TakeOnSuccess) != 0
|
||||
let isDestroyOnFailure : Bool =
|
||||
(flags & DynamicCastFlags.DestroyOnFailure) != 0
|
||||
|
||||
let srcMetadata = projectExistentialMetadata(src)
|
||||
|
||||
let isClass = isClassMetadata(dstMetadata)
|
||||
|
||||
if isClass {
|
||||
var success = false
|
||||
let obj = unsafe projectHeapObject(src)
|
||||
if isClassMetadata(srcMetadata) {
|
||||
if srcMetadata != dstMetadata {
|
||||
// check parent chain
|
||||
success = unsafe swift_dynamicCastClass(object: obj, targetMetadata: UnsafeMutableRawPointer(dstMetadata)) != nil
|
||||
} else {
|
||||
success = true
|
||||
}
|
||||
}
|
||||
if !success {
|
||||
if isDestroyOnFailure {
|
||||
unsafe _swift_embedded_existential_destroy(UnsafeMutableRawPointer(src))
|
||||
}
|
||||
|
||||
if isUnconditionalCast {
|
||||
fatalError("failed cast")
|
||||
}
|
||||
return false
|
||||
}
|
||||
if isTakeOnSuccess {
|
||||
let dst = unsafe UnsafeMutablePointer<UnsafeMutableRawPointer>(dest)
|
||||
unsafe dst.pointee = obj
|
||||
|
||||
} else {
|
||||
let dst = unsafe UnsafeMutablePointer<UnsafeMutableRawPointer>(dest)
|
||||
unsafe dst.pointee = obj
|
||||
swift_retain(object: obj._rawValue)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// destintation type is not a class. Test exact match.
|
||||
let success = srcMetadata == dstMetadata
|
||||
if !success {
|
||||
if isDestroyOnFailure {
|
||||
unsafe _swift_embedded_existential_destroy(UnsafeMutableRawPointer(src))
|
||||
}
|
||||
|
||||
if isUnconditionalCast {
|
||||
fatalError("failed cast")
|
||||
}
|
||||
return false
|
||||
}
|
||||
if isTakeOnSuccess {
|
||||
// take from an existential to a concrete type
|
||||
unsafe _swift_embedded_existential_init_with_take(
|
||||
UnsafeMutableRawPointer(dest), UnsafeMutableRawPointer(src))
|
||||
} else {
|
||||
// copy from an existential to a concrete type
|
||||
unsafe _swift_embedded_existential_init_with_copy(
|
||||
UnsafeMutableRawPointer(dest), UnsafeMutableRawPointer(src))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/// Refcounting
|
||||
|
||||
|
||||
@@ -190,6 +190,32 @@ func test4(_ p: any WithAssoc) {
|
||||
c.printValue()
|
||||
}
|
||||
|
||||
func test5(_ p: any Any) {
|
||||
print("test any as? MyStruct")
|
||||
if let c = p as? MyStruct {
|
||||
print("success")
|
||||
c.a()
|
||||
} else {
|
||||
print("cast failed")
|
||||
}
|
||||
}
|
||||
|
||||
func test6(_ p: any Any) {
|
||||
print("test any as? LargeMyStruct")
|
||||
if let c = p as? LargeMyStruct {
|
||||
print("success")
|
||||
c.a()
|
||||
} else {
|
||||
print("cast failed")
|
||||
}
|
||||
}
|
||||
|
||||
func test7(_ p: any Any) {
|
||||
print("test any as! LargeMyStruct")
|
||||
let c = p as! LargeMyStruct
|
||||
c.a()
|
||||
}
|
||||
|
||||
@main
|
||||
struct Main {
|
||||
static func main() {
|
||||
@@ -220,6 +246,48 @@ struct Main {
|
||||
// OUTPUT: my value of LargeMyStruct (mutating expect 10): 10
|
||||
// OUTPUT: my value of LargeMyStruct: 5
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT-NOT: deinit called
|
||||
|
||||
test5(MyStruct())
|
||||
// OUTPUT: test any as? MyStruct
|
||||
// OUTPUT: success
|
||||
// OUTPUT: a MyStruct 5
|
||||
test5(GC<Int>())
|
||||
// OUTPUT: test any as? MyStruct
|
||||
// OUTPUT: cast failed
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT-NOT: deinit called
|
||||
test5(LargeMyStruct())
|
||||
// OUTPUT: test any as? MyStruct
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT: cast failed
|
||||
// OUTPUT-NOT: deinit called
|
||||
test5(GenericStructWithClass<Int>())
|
||||
// OUTPUT: test any as? MyStruct
|
||||
// OUTPUT: cast failed
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT-NOT: deinit called
|
||||
test6(MyStruct())
|
||||
// OUTPUT: test any as? LargeMyStruct
|
||||
// OUTPUT: cast failed
|
||||
// OUTPUT-NOT: deinit called
|
||||
test6(LargeMyStruct())
|
||||
// OUTPUT: test any as? LargeMyStruct
|
||||
// OUTPUT: success
|
||||
// OUTPUT: a LargeMyStruct 5
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT-NOT: deinit called
|
||||
test6(GenericStructWithClass<Int>())
|
||||
// OUTPUT: test any as? LargeMyStruct
|
||||
// OUTPUT: cast failed
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT-NOT: deinit called
|
||||
test7(LargeMyStruct())
|
||||
// OUTPUT: test any as! LargeMyStruct
|
||||
// OUTPUT: a LargeMyStruct 5
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT-NOT: deinit called
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user