Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ 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)
}
} else if instruction is OpenExistentialAddrInst {
// okay in embedded with exitentials
} else {
// not supported even in embedded with exitentials
throw Diagnostic(.embedded_swift_existential_type, instruction.operands[0].value.type, at: instruction.location)
}

case let aeb as AllocExistentialBoxInst:
Expand All @@ -116,7 +125,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,
Expand Down Expand Up @@ -221,7 +243,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)
}

Expand Down
28 changes: 28 additions & 0 deletions SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,22 @@ 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
}

if !destType.isStruct && !destType.isClass && !destType.isEnum && !destType.isTuple {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a comment listing which types are not supported here? Function types? Also why aren't they supported when tuples are supported? Tuples can have arbitrary element types, including function types, in them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are not tests yet so only the types listed here are "supported" (at this time).

When I add metadata generation for function types they will be added.

The intend is to gradually add more types.

Foreign types don't work either. And the two types I have mentioned I am sure are not a complete list of unsupported types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A tuple of function types works because we know how to generate metadata for tuples.

The code to generate function type metadata I have not added yet.

return false
}

return true
}

extension CheckedCastAddrBranchInst {
var dynamicCastResult: Bool? {
switch classifyDynamicCastBridged(bridged) {
Expand All @@ -1010,6 +1026,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 {
Expand Down
6 changes: 5 additions & 1 deletion SwiftCompilerSources/Sources/SIL/Instruction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand Down
1 change: 1 addition & 0 deletions include/swift/SIL/SILBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,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;
Expand Down
3 changes: 3 additions & 0 deletions include/swift/SIL/SILBridgingImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,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;
Expand Down
9 changes: 8 additions & 1 deletion lib/IRGen/GenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand Down
88 changes: 77 additions & 11 deletions stdlib/public/SwiftShims/swift/shims/EmbeddedShims.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
107 changes: 107 additions & 0 deletions stdlib/public/core/EmbeddedRuntime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading